diff --git a/packages/editor/src/core/constants/document-collaborative-events.ts b/packages/editor/src/core/constants/document-collaborative-events.ts index 5e79efc7a71..72e8b1dbded 100644 --- a/packages/editor/src/core/constants/document-collaborative-events.ts +++ b/packages/editor/src/core/constants/document-collaborative-events.ts @@ -3,4 +3,6 @@ export const DocumentCollaborativeEvents = { unlock: { client: "unlocked", server: "unlock" }, archive: { client: "archived", server: "archive" }, unarchive: { client: "unarchived", server: "unarchive" }, + "make-public": { client: "made-public", server: "make-public" }, + "make-private": { client: "made-private", server: "make-private" }, } as const; diff --git a/web/core/components/pages/dropdowns/actions.tsx b/web/core/components/pages/dropdowns/actions.tsx index 16914f370b6..f979d7f43bd 100644 --- a/web/core/components/pages/dropdowns/actions.tsx +++ b/web/core/components/pages/dropdowns/actions.tsx @@ -14,6 +14,8 @@ import { LockKeyholeOpen, Trash2, } from "lucide-react"; +// plane editor +import { EditorReadOnlyRefApi, EditorRefApi } from "@plane/editor"; // plane ui import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem } from "@plane/ui"; // components @@ -44,6 +46,7 @@ export type TPageActions = | "move"; type Props = { + editorRef?: EditorRefApi | EditorReadOnlyRefApi | null; extraOptions?: (TContextMenuItem & { key: TPageActions })[]; optionsOrder: TPageActions[]; page: IPage; @@ -51,12 +54,15 @@ type Props = { }; export const PageActions: React.FC = observer((props) => { - const { extraOptions, optionsOrder, page, parentRef } = props; + const { editorRef, extraOptions, optionsOrder, page, parentRef } = props; // states const [deletePageModal, setDeletePageModal] = useState(false); const [movePageModal, setMovePageModal] = useState(false); // page operations - const { pageOperations } = usePageOperations(page); + const { pageOperations } = usePageOperations({ + editorRef, + page, + }); // derived values const { access, diff --git a/web/core/components/pages/editor/header/extra-options.tsx b/web/core/components/pages/editor/header/extra-options.tsx index 58fa60705bb..bfc6d35eaf7 100644 --- a/web/core/components/pages/editor/header/extra-options.tsx +++ b/web/core/components/pages/editor/header/extra-options.tsx @@ -16,13 +16,12 @@ import useOnlineStatus from "@/hooks/use-online-status"; import { IPage } from "@/store/pages/page"; type Props = { - editorRef: React.RefObject; + editorRef: EditorRefApi | EditorReadOnlyRefApi | null; page: IPage; - readOnlyEditorRef: React.RefObject; }; export const PageExtraOptions: React.FC = observer((props) => { - const { editorRef, page, readOnlyEditorRef } = props; + const { editorRef, page } = props; // derived values const { archived_at, @@ -84,8 +83,8 @@ export const PageExtraOptions: React.FC = observer((props) => { iconClassName="text-custom-text-100" /> )} - - + + ); }); diff --git a/web/core/components/pages/editor/header/mobile-root.tsx b/web/core/components/pages/editor/header/mobile-root.tsx index 0da8613aa0b..003237d915a 100644 --- a/web/core/components/pages/editor/header/mobile-root.tsx +++ b/web/core/components/pages/editor/header/mobile-root.tsx @@ -9,42 +9,34 @@ import { usePageFilters } from "@/hooks/use-page-filters"; import { IPage } from "@/store/pages/page"; type Props = { - editorReady: boolean; - editorRef: React.RefObject; + editorRef: EditorRefApi | EditorReadOnlyRefApi | null; page: IPage; - readOnlyEditorReady: boolean; - readOnlyEditorRef: React.RefObject; setSidePeekVisible: (sidePeekState: boolean) => void; sidePeekVisible: boolean; }; export const PageEditorMobileHeaderRoot: React.FC = observer((props) => { - const { editorReady, editorRef, page, readOnlyEditorReady, readOnlyEditorRef, setSidePeekVisible, sidePeekVisible } = - props; + const { editorRef, page, setSidePeekVisible, sidePeekVisible } = props; // derived values const { isContentEditable } = page; // page filters const { isFullWidth } = usePageFilters(); - if (!editorRef.current && !readOnlyEditorRef.current) return null; - return ( <>
- +
- {(editorReady || readOnlyEditorReady) && isContentEditable && editorRef.current && ( - - )} + {isContentEditable && editorRef && }
); diff --git a/web/core/components/pages/editor/header/options-dropdown.tsx b/web/core/components/pages/editor/header/options-dropdown.tsx index 58d89b57b17..3793eee9d28 100644 --- a/web/core/components/pages/editor/header/options-dropdown.tsx +++ b/web/core/components/pages/editor/header/options-dropdown.tsx @@ -98,11 +98,13 @@ export const PageOptionsDropdown: React.FC = observer((props) => { pageTitle={name ?? ""} /> = observer((props) => { const { isContentEditable } = page; // page filters const { isFullWidth } = usePageFilters(); + // derived values + const resolvedEditorRef = isContentEditable ? editorRef.current : readOnlyEditorRef.current; - if (!editorRef.current && !readOnlyEditorRef.current) return null; + if (!resolvedEditorRef) return null; return ( <> @@ -53,14 +55,11 @@ export const PageEditorHeaderRoot: React.FC = observer((props) => { )} - +
= observer((props) => { const page = usePage(pageId); const { getUserDetails } = useMember(); // page operations - const { pageOperations } = usePageOperations(page); + const { pageOperations } = usePageOperations({ + page, + }); // derived values const { access, created_at, is_favorite, owned_by, canCurrentUserFavoritePage } = page; const ownerDetails = owned_by ? getUserDetails(owned_by) : undefined; diff --git a/web/core/hooks/use-collaborative-page-actions.tsx b/web/core/hooks/use-collaborative-page-actions.tsx index 6ec9f799050..cd89607d644 100644 --- a/web/core/hooks/use-collaborative-page-actions.tsx +++ b/web/core/hooks/use-collaborative-page-actions.tsx @@ -14,7 +14,13 @@ type CollaborativeActionEvent = | { type: "sendMessageToServer"; message: TDocumentEventsServer } | { type: "receivedMessageFromServer"; message: TDocumentEventsClient }; -export const useCollaborativePageActions = (editorRef: EditorRefApi | EditorReadOnlyRefApi | null, page: IPage) => { +type Props = { + editorRef?: EditorRefApi | EditorReadOnlyRefApi | null; + page: IPage; +}; + +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(null); @@ -37,6 +43,14 @@ export const useCollaborativePageActions = (editorRef: EditorRefApi | EditorRead 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] ); @@ -64,6 +78,7 @@ export const useCollaborativePageActions = (editorRef: EditorRefApi | EditorRead ); useEffect(() => { + if (!editorRef) return; if (currentActionBeingProcessed) { const serverEventName = getServerEventName(currentActionBeingProcessed); if (serverEventName) { @@ -73,9 +88,12 @@ export const useCollaborativePageActions = (editorRef: EditorRefApi | EditorRead }, [currentActionBeingProcessed, editorRef]); useEffect(() => { - const realTimeStatelessMessageListener = editorRef?.listenToRealTimeUpdate(); + if (!editorRef) return; + const realTimeStatelessMessageListener = editorRef?.listenToRealTimeUpdate(); + console.log(realTimeStatelessMessageListener); const handleStatelessMessage = (message: { payload: TDocumentEventsClient }) => { + console.log("aaa", message); if (currentActionBeingProcessed === message.payload) { setCurrentActionBeingProcessed(null); return; diff --git a/web/core/hooks/use-page-operations.ts b/web/core/hooks/use-page-operations.ts index 1493aadb1cd..7990252a6b5 100644 --- a/web/core/hooks/use-page-operations.ts +++ b/web/core/hooks/use-page-operations.ts @@ -1,5 +1,7 @@ import { useMemo } from "react"; import { useParams } from "next/navigation"; +// plane editor +import { EditorReadOnlyRefApi, EditorRefApi } from "@plane/editor"; // plane ui import { setToast, TOAST_TYPE } from "@plane/ui"; // helpers @@ -19,32 +21,34 @@ export type TPageOperations = { toggleArchive: () => void; }; +type Props = { + editorRef?: EditorRefApi | EditorReadOnlyRefApi | null; + page: IPage; +}; + export const usePageOperations = ( - page: IPage + props: Props ): { pageOperations: TPageOperations; } => { + const { page } = props; // params const { workspaceSlug, projectId } = useParams(); // derived values const { access, addToFavorites, - archive, archived_at, duplicate, id, is_favorite, is_locked, - lock, makePrivate, makePublic, removePageFromFavorites, - restore, - unlock, } = page; // collaborative actions - const { executeCollaborativeAction } = useCollaborativePageActions(undefined, page); + const { executeCollaborativeAction } = useCollaborativePageActions(props); // page operations const pageOperations: TPageOperations = useMemo(() => { const pageLink = projectId ? `${workspaceSlug}/projects/${projectId}/pages/${id}` : `${workspaceSlug}/pages/${id}`; diff --git a/web/core/store/pages/page.ts b/web/core/store/pages/page.ts index bbb8dfd8eab..383e6d8ef19 100644 --- a/web/core/store/pages/page.ts +++ b/web/core/store/pages/page.ts @@ -33,8 +33,8 @@ export interface IPage extends TPage { update: (pageData: Partial) => Promise; updateTitle: (title: string) => void; updateDescription: (document: TDocumentPayload) => Promise; - makePublic: () => Promise; - makePrivate: () => Promise; + makePublic: (shouldSync?: boolean) => Promise; + makePrivate: (shouldSync?: boolean) => Promise; lock: (shouldSync?: boolean) => Promise; unlock: (shouldSync?: boolean) => Promise; archive: (shouldSync?: boolean) => Promise; @@ -415,44 +415,48 @@ export class Page implements IPage { /** * @description make the page public */ - makePublic = async () => { + makePublic = async (shouldSync: boolean = true) => { const { workspaceSlug, projectId } = this.store.router; if (!workspaceSlug || !projectId || !this.id) return undefined; const pageAccess = this.access; runInAction(() => (this.access = EPageAccess.PUBLIC)); - try { - await this.pageService.updateAccess(workspaceSlug, projectId, this.id, { - access: EPageAccess.PUBLIC, - }); - } catch (error) { - runInAction(() => { - this.access = pageAccess; - }); - throw error; + if (shouldSync) { + try { + await this.pageService.updateAccess(workspaceSlug, projectId, this.id, { + access: EPageAccess.PUBLIC, + }); + } catch (error) { + runInAction(() => { + this.access = pageAccess; + }); + throw error; + } } }; /** * @description make the page private */ - makePrivate = async () => { + makePrivate = async (shouldSync: boolean = true) => { const { workspaceSlug, projectId } = this.store.router; if (!workspaceSlug || !projectId || !this.id) return undefined; const pageAccess = this.access; runInAction(() => (this.access = EPageAccess.PRIVATE)); - try { - await this.pageService.updateAccess(workspaceSlug, projectId, this.id, { - access: EPageAccess.PRIVATE, - }); - } catch (error) { - runInAction(() => { - this.access = pageAccess; - }); - throw error; + if (shouldSync) { + try { + await this.pageService.updateAccess(workspaceSlug, projectId, this.id, { + access: EPageAccess.PRIVATE, + }); + } catch (error) { + runInAction(() => { + this.access = pageAccess; + }); + throw error; + } } };