Skip to content

Commit f416aa7

Browse files
committed
♻️(frontend) create useProviderStore
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.
1 parent 48a6753 commit f416aa7

File tree

9 files changed

+126
-73
lines changed

9 files changed

+126
-73
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to
1616
## Changed
1717

1818
- 🏗️(yjs-server) organize yjs server #528
19+
- ♻️(frontend) better separation collaboration process #528
1920

2021

2122
## [1.10.0] - 2024-12-17

src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { DocHeader } from '@/features/docs/doc-header';
1010
import {
1111
Doc,
1212
base64ToBlocknoteXmlFragment,
13-
useDocStore,
13+
useProviderStore,
1414
} from '@/features/docs/doc-management';
1515
import { Versions, useDocVersion } from '@/features/docs/doc-versioning/';
1616
import { useResponsiveStore } from '@/stores';
@@ -33,8 +33,7 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
3333

3434
const { colorsTokens } = useCunninghamTheme();
3535

36-
const { providers } = useDocStore();
37-
const provider = providers?.[doc.id];
36+
const { provider } = useProviderStore();
3837

3938
if (!provider) {
4039
return null;
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export * from './useCollaboration';
12
export * from './useTrans';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { useEffect } from 'react';
2+
3+
import { useCollaborationUrl } from '@/core/config';
4+
import { useBroadcastStore } from '@/stores';
5+
6+
import { useProviderStore } from '../stores/useProviderStore';
7+
import { Base64 } from '../types';
8+
9+
export const useCollaboration = (room?: string, initialContent?: Base64) => {
10+
const collaborationUrl = useCollaborationUrl(room);
11+
const { setBroadcastProvider } = useBroadcastStore();
12+
const { provider, createProvider, destroyProvider } = useProviderStore();
13+
14+
useEffect(() => {
15+
if (!room || !collaborationUrl || provider) {
16+
return;
17+
}
18+
19+
const newProvider = createProvider(collaborationUrl, room, initialContent);
20+
setBroadcastProvider(newProvider);
21+
}, [
22+
provider,
23+
collaborationUrl,
24+
room,
25+
initialContent,
26+
createProvider,
27+
setBroadcastProvider,
28+
]);
29+
30+
useEffect(() => {
31+
return () => {
32+
destroyProvider();
33+
};
34+
}, [destroyProvider]);
35+
};
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from './useDocStore';
2+
export * from './useProviderStore';

src/frontend/apps/impress/src/features/docs/doc-management/stores/useDocStore.tsx

+2-41
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,14 @@
1-
import { HocuspocusProvider } from '@hocuspocus/provider';
2-
import * as Y from 'yjs';
31
import { create } from 'zustand';
42

5-
import { Base64, Doc } from '@/features/docs/doc-management';
3+
import { Doc } from '@/features/docs/doc-management';
64

75
export interface UseDocStore {
86
currentDoc?: Doc;
9-
providers: {
10-
[storeId: string]: HocuspocusProvider;
11-
};
12-
createProvider: (
13-
providerUrl: string,
14-
storeId: string,
15-
initialDoc: Base64,
16-
) => HocuspocusProvider;
17-
setProviders: (storeId: string, providers: HocuspocusProvider) => void;
187
setCurrentDoc: (doc: Doc | undefined) => void;
198
}
209

21-
export const useDocStore = create<UseDocStore>((set, get) => ({
10+
export const useDocStore = create<UseDocStore>((set) => ({
2211
currentDoc: undefined,
23-
providers: {},
24-
createProvider: (providerUrl, storeId, initialDoc) => {
25-
const doc = new Y.Doc({
26-
guid: storeId,
27-
});
28-
29-
if (initialDoc) {
30-
Y.applyUpdate(doc, Buffer.from(initialDoc, 'base64'));
31-
}
32-
33-
const provider = new HocuspocusProvider({
34-
url: providerUrl,
35-
name: storeId,
36-
document: doc,
37-
});
38-
39-
get().setProviders(storeId, provider);
40-
41-
return provider;
42-
},
43-
setProviders: (storeId, provider) => {
44-
set(({ providers }) => ({
45-
providers: {
46-
...providers,
47-
[storeId]: provider,
48-
},
49-
}));
50-
},
5112
setCurrentDoc: (doc) => {
5213
set({ currentDoc: doc });
5314
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { HocuspocusProvider } from '@hocuspocus/provider';
2+
import * as Y from 'yjs';
3+
import { create } from 'zustand';
4+
5+
import { Base64 } from '@/features/docs/doc-management';
6+
7+
export interface UseCollaborationStore {
8+
createProvider: (
9+
providerUrl: string,
10+
storeId: string,
11+
initialDoc?: Base64,
12+
) => HocuspocusProvider;
13+
destroyProvider: () => void;
14+
provider: HocuspocusProvider | undefined;
15+
}
16+
17+
const defaultValues = {
18+
provider: undefined,
19+
};
20+
21+
export const useProviderStore = create<UseCollaborationStore>((set, get) => ({
22+
...defaultValues,
23+
createProvider: (wsUrl, storeId, initialDoc) => {
24+
const doc = new Y.Doc({
25+
guid: storeId,
26+
});
27+
28+
if (initialDoc) {
29+
Y.applyUpdate(doc, Buffer.from(initialDoc, 'base64'));
30+
}
31+
32+
const provider = new HocuspocusProvider({
33+
url: wsUrl,
34+
name: storeId,
35+
document: doc,
36+
});
37+
38+
set({
39+
provider,
40+
});
41+
42+
return provider;
43+
},
44+
destroyProvider: () => {
45+
const provider = get().provider;
46+
if (provider) {
47+
provider.destroy();
48+
}
49+
50+
set(defaultValues);
51+
},
52+
}));

src/frontend/apps/impress/src/features/docs/doc-versioning/components/ModalVersion.tsx

+6-6
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import { Box, Text } from '@/components';
1313
import {
1414
Doc,
1515
base64ToYDoc,
16-
useDocStore,
16+
useProviderStore,
1717
useUpdateDoc,
18-
} from '@/features/docs/doc-management';
18+
} from '@/features/docs/doc-management/';
1919

2020
import { useDocVersion } from '../api';
2121
import { KEY_LIST_DOC_VERSIONS } from '../api/useDocVersions';
@@ -40,7 +40,7 @@ export const ModalVersion = ({
4040
const { t } = useTranslation();
4141
const { toast } = useToastProvider();
4242
const { push } = useRouter();
43-
const { providers } = useDocStore();
43+
const { provider } = useProviderStore();
4444
const { mutate: updateDoc } = useUpdateDoc({
4545
listInvalideQueries: [KEY_LIST_DOC_VERSIONS],
4646
onSuccess: () => {
@@ -49,14 +49,14 @@ export const ModalVersion = ({
4949
void push(`/docs/${docId}`);
5050
};
5151

52-
if (!providers?.[docId] || !version?.content) {
52+
if (!provider || !version?.content) {
5353
onDisplaySuccess();
5454
return;
5555
}
5656

5757
revertUpdate(
58-
providers[docId].document,
59-
providers[docId].document,
58+
provider.document,
59+
provider.document,
6060
base64ToYDoc(version.content),
6161
);
6262

src/frontend/apps/impress/src/pages/docs/[id]/index.tsx

+26-23
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@ import { useEffect, useState } from 'react';
66

77
import { Box, Text } from '@/components';
88
import { TextErrors } from '@/components/TextErrors';
9-
import { useCollaborationUrl } from '@/core';
109
import { useAuthStore } from '@/core/auth';
1110
import { DocEditor } from '@/features/docs/doc-editor';
12-
import { KEY_DOC, useDoc, useDocStore } from '@/features/docs/doc-management';
11+
import {
12+
Doc,
13+
KEY_DOC,
14+
useCollaboration,
15+
useDoc,
16+
useDocStore,
17+
} from '@/features/docs/doc-management/';
1318
import { MainLayout } from '@/layouts';
1419
import { useBroadcastStore } from '@/stores';
1520
import { NextPageWithLayout } from '@/types/next';
@@ -41,14 +46,25 @@ interface DocProps {
4146

4247
const DocPage = ({ id }: DocProps) => {
4348
const { login } = useAuthStore();
44-
const { data: docQuery, isError, error } = useDoc({ id });
45-
const [doc, setDoc] = useState(docQuery);
46-
const { setCurrentDoc, createProvider, providers } = useDocStore();
47-
const { setBroadcastProvider, addTask } = useBroadcastStore();
49+
const {
50+
data: docQuery,
51+
isError,
52+
isFetching,
53+
error,
54+
} = useDoc(
55+
{ id },
56+
{
57+
staleTime: 0,
58+
queryKey: [KEY_DOC, { id }],
59+
},
60+
);
61+
62+
const [doc, setDoc] = useState<Doc>();
63+
const { setCurrentDoc } = useDocStore();
64+
const { addTask } = useBroadcastStore();
4865
const queryClient = useQueryClient();
4966
const { replace } = useRouter();
50-
const provider = providers?.[id];
51-
const collaborationUrl = useCollaborationUrl(doc?.id);
67+
useCollaboration(doc?.id, doc?.content);
5268

5369
useEffect(() => {
5470
if (doc?.title) {
@@ -59,26 +75,13 @@ const DocPage = ({ id }: DocProps) => {
5975
}, [doc?.title]);
6076

6177
useEffect(() => {
62-
if (!docQuery) {
78+
if (!docQuery || isFetching) {
6379
return;
6480
}
6581

6682
setDoc(docQuery);
6783
setCurrentDoc(docQuery);
68-
}, [docQuery, setCurrentDoc]);
69-
70-
useEffect(() => {
71-
if (!doc?.id || !collaborationUrl) {
72-
return;
73-
}
74-
75-
let newProvider = provider;
76-
if (!provider || provider.document.guid !== doc.id) {
77-
newProvider = createProvider(collaborationUrl, doc.id, doc.content);
78-
}
79-
80-
setBroadcastProvider(newProvider);
81-
}, [createProvider, doc, provider, setBroadcastProvider, collaborationUrl]);
84+
}, [docQuery, setCurrentDoc, isFetching]);
8285

8386
/**
8487
* We add a broadcast task to reset the query cache

0 commit comments

Comments
 (0)