From a092c2915b3d073b4479025e9d4664d75e7b8f26 Mon Sep 17 00:00:00 2001 From: Anthony LC Date: Wed, 11 Sep 2024 10:23:06 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F(frontend)=20adapt=20doc=20vi?= =?UTF-8?q?sibility=20to=20new=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We updated the way we handle the visibility of a doc in the backend. Now we use a new api to update the visibility (documents/{id}/link-configuration/) of a doc. We adapted the frontend to use this new api. We changed the types to reflect the new api and to keep the same logic. --- .../apps/e2e/__tests__/app-impress/common.ts | 3 +- .../__tests__/app-impress/doc-editor.spec.ts | 1 + .../__tests__/app-impress/doc-header.spec.ts | 6 ++- .../docs/doc-header/components/DocHeader.tsx | 2 +- .../doc-header/components/DocTagPublic.tsx | 22 +++----- .../features/docs/doc-management/api/index.ts | 1 + .../docs/doc-management/api/useUpdateDoc.tsx | 2 +- .../doc-management/api/useUpdateDocLink.tsx | 51 +++++++++++++++++++ .../components/DocVisibility.tsx | 18 ++++--- .../components/ModalCreateUpdateDoc.tsx | 1 - .../doc-management/components/ModalShare.tsx | 40 +++++++-------- .../features/docs/doc-management/types.tsx | 10 +++- .../docs/docs-grid/components/DocsGrid.tsx | 5 +- .../components/InvitationList.tsx | 2 +- .../members-add/components/AddMembers.tsx | 2 +- .../members-list/components/MemberList.tsx | 2 +- .../src/features/service-worker/ApiPlugin.ts | 1 + .../impress/src/pages/docs/[id]/index.tsx | 16 ++++-- 18 files changed, 128 insertions(+), 57 deletions(-) create mode 100644 src/frontend/apps/impress/src/features/docs/doc-management/api/useUpdateDocLink.tsx diff --git a/src/frontend/apps/e2e/__tests__/app-impress/common.ts b/src/frontend/apps/e2e/__tests__/app-impress/common.ts index df903e52..1d6af61b 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/common.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/common.ts @@ -164,6 +164,7 @@ export const mockedDocument = async (page: Page, json: object) => { accesses: [], abilities: { destroy: false, // Means not owner + link_configuration: false, versions_destroy: false, versions_list: true, versions_retrieve: true, @@ -172,7 +173,7 @@ export const mockedDocument = async (page: Page, json: object) => { partial_update: false, // Means not editor retrieve: true, }, - is_public: false, + link_reach: 'restricted', created_at: '2021-09-01T09:00:00Z', ...json, }, diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts index 966947c2..c5da0d51 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts @@ -136,6 +136,7 @@ test.describe('Doc Editor', () => { await mockedDocument(page, { abilities: { destroy: false, // Means not owner + link_configuration: false, versions_destroy: false, versions_list: true, versions_retrieve: true, diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts index e19403bb..9d1a4d1b 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts @@ -34,6 +34,7 @@ test.describe('Doc Header', () => { ], abilities: { destroy: true, // Means owner + link_configuration: true, versions_destroy: true, versions_list: true, versions_retrieve: true, @@ -42,7 +43,7 @@ test.describe('Doc Header', () => { partial_update: true, retrieve: true, }, - is_public: true, + link_reach: 'public', created_at: '2021-09-01T09:00:00Z', }); @@ -153,6 +154,7 @@ test.describe('Doc Header', () => { await mockedDocument(page, { abilities: { destroy: false, // Means not owner + link_configuration: true, versions_destroy: true, versions_list: true, versions_retrieve: true, @@ -184,6 +186,7 @@ test.describe('Doc Header', () => { await mockedDocument(page, { abilities: { destroy: false, // Means not owner + link_configuration: false, versions_destroy: true, versions_list: true, versions_retrieve: true, @@ -215,6 +218,7 @@ test.describe('Doc Header', () => { await mockedDocument(page, { abilities: { destroy: false, // Means not owner + link_configuration: false, versions_destroy: false, versions_list: true, versions_retrieve: true, diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx index ad99f4f1..4f551109 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx @@ -85,7 +85,7 @@ export const DocHeader = ({ doc, versionId }: DocHeaderProps) => { $wrap="wrap" > - + {t('Created at')} {formatDate(doc.created_at)} diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocTagPublic.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocTagPublic.tsx index 1b6d1ae9..19f29bf1 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocTagPublic.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocTagPublic.tsx @@ -1,26 +1,18 @@ -import { useRouter } from 'next/router'; import { useTranslation } from 'react-i18next'; import { Text } from '@/components'; import { useCunninghamTheme } from '@/cunningham'; -import { KEY_DOC_VISIBILITY, useDoc } from '@/features/docs/doc-management'; +import { Doc, LinkReach } from '@/features/docs/doc-management'; -export const DocTagPublic = () => { +interface DocTagPublicProps { + doc: Doc; +} + +export const DocTagPublic = ({ doc }: DocTagPublicProps) => { const { colorsTokens } = useCunninghamTheme(); const { t } = useTranslation(); - const { - query: { id }, - } = useRouter(); - const { data: doc } = useDoc( - { id: id as string }, - { - enabled: !!id, - queryKey: [KEY_DOC_VISIBILITY, { id }], - }, - ); - - if (!doc?.is_public) { + if (doc?.link_reach !== LinkReach.PUBLIC) { return null; } diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/api/index.ts b/src/frontend/apps/impress/src/features/docs/doc-management/api/index.ts index 4adddaae..8860f7da 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/api/index.ts +++ b/src/frontend/apps/impress/src/features/docs/doc-management/api/index.ts @@ -1,3 +1,4 @@ export * from './useDoc'; export * from './useDocs'; export * from './useUpdateDoc'; +export * from './useUpdateDocLink'; diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/api/useUpdateDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/api/useUpdateDoc.tsx index c3868f26..b944f50a 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/api/useUpdateDoc.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/api/useUpdateDoc.tsx @@ -4,7 +4,7 @@ import { APIError, errorCauses, fetchAPI } from '@/api'; import { Doc } from '@/features/docs'; export type UpdateDocParams = Pick & - Partial>; + Partial>; export const updateDoc = async ({ id, diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/api/useUpdateDocLink.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/api/useUpdateDocLink.tsx new file mode 100644 index 00000000..3dd539b7 --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-management/api/useUpdateDocLink.tsx @@ -0,0 +1,51 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; + +import { APIError, errorCauses, fetchAPI } from '@/api'; +import { Doc } from '@/features/docs'; + +export type UpdateDocLinkParams = Pick & + Partial>; + +export const updateDocLink = async ({ + id, + ...params +}: UpdateDocLinkParams): Promise => { + const response = await fetchAPI(`documents/${id}/link-configuration/`, { + method: 'PUT', + body: JSON.stringify({ + ...params, + }), + }); + + if (!response.ok) { + throw new APIError( + 'Failed to update the doc link', + await errorCauses(response), + ); + } + + return response.json() as Promise; +}; + +interface UpdateDocLinkProps { + onSuccess?: (data: Doc) => void; + listInvalideQueries?: string[]; +} + +export function useUpdateDocLink({ + onSuccess, + listInvalideQueries, +}: UpdateDocLinkProps = {}) { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: updateDocLink, + onSuccess: (data) => { + listInvalideQueries?.forEach((queryKey) => { + void queryClient.resetQueries({ + queryKey: [queryKey], + }); + }); + onSuccess?.(data); + }, + }); +} diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/DocVisibility.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocVisibility.tsx index cadff5a3..be39d2c4 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/components/DocVisibility.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocVisibility.tsx @@ -9,8 +9,8 @@ import { useTranslation } from 'react-i18next'; import { Box, Card, IconBG } from '@/components'; -import { KEY_DOC_VISIBILITY, KEY_LIST_DOC, useUpdateDoc } from '../api'; -import { Doc } from '../types'; +import { KEY_DOC, KEY_LIST_DOC, useUpdateDocLink } from '../api'; +import { Doc, LinkReach } from '../types'; interface DocVisibilityProps { doc: Doc; @@ -18,9 +18,11 @@ interface DocVisibilityProps { export const DocVisibility = ({ doc }: DocVisibilityProps) => { const { t } = useTranslation(); - const [docPublic, setDocPublic] = useState(doc.is_public); + const [docPublic, setDocPublic] = useState( + doc.link_reach === LinkReach.PUBLIC, + ); const { toast } = useToastProvider(); - const api = useUpdateDoc({ + const api = useUpdateDocLink({ onSuccess: () => { toast( t('The document visiblitity has been updated.'), @@ -30,13 +32,13 @@ export const DocVisibility = ({ doc }: DocVisibilityProps) => { }, ); }, - listInvalideQueries: [KEY_LIST_DOC, KEY_DOC_VISIBILITY], + listInvalideQueries: [KEY_LIST_DOC, KEY_DOC], }); return ( { onChange={() => { api.mutate({ id: doc.id, - is_public: !docPublic, + link_reach: docPublic ? LinkReach.RESTRICTED : LinkReach.PUBLIC, + link_role: 'reader', }); setDocPublic(!docPublic); }} + disabled={!doc.abilities.link_configuration} text={t( docPublic ? 'Anyone on the internet with the link can view' diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalCreateUpdateDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalCreateUpdateDoc.tsx index 9bf692a8..c883715d 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalCreateUpdateDoc.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalCreateUpdateDoc.tsx @@ -78,7 +78,6 @@ export const ModalUpdateDoc = ({ onClose, doc }: ModalUpdateDocProps) => { buttonText: t('Validate the modification'), onClose, initialTitle: doc.title, - isPublic: doc.is_public, infoText: t('Enter the new name of the selected document.'), titleModal: t('Update document "{{documentTitle}}"', { documentTitle: doc.title, diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalShare.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalShare.tsx index 0f11a6a9..e53dce6c 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalShare.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalShare.tsx @@ -46,28 +46,26 @@ export const ModalShare = ({ onClose, doc }: ModalShareProps) => { onClose={onClose} width="70vw" $css="min-width: 320px;max-width: 777px;" - title={ - - - share - - - - {t('Share')} - - - {doc.title} - - - - } > + + + share + + + + {t('Share')} + + + {doc.title} + + + diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/types.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/types.tsx index 5db4d395..0125cedc 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/types.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/types.tsx @@ -20,18 +20,26 @@ export enum Role { OWNER = 'owner', } +export enum LinkReach { + RESTRICTED = 'restricted', + PUBLIC = 'public', + AUTHENTICATED = 'authenticated', +} + export type Base64 = string; export interface Doc { id: string; title: string; content: Base64; - is_public: boolean; + link_reach: LinkReach; + link_role: 'reader' | 'editor'; accesses: Access[]; created_at: string; updated_at: string; abilities: { destroy: boolean; + link_configuration: boolean; manage_accesses: boolean; partial_update: boolean; retrieve: boolean; diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx index 90fb9d68..cbab72d4 100644 --- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx +++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx @@ -8,6 +8,7 @@ import { useCunninghamTheme } from '@/cunningham'; import { Doc, DocsOrdering, + LinkReach, currentDocRole, isDocsOrdering, useDocs, @@ -109,7 +110,7 @@ export const DocsGrid = () => { renderCell: ({ row }) => { return ( - {row.is_public && ( + {row.link_reach === LinkReach.PUBLIC && ( { $padding="xtiny" $radius="3px" > - {row.is_public ? t('Public') : ''} + {t('Public')} )} diff --git a/src/frontend/apps/impress/src/features/docs/members/invitation-list/components/InvitationList.tsx b/src/frontend/apps/impress/src/features/docs/members/invitation-list/components/InvitationList.tsx index 893ec7da..df82447d 100644 --- a/src/frontend/apps/impress/src/features/docs/members/invitation-list/components/InvitationList.tsx +++ b/src/frontend/apps/impress/src/features/docs/members/invitation-list/components/InvitationList.tsx @@ -98,7 +98,7 @@ export const InvitationList = ({ doc }: InvitationListProps) => { diff --git a/src/frontend/apps/impress/src/features/docs/members/members-add/components/AddMembers.tsx b/src/frontend/apps/impress/src/features/docs/members/members-add/components/AddMembers.tsx index 9bcb0741..6173aad4 100644 --- a/src/frontend/apps/impress/src/features/docs/members/members-add/components/AddMembers.tsx +++ b/src/frontend/apps/impress/src/features/docs/members/members-add/components/AddMembers.tsx @@ -138,7 +138,7 @@ export const AddMembers = ({ currentRole, doc }: ModalAddMembersProps) => { return ( { diff --git a/src/frontend/apps/impress/src/features/service-worker/ApiPlugin.ts b/src/frontend/apps/impress/src/features/service-worker/ApiPlugin.ts index 21b79873..ef5cd6fc 100644 --- a/src/frontend/apps/impress/src/features/service-worker/ApiPlugin.ts +++ b/src/frontend/apps/impress/src/features/service-worker/ApiPlugin.ts @@ -189,6 +189,7 @@ export class ApiPlugin implements WorkboxPlugin { updated_at: new Date().toISOString(), abilities: { destroy: true, + link_configuration: true, versions_destroy: true, versions_list: true, versions_retrieve: true, diff --git a/src/frontend/apps/impress/src/pages/docs/[id]/index.tsx b/src/frontend/apps/impress/src/pages/docs/[id]/index.tsx index 205a7cbf..3acd004c 100644 --- a/src/frontend/apps/impress/src/pages/docs/[id]/index.tsx +++ b/src/frontend/apps/impress/src/pages/docs/[id]/index.tsx @@ -1,7 +1,7 @@ import { Loader } from '@openfun/cunningham-react'; import { useRouter as useNavigate } from 'next/navigation'; import { useRouter } from 'next/router'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { Box, Text } from '@/components'; import { TextErrors } from '@/components/TextErrors'; @@ -31,7 +31,9 @@ interface DocProps { } const DocPage = ({ id }: DocProps) => { - const { data: doc, isLoading, isError, error } = useDoc({ id }); + const { data: docQuery, isError, error } = useDoc({ id }); + const [doc, setDoc] = useState(docQuery); + const navigate = useNavigate(); useEffect(() => { @@ -42,6 +44,14 @@ const DocPage = ({ id }: DocProps) => { } }, [doc?.title]); + useEffect(() => { + if (!docQuery) { + return; + } + + setDoc(docQuery); + }, [docQuery]); + if (isError && error) { if (error.status === 404) { navigate.replace(`/404`); @@ -64,7 +74,7 @@ const DocPage = ({ id }: DocProps) => { ); } - if (isLoading || !doc) { + if (!doc) { return (