⬆️(y-provider) update hocuspocus to 3.2.5

The last version of Blocknote seems to have a
conflict with hocuspocus 2.15.2, it is a good
moment to upgrade to hocuspocus 3.2.5.
This commit is contained in:
Anthony LC
2025-10-20 21:15:40 +02:00
parent fe24c00178
commit 3e5bcf96ea
16 changed files with 124 additions and 112 deletions

View File

@@ -35,7 +35,7 @@
"@fontsource/material-icons": "5.2.7",
"@gouvfr-lasuite/integration": "1.0.3",
"@gouvfr-lasuite/ui-kit": "0.16.2",
"@hocuspocus/provider": "2.15.2",
"@hocuspocus/provider": "3.3.0",
"@mantine/core": "8.3.4",
"@mantine/hooks": "8.3.4",
"@openfun/cunningham-react": "3.2.3",

View File

@@ -17,7 +17,11 @@ import { useTranslation } from 'react-i18next';
import * as Y from 'yjs';
import { Box, TextErrors } from '@/components';
import { Doc, useIsCollaborativeEditable } from '@/docs/doc-management';
import {
Doc,
useIsCollaborativeEditable,
useProviderStore,
} from '@/docs/doc-management';
import { useAuth } from '@/features/auth';
import {
@@ -79,9 +83,10 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
const { user } = useAuth();
const { setEditor } = useEditorStore();
const { t } = useTranslation();
const { isSynced } = useProviderStore();
const { isEditable, isLoading } = useIsCollaborativeEditable(doc);
const isConnectedToCollabServer = provider.isSynced;
const isConnectedToCollabServer = isSynced;
const readOnly = !doc.abilities.partial_update || !isEditable || isLoading;
const isDeletedDoc = !!doc.deleted_at;

View File

@@ -4,7 +4,7 @@ import { useEffect, useState } from 'react';
import { css } from 'styled-components';
import * as Y from 'yjs';
import { Box, Text, TextErrors } from '@/components';
import { Box, Loading, Text, TextErrors } from '@/components';
import { DocHeader, DocVersionHeader } from '@/docs/doc-header/';
import {
Doc,
@@ -27,8 +27,9 @@ export const DocEditor = ({ doc, versionId }: DocEditorProps) => {
const isVersion = !!versionId && typeof versionId === 'string';
const { provider } = useProviderStore();
// TODO: Use skeleton instead of loading
if (!provider) {
return null;
return <Loading />;
}
return (

View File

@@ -1,3 +1,4 @@
import { CloseEvent } from '@hocuspocus/common';
import { HocuspocusProvider, WebSocketStatus } from '@hocuspocus/provider';
import * as Y from 'yjs';
import { create } from 'zustand';
@@ -13,6 +14,7 @@ export interface UseCollaborationStore {
destroyProvider: () => void;
provider: HocuspocusProvider | undefined;
isConnected: boolean;
isSynced: boolean;
hasLostConnection: boolean;
resetLostConnection: () => void;
}
@@ -20,9 +22,12 @@ export interface UseCollaborationStore {
const defaultValues = {
provider: undefined,
isConnected: false,
isSynced: false,
hasLostConnection: false,
};
type ExtendedCloseEvent = CloseEvent & { wasClean: boolean };
export const useProviderStore = create<UseCollaborationStore>((set, get) => ({
...defaultValues,
createProvider: (wsUrl, storeId, initialDoc) => {
@@ -38,6 +43,12 @@ export const useProviderStore = create<UseCollaborationStore>((set, get) => ({
url: wsUrl,
name: storeId,
document: doc,
onDisconnect(data) {
// Attempt to reconnect if the disconnection was clean (initiated by the client or server)
if ((data.event as ExtendedCloseEvent).wasClean) {
provider.connect();
}
},
onStatus: ({ status }) => {
set((state) => {
const nextConnected = status === WebSocketStatus.Connected;
@@ -50,6 +61,21 @@ export const useProviderStore = create<UseCollaborationStore>((set, get) => ({
};
});
},
onSynced: ({ state }) => {
set({ isSynced: state });
},
onClose(data) {
/**
* Handle the "Reset Connection" event from the server
* This is triggered when the server wants to reset the connection
* for clients in the room.
* A disconnect is made automatically but it takes time to be triggered,
* so we force the disconnection here.
*/
if (data.event.code === 1000) {
provider.disconnect();
}
},
});
set({

View File

@@ -6,7 +6,6 @@ import {
import { APIError, errorCauses, fetchAPI } from '@/api';
import { Access, KEY_DOC, KEY_LIST_DOC, Role } from '@/docs/doc-management';
import { useBroadcastStore } from '@/stores';
import { KEY_LIST_DOC_ACCESSES } from './useDocAccesses';
@@ -45,7 +44,6 @@ type UseUpdateDocAccessOptions = UseMutationOptions<
export const useUpdateDocAccess = (options?: UseUpdateDocAccessOptions) => {
const queryClient = useQueryClient();
const { broadcast } = useBroadcastStore();
return useMutation<Access, APIError, UpdateDocAccessProps>({
mutationFn: updateDocAccess,
@@ -58,12 +56,10 @@ export const useUpdateDocAccess = (options?: UseUpdateDocAccessOptions) => {
queryKey: [KEY_DOC],
});
// Broadcast to every user connected to the document
broadcast(`${KEY_DOC}-${variables.docId}`);
void queryClient.invalidateQueries({
queryKey: [KEY_LIST_DOC],
});
if (options?.onSuccess) {
void options.onSuccess(data, variables, onMutateResult, context);
}

View File

@@ -147,7 +147,7 @@ const DocPage = ({ id }: DocProps) => {
}
addTask(`${KEY_DOC}-${doc.id}`, () => {
void queryClient.resetQueries({
void queryClient.invalidateQueries({
queryKey: [KEY_DOC, { id: doc.id }],
});
});