* fix: add lock unlock archive restore realtime sync * fix: show only after editor loads * fix: added strong types * fix: live events fixed * fix: remove unused vars and logs * fix: converted objects to enum * fix: error handling and removing the events in read only mode * fix: added check to only update if the image aspect ratio is not present already * fix: imports * fix: props order * revert: no need of these changes anymore * fix: updated type names * fix: order of things * fix: fixed types and renamed variables * fix: better typing for the real time updates * fix: trying multiplexing our socket connection * fix: multiplexing socket connection in read only editor as well * fix: remove single socket logic * fix: fixing the cleanup deps for the provider and localprovider * fix: add a better data structure for managing events * chore: refactored realtime events into hooks * feat: fetch page meta while focusing tabs * fix: cycling through items on slash command item in down arrow * fix: better naming convention for realtime events * fix: simplified localprovider initialization and cleaning * fix: types from ui * fix: abstracted away from exposing the provider directly * fix: coderabbit suggestions * regression: pass user in dependency array * fix: removed page action api calls by the other users the document is synced with * chore: removed unused imports
100 lines
3.8 KiB
TypeScript
100 lines
3.8 KiB
TypeScript
import { useState, useEffect, useCallback, useMemo } from "react";
|
|
import { EditorReadOnlyRefApi, EditorRefApi, TDocumentEventsServer } from "@plane/editor";
|
|
import { DocumentCollaborativeEvents, TDocumentEventsClient, getServerEventName } from "@plane/editor/lib";
|
|
import { TOAST_TYPE, setToast } from "@plane/ui";
|
|
import { IPage } from "@/store/pages/page";
|
|
|
|
// Better type naming and structure
|
|
type CollaborativeAction = {
|
|
execute: (shouldSync?: boolean) => Promise<void>;
|
|
errorMessage: string;
|
|
};
|
|
|
|
type CollaborativeActionEvent =
|
|
| { type: "sendMessageToServer"; message: TDocumentEventsServer }
|
|
| { type: "receivedMessageFromServer"; message: TDocumentEventsClient };
|
|
|
|
export const useCollaborativePageActions = (editorRef: EditorRefApi | EditorReadOnlyRefApi | null, page: IPage) => {
|
|
// currentUserAction local state to track if the current action is being processed, a
|
|
// local action is basically the action performed by the current user to avoid double operations
|
|
const [currentActionBeingProcessed, setCurrentActionBeingProcessed] = useState<TDocumentEventsClient | null>(null);
|
|
|
|
const actionHandlerMap: Record<TDocumentEventsClient, CollaborativeAction> = useMemo(
|
|
() => ({
|
|
[DocumentCollaborativeEvents.lock.client]: {
|
|
execute: (shouldSync) => page.lock(shouldSync),
|
|
errorMessage: "Page could not be locked. Please try again later.",
|
|
},
|
|
[DocumentCollaborativeEvents.unlock.client]: {
|
|
execute: (shouldSync) => page.unlock(shouldSync),
|
|
errorMessage: "Page could not be unlocked. Please try again later.",
|
|
},
|
|
[DocumentCollaborativeEvents.archive.client]: {
|
|
execute: (shouldSync) => page.archive(shouldSync),
|
|
errorMessage: "Page could not be archived. Please try again later.",
|
|
},
|
|
[DocumentCollaborativeEvents.unarchive.client]: {
|
|
execute: (shouldSync) => page.restore(shouldSync),
|
|
errorMessage: "Page could not be restored. Please try again later.",
|
|
},
|
|
}),
|
|
[page]
|
|
);
|
|
|
|
const executeCollaborativeAction = useCallback(
|
|
async (event: CollaborativeActionEvent) => {
|
|
const isPerformedByCurrentUser = event.type === "sendMessageToServer";
|
|
const clientAction = isPerformedByCurrentUser ? DocumentCollaborativeEvents[event.message].client : event.message;
|
|
const actionDetails = actionHandlerMap[clientAction];
|
|
|
|
try {
|
|
await actionDetails.execute(isPerformedByCurrentUser);
|
|
if (isPerformedByCurrentUser) {
|
|
setCurrentActionBeingProcessed(clientAction);
|
|
}
|
|
} catch {
|
|
setToast({
|
|
type: TOAST_TYPE.ERROR,
|
|
title: "Error!",
|
|
message: actionDetails.errorMessage,
|
|
});
|
|
}
|
|
},
|
|
[actionHandlerMap]
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (currentActionBeingProcessed) {
|
|
const serverEventName = getServerEventName(currentActionBeingProcessed);
|
|
if (serverEventName) {
|
|
editorRef?.emitRealTimeUpdate(serverEventName);
|
|
}
|
|
}
|
|
}, [currentActionBeingProcessed, editorRef]);
|
|
|
|
useEffect(() => {
|
|
const realTimeStatelessMessageListener = editorRef?.listenToRealTimeUpdate();
|
|
|
|
const handleStatelessMessage = (message: { payload: TDocumentEventsClient }) => {
|
|
if (currentActionBeingProcessed === message.payload) {
|
|
setCurrentActionBeingProcessed(null);
|
|
return;
|
|
}
|
|
|
|
if (message.payload) {
|
|
executeCollaborativeAction({ type: "receivedMessageFromServer", message: message.payload });
|
|
}
|
|
};
|
|
|
|
realTimeStatelessMessageListener?.on("stateless", handleStatelessMessage);
|
|
|
|
return () => {
|
|
realTimeStatelessMessageListener?.off("stateless", handleStatelessMessage);
|
|
};
|
|
}, [editorRef, currentActionBeingProcessed, executeCollaborativeAction]);
|
|
|
|
return {
|
|
executeCollaborativeAction,
|
|
EVENT_ACTION_DETAILS_MAP: actionHandlerMap,
|
|
};
|
|
};
|