mirror of
https://github.com/makeplane/plane.git
synced 2025-12-21 14:19:38 +01:00
* dev: support for edition specific options in pages * refactor: page quick actions * chore: add customizable page actions * fix: type errors * dev: hook to get page operations * refactor: remove unnecessary props * chore: add permisssions to duplicate page endpoint * chore: memoize arranged options * chore: use enum for page access * chore: add type assertion * fix: auth for access change and delete * fix: removing readonly editor * chore: add sync for page access cahnge * fix: sync state * fix: indexeddb sync loader added * fix: remove node error fixed * style: page title and checkbox * chore: removing the syncing logic * revert: is editable check removed in display message * fix: editable field optional * fix: editable removed as optional prop * fix: extra options import fix * fix: remove readonly stuff * fix: added toggle access * chore: add access change sync * fix: full width toggle * refactor: types and enums added * refactore: update store action * chore: changed the duplicate viewset * fix: remove the page binary * fix: duplicate page action * fix: merge conflicts --------- Co-authored-by: Palanikannan M <akashmalinimurugu@gmail.com> Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
110 lines
4.1 KiB
TypeScript
110 lines
4.1 KiB
TypeScript
import { useState, useEffect, useCallback, useMemo } from "react";
|
|
import { EditorRefApi, TDocumentEventsServer } from "@plane/editor";
|
|
import { DocumentCollaborativeEvents, TDocumentEventsClient, getServerEventName } from "@plane/editor/lib";
|
|
// plane ui
|
|
import { TOAST_TYPE, setToast } from "@plane/ui";
|
|
// store
|
|
import { TPageInstance } from "@/store/pages/base-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 };
|
|
|
|
type Props = {
|
|
editorRef?: EditorRefApi | null;
|
|
page: TPageInstance;
|
|
};
|
|
|
|
export const useCollaborativePageActions = (props: Props) => {
|
|
const { editorRef, page } = props;
|
|
// 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.",
|
|
},
|
|
[DocumentCollaborativeEvents["make-public"].client]: {
|
|
execute: (shouldSync) => page.makePublic(shouldSync),
|
|
errorMessage: "Page could not be made public. Please try again later.",
|
|
},
|
|
[DocumentCollaborativeEvents["make-private"].client]: {
|
|
execute: (shouldSync) => page.makePrivate(shouldSync),
|
|
errorMessage: "Page could not be made private. 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) {
|
|
const serverEventName = getServerEventName(clientAction);
|
|
if (serverEventName) {
|
|
editorRef?.emitRealTimeUpdate(serverEventName);
|
|
}
|
|
setCurrentActionBeingProcessed(clientAction);
|
|
}
|
|
} catch {
|
|
setToast({
|
|
type: TOAST_TYPE.ERROR,
|
|
title: "Error!",
|
|
message: actionDetails.errorMessage,
|
|
});
|
|
}
|
|
},
|
|
[actionHandlerMap, 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,
|
|
};
|
|
};
|