From f416aa734d72eb0516a6cab37d53742d6c896d3b Mon Sep 17 00:00:00 2001 From: Anthony LC Date: Mon, 23 Dec 2024 11:07:55 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F(frontend)=20create=20useProv?= =?UTF-8?q?iderStore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We created useProviderStore, a store dedicated to managing the provider of the document. We created as well a new hook useCollaboration, it will be use to interact with the provider store. This refacto is a first step to implement the long polling. --- CHANGELOG.md | 1 + .../docs/doc-editor/components/DocEditor.tsx | 5 +- .../docs/doc-management/hooks/index.ts | 1 + .../doc-management/hooks/useCollaboration.tsx | 35 +++++++++++++ .../docs/doc-management/stores/index.tsx | 1 + .../doc-management/stores/useDocStore.tsx | 43 +-------------- .../stores/useProviderStore.tsx | 52 +++++++++++++++++++ .../components/ModalVersion.tsx | 12 ++--- .../impress/src/pages/docs/[id]/index.tsx | 49 +++++++++-------- 9 files changed, 126 insertions(+), 73 deletions(-) create mode 100644 src/frontend/apps/impress/src/features/docs/doc-management/hooks/useCollaboration.tsx create mode 100644 src/frontend/apps/impress/src/features/docs/doc-management/stores/useProviderStore.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index a41b6efe9..5e168ea54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to ## Changed - 🏗️(yjs-server) organize yjs server #528 +- ♻️(frontend) better separation collaboration process #528 ## [1.10.0] - 2024-12-17 diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx index 935e193ac..bd165df1e 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx @@ -10,7 +10,7 @@ import { DocHeader } from '@/features/docs/doc-header'; import { Doc, base64ToBlocknoteXmlFragment, - useDocStore, + useProviderStore, } from '@/features/docs/doc-management'; import { Versions, useDocVersion } from '@/features/docs/doc-versioning/'; import { useResponsiveStore } from '@/stores'; @@ -33,8 +33,7 @@ export const DocEditor = ({ doc }: DocEditorProps) => { const { colorsTokens } = useCunninghamTheme(); - const { providers } = useDocStore(); - const provider = providers?.[doc.id]; + const { provider } = useProviderStore(); if (!provider) { return null; diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/index.ts b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/index.ts index 7697d915a..b352e1890 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/index.ts +++ b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/index.ts @@ -1 +1,2 @@ +export * from './useCollaboration'; export * from './useTrans'; diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useCollaboration.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useCollaboration.tsx new file mode 100644 index 000000000..848ccd005 --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useCollaboration.tsx @@ -0,0 +1,35 @@ +import { useEffect } from 'react'; + +import { useCollaborationUrl } from '@/core/config'; +import { useBroadcastStore } from '@/stores'; + +import { useProviderStore } from '../stores/useProviderStore'; +import { Base64 } from '../types'; + +export const useCollaboration = (room?: string, initialContent?: Base64) => { + const collaborationUrl = useCollaborationUrl(room); + const { setBroadcastProvider } = useBroadcastStore(); + const { provider, createProvider, destroyProvider } = useProviderStore(); + + useEffect(() => { + if (!room || !collaborationUrl || provider) { + return; + } + + const newProvider = createProvider(collaborationUrl, room, initialContent); + setBroadcastProvider(newProvider); + }, [ + provider, + collaborationUrl, + room, + initialContent, + createProvider, + setBroadcastProvider, + ]); + + useEffect(() => { + return () => { + destroyProvider(); + }; + }, [destroyProvider]); +}; diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/stores/index.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/stores/index.tsx index e742cba56..9926e4e32 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/stores/index.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/stores/index.tsx @@ -1 +1,2 @@ export * from './useDocStore'; +export * from './useProviderStore'; diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/stores/useDocStore.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/stores/useDocStore.tsx index fbc4de4cc..449995a75 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/stores/useDocStore.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/stores/useDocStore.tsx @@ -1,53 +1,14 @@ -import { HocuspocusProvider } from '@hocuspocus/provider'; -import * as Y from 'yjs'; import { create } from 'zustand'; -import { Base64, Doc } from '@/features/docs/doc-management'; +import { Doc } from '@/features/docs/doc-management'; export interface UseDocStore { currentDoc?: Doc; - providers: { - [storeId: string]: HocuspocusProvider; - }; - createProvider: ( - providerUrl: string, - storeId: string, - initialDoc: Base64, - ) => HocuspocusProvider; - setProviders: (storeId: string, providers: HocuspocusProvider) => void; setCurrentDoc: (doc: Doc | undefined) => void; } -export const useDocStore = create((set, get) => ({ +export const useDocStore = create((set) => ({ currentDoc: undefined, - providers: {}, - createProvider: (providerUrl, storeId, initialDoc) => { - const doc = new Y.Doc({ - guid: storeId, - }); - - if (initialDoc) { - Y.applyUpdate(doc, Buffer.from(initialDoc, 'base64')); - } - - const provider = new HocuspocusProvider({ - url: providerUrl, - name: storeId, - document: doc, - }); - - get().setProviders(storeId, provider); - - return provider; - }, - setProviders: (storeId, provider) => { - set(({ providers }) => ({ - providers: { - ...providers, - [storeId]: provider, - }, - })); - }, setCurrentDoc: (doc) => { set({ currentDoc: doc }); }, diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/stores/useProviderStore.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/stores/useProviderStore.tsx new file mode 100644 index 000000000..a638045a1 --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-management/stores/useProviderStore.tsx @@ -0,0 +1,52 @@ +import { HocuspocusProvider } from '@hocuspocus/provider'; +import * as Y from 'yjs'; +import { create } from 'zustand'; + +import { Base64 } from '@/features/docs/doc-management'; + +export interface UseCollaborationStore { + createProvider: ( + providerUrl: string, + storeId: string, + initialDoc?: Base64, + ) => HocuspocusProvider; + destroyProvider: () => void; + provider: HocuspocusProvider | undefined; +} + +const defaultValues = { + provider: undefined, +}; + +export const useProviderStore = create((set, get) => ({ + ...defaultValues, + createProvider: (wsUrl, storeId, initialDoc) => { + const doc = new Y.Doc({ + guid: storeId, + }); + + if (initialDoc) { + Y.applyUpdate(doc, Buffer.from(initialDoc, 'base64')); + } + + const provider = new HocuspocusProvider({ + url: wsUrl, + name: storeId, + document: doc, + }); + + set({ + provider, + }); + + return provider; + }, + destroyProvider: () => { + const provider = get().provider; + if (provider) { + provider.destroy(); + } + + set(defaultValues); + }, +})); diff --git a/src/frontend/apps/impress/src/features/docs/doc-versioning/components/ModalVersion.tsx b/src/frontend/apps/impress/src/features/docs/doc-versioning/components/ModalVersion.tsx index 8ffba2db3..d1f6588c0 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-versioning/components/ModalVersion.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-versioning/components/ModalVersion.tsx @@ -13,9 +13,9 @@ import { Box, Text } from '@/components'; import { Doc, base64ToYDoc, - useDocStore, + useProviderStore, useUpdateDoc, -} from '@/features/docs/doc-management'; +} from '@/features/docs/doc-management/'; import { useDocVersion } from '../api'; import { KEY_LIST_DOC_VERSIONS } from '../api/useDocVersions'; @@ -40,7 +40,7 @@ export const ModalVersion = ({ const { t } = useTranslation(); const { toast } = useToastProvider(); const { push } = useRouter(); - const { providers } = useDocStore(); + const { provider } = useProviderStore(); const { mutate: updateDoc } = useUpdateDoc({ listInvalideQueries: [KEY_LIST_DOC_VERSIONS], onSuccess: () => { @@ -49,14 +49,14 @@ export const ModalVersion = ({ void push(`/docs/${docId}`); }; - if (!providers?.[docId] || !version?.content) { + if (!provider || !version?.content) { onDisplaySuccess(); return; } revertUpdate( - providers[docId].document, - providers[docId].document, + provider.document, + provider.document, base64ToYDoc(version.content), ); 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 a51c2bca4..a84119ca5 100644 --- a/src/frontend/apps/impress/src/pages/docs/[id]/index.tsx +++ b/src/frontend/apps/impress/src/pages/docs/[id]/index.tsx @@ -6,10 +6,15 @@ import { useEffect, useState } from 'react'; import { Box, Text } from '@/components'; import { TextErrors } from '@/components/TextErrors'; -import { useCollaborationUrl } from '@/core'; import { useAuthStore } from '@/core/auth'; import { DocEditor } from '@/features/docs/doc-editor'; -import { KEY_DOC, useDoc, useDocStore } from '@/features/docs/doc-management'; +import { + Doc, + KEY_DOC, + useCollaboration, + useDoc, + useDocStore, +} from '@/features/docs/doc-management/'; import { MainLayout } from '@/layouts'; import { useBroadcastStore } from '@/stores'; import { NextPageWithLayout } from '@/types/next'; @@ -41,14 +46,25 @@ interface DocProps { const DocPage = ({ id }: DocProps) => { const { login } = useAuthStore(); - const { data: docQuery, isError, error } = useDoc({ id }); - const [doc, setDoc] = useState(docQuery); - const { setCurrentDoc, createProvider, providers } = useDocStore(); - const { setBroadcastProvider, addTask } = useBroadcastStore(); + const { + data: docQuery, + isError, + isFetching, + error, + } = useDoc( + { id }, + { + staleTime: 0, + queryKey: [KEY_DOC, { id }], + }, + ); + + const [doc, setDoc] = useState(); + const { setCurrentDoc } = useDocStore(); + const { addTask } = useBroadcastStore(); const queryClient = useQueryClient(); const { replace } = useRouter(); - const provider = providers?.[id]; - const collaborationUrl = useCollaborationUrl(doc?.id); + useCollaboration(doc?.id, doc?.content); useEffect(() => { if (doc?.title) { @@ -59,26 +75,13 @@ const DocPage = ({ id }: DocProps) => { }, [doc?.title]); useEffect(() => { - if (!docQuery) { + if (!docQuery || isFetching) { return; } setDoc(docQuery); setCurrentDoc(docQuery); - }, [docQuery, setCurrentDoc]); - - useEffect(() => { - if (!doc?.id || !collaborationUrl) { - return; - } - - let newProvider = provider; - if (!provider || provider.document.guid !== doc.id) { - newProvider = createProvider(collaborationUrl, doc.id, doc.content); - } - - setBroadcastProvider(newProvider); - }, [createProvider, doc, provider, setBroadcastProvider, collaborationUrl]); + }, [docQuery, setCurrentDoc, isFetching]); /** * We add a broadcast task to reset the query cache