- Extension.create({
- name: "dropHandler",
- priority: 1000,
+export const DropHandlerExtension = Extension.create({
+ name: "dropHandler",
+ priority: 1000,
- addProseMirrorPlugins() {
- const editor = this.editor;
- return [
- new Plugin({
- key: new PluginKey("drop-handler-plugin"),
- props: {
- handlePaste: (view: EditorView, event: ClipboardEvent) => {
- if (event.clipboardData && event.clipboardData.files && event.clipboardData.files.length > 0) {
- event.preventDefault();
- const files = Array.from(event.clipboardData.files);
- const imageFiles = files.filter((file) => file.type.startsWith("image"));
+ addProseMirrorPlugins() {
+ const editor = this.editor;
+ return [
+ new Plugin({
+ key: new PluginKey("drop-handler-plugin"),
+ props: {
+ handlePaste: (view: EditorView, event: ClipboardEvent) => {
+ if (
+ editor.isEditable &&
+ event.clipboardData &&
+ event.clipboardData.files &&
+ event.clipboardData.files.length > 0
+ ) {
+ event.preventDefault();
+ const files = Array.from(event.clipboardData.files);
+ const imageFiles = files.filter((file) => file.type.startsWith("image"));
- if (imageFiles.length > 0) {
- const pos = view.state.selection.from;
- insertImagesSafely({ editor, files: imageFiles, initialPos: pos, event: "drop" });
- }
- return true;
+ if (imageFiles.length > 0) {
+ const pos = view.state.selection.from;
+ insertImagesSafely({ editor, files: imageFiles, initialPos: pos, event: "drop" });
}
- return false;
- },
- handleDrop: (view: EditorView, event: DragEvent, _slice: any, moved: boolean) => {
- if (!moved && event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length > 0) {
- event.preventDefault();
- const files = Array.from(event.dataTransfer.files);
- const imageFiles = files.filter((file) => file.type.startsWith("image"));
+ return true;
+ }
+ return false;
+ },
+ handleDrop: (view: EditorView, event: DragEvent, _slice: any, moved: boolean) => {
+ if (
+ editor.isEditable &&
+ !moved &&
+ event.dataTransfer &&
+ event.dataTransfer.files &&
+ event.dataTransfer.files.length > 0
+ ) {
+ event.preventDefault();
+ const files = Array.from(event.dataTransfer.files);
+ const imageFiles = files.filter((file) => file.type.startsWith("image"));
- if (imageFiles.length > 0) {
- const coordinates = view.posAtCoords({
- left: event.clientX,
- top: event.clientY,
- });
+ if (imageFiles.length > 0) {
+ const coordinates = view.posAtCoords({
+ left: event.clientX,
+ top: event.clientY,
+ });
- if (coordinates) {
- const pos = coordinates.pos;
- insertImagesSafely({ editor, files: imageFiles, initialPos: pos, event: "drop" });
- }
- return true;
+ if (coordinates) {
+ const pos = coordinates.pos;
+ insertImagesSafely({ editor, files: imageFiles, initialPos: pos, event: "drop" });
}
+ return true;
}
- return false;
- },
+ }
+ return false;
},
- }),
- ];
- },
- });
-
+ },
+ }),
+ ];
+ },
+});
export const insertImagesSafely = async ({
editor,
files,
diff --git a/packages/editor/src/core/extensions/extensions.tsx b/packages/editor/src/core/extensions/extensions.tsx
index 0e06f774bb7..71126c57664 100644
--- a/packages/editor/src/core/extensions/extensions.tsx
+++ b/packages/editor/src/core/extensions/extensions.tsx
@@ -47,10 +47,11 @@ type TArguments = {
};
placeholder?: string | ((isFocused: boolean, value: string) => string);
tabIndex?: number;
+ editable: boolean;
};
export const CoreEditorExtensions = (args: TArguments): Extensions => {
- const { disabledExtensions, enableHistory, fileHandler, mentionConfig, placeholder, tabIndex } = args;
+ const { disabledExtensions, enableHistory, fileHandler, mentionConfig, placeholder, tabIndex, editable } = args;
return [
StarterKit.configure({
@@ -89,7 +90,7 @@ export const CoreEditorExtensions = (args: TArguments): Extensions => {
...(enableHistory ? {} : { history: false }),
}),
CustomQuoteExtension,
- DropHandlerExtension(),
+ DropHandlerExtension,
CustomHorizontalRule.configure({
HTMLAttributes: {
class: "py-4 border-custom-border-400",
@@ -137,6 +138,7 @@ export const CoreEditorExtensions = (args: TArguments): Extensions => {
CustomCodeInlineExtension,
Markdown.configure({
html: true,
+ transformCopiedText: true,
transformPastedText: true,
breaks: true,
}),
@@ -145,12 +147,14 @@ export const CoreEditorExtensions = (args: TArguments): Extensions => {
TableCell,
TableRow,
CustomMention({
- mentionSuggestions: mentionConfig.mentionSuggestions,
+ mentionSuggestions: editable ? mentionConfig.mentionSuggestions : undefined,
mentionHighlights: mentionConfig.mentionHighlights,
- readonly: false,
+ readonly: !editable,
}),
Placeholder.configure({
placeholder: ({ editor, node }) => {
+ if (!editor.isEditable) return;
+
if (node.type.name === "heading") return `Heading ${node.attrs.level}`;
if (editor.storage.imageComponent.uploadInProgress) return "";
diff --git a/packages/editor/src/core/extensions/read-only-extensions.tsx b/packages/editor/src/core/extensions/read-only-extensions.tsx
index 4debda019c1..7e9b055210b 100644
--- a/packages/editor/src/core/extensions/read-only-extensions.tsx
+++ b/packages/editor/src/core/extensions/read-only-extensions.tsx
@@ -20,7 +20,6 @@ import {
TableRow,
Table,
CustomMention,
- HeadingListExtension,
CustomReadOnlyImageExtension,
CustomTextAlignExtension,
CustomCalloutReadOnlyExtension,
@@ -139,7 +138,6 @@ export const CoreReadOnlyEditorExtensions = (props: Props): Extensions => {
}),
CharacterCount,
CustomColorExtension,
- HeadingListExtension,
CustomTextAlignExtension,
CustomCalloutReadOnlyExtension,
...CoreReadOnlyEditorAdditionalExtensions({
diff --git a/packages/editor/src/core/hooks/use-collaborative-editor.ts b/packages/editor/src/core/hooks/use-collaborative-editor.ts
index b3c7d6cfc2e..4abf7d6d1ff 100644
--- a/packages/editor/src/core/hooks/use-collaborative-editor.ts
+++ b/packages/editor/src/core/hooks/use-collaborative-editor.ts
@@ -15,6 +15,7 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => {
const {
onTransaction,
disabledExtensions,
+ editable,
editorClassName,
editorProps = {},
embedHandler,
@@ -75,7 +76,7 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => {
const editor = useEditor({
disabledExtensions,
id,
- onTransaction,
+ editable,
editorProps,
editorClassName,
enableHistory: false,
@@ -97,9 +98,10 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => {
}),
],
fileHandler,
- handleEditorReady,
forwardedRef,
+ handleEditorReady,
mentionHandler,
+ onTransaction,
placeholder,
provider,
tabIndex,
diff --git a/packages/editor/src/core/hooks/use-editor.ts b/packages/editor/src/core/hooks/use-editor.ts
index 15fbd19d5c8..dd17373f76e 100644
--- a/packages/editor/src/core/hooks/use-editor.ts
+++ b/packages/editor/src/core/hooks/use-editor.ts
@@ -27,6 +27,7 @@ import type {
} from "@/types";
export interface CustomEditorProps {
+ editable: boolean;
editorClassName: string;
editorProps?: EditorProps;
enableHistory: boolean;
@@ -55,6 +56,7 @@ export interface CustomEditorProps {
export const useEditor = (props: CustomEditorProps) => {
const {
disabledExtensions,
+ editable = true,
editorClassName,
editorProps = {},
enableHistory,
@@ -74,42 +76,46 @@ export const useEditor = (props: CustomEditorProps) => {
autofocus = false,
} = props;
// states
-
const [savedSelection, setSavedSelection] = useState
(null);
// refs
const editorRef: MutableRefObject = useRef(null);
const savedSelectionRef = useRef(savedSelection);
- const editor = useTiptapEditor({
- autofocus,
- editorProps: {
- ...CoreEditorProps({
- editorClassName,
- }),
- ...editorProps,
- },
- extensions: [
- ...CoreEditorExtensions({
- disabledExtensions,
- enableHistory,
- fileHandler,
- mentionConfig: {
- mentionSuggestions: mentionHandler.suggestions ?? (() => Promise.resolve([])),
- mentionHighlights: mentionHandler.highlights,
- },
- placeholder,
- tabIndex,
- }),
- ...extensions,
- ],
- content: typeof initialValue === "string" && initialValue.trim() !== "" ? initialValue : "",
- onCreate: () => handleEditorReady?.(true),
- onTransaction: ({ editor }) => {
- setSavedSelection(editor.state.selection);
- onTransaction?.();
+ const editor = useTiptapEditor(
+ {
+ editable,
+ autofocus,
+ editorProps: {
+ ...CoreEditorProps({
+ editorClassName,
+ }),
+ ...editorProps,
+ },
+ extensions: [
+ ...CoreEditorExtensions({
+ editable,
+ disabledExtensions,
+ enableHistory,
+ fileHandler,
+ mentionConfig: {
+ mentionSuggestions: mentionHandler.suggestions ?? (() => Promise.resolve([])),
+ mentionHighlights: mentionHandler.highlights,
+ },
+ placeholder,
+ tabIndex,
+ }),
+ ...extensions,
+ ],
+ content: typeof initialValue === "string" && initialValue.trim() !== "" ? initialValue : "",
+ onCreate: () => handleEditorReady?.(true),
+ onTransaction: ({ editor }) => {
+ setSavedSelection(editor.state.selection);
+ onTransaction?.();
+ },
+ onUpdate: ({ editor }) => onChange?.(editor.getJSON(), editor.getHTML()),
+ onDestroy: () => handleEditorReady?.(false),
},
- onUpdate: ({ editor }) => onChange?.(editor.getJSON(), editor.getHTML()),
- onDestroy: () => handleEditorReady?.(false),
- });
+ [editable]
+ );
// Update the ref whenever savedSelection changes
useEffect(() => {
diff --git a/packages/editor/src/core/hooks/use-file-upload.ts b/packages/editor/src/core/hooks/use-file-upload.ts
index f5f930f2900..65daa2f8e49 100644
--- a/packages/editor/src/core/hooks/use-file-upload.ts
+++ b/packages/editor/src/core/hooks/use-file-upload.ts
@@ -105,7 +105,7 @@ export const useDropZone = (args: TDropzoneArgs) => {
async (e: DragEvent) => {
e.preventDefault();
setDraggedInside(false);
- if (e.dataTransfer.files.length === 0) {
+ if (e.dataTransfer.files.length === 0 || !editor.isEditable) {
return;
}
const filesList = e.dataTransfer.files;
diff --git a/packages/editor/src/core/hooks/use-read-only-collaborative-editor.ts b/packages/editor/src/core/hooks/use-read-only-collaborative-editor.ts
deleted file mode 100644
index 01ca19b8148..00000000000
--- a/packages/editor/src/core/hooks/use-read-only-collaborative-editor.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import { useEffect, useMemo, useState } from "react";
-import { HocuspocusProvider } from "@hocuspocus/provider";
-import Collaboration from "@tiptap/extension-collaboration";
-import { IndexeddbPersistence } from "y-indexeddb";
-// extensions
-import { HeadingListExtension } from "@/extensions";
-// hooks
-import { useReadOnlyEditor } from "@/hooks/use-read-only-editor";
-// types
-import { TReadOnlyCollaborativeEditorProps } from "@/types";
-
-export const useReadOnlyCollaborativeEditor = (props: TReadOnlyCollaborativeEditorProps) => {
- const {
- disabledExtensions,
- editorClassName,
- editorProps = {},
- extensions,
- fileHandler,
- forwardedRef,
- handleEditorReady,
- id,
- mentionHandler,
- realtimeConfig,
- serverHandler,
- user,
- } = props;
- // states
- const [hasServerConnectionFailed, setHasServerConnectionFailed] = useState(false);
- const [hasServerSynced, setHasServerSynced] = useState(false);
- // initialize Hocuspocus provider
- const provider = useMemo(
- () =>
- new HocuspocusProvider({
- name: id,
- url: realtimeConfig.url,
- token: JSON.stringify(user),
- parameters: realtimeConfig.queryParams,
- onAuthenticationFailed: () => {
- serverHandler?.onServerError?.();
- setHasServerConnectionFailed(true);
- },
- onConnect: () => serverHandler?.onConnect?.(),
- onClose: (data) => {
- if (data.event.code === 1006) {
- serverHandler?.onServerError?.();
- setHasServerConnectionFailed(true);
- }
- },
- onSynced: () => setHasServerSynced(true),
- }),
- [id, realtimeConfig, serverHandler, user]
- );
-
- // indexed db integration for offline support
- const localProvider = useMemo(
- () => (id ? new IndexeddbPersistence(id, provider.document) : undefined),
- [id, provider]
- );
-
- // destroy and disconnect connection on unmount
- useEffect(
- () => () => {
- provider.destroy();
- localProvider?.destroy();
- },
- [provider, localProvider]
- );
-
- const editor = useReadOnlyEditor({
- disabledExtensions,
- editorProps,
- editorClassName,
- extensions: [
- ...(extensions ?? []),
- HeadingListExtension,
- Collaboration.configure({
- document: provider.document,
- }),
- ],
- fileHandler,
- forwardedRef,
- handleEditorReady,
- mentionHandler,
- provider,
- });
-
- return {
- editor,
- hasServerConnectionFailed,
- hasServerSynced,
- };
-};
diff --git a/packages/editor/src/core/types/collaboration.ts b/packages/editor/src/core/types/collaboration.ts
index 35fbdb99680..b95f7283eb5 100644
--- a/packages/editor/src/core/types/collaboration.ts
+++ b/packages/editor/src/core/types/collaboration.ts
@@ -21,6 +21,7 @@ export type TServerHandler = {
type TCollaborativeEditorHookProps = {
disabledExtensions: TExtensions[];
+ editable?: boolean;
editorClassName: string;
editorProps?: EditorProps;
extensions?: Extensions;
diff --git a/packages/editor/src/core/types/editor.ts b/packages/editor/src/core/types/editor.ts
index e91af8e4923..404a3021d12 100644
--- a/packages/editor/src/core/types/editor.ts
+++ b/packages/editor/src/core/types/editor.ts
@@ -138,6 +138,7 @@ export interface IRichTextEditor extends IEditorProps {
export interface ICollaborativeDocumentEditor
extends Omit {
+ editable: boolean;
aiHandler?: TAIHandler;
embedHandler: TEmbedConfig;
handleEditorReady?: (value: boolean) => void;
diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts
index ed7d9134698..9dd0db267f2 100644
--- a/packages/editor/src/index.ts
+++ b/packages/editor/src/index.ts
@@ -9,7 +9,6 @@ import "./styles/drag-drop.css";
// editors
export {
CollaborativeDocumentEditorWithRef,
- CollaborativeDocumentReadOnlyEditorWithRef,
DocumentReadOnlyEditorWithRef,
LiteTextEditorWithRef,
LiteTextReadOnlyEditorWithRef,
diff --git a/packages/editor/src/styles/editor.css b/packages/editor/src/styles/editor.css
index db60c7cf5f5..e234f87cf86 100644
--- a/packages/editor/src/styles/editor.css
+++ b/packages/editor/src/styles/editor.css
@@ -111,8 +111,12 @@ ul[data-type="taskList"] li > label input[type="checkbox"] {
transform: scale(1.05);
}
-ul[data-type="taskList"] li > label input[type="checkbox"]:hover {
- background-color: rgba(var(--color-background-80)) !important;
+.ProseMirror[contenteditable="true"] input[type="checkbox"]:hover {
+ background-color: rgba(var(--color-background-80));
+}
+
+.ProseMirror[contenteditable="false"] input[type="checkbox"] {
+ pointer-events: none;
}
ul[data-type="taskList"] li > label input[type="checkbox"][checked] {
@@ -151,10 +155,6 @@ ul[data-type="taskList"] li > label input[type="checkbox"] {
margin-right: 0.2rem;
margin-top: 0.15rem;
- &:hover {
- background-color: rgb(var(--color-background-80));
- }
-
&:active {
background-color: rgb(var(--color-background-90));
}
diff --git a/web/core/components/pages/editor/editor-body.tsx b/web/core/components/pages/editor/editor-body.tsx
index 6f88445ede9..adbfd63680d 100644
--- a/web/core/components/pages/editor/editor-body.tsx
+++ b/web/core/components/pages/editor/editor-body.tsx
@@ -1,11 +1,9 @@
-import { useCallback, useMemo } from "react";
+import { Dispatch, SetStateAction, useCallback, useMemo } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// document-editor
import {
CollaborativeDocumentEditorWithRef,
- CollaborativeDocumentReadOnlyEditorWithRef,
- EditorReadOnlyRefApi,
EditorRefApi,
TAIMenuProps,
TDisplayConfig,
@@ -20,7 +18,7 @@ import { Row } from "@plane/ui";
import { PageContentBrowser, PageContentLoader, PageEditorTitle } from "@/components/pages";
// helpers
import { cn, LIVE_BASE_PATH, LIVE_BASE_URL } from "@/helpers/common.helper";
-import { getEditorFileHandlers, getReadOnlyEditorFileHandlers } from "@/helpers/editor.helper";
+import { getEditorFileHandlers } from "@/helpers/editor.helper";
import { generateRandomColor } from "@/helpers/string.helper";
// hooks
import { useMember, useMention, useUser, useWorkspace } from "@/hooks/store";
@@ -42,24 +40,14 @@ const fileService = new FileService();
type Props = {
editorRef: React.RefObject;
editorReady: boolean;
- handleConnectionStatus: (status: boolean) => void;
- handleEditorReady: (value: boolean) => void;
- handleReadOnlyEditorReady: (value: boolean) => void;
+ handleConnectionStatus: Dispatch>;
+ handleEditorReady: Dispatch>;
page: IPage;
- readOnlyEditorRef: React.RefObject;
sidePeekVisible: boolean;
};
export const PageEditorBody: React.FC = observer((props) => {
- const {
- editorRef,
- handleConnectionStatus,
- handleEditorReady,
- handleReadOnlyEditorReady,
- page,
- readOnlyEditorRef,
- sidePeekVisible,
- } = props;
+ const { editorRef, handleConnectionStatus, handleEditorReady, page, sidePeekVisible } = props;
// router
const { workspaceSlug, projectId } = useParams();
// store hooks
@@ -112,11 +100,11 @@ export const PageEditorBody: React.FC = observer((props) => {
const handleServerConnect = useCallback(() => {
handleConnectionStatus(false);
- }, []);
+ }, [handleConnectionStatus]);
const handleServerError = useCallback(() => {
handleConnectionStatus(true);
- }, []);
+ }, [handleConnectionStatus]);
const serverHandler: TServerHandler = useMemo(
() => ({
@@ -169,18 +157,16 @@ export const PageEditorBody: React.FC = observer((props) => {
"w-[5%]": isFullWidth,
})}
>
- {!isFullWidth && (
-
- )}
+ {!isFullWidth && }
-
-
+
+
= observer((props) => {
readOnly={!isContentEditable}
/>
- {isContentEditable ? (
-
{
- const { asset_id } = await fileService.uploadProjectAsset(
- workspaceSlug?.toString() ?? "",
- projectId?.toString() ?? "",
- {
- entity_identifier: pageId,
- entity_type: EFileAssetType.PAGE_DESCRIPTION,
- },
- file
- );
- return asset_id;
- },
- workspaceId,
- workspaceSlug: workspaceSlug?.toString() ?? "",
- })}
- handleEditorReady={handleEditorReady}
- ref={editorRef}
- containerClassName="h-full p-0 pb-64"
- displayConfig={displayConfig}
- editorClassName="pl-10"
- mentionHandler={{
- highlights: mentionHighlights,
- suggestions: mentionSuggestions,
- }}
- embedHandler={{
- issue: issueEmbedProps,
- }}
- realtimeConfig={realtimeConfig}
- serverHandler={serverHandler}
- user={userConfig}
- disabledExtensions={disabledExtensions}
- aiHandler={{
- menu: getAIMenu,
- }}
- />
- ) : (
-
- )}
+ {
+ const { asset_id } = await fileService.uploadProjectAsset(
+ workspaceSlug?.toString() ?? "",
+ projectId?.toString() ?? "",
+ {
+ entity_identifier: pageId,
+ entity_type: EFileAssetType.PAGE_DESCRIPTION,
+ },
+ file
+ );
+ return asset_id;
+ },
+ workspaceId,
+ workspaceSlug: workspaceSlug?.toString() ?? "",
+ })}
+ handleEditorReady={handleEditorReady}
+ ref={editorRef}
+ containerClassName="h-full p-0 pb-64"
+ displayConfig={displayConfig}
+ editorClassName="pl-10"
+ mentionHandler={{
+ highlights: mentionHighlights,
+ suggestions: mentionSuggestions,
+ }}
+ embedHandler={{
+ issue: issueEmbedProps,
+ }}
+ realtimeConfig={realtimeConfig}
+ serverHandler={serverHandler}
+ user={userConfig}
+ disabledExtensions={disabledExtensions}
+ aiHandler={{
+ menu: getAIMenu,
+ }}
+ />
;
handleDuplicatePage: () => void;
page: IPage;
- readOnlyEditorRef: React.RefObject
;
};
export const PageExtraOptions: React.FC = observer((props) => {
- const { editorRef, handleDuplicatePage, page, readOnlyEditorRef } = props;
+ const { editorRef, handleDuplicatePage, page } = props;
// derived values
const {
archived_at,
@@ -85,12 +84,8 @@ export const PageExtraOptions: React.FC = observer((props) => {
iconClassName="text-custom-text-100"
/>
)}
-
-
+
+
);
});
diff --git a/web/core/components/pages/editor/header/info-popover.tsx b/web/core/components/pages/editor/header/info-popover.tsx
index e295d8ea278..13a1b17666a 100644
--- a/web/core/components/pages/editor/header/info-popover.tsx
+++ b/web/core/components/pages/editor/header/info-popover.tsx
@@ -2,12 +2,12 @@ import { useState } from "react";
import { usePopper } from "react-popper";
import { Info } from "lucide-react";
// plane editor
-import { EditorReadOnlyRefApi, EditorRefApi } from "@plane/editor";
+import { EditorRefApi } from "@plane/editor";
// helpers
import { getReadTimeFromWordsCount } from "@/helpers/date-time.helper";
type Props = {
- editorRef: EditorRefApi | EditorReadOnlyRefApi | null;
+ editorRef: EditorRefApi | null;
};
export const PageInfoPopover: React.FC
= (props) => {
diff --git a/web/core/components/pages/editor/header/mobile-root.tsx b/web/core/components/pages/editor/header/mobile-root.tsx
index ac831796cbe..df93a70e922 100644
--- a/web/core/components/pages/editor/header/mobile-root.tsx
+++ b/web/core/components/pages/editor/header/mobile-root.tsx
@@ -13,52 +13,34 @@ type Props = {
editorRef: React.RefObject;
handleDuplicatePage: () => void;
page: IPage;
- readOnlyEditorReady: boolean;
- readOnlyEditorRef: React.RefObject;
setSidePeekVisible: (sidePeekState: boolean) => void;
sidePeekVisible: boolean;
};
export const PageEditorMobileHeaderRoot: React.FC = observer((props) => {
- const {
- editorReady,
- editorRef,
- handleDuplicatePage,
- page,
- readOnlyEditorReady,
- readOnlyEditorRef,
- setSidePeekVisible,
- sidePeekVisible,
- } = props;
+ const { editorReady, editorRef, handleDuplicatePage, page, setSidePeekVisible, sidePeekVisible } = props;
// derived values
const { isContentEditable } = page;
// page filters
const { isFullWidth } = usePageFilters();
- if (!editorRef.current && !readOnlyEditorRef.current) return null;
+ if (!editorRef.current) return null;
return (
<>
- {(editorReady || readOnlyEditorReady) && isContentEditable && editorRef.current && (
-
- )}
+ {editorReady && isContentEditable && editorRef.current && }
>
);
diff --git a/web/core/components/pages/editor/header/options-dropdown.tsx b/web/core/components/pages/editor/header/options-dropdown.tsx
index ff0987a9dc2..a1db6f97241 100644
--- a/web/core/components/pages/editor/header/options-dropdown.tsx
+++ b/web/core/components/pages/editor/header/options-dropdown.tsx
@@ -15,7 +15,7 @@ import {
LucideIcon,
} from "lucide-react";
// document editor
-import { EditorReadOnlyRefApi, EditorRefApi } from "@plane/editor";
+import { EditorRefApi } from "@plane/editor";
// ui
import { ArchiveIcon, CustomMenu, type ISvgIcons, TOAST_TYPE, ToggleSwitch, setToast } from "@plane/ui";
// components
@@ -30,7 +30,7 @@ import { useQueryParams } from "@/hooks/use-query-params";
import { IPage } from "@/store/pages/page";
type Props = {
- editorRef: EditorRefApi | EditorReadOnlyRefApi | null;
+ editorRef: EditorRefApi | null;
handleDuplicatePage: () => void;
page: IPage;
};
diff --git a/web/core/components/pages/editor/header/root.tsx b/web/core/components/pages/editor/header/root.tsx
index 9640f4e43b6..41933feb725 100644
--- a/web/core/components/pages/editor/header/root.tsx
+++ b/web/core/components/pages/editor/header/root.tsx
@@ -1,5 +1,5 @@
import { observer } from "mobx-react";
-import { EditorReadOnlyRefApi, EditorRefApi } from "@plane/editor";
+import { EditorRefApi } from "@plane/editor";
// components
import { Header, EHeaderVariant } from "@plane/ui";
import { PageEditorMobileHeaderRoot, PageExtraOptions, PageSummaryPopover, PageToolbar } from "@/components/pages";
@@ -15,35 +15,24 @@ type Props = {
editorRef: React.RefObject;
handleDuplicatePage: () => void;
page: IPage;
- readOnlyEditorReady: boolean;
- readOnlyEditorRef: React.RefObject;
setSidePeekVisible: (sidePeekState: boolean) => void;
sidePeekVisible: boolean;
};
export const PageEditorHeaderRoot: React.FC = observer((props) => {
- const {
- editorReady,
- editorRef,
- handleDuplicatePage,
- page,
- readOnlyEditorReady,
- readOnlyEditorRef,
- setSidePeekVisible,
- sidePeekVisible,
- } = props;
+ const { editorReady, editorRef, setSidePeekVisible, sidePeekVisible, handleDuplicatePage, page } = props;
// derived values
const { isContentEditable } = page;
// page filters
const { isFullWidth } = usePageFilters();
- if (!editorRef.current && !readOnlyEditorRef.current) return null;
+ if (!editorRef.current) return null;
return (
<>
- {(editorReady || readOnlyEditorReady) && (
+ {editorReady && (
= observer((props) => {
})}
>
)}
- {(editorReady || readOnlyEditorReady) && isContentEditable && editorRef.current && (
-
- )}
+ {editorReady && isContentEditable && editorRef.current && }
-
+
{
// states
const [editorReady, setEditorReady] = useState(false);
const [hasConnectionFailed, setHasConnectionFailed] = useState(false);
- const [readOnlyEditorReady, setReadOnlyEditorReady] = useState(false);
const [sidePeekVisible, setSidePeekVisible] = useState(window.innerWidth >= 768);
const [isVersionsOverlayOpen, setIsVersionsOverlayOpen] = useState(false);
// refs
const editorRef = useRef(null);
- const readOnlyEditorRef = useRef(null);
// router
const router = useAppRouter();
// search params
@@ -99,9 +97,7 @@ export const PageRoot = observer((props: TPageRootProps) => {
editorRef.current?.clearEditor();
editorRef.current?.setEditorValue(descriptionHTML);
};
- const currentVersionDescription = isContentEditable
- ? editorRef.current?.getDocument().html
- : readOnlyEditorRef.current?.getDocument().html;
+ const currentVersionDescription = editorRef.current?.getDocument().html;
return (
<>
@@ -137,19 +133,15 @@ export const PageRoot = observer((props: TPageRootProps) => {
editorRef={editorRef}
handleDuplicatePage={handleDuplicatePage}
page={page}
- readOnlyEditorReady={readOnlyEditorReady}
- readOnlyEditorRef={readOnlyEditorRef}
setSidePeekVisible={(state) => setSidePeekVisible(state)}
sidePeekVisible={sidePeekVisible}
/>
setHasConnectionFailed(status)}
- handleEditorReady={(val) => setEditorReady(val)}
- handleReadOnlyEditorReady={() => setReadOnlyEditorReady(true)}
+ handleConnectionStatus={setHasConnectionFailed}
+ handleEditorReady={setEditorReady}
page={page}
- readOnlyEditorRef={readOnlyEditorRef}
sidePeekVisible={sidePeekVisible}
/>
>
diff --git a/web/core/components/pages/editor/summary/content-browser.tsx b/web/core/components/pages/editor/summary/content-browser.tsx
index 669d2e978c8..16d818aaeb7 100644
--- a/web/core/components/pages/editor/summary/content-browser.tsx
+++ b/web/core/components/pages/editor/summary/content-browser.tsx
@@ -1,11 +1,11 @@
import { useState, useEffect } from "react";
// plane editor
-import { EditorReadOnlyRefApi, EditorRefApi, IMarking } from "@plane/editor";
+import { EditorRefApi, IMarking } from "@plane/editor";
// components
import { OutlineHeading1, OutlineHeading2, OutlineHeading3 } from "./heading-components";
type Props = {
- editorRef: EditorRefApi | EditorReadOnlyRefApi | null;
+ editorRef: EditorRefApi | null;
setSidePeekVisible?: (sidePeekState: boolean) => void;
};
diff --git a/web/core/components/pages/editor/summary/popover.tsx b/web/core/components/pages/editor/summary/popover.tsx
index 5d14234f037..9acc4a7cc0c 100644
--- a/web/core/components/pages/editor/summary/popover.tsx
+++ b/web/core/components/pages/editor/summary/popover.tsx
@@ -2,14 +2,14 @@ import { useState } from "react";
import { usePopper } from "react-popper";
import { List } from "lucide-react";
// document editor
-import { EditorReadOnlyRefApi, EditorRefApi } from "@plane/editor";
+import { EditorRefApi } from "@plane/editor";
// helpers
import { cn } from "@/helpers/common.helper";
// components
import { PageContentBrowser } from "./content-browser";
type Props = {
- editorRef: EditorRefApi | EditorReadOnlyRefApi | null;
+ editorRef: EditorRefApi | null;
isFullWidth: boolean;
sidePeekVisible: boolean;
setSidePeekVisible: (sidePeekState: boolean) => void;
diff --git a/web/core/components/pages/modals/export-page-modal.tsx b/web/core/components/pages/modals/export-page-modal.tsx
index cf4f0a0f4b5..acf4ff08311 100644
--- a/web/core/components/pages/modals/export-page-modal.tsx
+++ b/web/core/components/pages/modals/export-page-modal.tsx
@@ -4,7 +4,7 @@ import { useState } from "react";
import { PageProps, pdf } from "@react-pdf/renderer";
import { Controller, useForm } from "react-hook-form";
// plane editor
-import { EditorReadOnlyRefApi, EditorRefApi } from "@plane/editor";
+import { EditorRefApi } from "@plane/editor";
// plane ui
import { Button, CustomSelect, EModalPosition, EModalWidth, ModalCore, setToast, TOAST_TYPE } from "@plane/ui";
// components
@@ -16,7 +16,7 @@ import {
} from "@/helpers/editor.helper";
type Props = {
- editorRef: EditorRefApi | EditorReadOnlyRefApi | null;
+ editorRef: EditorRefApi | null;
isOpen: boolean;
onClose: () => void;
pageTitle: string;
diff --git a/web/core/hooks/use-collaborative-page-actions.tsx b/web/core/hooks/use-collaborative-page-actions.tsx
index 6ec9f799050..6196929b6a4 100644
--- a/web/core/hooks/use-collaborative-page-actions.tsx
+++ b/web/core/hooks/use-collaborative-page-actions.tsx
@@ -50,6 +50,10 @@ export const useCollaborativePageActions = (editorRef: EditorRefApi | EditorRead
try {
await actionDetails.execute(isPerformedByCurrentUser);
if (isPerformedByCurrentUser) {
+ const serverEventName = getServerEventName(clientAction);
+ if (serverEventName) {
+ editorRef?.emitRealTimeUpdate(serverEventName);
+ }
setCurrentActionBeingProcessed(clientAction);
}
} catch {
@@ -60,18 +64,9 @@ export const useCollaborativePageActions = (editorRef: EditorRefApi | EditorRead
});
}
},
- [actionHandlerMap]
+ [actionHandlerMap, editorRef]
);
- useEffect(() => {
- if (currentActionBeingProcessed) {
- const serverEventName = getServerEventName(currentActionBeingProcessed);
- if (serverEventName) {
- editorRef?.emitRealTimeUpdate(serverEventName);
- }
- }
- }, [currentActionBeingProcessed, editorRef]);
-
useEffect(() => {
const realTimeStatelessMessageListener = editorRef?.listenToRealTimeUpdate();
@@ -95,6 +90,5 @@ export const useCollaborativePageActions = (editorRef: EditorRefApi | EditorRead
return {
executeCollaborativeAction,
- EVENT_ACTION_DETAILS_MAP: actionHandlerMap,
};
};