Skip to content

Commit bb45308

Browse files
committed
✨(frontend) Can mask a document in the list view
We can be member of some documents, but sometimes we want to mask them from the list view because we don't want to interact with them anymore. This commit adds the ability to mask a document in the list view.
1 parent 65c2ddf commit bb45308

File tree

10 files changed

+170
-20
lines changed

10 files changed

+170
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to
1515
- ✨(frontend) subdocs can manage link reach #1190
1616
- ✨(frontend) add duplicate action to doc tree #1175
1717
- ✨(frontend) add multi columns support for editor #1219
18+
- ✨(frontend) Can mask a document from the list view #1233
1819

1920
### Changed
2021

src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
useCreateFavoriteDoc,
2525
useDeleteFavoriteDoc,
2626
useDuplicateDoc,
27+
useMaskDocOption,
2728
} from '@/docs/doc-management';
2829
import { DocShareModal } from '@/docs/doc-share';
2930
import {
@@ -81,6 +82,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
8182
const makeFavoriteDoc = useCreateFavoriteDoc({
8283
listInvalideQueries: [KEY_LIST_DOC, KEY_DOC],
8384
});
85+
const maskDocOption = useMaskDocOption(doc);
8486

8587
useEffect(() => {
8688
if (selectHistoryModal.isOpen) {
@@ -126,6 +128,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
126128
}
127129
},
128130
testId: `docs-actions-${doc.is_favorite ? 'unpin' : 'pin'}-${doc.id}`,
131+
showSeparator: true,
129132
},
130133
{
131134
label: t('Version history'),
@@ -162,17 +165,23 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
162165
canSave: doc.abilities.partial_update,
163166
});
164167
},
165-
},
166-
{
167-
label: t('Delete document'),
168-
icon: 'delete',
169-
disabled: !doc.abilities.destroy,
170-
callback: () => {
171-
setIsModalRemoveOpen(true);
172-
},
168+
showSeparator: true,
173169
},
174170
];
175171

172+
const leaveDocOption: DropdownMenuOption = doc.abilities.destroy
173+
? {
174+
label: t('Delete document'),
175+
icon: 'delete',
176+
disabled: !doc.abilities.destroy,
177+
callback: () => {
178+
setIsModalRemoveOpen(true);
179+
},
180+
}
181+
: maskDocOption;
182+
183+
options.push(leaveDocOption);
184+
176185
const copyCurrentEditorToClipboard = useCopyCurrentEditorToClipboard();
177186

178187
return (

src/frontend/apps/impress/src/features/docs/doc-management/api/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ export * from './useDeleteFavoriteDoc';
44
export * from './useDoc';
55
export * from './useDocOptions';
66
export * from './useDocs';
7-
export * from './useSubDocs';
87
export * from './useDuplicateDoc';
8+
export * from './useMaskDoc';
9+
export * from './useSubDocs';
910
export * from './useUpdateDoc';
1011
export * from './useUpdateDocLink';

src/frontend/apps/impress/src/features/docs/doc-management/api/useDocs.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export type DocsParams = {
1616
is_creator_me?: boolean;
1717
title?: string;
1818
is_favorite?: boolean;
19+
is_masked?: boolean;
1920
};
2021

2122
export const constructParams = (params: DocsParams): URLSearchParams => {
@@ -36,6 +37,9 @@ export const constructParams = (params: DocsParams): URLSearchParams => {
3637
if (params.is_favorite !== undefined) {
3738
searchParams.set('is_favorite', params.is_favorite.toString());
3839
}
40+
if (params.is_masked !== undefined) {
41+
searchParams.set('is_masked', params.is_masked.toString());
42+
}
3943

4044
return searchParams;
4145
};
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { useMutation, useQueryClient } from '@tanstack/react-query';
2+
3+
import { APIError, errorCauses, fetchAPI } from '@/api';
4+
import { Doc } from '@/docs/doc-management';
5+
6+
export type MaskDocParams = Pick<Doc, 'id'>;
7+
8+
export const maskDoc = async ({ id }: MaskDocParams) => {
9+
const response = await fetchAPI(`documents/${id}/mask/`, {
10+
method: 'POST',
11+
});
12+
13+
if (!response.ok) {
14+
throw new APIError(
15+
'Failed to make the doc as masked',
16+
await errorCauses(response),
17+
);
18+
}
19+
};
20+
21+
interface MaskDocProps {
22+
onSuccess?: () => void;
23+
listInvalideQueries?: string[];
24+
}
25+
26+
export function useMaskDoc({ onSuccess, listInvalideQueries }: MaskDocProps) {
27+
const queryClient = useQueryClient();
28+
return useMutation<void, APIError, MaskDocParams>({
29+
mutationFn: maskDoc,
30+
onSuccess: () => {
31+
listInvalideQueries?.forEach((queryKey) => {
32+
void queryClient.invalidateQueries({
33+
queryKey: [queryKey],
34+
});
35+
});
36+
onSuccess?.();
37+
},
38+
});
39+
}
40+
41+
export type DeleteMaskDocParams = Pick<Doc, 'id'>;
42+
43+
export const deleteMaskDoc = async ({ id }: DeleteMaskDocParams) => {
44+
const response = await fetchAPI(`documents/${id}/mask/`, {
45+
method: 'DELETE',
46+
});
47+
48+
if (!response.ok) {
49+
throw new APIError(
50+
'Failed to remove the doc as masked',
51+
await errorCauses(response),
52+
);
53+
}
54+
};
55+
56+
interface DeleteMaskDocProps {
57+
onSuccess?: () => void;
58+
listInvalideQueries?: string[];
59+
}
60+
61+
export function useDeleteMaskDoc({
62+
onSuccess,
63+
listInvalideQueries,
64+
}: DeleteMaskDocProps) {
65+
const queryClient = useQueryClient();
66+
return useMutation<void, APIError, DeleteMaskDocParams>({
67+
mutationFn: deleteMaskDoc,
68+
onSuccess: () => {
69+
listInvalideQueries?.forEach((queryKey) => {
70+
void queryClient.invalidateQueries({
71+
queryKey: [queryKey],
72+
});
73+
});
74+
onSuccess?.();
75+
},
76+
});
77+
}

src/frontend/apps/impress/src/features/docs/doc-management/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export * from './useCollaboration';
22
export * from './useCopyDocLink';
33
export * from './useDocUtils';
44
export * from './useIsCollaborativeEditable';
5+
export * from './useMaskDocOption';
56
export * from './useTrans';
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { useTranslation } from 'react-i18next';
2+
3+
import { DropdownMenuOption } from '@/components';
4+
5+
import { KEY_LIST_DOC, useDeleteMaskDoc, useMaskDoc } from '../api';
6+
import { Doc } from '../types';
7+
8+
export const useMaskDocOption = (doc: Doc) => {
9+
const { t } = useTranslation();
10+
const maskDoc = useMaskDoc({
11+
listInvalideQueries: [KEY_LIST_DOC],
12+
});
13+
const deleteMaskDoc = useDeleteMaskDoc({
14+
listInvalideQueries: [KEY_LIST_DOC],
15+
});
16+
17+
const leaveDocOption: DropdownMenuOption = doc.is_masked
18+
? {
19+
label: t('Join the doc'),
20+
icon: 'login',
21+
callback: () => {
22+
deleteMaskDoc.mutate({
23+
id: doc.id,
24+
});
25+
},
26+
disabled: !doc.abilities.mask,
27+
testId: `docs-grid-actions-mask-${doc.id}`,
28+
}
29+
: {
30+
label: t('Leave doc'),
31+
icon: 'logout',
32+
callback: () => {
33+
maskDoc.mutate({
34+
id: doc.id,
35+
});
36+
},
37+
disabled: !doc.abilities.mask,
38+
testId: `docs-grid-actions-mask-${doc.id}`,
39+
};
40+
41+
return leaveDocOption;
42+
};

src/frontend/apps/impress/src/features/docs/doc-management/types.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export interface Doc {
5959
depth: number;
6060
path: string;
6161
is_favorite: boolean;
62+
is_masked: boolean;
6263
link_reach: LinkReach;
6364
link_role: LinkRole;
6465
nb_accesses_direct: number;
@@ -84,6 +85,7 @@ export interface Doc {
8485
favorite: boolean;
8586
invite_owner: boolean;
8687
link_configuration: boolean;
88+
mask: boolean;
8789
media_auth: boolean;
8890
move: boolean;
8991
partial_update: boolean;

src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,14 @@ export const DocsGrid = ({
3232
hasNextPage,
3333
} = useInfiniteDocs({
3434
page: 1,
35-
...(target &&
36-
target !== DocDefaultFilter.ALL_DOCS && {
37-
is_creator_me: target === DocDefaultFilter.MY_DOCS,
38-
}),
35+
is_masked:
36+
!target || target === DocDefaultFilter.ALL_DOCS ? false : undefined,
37+
is_creator_me:
38+
target === DocDefaultFilter.MY_DOCS
39+
? true
40+
: target === DocDefaultFilter.SHARED_WITH_ME
41+
? false
42+
: undefined,
3943
});
4044

4145
const docs = data?.pages.flatMap((page) => page.results) ?? [];

src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridActions.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
useCreateFavoriteDoc,
1010
useDeleteFavoriteDoc,
1111
useDuplicateDoc,
12+
useMaskDocOption,
1213
} from '@/docs/doc-management';
1314

1415
interface DocsGridActionsProps {
@@ -31,6 +32,7 @@ export const DocsGridActions = ({
3132
const makeFavoriteDoc = useCreateFavoriteDoc({
3233
listInvalideQueries: [KEY_LIST_DOC],
3334
});
35+
const maskDocOption = useMaskDocOption(doc);
3436

3537
const options: DropdownMenuOption[] = [
3638
{
@@ -44,6 +46,7 @@ export const DocsGridActions = ({
4446
}
4547
},
4648
testId: `docs-grid-actions-${doc.is_favorite ? 'unpin' : 'pin'}-${doc.id}`,
49+
showSeparator: true,
4750
},
4851
{
4952
label: t('Share'),
@@ -65,16 +68,22 @@ export const DocsGridActions = ({
6568
canSave: false,
6669
});
6770
},
68-
},
69-
{
70-
label: t('Remove'),
71-
icon: 'delete',
72-
callback: () => deleteModal.open(),
73-
disabled: !doc.abilities.destroy,
74-
testId: `docs-grid-actions-remove-${doc.id}`,
71+
showSeparator: true,
7572
},
7673
];
7774

75+
const leaveDocOption: DropdownMenuOption = doc.abilities.destroy
76+
? {
77+
label: t('Delete document'),
78+
icon: 'delete',
79+
callback: () => deleteModal.open(),
80+
disabled: !doc.abilities.destroy,
81+
testId: `docs-grid-actions-remove-${doc.id}`,
82+
}
83+
: maskDocOption;
84+
85+
options.push(leaveDocOption);
86+
7887
return (
7988
<>
8089
<DropdownMenu options={options}>

0 commit comments

Comments
 (0)