mirror of
https://github.com/suitenumerique/docs
synced 2026-04-21 13:37:20 +00:00
✨(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.
This commit is contained in:
parent
11dfc9ff03
commit
9e4e557173
11 changed files with 172 additions and 20 deletions
|
|
@ -15,6 +15,7 @@ and this project adheres to
|
|||
- ✨(frontend) subdocs can manage link reach #1190
|
||||
- ✨(frontend) add duplicate action to doc tree #1175
|
||||
- ✨(frontend) add multi columns support for editor #1219
|
||||
- ✨(frontend) Can mask a document from the list view #1233
|
||||
|
||||
### Changed
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import {
|
|||
useCreateFavoriteDoc,
|
||||
useDeleteFavoriteDoc,
|
||||
useDuplicateDoc,
|
||||
useMaskDocOption,
|
||||
} from '@/docs/doc-management';
|
||||
import { DocShareModal } from '@/docs/doc-share';
|
||||
import {
|
||||
|
|
@ -81,6 +82,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
|||
const makeFavoriteDoc = useCreateFavoriteDoc({
|
||||
listInvalideQueries: [KEY_LIST_DOC, KEY_DOC],
|
||||
});
|
||||
const maskDocOption = useMaskDocOption(doc);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectHistoryModal.isOpen) {
|
||||
|
|
@ -126,6 +128,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
|||
}
|
||||
},
|
||||
testId: `docs-actions-${doc.is_favorite ? 'unpin' : 'pin'}-${doc.id}`,
|
||||
showSeparator: true,
|
||||
},
|
||||
{
|
||||
label: t('Version history'),
|
||||
|
|
@ -162,17 +165,23 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
|||
canSave: doc.abilities.partial_update,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('Delete document'),
|
||||
icon: 'delete',
|
||||
disabled: !doc.abilities.destroy,
|
||||
callback: () => {
|
||||
setIsModalRemoveOpen(true);
|
||||
},
|
||||
showSeparator: true,
|
||||
},
|
||||
];
|
||||
|
||||
const leaveDocOption: DropdownMenuOption = doc.abilities.destroy
|
||||
? {
|
||||
label: t('Delete document'),
|
||||
icon: 'delete',
|
||||
disabled: !doc.abilities.destroy,
|
||||
callback: () => {
|
||||
setIsModalRemoveOpen(true);
|
||||
},
|
||||
}
|
||||
: maskDocOption;
|
||||
|
||||
options.push(leaveDocOption);
|
||||
|
||||
const copyCurrentEditorToClipboard = useCopyCurrentEditorToClipboard();
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ export * from './useDeleteFavoriteDoc';
|
|||
export * from './useDoc';
|
||||
export * from './useDocOptions';
|
||||
export * from './useDocs';
|
||||
export * from './useSubDocs';
|
||||
export * from './useDuplicateDoc';
|
||||
export * from './useMaskDoc';
|
||||
export * from './useSubDocs';
|
||||
export * from './useUpdateDoc';
|
||||
export * from './useUpdateDocLink';
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export type DocsParams = {
|
|||
is_creator_me?: boolean;
|
||||
title?: string;
|
||||
is_favorite?: boolean;
|
||||
is_masked?: boolean;
|
||||
};
|
||||
|
||||
export const constructParams = (params: DocsParams): URLSearchParams => {
|
||||
|
|
@ -36,6 +37,9 @@ export const constructParams = (params: DocsParams): URLSearchParams => {
|
|||
if (params.is_favorite !== undefined) {
|
||||
searchParams.set('is_favorite', params.is_favorite.toString());
|
||||
}
|
||||
if (params.is_masked !== undefined) {
|
||||
searchParams.set('is_masked', params.is_masked.toString());
|
||||
}
|
||||
|
||||
return searchParams;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { APIError, errorCauses, fetchAPI } from '@/api';
|
||||
import { Doc } from '@/docs/doc-management';
|
||||
|
||||
export type MaskDocParams = Pick<Doc, 'id'>;
|
||||
|
||||
export const maskDoc = async ({ id }: MaskDocParams) => {
|
||||
const response = await fetchAPI(`documents/${id}/mask/`, {
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new APIError(
|
||||
'Failed to make the doc as masked',
|
||||
await errorCauses(response),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
interface MaskDocProps {
|
||||
onSuccess?: () => void;
|
||||
listInvalideQueries?: string[];
|
||||
}
|
||||
|
||||
export function useMaskDoc({ onSuccess, listInvalideQueries }: MaskDocProps) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<void, APIError, MaskDocParams>({
|
||||
mutationFn: maskDoc,
|
||||
onSuccess: () => {
|
||||
listInvalideQueries?.forEach((queryKey) => {
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: [queryKey],
|
||||
});
|
||||
});
|
||||
onSuccess?.();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export type DeleteMaskDocParams = Pick<Doc, 'id'>;
|
||||
|
||||
export const deleteMaskDoc = async ({ id }: DeleteMaskDocParams) => {
|
||||
const response = await fetchAPI(`documents/${id}/mask/`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new APIError(
|
||||
'Failed to remove the doc as masked',
|
||||
await errorCauses(response),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
interface DeleteMaskDocProps {
|
||||
onSuccess?: () => void;
|
||||
listInvalideQueries?: string[];
|
||||
}
|
||||
|
||||
export function useDeleteMaskDoc({
|
||||
onSuccess,
|
||||
listInvalideQueries,
|
||||
}: DeleteMaskDocProps) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<void, APIError, DeleteMaskDocParams>({
|
||||
mutationFn: deleteMaskDoc,
|
||||
onSuccess: () => {
|
||||
listInvalideQueries?.forEach((queryKey) => {
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: [queryKey],
|
||||
});
|
||||
});
|
||||
onSuccess?.();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -2,4 +2,5 @@ export * from './useCollaboration';
|
|||
export * from './useCopyDocLink';
|
||||
export * from './useDocUtils';
|
||||
export * from './useIsCollaborativeEditable';
|
||||
export * from './useMaskDocOption';
|
||||
export * from './useTrans';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { DropdownMenuOption } from '@/components';
|
||||
|
||||
import { KEY_DOC, KEY_LIST_DOC, useDeleteMaskDoc, useMaskDoc } from '../api';
|
||||
import { Doc } from '../types';
|
||||
|
||||
export const useMaskDocOption = (doc: Doc) => {
|
||||
const { t } = useTranslation();
|
||||
const maskDoc = useMaskDoc({
|
||||
listInvalideQueries: [KEY_LIST_DOC, KEY_DOC],
|
||||
});
|
||||
const deleteMaskDoc = useDeleteMaskDoc({
|
||||
listInvalideQueries: [KEY_LIST_DOC, KEY_DOC],
|
||||
});
|
||||
|
||||
const leaveDocOption: DropdownMenuOption = doc.is_masked
|
||||
? {
|
||||
label: t('Join the doc'),
|
||||
icon: 'login',
|
||||
callback: () => {
|
||||
deleteMaskDoc.mutate({
|
||||
id: doc.id,
|
||||
});
|
||||
},
|
||||
disabled: !doc.abilities.mask,
|
||||
testId: `docs-grid-actions-mask-${doc.id}`,
|
||||
}
|
||||
: {
|
||||
label: t('Leave doc'),
|
||||
icon: 'logout',
|
||||
callback: () => {
|
||||
maskDoc.mutate({
|
||||
id: doc.id,
|
||||
});
|
||||
},
|
||||
disabled: !doc.abilities.mask,
|
||||
testId: `docs-grid-actions-mask-${doc.id}`,
|
||||
};
|
||||
|
||||
return leaveDocOption;
|
||||
};
|
||||
|
|
@ -59,6 +59,7 @@ export interface Doc {
|
|||
depth: number;
|
||||
path: string;
|
||||
is_favorite: boolean;
|
||||
is_masked: boolean;
|
||||
link_reach: LinkReach;
|
||||
link_role: LinkRole;
|
||||
nb_accesses_direct: number;
|
||||
|
|
@ -84,6 +85,7 @@ export interface Doc {
|
|||
favorite: boolean;
|
||||
invite_owner: boolean;
|
||||
link_configuration: boolean;
|
||||
mask: boolean;
|
||||
media_auth: boolean;
|
||||
move: boolean;
|
||||
partial_update: boolean;
|
||||
|
|
|
|||
|
|
@ -32,10 +32,14 @@ export const DocsGrid = ({
|
|||
hasNextPage,
|
||||
} = useInfiniteDocs({
|
||||
page: 1,
|
||||
...(target &&
|
||||
target !== DocDefaultFilter.ALL_DOCS && {
|
||||
is_creator_me: target === DocDefaultFilter.MY_DOCS,
|
||||
}),
|
||||
is_masked:
|
||||
!target || target === DocDefaultFilter.ALL_DOCS ? false : undefined,
|
||||
is_creator_me:
|
||||
target === DocDefaultFilter.MY_DOCS
|
||||
? true
|
||||
: target === DocDefaultFilter.SHARED_WITH_ME
|
||||
? false
|
||||
: undefined,
|
||||
});
|
||||
|
||||
const docs = data?.pages.flatMap((page) => page.results) ?? [];
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
useCreateFavoriteDoc,
|
||||
useDeleteFavoriteDoc,
|
||||
useDuplicateDoc,
|
||||
useMaskDocOption,
|
||||
} from '@/docs/doc-management';
|
||||
|
||||
interface DocsGridActionsProps {
|
||||
|
|
@ -31,6 +32,7 @@ export const DocsGridActions = ({
|
|||
const makeFavoriteDoc = useCreateFavoriteDoc({
|
||||
listInvalideQueries: [KEY_LIST_DOC],
|
||||
});
|
||||
const maskDocOption = useMaskDocOption(doc);
|
||||
|
||||
const options: DropdownMenuOption[] = [
|
||||
{
|
||||
|
|
@ -44,6 +46,7 @@ export const DocsGridActions = ({
|
|||
}
|
||||
},
|
||||
testId: `docs-grid-actions-${doc.is_favorite ? 'unpin' : 'pin'}-${doc.id}`,
|
||||
showSeparator: true,
|
||||
},
|
||||
{
|
||||
label: t('Share'),
|
||||
|
|
@ -65,16 +68,22 @@ export const DocsGridActions = ({
|
|||
canSave: false,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('Remove'),
|
||||
icon: 'delete',
|
||||
callback: () => deleteModal.open(),
|
||||
disabled: !doc.abilities.destroy,
|
||||
testId: `docs-grid-actions-remove-${doc.id}`,
|
||||
showSeparator: true,
|
||||
},
|
||||
];
|
||||
|
||||
const leaveDocOption: DropdownMenuOption = doc.abilities.destroy
|
||||
? {
|
||||
label: t('Delete document'),
|
||||
icon: 'delete',
|
||||
callback: () => deleteModal.open(),
|
||||
disabled: !doc.abilities.destroy,
|
||||
testId: `docs-grid-actions-remove-${doc.id}`,
|
||||
}
|
||||
: maskDocOption;
|
||||
|
||||
options.push(leaveDocOption);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownMenu options={options}>
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@ export class ApiPlugin implements WorkboxPlugin {
|
|||
creator: 'dummy-id',
|
||||
depth: 1,
|
||||
is_favorite: false,
|
||||
is_masked: false,
|
||||
nb_accesses_direct: 1,
|
||||
nb_accesses_ancestors: 1,
|
||||
numchild: 0,
|
||||
|
|
@ -192,6 +193,7 @@ export class ApiPlugin implements WorkboxPlugin {
|
|||
favorite: true,
|
||||
invite_owner: true,
|
||||
link_configuration: true,
|
||||
mask: true,
|
||||
media_auth: true,
|
||||
move: true,
|
||||
partial_update: true,
|
||||
|
|
|
|||
Loading…
Reference in a new issue