Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit dee4c44

Browse files
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)

AltStyle によって変換されたページ (->オリジナル) /