[PE-31] feat: Add lock unlock archive restore realtime sync (#5629)
* 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
This commit is contained in:
parent
8c04aa6f51
commit
3c6006d04a
21 changed files with 277 additions and 115 deletions
100
web/core/hooks/use-collaborative-page-actions.tsx
Normal file
100
web/core/hooks/use-collaborative-page-actions.tsx
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
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,
|
||||
};
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue