🚚(frontend) better separation concern doc-versioning features

We move some components related to doc versioning
into the doc-versioning feature folder to have a
better separation of concerns.
We don't need a provider for the doc versioning components
since they will receive the doc data directly via
a request.
This commit is contained in:
Anthony LC
2025-10-21 17:22:16 +02:00
parent d5c3f248a5
commit 39c22b074d
6 changed files with 144 additions and 118 deletions

View File

@@ -1,31 +1,63 @@
import { Loader } from '@openfun/cunningham-react';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import { useEffect } from 'react';
import { css } from 'styled-components';
import * as Y from 'yjs';
import { Box, Loading, Text, TextErrors } from '@/components';
import { DocHeader, DocVersionHeader } from '@/docs/doc-header/';
import {
Doc,
base64ToBlocknoteXmlFragment,
useProviderStore,
} from '@/docs/doc-management';
import { Box, Loading } from '@/components';
import { DocHeader } from '@/docs/doc-header/';
import { Doc, useProviderStore } from '@/docs/doc-management';
import { TableContent } from '@/docs/doc-table-content/';
import { Versions, useDocVersion } from '@/docs/doc-versioning/';
import { useSkeletonStore } from '@/features/skeletons';
import { useResponsiveStore } from '@/stores';
import { BlockNoteEditor, BlockNoteEditorVersion } from './BlockNoteEditor';
import { BlockNoteEditor } from './BlockNoteEditor';
interface DocEditorContainerProps {
docHeader: React.ReactNode;
docEditor: React.ReactNode;
}
export const DocEditorContainer = ({
docHeader,
docEditor,
}: DocEditorContainerProps) => {
const { isDesktop } = useResponsiveStore();
return (
<>
<Box
$maxWidth="868px"
$width="100%"
$height="100%"
className="--docs--doc-editor"
>
<Box
$padding={{ horizontal: isDesktop ? '54px' : 'base' }}
className="--docs--doc-editor-header"
>
{docHeader}
</Box>
<Box
$direction="row"
$width="100%"
$css="overflow-x: clip; flex: 1;"
$position="relative"
className="--docs--doc-editor-content"
>
<Box $css="flex:1;" $position="relative" $width="100%">
{docEditor}
</Box>
</Box>
</Box>
</>
);
};
interface DocEditorProps {
doc: Doc;
versionId?: Versions['version_id'];
}
export const DocEditor = ({ doc, versionId }: DocEditorProps) => {
export const DocEditor = ({ doc }: DocEditorProps) => {
const { isDesktop } = useResponsiveStore();
const isVersion = !!versionId && typeof versionId === 'string';
const { provider, isReady } = useProviderStore();
const { setIsSkeletonVisible } = useSkeletonStore();
const isProviderReady = isReady && provider;
@@ -42,7 +74,7 @@ export const DocEditor = ({ doc, versionId }: DocEditorProps) => {
return (
<>
{isDesktop && !isVersion && (
{isDesktop && (
<Box
$position="absolute"
$css={css`
@@ -53,102 +85,10 @@ export const DocEditor = ({ doc, versionId }: DocEditorProps) => {
<TableContent />
</Box>
)}
<Box
$maxWidth="868px"
$width="100%"
$height="100%"
className="--docs--doc-editor"
>
<Box
$padding={{ horizontal: isDesktop ? '54px' : 'base' }}
className="--docs--doc-editor-header"
>
{isVersion ? <DocVersionHeader /> : <DocHeader doc={doc} />}
</Box>
<Box
$direction="row"
$width="100%"
$css="overflow-x: clip; flex: 1;"
$position="relative"
className="--docs--doc-editor-content"
>
<Box $css="flex:1;" $position="relative" $width="100%">
{isVersion ? (
<DocVersionEditor docId={doc.id} versionId={versionId} />
) : (
<BlockNoteEditor doc={doc} provider={provider} />
)}
</Box>
</Box>
</Box>
<DocEditorContainer
docHeader={<DocHeader doc={doc} />}
docEditor={<BlockNoteEditor doc={doc} provider={provider} />}
/>
</>
);
};
interface DocVersionEditorProps {
docId: Doc['id'];
versionId: Versions['version_id'];
}
export const DocVersionEditor = ({
docId,
versionId,
}: DocVersionEditorProps) => {
const {
data: version,
isLoading,
isError,
error,
} = useDocVersion({
docId,
versionId,
});
const { replace } = useRouter();
const [initialContent, setInitialContent] = useState<Y.XmlFragment>();
useEffect(() => {
if (!version?.content) {
return;
}
setInitialContent(base64ToBlocknoteXmlFragment(version.content));
}, [version?.content]);
if (isError && error) {
if (error.status === 404) {
void replace(`/404`);
return null;
}
return (
<Box $margin="large" className="--docs--doc-version-editor-error">
<TextErrors
causes={error.cause}
icon={
error.status === 502 ? (
<Text
className="material-icons"
$theme="danger"
aria-hidden={true}
>
wifi_off
</Text>
) : undefined
}
/>
</Box>
);
}
if (isLoading || !version || !initialContent) {
return (
<Box $align="center" $justify="center" $height="100%">
<Loader />
</Box>
);
}
return <BlockNoteEditorVersion initialContent={initialContent} />;
};

View File

@@ -1,3 +1,4 @@
export * from './BlockNoteEditor';
export * from './DocEditor';
export * from './EmojiPicker';
export * from './custom-blocks/';

View File

@@ -1,2 +1,2 @@
export * from './DocHeader';
export * from './DocVersionHeader';
export * from './DocTitle';

View File

@@ -0,0 +1,83 @@
import { Loader } from '@openfun/cunningham-react';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import * as Y from 'yjs';
import { Box, Text, TextErrors } from '@/components';
import { BlockNoteEditorVersion, DocEditorContainer } from '@/docs/doc-editor/';
import { Doc, base64ToBlocknoteXmlFragment } from '@/docs/doc-management';
import { Versions, useDocVersion } from '@/docs/doc-versioning/';
import { DocVersionHeader } from './DocVersionHeader';
interface DocVersionEditorProps {
docId: Doc['id'];
versionId: Versions['version_id'];
}
export const DocVersionEditor = ({
docId,
versionId,
}: DocVersionEditorProps) => {
const {
data: version,
isLoading,
isError,
error,
} = useDocVersion({
docId,
versionId,
});
const { replace } = useRouter();
const [initialContent, setInitialContent] = useState<Y.XmlFragment>();
useEffect(() => {
if (!version?.content) {
return;
}
setInitialContent(base64ToBlocknoteXmlFragment(version.content));
}, [version?.content]);
if (isError && error) {
if (error.status === 404) {
void replace(`/404`);
return null;
}
return (
<Box $margin="large" className="--docs--doc-version-editor-error">
<TextErrors
causes={error.cause}
icon={
error.status === 502 ? (
<Text
className="material-icons"
$theme="danger"
aria-hidden={true}
>
wifi_off
</Text>
) : undefined
}
/>
</Box>
);
}
if (isLoading || !version || !initialContent) {
return (
<Box $align="center" $justify="center" $height="100%">
<Loader />
</Box>
);
}
return (
<DocEditorContainer
docHeader={<DocVersionHeader />}
docEditor={<BlockNoteEditorVersion initialContent={initialContent} />}
/>
);
};

View File

@@ -2,8 +2,7 @@ import { useTranslation } from 'react-i18next';
import { Box, HorizontalSeparator } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { DocTitleText } from './DocTitle';
import { DocTitleText } from '@/docs/doc-header';
export const DocVersionHeader = () => {
const { spacingsTokens } = useCunninghamTheme();

View File

@@ -4,11 +4,11 @@ import { useTranslation } from 'react-i18next';
import { createGlobalStyle, css } from 'styled-components';
import { Box, ButtonCloseModal, Text } from '@/components';
import { DocEditor } from '@/docs/doc-editor';
import { Doc } from '@/docs/doc-management';
import { Versions } from '../types';
import { DocVersionEditor } from './DocVersionEditor';
import { ModalConfirmationVersion } from './ModalConfirmationVersion';
import { VersionList } from './VersionList';
@@ -81,7 +81,10 @@ export const ModalSelectVersion = ({
$align="center"
>
{selectedVersionId && (
<DocEditor doc={doc} versionId={selectedVersionId} />
<DocVersionEditor
docId={doc.id}
versionId={selectedVersionId}
/>
)}
{!selectedVersionId && (
<Box $align="center" $justify="center" $height="100%">