Skip to content

Commit 541d0a3

Browse files
committed
feat(pages): add popup menus to page items
1 parent fa8757b commit 541d0a3

File tree

16 files changed

+279
-184
lines changed

16 files changed

+279
-184
lines changed

apps/app-server/src/trpc/api/users/pages/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import { getCurrentPathProcedure } from './get-current-path';
77
import { getGroupIdsProcedure } from './get-group-ids';
88
import { getStartingPageIdProcedure } from './get-starting-page-id';
99
import { notificationsRouter } from './notifications';
10-
import { removeFavoritePageProcedure } from './remove-favorite-page';
11-
import { removeRecentPageProcedure } from './remove-recent-page';
10+
import { removeFavoritePagesProcedure } from './remove-favorite-pages';
11+
import { removeRecentPagesProcedure } from './remove-recent-pages';
1212
import { setEncryptedDefaultArrowProcedure } from './set-encrypted-default-arrow';
1313
import { setEncryptedDefaultNoteProcedure } from './set-encrypted-default-note';
1414

@@ -18,11 +18,11 @@ export const pagesRouter = trpc.router({
1818
getStartingPageId: getStartingPageIdProcedure(),
1919
getCurrentPath: getCurrentPathProcedure(),
2020

21-
removeRecentPage: removeRecentPageProcedure(),
21+
removeRecentPages: removeRecentPagesProcedure(),
2222
clearRecentPages: clearRecentPagesProcedure(),
2323

2424
addFavoritePages: addFavoritePagesProcedure(),
25-
removeFavoritePage: removeFavoritePageProcedure(),
25+
removeFavoritePages: removeFavoritePagesProcedure(),
2626
clearFavoritePages: clearFavoritePagesProcedure(),
2727

2828
setEncryptedDefaultNote: setEncryptedDefaultNoteProcedure(),

apps/app-server/src/trpc/api/users/pages/remove-favorite-page.ts renamed to apps/app-server/src/trpc/api/users/pages/remove-favorite-pages.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ import { z } from 'zod';
88

99
const baseProcedure = authProcedure.input(
1010
z.object({
11-
pageId: z.string().refine(isNanoID),
11+
pageIds: z.string().refine(isNanoID).array(),
1212
}),
1313
);
1414

15-
export const removeFavoritePageProcedure = once(() =>
15+
export const removeFavoritePagesProcedure = once(() =>
1616
baseProcedure.mutation(removeFavoritePage),
1717
);
1818

@@ -35,7 +35,7 @@ export async function removeFavoritePage({
3535

3636
const originalLength = favoritePageIds.length;
3737

38-
if (pull(favoritePageIds, input.pageId).length === originalLength) {
38+
if (pull(favoritePageIds, ...input.pageIds).length === originalLength) {
3939
throw new TRPCError({
4040
message: 'Favorite page not found.',
4141
code: 'NOT_FOUND',

apps/app-server/src/trpc/api/users/pages/remove-recent-page.ts renamed to apps/app-server/src/trpc/api/users/pages/remove-recent-pages.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ import { z } from 'zod';
88

99
const baseProcedure = authProcedure.input(
1010
z.object({
11-
pageId: z.string().refine(isNanoID),
11+
pageIds: z.string().refine(isNanoID).array(),
1212
}),
1313
);
1414

15-
export const removeRecentPageProcedure = once(() =>
16-
baseProcedure.mutation(removeRecentPage),
15+
export const removeRecentPagesProcedure = once(() =>
16+
baseProcedure.mutation(removeRecentPages),
1717
);
1818

19-
export async function removeRecentPage({
19+
export async function removeRecentPages({
2020
ctx,
2121
input,
2222
}: InferProcedureOpts<typeof baseProcedure>) {
@@ -35,7 +35,7 @@ export async function removeRecentPage({
3535

3636
const originalLength = recentPageIds.length;
3737

38-
if (pull(recentPageIds, input.pageId).length === originalLength) {
38+
if (pull(recentPageIds, ...input.pageIds).length === originalLength) {
3939
throw new TRPCError({
4040
message: 'Recent page not found.',
4141
code: 'NOT_FOUND',

apps/client/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,6 @@ yarn-error.log*
3131
*.ntvs*
3232
*.njsproj
3333
*.sln
34+
35+
/auto-imports.d.ts
36+
/components.d.ts
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { pluralS } from '@stdlib/misc';
2+
import { handleError } from 'src/code/utils/misc';
3+
4+
export async function addFavoritePages(pageIds: string[]) {
5+
try {
6+
await trpcClient.users.pages.addFavoritePages.mutate({
7+
pageIds: pageIds,
8+
});
9+
10+
$quasar().notify({
11+
message: `Page${pluralS(pageIds.length)} added to favorites.`,
12+
color: 'positive',
13+
});
14+
} catch (error) {
15+
handleError(error);
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { pluralS } from '@stdlib/misc';
2+
import { handleError } from 'src/code/utils/misc';
3+
4+
export async function removeFavoritePages(pageIds: string[]) {
5+
try {
6+
await trpcClient.users.pages.removeFavoritePages.mutate({
7+
pageIds: pageIds,
8+
});
9+
10+
$quasar().notify({
11+
message: `Page${pluralS(pageIds.length)} removed from favorites.`,
12+
color: 'negative',
13+
});
14+
} catch (error) {
15+
handleError(error);
16+
}
17+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { pull } from 'lodash';
2+
import { handleError } from 'src/code/utils/misc';
3+
4+
export async function removeRecentPages(pageIds: string[]) {
5+
try {
6+
await trpcClient.users.pages.removeRecentPages.mutate({
7+
pageIds: pageIds,
8+
});
9+
10+
internals.pages.recentPageIdsKeepOverride = true;
11+
internals.pages.react.recentPageIdsOverride =
12+
internals.pages.react.recentPageIds
13+
.filter((pageId) =>
14+
internals.realtime.globalCtx.hget('page', pageId, 'exists'),
15+
)
16+
.slice();
17+
pull(internals.pages.react.recentPageIdsOverride, ...pageIds);
18+
} catch (error) {
19+
handleError(error);
20+
}
21+
}

apps/client/src/components/PageItem.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
:page-id="pageId"
2121
:prefer="prefer"
2222
/>
23+
24+
<slot></slot>
2325
</q-item>
2426
</a>
2527
</template>
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
<template>
2+
<DeepBtn
3+
icon="mdi-dots-vertical"
4+
round
5+
flat
6+
style="min-width: 0; min-height: 0; width: 32px; height: 32px"
7+
@click.stop
8+
>
9+
<q-menu
10+
v-bind="menuProps"
11+
auto-close
12+
@before-show="beforeShow"
13+
>
14+
<q-list>
15+
<q-item
16+
v-if="internals.pages.react.recentPageIds.includes(pageId)"
17+
clickable
18+
v-ripple
19+
@click="removeRecentPages([pageId])"
20+
>
21+
<q-item-section avatar>
22+
<q-icon name="mdi-trash-can" />
23+
</q-item-section>
24+
25+
<q-item-section>
26+
<q-item-label>Remove from recent pages</q-item-label>
27+
</q-item-section>
28+
</q-item>
29+
30+
<q-item
31+
v-if="pageFavorited"
32+
clickable
33+
v-ripple
34+
@click="removeFavoritePages([pageId])"
35+
>
36+
<q-item-section avatar>
37+
<q-icon name="mdi-star" />
38+
</q-item-section>
39+
40+
<q-item-section>
41+
<q-item-label>Remove from favorite pages</q-item-label>
42+
</q-item-section>
43+
</q-item>
44+
<q-item
45+
v-else
46+
clickable
47+
v-ripple
48+
@click="addFavoritePages([pageId])"
49+
>
50+
<q-item-section avatar>
51+
<q-icon name="mdi-star" />
52+
</q-item-section>
53+
54+
<q-item-section>
55+
<q-item-label>Add to favorite pages</q-item-label>
56+
</q-item-section>
57+
</q-item>
58+
59+
<q-item
60+
v-if="pageSelected"
61+
clickable
62+
v-ripple
63+
@click="deselectPage"
64+
>
65+
<q-item-section avatar>
66+
<q-icon name="mdi-selection-multiple" />
67+
</q-item-section>
68+
69+
<q-item-section>
70+
<q-item-label>Remove from selected pages</q-item-label>
71+
</q-item-section>
72+
</q-item>
73+
<q-item
74+
v-else
75+
clickable
76+
v-ripple
77+
@click="selectPage"
78+
>
79+
<q-item-section avatar>
80+
<q-icon name="mdi-selection-multiple" />
81+
</q-item-section>
82+
83+
<q-item-section>
84+
<q-item-label>Add to selected pages</q-item-label>
85+
</q-item-section>
86+
</q-item>
87+
88+
<slot></slot>
89+
</q-list>
90+
</q-menu>
91+
</DeepBtn>
92+
</template>
93+
94+
<script setup lang="ts">
95+
import type { QMenuProps } from 'quasar';
96+
import { addFavoritePages } from 'src/code/api-interface/users/add-favorite-pages';
97+
import { removeFavoritePages } from 'src/code/api-interface/users/remove-favorite-pages';
98+
import { removeRecentPages } from 'src/code/api-interface/users/remove-recent-pages';
99+
import { pageSelectionStore } from 'src/stores/page-selection';
100+
101+
import type { DeepBtnProps } from './DeepBtn.vue';
102+
103+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
104+
interface Props extends DeepBtnProps {
105+
pageId: string;
106+
107+
menuProps?: QMenuProps;
108+
}
109+
110+
const props = defineProps<Props>();
111+
112+
const pageFavorited = ref(false);
113+
const pageSelected = ref(false);
114+
115+
function beforeShow() {
116+
pageFavorited.value = internals.pages.react.favoritePageIds.includes(
117+
props.pageId,
118+
);
119+
pageSelected.value = pageSelectionStore().selectedPages.has(props.pageId);
120+
}
121+
122+
function selectPage() {
123+
pageSelectionStore().selectedPages.add(props.pageId);
124+
125+
$quasar().notify({
126+
message: 'Page added to selection.',
127+
color: 'positive',
128+
timeout: 1000,
129+
});
130+
}
131+
132+
function deselectPage() {
133+
pageSelectionStore().selectedPages.delete(props.pageId);
134+
135+
$quasar().notify({
136+
message: 'Page removed from selection.',
137+
color: 'negative',
138+
timeout: 1000,
139+
});
140+
}
141+
</script>

apps/client/src/layouts/PagesLayout/LeftSidebar/CurrentPath.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,12 @@
5050
:page-id="pageId"
5151
:active="pageId === internals.pages.react.pageId"
5252
prefer="relative"
53-
class="current-path"
54-
/>
53+
style="padding-right: 8px"
54+
>
55+
<q-item-section side>
56+
<PagePopupOptions :page-id="pageId" />
57+
</q-item-section>
58+
</PageItem>
5559
</q-list>
5660

5761
<div
@@ -79,6 +83,7 @@
7983

8084
<script setup lang="ts">
8185
import { listenPointerEvents, map, negateProp } from '@stdlib/misc';
86+
import PagePopupOptions from 'src/components/PagePopupOptions.vue';
8287
import type { LeftSidebarSectionName } from 'src/stores/ui';
8388
import {
8489
leftSidebarSectionIndexes,

0 commit comments

Comments
 (0)