Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: removing readonly editor #6209

Draft
wants to merge 4 commits into
base: preview
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect, useState } from "react";
// components
import { DocumentContentLoader, PageRenderer } from "@/components/editors";
// constants
Expand All @@ -19,6 +19,7 @@ const CollaborativeDocumentEditor = (props: ICollaborativeDocumentEditor) => {
containerClassName,
disabledExtensions,
displayConfig = DEFAULT_DISPLAY_CONFIG,
editable,
editorClassName = "",
embedHandler,
fileHandler,
Expand All @@ -43,33 +44,59 @@ const CollaborativeDocumentEditor = (props: ICollaborativeDocumentEditor) => {
}

// use document editor
const { editor, hasServerConnectionFailed, hasServerSynced } = useCollaborativeEditor({
onTransaction,
disabledExtensions,
editorClassName,
embedHandler,
extensions,
fileHandler,
forwardedRef,
handleEditorReady,
id,
mentionHandler,
placeholder,
realtimeConfig,
serverHandler,
tabIndex,
user,
});
const { editor, hasServerConnectionFailed, hasServerSynced, localProvider, hasIndexedDbSynced } =
useCollaborativeEditor({
disabledExtensions,
editable,
editorClassName,
embedHandler,
extensions,
fileHandler,
forwardedRef,
handleEditorReady,
id,
mentionHandler,
onTransaction,
placeholder,
realtimeConfig,
serverHandler,
tabIndex,
user,
});

const editorContainerClassNames = getEditorClassNames({
noBorder: true,
borderOnFocus: false,
containerClassName,
});

const [hasIndexedDbEntry, setHasIndexedDbEntry] = useState(null);

useEffect(() => {
async function documentIndexedDbEntry(dbName: string) {
try {
const databases = await indexedDB.databases();
const hasEntry = databases.some((db) => db.name === dbName);
setHasIndexedDbEntry(hasEntry);
} catch (error) {
console.error("Error checking database existence:", error);
return false;
}
}
documentIndexedDbEntry(id);
}, [id, localProvider]);

if (!editor) return null;

if (!hasServerSynced && !hasServerConnectionFailed) return <DocumentContentLoader />;
if (
hasServerConnectionFailed ||
(!hasIndexedDbEntry && !hasServerSynced) ||
!hasIndexedDbSynced ||
!hasIndexedDbEntry
) {
console.log("syncing indexedDB");
return <DocumentContentLoader />;
}

return (
<PageRenderer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,10 @@ export const PageRenderer = (props: IPageRenderer) => {
>
<EditorContentWrapper editor={editor} id={id} tabIndex={tabIndex} />
{editor.isEditable && (
<>
<div>
<BlockMenu editor={editor} />
<AIFeaturesMenu menu={aiHandler?.menu} />
</>
</div>
)}
</EditorContainer>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const EditorWrapper: React.FC<Props> = (props) => {
containerClassName,
disabledExtensions,
displayConfig = DEFAULT_DISPLAY_CONFIG,
editable,
editorClassName = "",
extensions,
id,
Expand All @@ -38,6 +39,7 @@ export const EditorWrapper: React.FC<Props> = (props) => {
} = props;

const editor = useEditor({
editable,
disabledExtensions,
editorClassName,
enableHistory: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,24 +127,30 @@ export const CustomImageUploader = (props: CustomImageUploaderProps) => {
return "Uploading...";
}

if (draggedInside) {
if (draggedInside && editor.isEditable) {
return "Drop image here";
}

return "Add an image";
if (!editor.isEditable) {
return "Viewing Mode: Image Upload Disabled";
} else {
return "Add an image";
}
}, [draggedInside, failedToLoadImage, isImageBeingUploaded]);

return (
<div
className={cn(
"image-upload-component flex items-center justify-start gap-2 py-3 px-2 rounded-lg text-custom-text-300 hover:text-custom-text-200 bg-custom-background-90 hover:bg-custom-background-80 border border-dashed border-custom-border-300 transition-all duration-200 ease-in-out cursor-default",
"image-upload-component flex items-center justify-start gap-2 py-3 px-2 rounded-lg text-custom-text-300 bg-custom-background-90 border border-dashed border-custom-border-300 transition-all duration-200 ease-in-out cursor-default",
{
"hover:text-custom-text-200 cursor-pointer": editor.isEditable,
"bg-custom-background-80 text-custom-text-200": draggedInside,
"text-custom-primary-200 bg-custom-primary-100/10 hover:bg-custom-primary-100/10 hover:text-custom-primary-200 border-custom-primary-200/10":
selected,
"text-red-500 cursor-default hover:text-red-500": failedToLoadImage,
"bg-red-500/10 hover:bg-red-500/10": failedToLoadImage && selected,
"hover:text-custom-text-200 hover:bg-custom-background-80 cursor-pointer": editor.isEditable,
"bg-custom-background-80 text-custom-text-200": draggedInside && editor.isEditable,
"text-custom-primary-200 bg-custom-primary-100/10 border-custom-primary-200/10 hover:bg-custom-primary-100/10 hover:text-custom-primary-200":
selected && editor.isEditable,
"text-red-500 cursor-default": failedToLoadImage,
"hover:text-red-500": failedToLoadImage && editor.isEditable,
"bg-red-500/10": failedToLoadImage && selected,
"hover:bg-red-500/10": failedToLoadImage && selected && editor.isEditable,
}
)}
onDrop={onDrop}
Expand Down
97 changes: 53 additions & 44 deletions packages/editor/src/core/extensions/drop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,67 @@ import { Extension, Editor } from "@tiptap/core";
import { Plugin, PluginKey } from "@tiptap/pm/state";
import { EditorView } from "@tiptap/pm/view";

export const DropHandlerExtension = () =>
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,
Expand Down
9 changes: 5 additions & 4 deletions packages/editor/src/core/extensions/extensions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -145,9 +146,9 @@ 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 }) => {
Expand Down
18 changes: 15 additions & 3 deletions packages/editor/src/core/hooks/use-collaborative-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => {
const {
onTransaction,
disabledExtensions,
editable,
editorClassName,
editorProps = {},
embedHandler,
Expand All @@ -33,6 +34,7 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => {
// states
const [hasServerConnectionFailed, setHasServerConnectionFailed] = useState(false);
const [hasServerSynced, setHasServerSynced] = useState(false);
const [hasIndexedDbSynced, setHasIndexedDbSynced] = useState(false);
// initialize Hocuspocus provider
const provider = useMemo(
() =>
Expand All @@ -53,7 +55,10 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => {
setHasServerConnectionFailed(true);
}
},
onSynced: () => setHasServerSynced(true),
onSynced: () => {
serverHandler?.onServerSync?.();
setHasServerSynced(true);
},
}),
[id, realtimeConfig, serverHandler, user]
);
Expand All @@ -63,6 +68,10 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => {
[id, provider]
);

localProvider?.on("synced", () => {
setHasIndexedDbSynced(true);
});

// destroy and disconnect all providers connection on unmount
useEffect(
() => () => {
Expand All @@ -75,7 +84,7 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => {
const editor = useEditor({
disabledExtensions,
id,
onTransaction,
editable,
editorProps,
editorClassName,
enableHistory: false,
Expand All @@ -97,9 +106,10 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => {
}),
],
fileHandler,
handleEditorReady,
forwardedRef,
handleEditorReady,
mentionHandler,
onTransaction,
placeholder,
provider,
tabIndex,
Expand All @@ -109,5 +119,7 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => {
editor,
hasServerConnectionFailed,
hasServerSynced,
hasIndexedDbSynced,
localProvider,
};
};
Loading
Loading