♻️(frontend) stop to use provider with version
Version are not editable, we don't need to activate the collaboration provider for them. Simplify the code by removing the provider from the version.
This commit is contained in:
@@ -19,6 +19,7 @@ and this project adheres to
|
|||||||
- 🔨(frontend) encapsulated title to its own component #474
|
- 🔨(frontend) encapsulated title to its own component #474
|
||||||
- 🐛(frontend) Fix hidden menu on Firefox #468
|
- 🐛(frontend) Fix hidden menu on Firefox #468
|
||||||
- ⚡️(backend) optimize number of queries on document list view #411
|
- ⚡️(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
|
- 🚚(collaboration) change the websocket key name #480
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useCreateBlockNote } from '@blocknote/react';
|
|||||||
import { HocuspocusProvider } from '@hocuspocus/provider';
|
import { HocuspocusProvider } from '@hocuspocus/provider';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import * as Y from 'yjs';
|
||||||
|
|
||||||
import { Box, TextErrors } from '@/components';
|
import { Box, TextErrors } from '@/components';
|
||||||
import { useAuthStore } from '@/core/auth';
|
import { useAuthStore } from '@/core/auth';
|
||||||
@@ -65,19 +66,14 @@ const cssEditor = (readonly: boolean) => `
|
|||||||
interface BlockNoteEditorProps {
|
interface BlockNoteEditorProps {
|
||||||
doc: Doc;
|
doc: Doc;
|
||||||
provider: HocuspocusProvider;
|
provider: HocuspocusProvider;
|
||||||
storeId: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BlockNoteEditor = ({
|
export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
||||||
doc,
|
|
||||||
provider,
|
|
||||||
storeId,
|
|
||||||
}: BlockNoteEditorProps) => {
|
|
||||||
const isVersion = doc.id !== storeId;
|
|
||||||
const { userData } = useAuthStore();
|
const { userData } = useAuthStore();
|
||||||
const { setEditor } = useEditorStore();
|
const { setEditor } = useEditorStore();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const readOnly = !doc.abilities.partial_update || isVersion;
|
|
||||||
|
const readOnly = !doc.abilities.partial_update;
|
||||||
useSaveDoc(doc.id, provider.document, !readOnly);
|
useSaveDoc(doc.id, provider.document, !readOnly);
|
||||||
const { setHeadings, resetHeadings } = useHeadingStore();
|
const { setHeadings, resetHeadings } = useHeadingStore();
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
@@ -174,3 +170,46 @@ export const BlockNoteEditor = ({
|
|||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<Box $css={cssEditor(readOnly)}>
|
||||||
|
<BlockNoteView editor={editor} editable={!readOnly} theme="light" />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
import { Alert, Loader, VariantType } from '@openfun/cunningham-react';
|
import { Alert, Loader, VariantType } from '@openfun/cunningham-react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import * as Y from 'yjs';
|
||||||
|
|
||||||
import { Box, Card, Text, TextErrors } from '@/components';
|
import { Box, Card, Text, TextErrors } from '@/components';
|
||||||
import { useCollaborationUrl } from '@/core';
|
|
||||||
import { useCunninghamTheme } from '@/cunningham';
|
import { useCunninghamTheme } from '@/cunningham';
|
||||||
import { DocHeader } from '@/features/docs/doc-header';
|
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 { Versions, useDocVersion } from '@/features/docs/doc-versioning/';
|
||||||
import { useResponsiveStore } from '@/stores';
|
import { useResponsiveStore } from '@/stores';
|
||||||
|
|
||||||
import { useHeadingStore } from '../stores';
|
import { useHeadingStore } from '../stores';
|
||||||
|
|
||||||
import { BlockNoteEditor } from './BlockNoteEditor';
|
import { BlockNoteEditor, BlockNoteEditorVersion } from './BlockNoteEditor';
|
||||||
import { IconOpenPanelEditor, PanelEditor } from './PanelEditor';
|
import { IconOpenPanelEditor, PanelEditor } from './PanelEditor';
|
||||||
|
|
||||||
interface DocEditorProps {
|
interface DocEditorProps {
|
||||||
@@ -41,7 +45,7 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DocHeader doc={doc} versionId={versionId as Versions['version_id']} />
|
<DocHeader doc={doc} />
|
||||||
{!doc.abilities.partial_update && (
|
{!doc.abilities.partial_update && (
|
||||||
<Box $margin={{ all: 'small', top: 'none' }}>
|
<Box $margin={{ all: 'small', top: 'none' }}>
|
||||||
<Alert type={VariantType.WARNING}>
|
<Alert type={VariantType.WARNING}>
|
||||||
@@ -71,9 +75,9 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
|
|||||||
$position="relative"
|
$position="relative"
|
||||||
>
|
>
|
||||||
{isVersion ? (
|
{isVersion ? (
|
||||||
<DocVersionEditor doc={doc} versionId={versionId} />
|
<DocVersionEditor docId={doc.id} versionId={versionId} />
|
||||||
) : (
|
) : (
|
||||||
<BlockNoteEditor doc={doc} storeId={doc.id} provider={provider} />
|
<BlockNoteEditor doc={doc} provider={provider} />
|
||||||
)}
|
)}
|
||||||
{!isMobile && <IconOpenPanelEditor headings={headings} />}
|
{!isMobile && <IconOpenPanelEditor headings={headings} />}
|
||||||
</Card>
|
</Card>
|
||||||
@@ -84,35 +88,34 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface DocVersionEditorProps {
|
interface DocVersionEditorProps {
|
||||||
doc: Doc;
|
docId: Doc['id'];
|
||||||
versionId: Versions['version_id'];
|
versionId: Versions['version_id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DocVersionEditor = ({ doc, versionId }: DocVersionEditorProps) => {
|
export const DocVersionEditor = ({
|
||||||
|
docId,
|
||||||
|
versionId,
|
||||||
|
}: DocVersionEditorProps) => {
|
||||||
const {
|
const {
|
||||||
data: version,
|
data: version,
|
||||||
isLoading,
|
isLoading,
|
||||||
isError,
|
isError,
|
||||||
error,
|
error,
|
||||||
} = useDocVersion({
|
} = useDocVersion({
|
||||||
docId: doc.id,
|
docId,
|
||||||
versionId,
|
versionId,
|
||||||
});
|
});
|
||||||
const { createProvider, providers } = useDocStore();
|
|
||||||
const collaborationUrl = useCollaborationUrl(versionId);
|
|
||||||
|
|
||||||
const { replace } = useRouter();
|
const { replace } = useRouter();
|
||||||
|
const [initialContent, setInitialContent] = useState<Y.XmlFragment>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!version?.id || !collaborationUrl) {
|
if (!version?.content) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = providers?.[version.id];
|
setInitialContent(base64ToBlocknoteXmlFragment(version.content));
|
||||||
if (!provider || provider.document.guid !== version.id) {
|
}, [version?.content]);
|
||||||
createProvider(collaborationUrl, version.id, version.content);
|
|
||||||
}
|
|
||||||
}, [createProvider, providers, version, collaborationUrl]);
|
|
||||||
|
|
||||||
if (isError && error) {
|
if (isError && error) {
|
||||||
if (error.status === 404) {
|
if (error.status === 404) {
|
||||||
@@ -136,7 +139,7 @@ export const DocVersionEditor = ({ doc, versionId }: DocVersionEditorProps) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLoading || !version) {
|
if (isLoading || !version || !initialContent) {
|
||||||
return (
|
return (
|
||||||
<Box $align="center" $justify="center" $height="100%">
|
<Box $align="center" $justify="center" $height="100%">
|
||||||
<Loader />
|
<Loader />
|
||||||
@@ -144,11 +147,5 @@ export const DocVersionEditor = ({ doc, versionId }: DocVersionEditorProps) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = providers?.[version.id];
|
return <BlockNoteEditorVersion initialContent={initialContent} />;
|
||||||
|
|
||||||
if (!provider) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <BlockNoteEditor doc={doc} storeId={version.id} provider={provider} />;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { css } from 'styled-components';
|
|||||||
import { Box, Card, StyledLink, Text } from '@/components';
|
import { Box, Card, StyledLink, Text } from '@/components';
|
||||||
import { useCunninghamTheme } from '@/cunningham';
|
import { useCunninghamTheme } from '@/cunningham';
|
||||||
import { Doc, currentDocRole, useTrans } from '@/features/docs/doc-management';
|
import { Doc, currentDocRole, useTrans } from '@/features/docs/doc-management';
|
||||||
import { Versions } from '@/features/docs/doc-versioning';
|
|
||||||
import { useDate } from '@/hook';
|
import { useDate } from '@/hook';
|
||||||
import { useResponsiveStore } from '@/stores';
|
import { useResponsiveStore } from '@/stores';
|
||||||
|
|
||||||
@@ -15,10 +14,9 @@ import { DocToolBox } from './DocToolBox';
|
|||||||
|
|
||||||
interface DocHeaderProps {
|
interface DocHeaderProps {
|
||||||
doc: Doc;
|
doc: Doc;
|
||||||
versionId?: Versions['version_id'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DocHeader = ({ doc, versionId }: DocHeaderProps) => {
|
export const DocHeader = ({ doc }: DocHeaderProps) => {
|
||||||
const { colorsTokens } = useCunninghamTheme();
|
const { colorsTokens } = useCunninghamTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { formatDate } = useDate();
|
const { formatDate } = useDate();
|
||||||
@@ -69,7 +67,7 @@ export const DocHeader = ({ doc, versionId }: DocHeaderProps) => {
|
|||||||
$align="center"
|
$align="center"
|
||||||
>
|
>
|
||||||
<DocTitle doc={doc} />
|
<DocTitle doc={doc} />
|
||||||
<DocToolBox doc={doc} versionId={versionId} />
|
<DocToolBox doc={doc} />
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
VariantType,
|
VariantType,
|
||||||
useToastProvider,
|
useToastProvider,
|
||||||
} from '@openfun/cunningham-react';
|
} from '@openfun/cunningham-react';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@@ -17,17 +18,19 @@ import {
|
|||||||
ModalRemoveDoc,
|
ModalRemoveDoc,
|
||||||
ModalShare,
|
ModalShare,
|
||||||
} from '@/features/docs/doc-management';
|
} from '@/features/docs/doc-management';
|
||||||
import { ModalVersion, Versions } from '@/features/docs/doc-versioning';
|
import { ModalVersion } from '@/features/docs/doc-versioning';
|
||||||
import { useResponsiveStore } from '@/stores';
|
import { useResponsiveStore } from '@/stores';
|
||||||
|
|
||||||
import { ModalPDF } from './ModalExport';
|
import { ModalPDF } from './ModalExport';
|
||||||
|
|
||||||
interface DocToolBoxProps {
|
interface DocToolBoxProps {
|
||||||
doc: Doc;
|
doc: Doc;
|
||||||
versionId?: Versions['version_id'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DocToolBox = ({ doc, versionId }: DocToolBoxProps) => {
|
export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
||||||
|
const {
|
||||||
|
query: { versionId },
|
||||||
|
} = useRouter();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [isModalShareOpen, setIsModalShareOpen] = useState(false);
|
const [isModalShareOpen, setIsModalShareOpen] = useState(false);
|
||||||
const [isModalRemoveOpen, setIsModalRemoveOpen] = useState(false);
|
const [isModalRemoveOpen, setIsModalRemoveOpen] = useState(false);
|
||||||
@@ -194,7 +197,7 @@ export const DocToolBox = ({ doc, versionId }: DocToolBoxProps) => {
|
|||||||
<ModalVersion
|
<ModalVersion
|
||||||
onClose={() => setIsModalVersionOpen(false)}
|
onClose={() => setIsModalVersionOpen(false)}
|
||||||
docId={doc.id}
|
docId={doc.id}
|
||||||
versionId={versionId}
|
versionId={versionId as string}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -28,3 +28,14 @@ export const blocksToYDoc = (blocks: BasicBlock[], doc: Y.Doc) => {
|
|||||||
xmlFragment.push([xmlElement]);
|
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');
|
||||||
|
};
|
||||||
|
|||||||
@@ -8,12 +8,16 @@ import {
|
|||||||
} from '@openfun/cunningham-react';
|
} from '@openfun/cunningham-react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import * as Y from 'yjs';
|
|
||||||
|
|
||||||
import { Box, Text } from '@/components';
|
import { Box, Text } from '@/components';
|
||||||
import { toBase64 } from '@/features/docs/doc-editor';
|
import {
|
||||||
import { Doc, useDocStore, useUpdateDoc } from '@/features/docs/doc-management';
|
Doc,
|
||||||
|
base64ToYDoc,
|
||||||
|
useDocStore,
|
||||||
|
useUpdateDoc,
|
||||||
|
} from '@/features/docs/doc-management';
|
||||||
|
|
||||||
|
import { useDocVersion } from '../api';
|
||||||
import { KEY_LIST_DOC_VERSIONS } from '../api/useDocVersions';
|
import { KEY_LIST_DOC_VERSIONS } from '../api/useDocVersions';
|
||||||
import { Versions } from '../types';
|
import { Versions } from '../types';
|
||||||
import { revertUpdate } from '../utils';
|
import { revertUpdate } from '../utils';
|
||||||
@@ -21,7 +25,6 @@ import { revertUpdate } from '../utils';
|
|||||||
interface ModalVersionProps {
|
interface ModalVersionProps {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
docId: Doc['id'];
|
docId: Doc['id'];
|
||||||
|
|
||||||
versionId: Versions['version_id'];
|
versionId: Versions['version_id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,6 +33,10 @@ export const ModalVersion = ({
|
|||||||
docId,
|
docId,
|
||||||
versionId,
|
versionId,
|
||||||
}: ModalVersionProps) => {
|
}: ModalVersionProps) => {
|
||||||
|
const { data: version } = useDocVersion({
|
||||||
|
docId,
|
||||||
|
versionId,
|
||||||
|
});
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { toast } = useToastProvider();
|
const { toast } = useToastProvider();
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
@@ -42,7 +49,7 @@ export const ModalVersion = ({
|
|||||||
void push(`/docs/${docId}`);
|
void push(`/docs/${docId}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!providers?.[docId] || !providers?.[versionId]) {
|
if (!providers?.[docId] || !version?.content) {
|
||||||
onDisplaySuccess();
|
onDisplaySuccess();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -50,7 +57,7 @@ export const ModalVersion = ({
|
|||||||
revertUpdate(
|
revertUpdate(
|
||||||
providers[docId].document,
|
providers[docId].document,
|
||||||
providers[docId].document,
|
providers[docId].document,
|
||||||
providers[versionId].document,
|
base64ToYDoc(version.content),
|
||||||
);
|
);
|
||||||
|
|
||||||
onDisplaySuccess();
|
onDisplaySuccess();
|
||||||
@@ -79,13 +86,13 @@ export const ModalVersion = ({
|
|||||||
color="primary"
|
color="primary"
|
||||||
fullWidth
|
fullWidth
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const newDoc = toBase64(
|
if (!version?.content) {
|
||||||
Y.encodeStateAsUpdate(providers?.[versionId].document),
|
return;
|
||||||
);
|
}
|
||||||
|
|
||||||
updateDoc({
|
updateDoc({
|
||||||
id: docId,
|
id: docId,
|
||||||
content: newDoc,
|
content: version.content,
|
||||||
});
|
});
|
||||||
|
|
||||||
onClose();
|
onClose();
|
||||||
|
|||||||
Reference in New Issue
Block a user