Skip to content

Commit dee4c44

Browse files
committed
Merge branch 'favorite-pages' into dev
2 parents 2ec7642 + 6070d41 commit dee4c44

File tree

16 files changed

+626
-49
lines changed

16 files changed

+626
-49
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { isNanoID } from '@stdlib/misc';
2+
import { checkRedlockSignalAborted } from '@stdlib/redlock';
3+
import { once, union } from 'lodash';
4+
import type { InferProcedureOpts } from 'src/trpc/helpers';
5+
import { authProcedure } from 'src/trpc/helpers';
6+
import { z } from 'zod';
7+
8+
const baseProcedure = authProcedure.input(
9+
z.object({
10+
pageIds: z.string().refine(isNanoID).array(),
11+
}),
12+
);
13+
14+
export const addFavoritePagesProcedure = once(() =>
15+
baseProcedure.mutation(addFavoritePages),
16+
);
17+
18+
export async function addFavoritePages({
19+
ctx,
20+
input,
21+
}: InferProcedureOpts<typeof baseProcedure>) {
22+
return await ctx.usingLocks(
23+
[[`user-lock:${ctx.userId}`]],
24+
async (signals) => {
25+
// Get favorite page IDs
26+
27+
let favoritePageIds: string[] = await ctx.dataAbstraction.hget(
28+
'user',
29+
ctx.userId,
30+
'favorite-page-ids',
31+
);
32+
33+
// Add page ID from favorite page IDs
34+
35+
favoritePageIds = union(input.pageIds, favoritePageIds);
36+
37+
checkRedlockSignalAborted(signals);
38+
39+
// Update favorite page IDs
40+
41+
await ctx.dataAbstraction.patch('user', ctx.userId, {
42+
favorite_page_ids: favoritePageIds,
43+
});
44+
},
45+
);
46+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { once } from 'lodash';
2+
import type { InferProcedureOpts } from 'src/trpc/helpers';
3+
import { authProcedure } from 'src/trpc/helpers';
4+
5+
const baseProcedure = authProcedure;
6+
7+
export const clearFavoritePagesProcedure = once(() =>
8+
baseProcedure.mutation(clearFavoritePages),
9+
);
10+
11+
export async function clearFavoritePages({
12+
ctx,
13+
}: InferProcedureOpts<typeof baseProcedure>) {
14+
return await ctx.usingLocks([[`user-lock:${ctx.userId}`]], async () => {
15+
await ctx.dataAbstraction.patch('user', ctx.userId, {
16+
favorite_page_ids: [],
17+
});
18+
});
19+
}

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { trpc } from 'src/trpc/server';
22

3+
import { addFavoritePagesProcedure } from './add-favorite-pages';
4+
import { clearFavoritePagesProcedure } from './clear-favorite-pages';
35
import { clearRecentPagesProcedure } from './clear-recent-pages';
46
import { getCurrentPathProcedure } from './get-current-path';
57
import { getGroupIdsProcedure } from './get-group-ids';
68
import { getStartingPageIdProcedure } from './get-starting-page-id';
79
import { notificationsRouter } from './notifications';
10+
import { removeFavoritePageProcedure } from './remove-favorite-page';
811
import { removeRecentPageProcedure } from './remove-recent-page';
912
import { setEncryptedDefaultArrowProcedure } from './set-encrypted-default-arrow';
1013
import { setEncryptedDefaultNoteProcedure } from './set-encrypted-default-note';
@@ -15,8 +18,12 @@ export const pagesRouter = trpc.router({
1518
getStartingPageId: getStartingPageIdProcedure(),
1619
getCurrentPath: getCurrentPathProcedure(),
1720

18-
clearRecentPages: clearRecentPagesProcedure(),
1921
removeRecentPage: removeRecentPageProcedure(),
22+
clearRecentPages: clearRecentPagesProcedure(),
23+
24+
addFavoritePages: addFavoritePagesProcedure(),
25+
removeFavoritePage: removeFavoritePageProcedure(),
26+
clearFavoritePages: clearFavoritePagesProcedure(),
2027

2128
setEncryptedDefaultNote: setEncryptedDefaultNoteProcedure(),
2229
setEncryptedDefaultArrow: setEncryptedDefaultArrowProcedure(),
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { isNanoID } from '@stdlib/misc';
2+
import { checkRedlockSignalAborted } from '@stdlib/redlock';
3+
import { TRPCError } from '@trpc/server';
4+
import { once, pull } from 'lodash';
5+
import type { InferProcedureOpts } from 'src/trpc/helpers';
6+
import { authProcedure } from 'src/trpc/helpers';
7+
import { z } from 'zod';
8+
9+
const baseProcedure = authProcedure.input(
10+
z.object({
11+
pageId: z.string().refine(isNanoID),
12+
}),
13+
);
14+
15+
export const removeFavoritePageProcedure = once(() =>
16+
baseProcedure.mutation(removeFavoritePage),
17+
);
18+
19+
export async function removeFavoritePage({
20+
ctx,
21+
input,
22+
}: InferProcedureOpts<typeof baseProcedure>) {
23+
return await ctx.usingLocks(
24+
[[`user-lock:${ctx.userId}`]],
25+
async (signals) => {
26+
// Get favorite page IDs
27+
28+
const favoritePageIds: string[] = await ctx.dataAbstraction.hget(
29+
'user',
30+
ctx.userId,
31+
'favorite-page-ids',
32+
);
33+
34+
// Remove page ID from favorite page IDs
35+
36+
const originalLength = favoritePageIds.length;
37+
38+
if (pull(favoritePageIds, input.pageId).length === originalLength) {
39+
throw new TRPCError({
40+
message: 'Favorite page not found.',
41+
code: 'NOT_FOUND',
42+
});
43+
}
44+
45+
checkRedlockSignalAborted(signals);
46+
47+
// Update favorite page IDs
48+
49+
await ctx.dataAbstraction.patch('user', ctx.userId, {
50+
favorite_page_ids: favoritePageIds,
51+
});
52+
},
53+
);
54+
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ export async function removeRecentPage({
3333

3434
// Remove page ID from recent page IDs
3535

36-
if (pull(recentPageIds, input.pageId).length === 0) {
36+
const originalLength = recentPageIds.length;
37+
38+
if (pull(recentPageIds, input.pageId).length === originalLength) {
3739
throw new TRPCError({
3840
message: 'Recent page not found.',
3941
code: 'NOT_FOUND',

apps/client/src/code/pages/pages.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ export interface IAppReact {
1818
recentPageIdsOverride?: string[];
1919
recentPageIds: ComputedRef<string[]>;
2020

21+
favoritePageIdsOverride?: string[];
22+
favoritePageIds: ComputedRef<string[]>;
23+
2124
page: ShallowRef<Page>;
2225
pageId: ComputedRef<string | undefined>;
2326
pageIndex: ComputedRef<number>;
@@ -45,6 +48,7 @@ export class Pages {
4548
parentPageId?: string;
4649

4750
recentPageIdsKeepOverride?: boolean;
51+
favoritePageIdsKeepOverride?: boolean;
4852

4953
constructor(input: { factories: Factories }) {
5054
this.factories = input.factories;
@@ -54,6 +58,7 @@ export class Pages {
5458

5559
this.react = reactive({
5660
pathPageIds: [],
61+
5762
recentPageIds: computed(() => {
5863
if (this.recentPageIdsKeepOverride) {
5964
this.recentPageIdsKeepOverride = undefined;
@@ -70,6 +75,22 @@ export class Pages {
7075
return this.react.recentPageIdsOverride ?? recentPageIds ?? [];
7176
}),
7277

78+
favoritePageIds: computed(() => {
79+
if (this.favoritePageIdsKeepOverride) {
80+
this.favoritePageIdsKeepOverride = undefined;
81+
} else {
82+
this.react.favoritePageIdsOverride = undefined;
83+
}
84+
85+
const favoritePageIds = internals.realtime.globalCtx.hget(
86+
'user',
87+
authStore().userId,
88+
'favorite-page-ids',
89+
);
90+
91+
return this.react.favoritePageIdsOverride ?? favoritePageIds ?? [];
92+
}),
93+
7394
page: shallowRef(null) as any,
7495
pageId: computed(() => this.react.page?.id),
7596
pageIndex: computed(() =>

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

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
</q-toolbar>
3737

3838
<q-list
39+
:id="`${sectionName}List`"
3940
ref="listRef"
4041
style="height: 0; overflow-x: hidden; overflow-y: auto"
4142
:style="{
@@ -55,8 +56,14 @@
5556

5657
<div
5758
v-if="
58-
uiStore().currentPathExpanded &&
59-
(uiStore().recentPagesExpanded || uiStore().selectedPagesExpanded)
59+
uiStore()[`${sectionName}Expanded`] &&
60+
leftSidebarSectionNames.reduce((acc, section, index) => {
61+
if (index > sectionIndex) {
62+
acc ||= uiStore()[`${section}Expanded`] ? true : false;
63+
}
64+
65+
return acc;
66+
}, false)
6067
"
6168
style="position: relative"
6269
>
@@ -72,27 +79,59 @@
7279

7380
<script setup lang="ts">
7481
import { listenPointerEvents, map, negateProp } from '@stdlib/misc';
82+
import type { LeftSidebarSectionName } from 'src/stores/ui';
83+
import {
84+
leftSidebarSectionIndexes,
85+
leftSidebarSectionNames,
86+
} from 'src/stores/ui';
7587
import type { ComponentPublicInstance } from 'vue';
7688
7789
const listRef = ref<ComponentPublicInstance>();
7890
91+
const sectionName = 'currentPath';
92+
const sectionIndex = leftSidebarSectionIndexes[sectionName];
93+
7994
function resizeHandlePointerDown(downEvent: PointerEvent) {
8095
listenPointerEvents(downEvent, {
8196
move(moveEvent) {
82-
const clientRect = listRef.value!.$el.getBoundingClientRect();
83-
84-
const othersHeight = uiStore().height - clientRect.height - 32 * 3 - 2;
85-
86-
const othersWeight =
87-
(uiStore().recentPagesExpanded ? uiStore().recentPagesWeight : 0) +
88-
(uiStore().selectedPagesExpanded ? uiStore().selectedPagesWeight : 0);
97+
const clientRect = document
98+
.querySelector(`#${sectionName}List`)!
99+
.getBoundingClientRect();
100+
101+
const othersHeight =
102+
uiStore().height -
103+
clientRect.height -
104+
32 * leftSidebarSectionNames.length -
105+
2;
106+
107+
const othersWeight = leftSidebarSectionNames.reduce((acc, section) => {
108+
if (section !== sectionName) {
109+
acc += uiStore()[`${section}Expanded`]
110+
? uiStore()[`${section}Weight`]
111+
: 0;
112+
}
113+
114+
return acc;
115+
}, 0);
89116
90117
const myNewHeight = moveEvent.clientY - clientRect.y;
91118
92119
const myNewWeight = map(myNewHeight, 0, othersHeight, 0, othersWeight);
93120
94-
uiStore().recentPagesWeight -= myNewWeight - uiStore().currentPathWeight;
95-
uiStore().currentPathWeight = myNewWeight;
121+
const nextExpandedSection = leftSidebarSectionNames.reduce(
122+
(acc, section, index) => {
123+
if (index > sectionIndex) {
124+
acc ||= uiStore()[`${section}Expanded`] ? section : null;
125+
}
126+
127+
return acc;
128+
},
129+
null as LeftSidebarSectionName | null,
130+
)!;
131+
132+
uiStore()[`${nextExpandedSection}Weight`] -=
133+
myNewWeight - uiStore()[`${sectionName}Weight`];
134+
uiStore()[`${sectionName}Weight`] = myNewWeight;
96135
97136
uiStore().normalizeWeights();
98137
},
@@ -101,10 +140,12 @@ function resizeHandlePointerDown(downEvent: PointerEvent) {
101140
102141
function resizeHandleDoubleClick() {
103142
const avgWeight =
104-
(uiStore().currentPathWeight + uiStore().recentPagesWeight) / 2;
143+
(uiStore()[`${sectionName}Weight`] +
144+
uiStore()[`${leftSidebarSectionNames[sectionIndex + 1]}Weight`]) /
145+
2;
105146
106-
uiStore().currentPathWeight = avgWeight;
107-
uiStore().recentPagesWeight = avgWeight;
147+
uiStore()[`${sectionName}Weight`] = avgWeight;
148+
uiStore()[`${leftSidebarSectionNames[sectionIndex + 1]}Weight`] = avgWeight;
108149
}
109150
</script>
110151

0 commit comments

Comments
 (0)