diff --git a/CHANGELOG.md b/CHANGELOG.md index 981e3b80..2a6657a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to - 🔨(frontend) encapsulated title to its own component #474 - 🐛(frontend) Fix hidden menu on Firefox #468 - ⚡️(backend) optimize number of queries on document list view #411 +- ♻️(frontend) stop to use provider with version #480 - 🚚(collaboration) change the websocket key name #480 diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx index 8c04127b..cafecdbf 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx @@ -6,6 +6,7 @@ import { useCreateBlockNote } from '@blocknote/react'; import { HocuspocusProvider } from '@hocuspocus/provider'; import React, { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; +import * as Y from 'yjs'; import { Box, TextErrors } from '@/components'; import { useAuthStore } from '@/core/auth'; @@ -65,19 +66,14 @@ const cssEditor = (readonly: boolean) => ` interface BlockNoteEditorProps { doc: Doc; provider: HocuspocusProvider; - storeId: string; } -export const BlockNoteEditor = ({ - doc, - provider, - storeId, -}: BlockNoteEditorProps) => { - const isVersion = doc.id !== storeId; +export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => { const { userData } = useAuthStore(); const { setEditor } = useEditorStore(); const { t } = useTranslation(); - const readOnly = !doc.abilities.partial_update || isVersion; + + const readOnly = !doc.abilities.partial_update; useSaveDoc(doc.id, provider.document, !readOnly); const { setHeadings, resetHeadings } = useHeadingStore(); const { i18n } = useTranslation(); @@ -174,3 +170,46 @@ export const BlockNoteEditor = ({ ); }; + +interface BlockNoteEditorVersionProps { + initialContent: Y.XmlFragment; +} + +export const BlockNoteEditorVersion = ({ + initialContent, +}: BlockNoteEditorVersionProps) => { + const readOnly = true; + const { setHeadings, resetHeadings } = useHeadingStore(); + + const editor = useCreateBlockNote( + { + collaboration: { + fragment: initialContent, + user: { + name: '', + color: '', + }, + provider: undefined, + }, + }, + [initialContent], + ); + + useEffect(() => { + setHeadings(editor); + + editor?.onEditorContentChange(() => { + setHeadings(editor); + }); + + return () => { + resetHeadings(); + }; + }, [editor, resetHeadings, setHeadings]); + + return ( + + + + ); +}; 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 50b2b73a..58cce28f 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 @@ -1,19 +1,23 @@ import { Alert, Loader, VariantType } from '@openfun/cunningham-react'; import { useRouter } from 'next/router'; -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import * as Y from 'yjs'; import { Box, Card, Text, TextErrors } from '@/components'; -import { useCollaborationUrl } from '@/core'; import { useCunninghamTheme } from '@/cunningham'; import { DocHeader } from '@/features/docs/doc-header'; -import { Doc, useDocStore } from '@/features/docs/doc-management'; +import { + Doc, + base64ToBlocknoteXmlFragment, + useDocStore, +} from '@/features/docs/doc-management'; import { Versions, useDocVersion } from '@/features/docs/doc-versioning/'; import { useResponsiveStore } from '@/stores'; import { useHeadingStore } from '../stores'; -import { BlockNoteEditor } from './BlockNoteEditor'; +import { BlockNoteEditor, BlockNoteEditorVersion } from './BlockNoteEditor'; import { IconOpenPanelEditor, PanelEditor } from './PanelEditor'; interface DocEditorProps { @@ -41,7 +45,7 @@ export const DocEditor = ({ doc }: DocEditorProps) => { return ( <> - + {!doc.abilities.partial_update && ( @@ -71,9 +75,9 @@ export const DocEditor = ({ doc }: DocEditorProps) => { $position="relative" > {isVersion ? ( - + ) : ( - + )} {!isMobile && } @@ -84,35 +88,34 @@ export const DocEditor = ({ doc }: DocEditorProps) => { }; interface DocVersionEditorProps { - doc: Doc; + docId: Doc['id']; versionId: Versions['version_id']; } -export const DocVersionEditor = ({ doc, versionId }: DocVersionEditorProps) => { +export const DocVersionEditor = ({ + docId, + versionId, +}: DocVersionEditorProps) => { const { data: version, isLoading, isError, error, } = useDocVersion({ - docId: doc.id, + docId, versionId, }); - const { createProvider, providers } = useDocStore(); - const collaborationUrl = useCollaborationUrl(versionId); const { replace } = useRouter(); + const [initialContent, setInitialContent] = useState(); useEffect(() => { - if (!version?.id || !collaborationUrl) { + if (!version?.content) { return; } - const provider = providers?.[version.id]; - if (!provider || provider.document.guid !== version.id) { - createProvider(collaborationUrl, version.id, version.content); - } - }, [createProvider, providers, version, collaborationUrl]); + setInitialContent(base64ToBlocknoteXmlFragment(version.content)); + }, [version?.content]); if (isError && error) { if (error.status === 404) { @@ -136,7 +139,7 @@ export const DocVersionEditor = ({ doc, versionId }: DocVersionEditorProps) => { ); } - if (isLoading || !version) { + if (isLoading || !version || !initialContent) { return ( @@ -144,11 +147,5 @@ export const DocVersionEditor = ({ doc, versionId }: DocVersionEditorProps) => { ); } - const provider = providers?.[version.id]; - - if (!provider) { - return null; - } - - return ; + return ; }; 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 0e565b10..d169044a 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 @@ -5,7 +5,6 @@ import { css } from 'styled-components'; import { Box, Card, StyledLink, Text } from '@/components'; import { useCunninghamTheme } from '@/cunningham'; import { Doc, currentDocRole, useTrans } from '@/features/docs/doc-management'; -import { Versions } from '@/features/docs/doc-versioning'; import { useDate } from '@/hook'; import { useResponsiveStore } from '@/stores'; @@ -15,10 +14,9 @@ import { DocToolBox } from './DocToolBox'; interface DocHeaderProps { doc: Doc; - versionId?: Versions['version_id']; } -export const DocHeader = ({ doc, versionId }: DocHeaderProps) => { +export const DocHeader = ({ doc }: DocHeaderProps) => { const { colorsTokens } = useCunninghamTheme(); const { t } = useTranslation(); const { formatDate } = useDate(); @@ -69,7 +67,7 @@ export const DocHeader = ({ doc, versionId }: DocHeaderProps) => { $align="center" > - + { +export const DocToolBox = ({ doc }: DocToolBoxProps) => { + const { + query: { versionId }, + } = useRouter(); const { t } = useTranslation(); const [isModalShareOpen, setIsModalShareOpen] = useState(false); const [isModalRemoveOpen, setIsModalRemoveOpen] = useState(false); @@ -194,7 +197,7 @@ export const DocToolBox = ({ doc, versionId }: DocToolBoxProps) => { setIsModalVersionOpen(false)} docId={doc.id} - versionId={versionId} + versionId={versionId as string} /> )} diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/utils.ts b/src/frontend/apps/impress/src/features/docs/doc-management/utils.ts index 7ba6571a..198c913c 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/utils.ts +++ b/src/frontend/apps/impress/src/features/docs/doc-management/utils.ts @@ -28,3 +28,14 @@ export const blocksToYDoc = (blocks: BasicBlock[], doc: Y.Doc) => { xmlFragment.push([xmlElement]); }); }; + +export const base64ToYDoc = (base64: string) => { + const uint8Array = Buffer.from(base64, 'base64'); + const ydoc = new Y.Doc(); + Y.applyUpdate(ydoc, uint8Array); + return ydoc; +}; + +export const base64ToBlocknoteXmlFragment = (base64: string) => { + return base64ToYDoc(base64).getXmlFragment('document-store'); +}; 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 7bc45046..8ffba2db 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 @@ -8,12 +8,16 @@ import { } from '@openfun/cunningham-react'; import { useRouter } from 'next/router'; import { useTranslation } from 'react-i18next'; -import * as Y from 'yjs'; import { Box, Text } from '@/components'; -import { toBase64 } from '@/features/docs/doc-editor'; -import { Doc, useDocStore, useUpdateDoc } from '@/features/docs/doc-management'; +import { + Doc, + base64ToYDoc, + useDocStore, + useUpdateDoc, +} from '@/features/docs/doc-management'; +import { useDocVersion } from '../api'; import { KEY_LIST_DOC_VERSIONS } from '../api/useDocVersions'; import { Versions } from '../types'; import { revertUpdate } from '../utils'; @@ -21,7 +25,6 @@ import { revertUpdate } from '../utils'; interface ModalVersionProps { onClose: () => void; docId: Doc['id']; - versionId: Versions['version_id']; } @@ -30,6 +33,10 @@ export const ModalVersion = ({ docId, versionId, }: ModalVersionProps) => { + const { data: version } = useDocVersion({ + docId, + versionId, + }); const { t } = useTranslation(); const { toast } = useToastProvider(); const { push } = useRouter(); @@ -42,7 +49,7 @@ export const ModalVersion = ({ void push(`/docs/${docId}`); }; - if (!providers?.[docId] || !providers?.[versionId]) { + if (!providers?.[docId] || !version?.content) { onDisplaySuccess(); return; } @@ -50,7 +57,7 @@ export const ModalVersion = ({ revertUpdate( providers[docId].document, providers[docId].document, - providers[versionId].document, + base64ToYDoc(version.content), ); onDisplaySuccess(); @@ -79,13 +86,13 @@ export const ModalVersion = ({ color="primary" fullWidth onClick={() => { - const newDoc = toBase64( - Y.encodeStateAsUpdate(providers?.[versionId].document), - ); + if (!version?.content) { + return; + } updateDoc({ id: docId, - content: newDoc, + content: version.content, }); onClose();