diff --git a/packages/editor/core/src/ui/hooks/useEditor.tsx b/packages/editor/core/src/ui/hooks/useEditor.tsx index 837700915..f58c7964b 100644 --- a/packages/editor/core/src/ui/hooks/useEditor.tsx +++ b/packages/editor/core/src/ui/hooks/useEditor.tsx @@ -1,18 +1,23 @@ import { useEditor as useCustomEditor, Editor } from "@tiptap/react"; -import { useImperativeHandle, useRef, MutableRefObject } from "react"; -import { useDebouncedCallback } from "use-debounce"; -import { DeleteImage } from '../../types/delete-image'; +import { + useImperativeHandle, + useRef, + MutableRefObject, + useEffect, +} from "react"; +import { DeleteImage } from "../../types/delete-image"; import { CoreEditorProps } from "../props"; import { CoreEditorExtensions } from "../extensions"; -import { EditorProps } from '@tiptap/pm/view'; +import { EditorProps } from "@tiptap/pm/view"; import { getTrimmedHTML } from "../../lib/utils"; import { UploadImage } from "../../types/upload-image"; - -const DEBOUNCE_DELAY = 1500; +import { useInitializedContent } from "./useInitializedContent"; interface CustomEditorProps { uploadFile: UploadImage; - setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void; + setIsSubmitting?: ( + isSubmitting: "submitting" | "submitted" | "saved", + ) => void; setShouldShowAlert?: (showAlert: boolean) => void; value: string; deleteFile: DeleteImage; @@ -23,25 +28,37 @@ interface CustomEditorProps { forwardedRef?: any; } -export const useEditor = ({ uploadFile, deleteFile, editorProps = {}, value, extensions = [], onChange, setIsSubmitting, debouncedUpdatesEnabled, forwardedRef, setShouldShowAlert, }: CustomEditorProps) => { - const editor = useCustomEditor({ - editorProps: { - ...CoreEditorProps(uploadFile, setIsSubmitting), - ...editorProps, - }, - extensions: [...CoreEditorExtensions(deleteFile), ...extensions], - content: (typeof value === "string" && value.trim() !== "") ? value : "

", - onUpdate: async ({ editor }) => { - // for instant feedback loop - setIsSubmitting?.("submitting"); - setShouldShowAlert?.(true); - if (debouncedUpdatesEnabled) { - debouncedUpdates({ onChange: onChange, editor }); - } else { +export const useEditor = ({ + uploadFile, + deleteFile, + editorProps = {}, + value, + extensions = [], + onChange, + setIsSubmitting, + forwardedRef, + setShouldShowAlert, +}: CustomEditorProps) => { + const editor = useCustomEditor( + { + editorProps: { + ...CoreEditorProps(uploadFile, setIsSubmitting), + ...editorProps, + }, + extensions: [...CoreEditorExtensions(deleteFile), ...extensions], + content: + typeof value === "string" && value.trim() !== "" ? value : "

", + onUpdate: async ({ editor }) => { + // for instant feedback loop + setIsSubmitting?.("submitting"); + setShouldShowAlert?.(true); onChange?.(editor.getJSON(), getTrimmedHTML(editor.getHTML())); - } + }, }, - }); + [], + ); + + useInitializedContent(editor, value); const editorRef: MutableRefObject = useRef(null); editorRef.current = editor; @@ -55,12 +72,6 @@ export const useEditor = ({ uploadFile, deleteFile, editorProps = {}, value, ext }, })); - const debouncedUpdates = useDebouncedCallback(async ({ onChange, editor }) => { - if (onChange) { - onChange(editor.getJSON(), getTrimmedHTML(editor.getHTML())); - } - }, DEBOUNCE_DELAY); - if (!editor) { return null; } diff --git a/packages/editor/core/src/ui/hooks/useInitializedContent.tsx b/packages/editor/core/src/ui/hooks/useInitializedContent.tsx new file mode 100644 index 000000000..8e2ce1717 --- /dev/null +++ b/packages/editor/core/src/ui/hooks/useInitializedContent.tsx @@ -0,0 +1,19 @@ +import { Editor } from "@tiptap/react"; +import { useEffect, useRef } from "react"; + +export const useInitializedContent = (editor: Editor | null, value: string) => { + const hasInitializedContent = useRef(false); + + useEffect(() => { + if (editor) { + const cleanedValue = + typeof value === "string" && value.trim() !== "" ? value : "

"; + if (cleanedValue !== "

" && !hasInitializedContent.current) { + editor.commands.setContent(cleanedValue); + hasInitializedContent.current = true; + } else if (cleanedValue === "

" && hasInitializedContent.current) { + hasInitializedContent.current = false; + } + } + }, [value, editor]); +}; diff --git a/packages/editor/core/src/ui/hooks/useReadOnlyEditor.tsx b/packages/editor/core/src/ui/hooks/useReadOnlyEditor.tsx index 3e32c5044..522cd94b8 100644 --- a/packages/editor/core/src/ui/hooks/useReadOnlyEditor.tsx +++ b/packages/editor/core/src/ui/hooks/useReadOnlyEditor.tsx @@ -1,8 +1,13 @@ import { useEditor as useCustomEditor, Editor } from "@tiptap/react"; -import { useImperativeHandle, useRef, MutableRefObject } from "react"; +import { + useImperativeHandle, + useRef, + MutableRefObject, + useEffect, +} from "react"; import { CoreReadOnlyEditorExtensions } from "../../ui/read-only/extensions"; import { CoreReadOnlyEditorProps } from "../../ui/read-only/props"; -import { EditorProps } from '@tiptap/pm/view'; +import { EditorProps } from "@tiptap/pm/view"; interface CustomReadOnlyEditorProps { value: string; @@ -11,10 +16,16 @@ interface CustomReadOnlyEditorProps { editorProps?: EditorProps; } -export const useReadOnlyEditor = ({ value, forwardedRef, extensions = [], editorProps = {} }: CustomReadOnlyEditorProps) => { +export const useReadOnlyEditor = ({ + value, + forwardedRef, + extensions = [], + editorProps = {}, +}: CustomReadOnlyEditorProps) => { const editor = useCustomEditor({ editable: false, - content: (typeof value === "string" && value.trim() !== "") ? value : "

", + content: + typeof value === "string" && value.trim() !== "" ? value : "

", editorProps: { ...CoreReadOnlyEditorProps, ...editorProps, @@ -22,6 +33,14 @@ export const useReadOnlyEditor = ({ value, forwardedRef, extensions = [], editor extensions: [...CoreReadOnlyEditorExtensions, ...extensions], }); + const hasIntiliazedContent = useRef(false); + useEffect(() => { + if (editor && !value && !hasIntiliazedContent.current) { + editor.commands.setContent(value); + hasIntiliazedContent.current = true; + } + }, [value]); + const editorRef: MutableRefObject = useRef(null); editorRef.current = editor; @@ -34,7 +53,6 @@ export const useReadOnlyEditor = ({ value, forwardedRef, extensions = [], editor }, })); - if (!editor) { return null; } diff --git a/packages/editor/core/src/ui/plugins/delete-image.tsx b/packages/editor/core/src/ui/plugins/delete-image.tsx index ba21d686d..56284472b 100644 --- a/packages/editor/core/src/ui/plugins/delete-image.tsx +++ b/packages/editor/core/src/ui/plugins/delete-image.tsx @@ -16,7 +16,7 @@ const TrackImageDeletionPlugin = (deleteImage: DeleteImage): Plugin => new Plugin({ key: deleteKey, appendTransaction: (transactions: readonly Transaction[], oldState: EditorState, newState: EditorState) => { - const newImageSources = new Set(); + const newImageSources = new Set(); newState.doc.descendants((node) => { if (node.type.name === IMAGE_NODE_TYPE) { newImageSources.add(node.attrs.src); diff --git a/packages/editor/lite-text-editor/src/ui/extensions/custom-list-extension.tsx b/packages/editor/lite-text-editor/src/ui/extensions/custom-list-extension.tsx deleted file mode 100644 index f0bc70cff..000000000 --- a/packages/editor/lite-text-editor/src/ui/extensions/custom-list-extension.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import ListItem from '@tiptap/extension-list-item' - -export const CustomListItem = ListItem.extend({ - addKeyboardShortcuts() { - return { - 'Shift-Enter': () => this.editor.chain().focus().splitListItem('listItem').run(), - } - }, -}) diff --git a/packages/editor/lite-text-editor/src/ui/extensions/enter-key-extension.tsx b/packages/editor/lite-text-editor/src/ui/extensions/enter-key-extension.tsx index 04c4a1fbe..129efa4ee 100644 --- a/packages/editor/lite-text-editor/src/ui/extensions/enter-key-extension.tsx +++ b/packages/editor/lite-text-editor/src/ui/extensions/enter-key-extension.tsx @@ -1,16 +1,25 @@ -import { Extension } from '@tiptap/core'; +import { Extension } from "@tiptap/core"; -export const EnterKeyExtension = (onEnterKeyPress?: () => void) => Extension.create({ - name: 'enterKey', +export const EnterKeyExtension = (onEnterKeyPress?: () => void) => + Extension.create({ + name: "enterKey", - addKeyboardShortcuts() { - return { - 'Enter': () => { - if (onEnterKeyPress) { - onEnterKeyPress(); - } - return true; - }, - } - }, -}); + addKeyboardShortcuts() { + return { + Enter: () => { + if (onEnterKeyPress) { + onEnterKeyPress(); + } + return true; + }, + "Shift-Enter": ({ editor }) => + editor.commands.first(({ commands }) => [ + () => commands.newlineInCode(), + () => commands.splitListItem("listItem"), + () => commands.createParagraphNear(), + () => commands.liftEmptyBlock(), + () => commands.splitBlock(), + ]), + }; + }, + }); diff --git a/packages/editor/lite-text-editor/src/ui/extensions/index.tsx b/packages/editor/lite-text-editor/src/ui/extensions/index.tsx index ccd04a395..358f15294 100644 --- a/packages/editor/lite-text-editor/src/ui/extensions/index.tsx +++ b/packages/editor/lite-text-editor/src/ui/extensions/index.tsx @@ -1,7 +1,5 @@ -import { CustomListItem } from "./custom-list-extension"; import { EnterKeyExtension } from "./enter-key-extension"; export const LiteTextEditorExtensions = (onEnterKeyPress?: () => void) => [ - CustomListItem, EnterKeyExtension(onEnterKeyPress), ]; diff --git a/web/components/issues/comment/add-comment.tsx b/web/components/issues/comment/add-comment.tsx index f836efb01..7baca7d61 100644 --- a/web/components/issues/comment/add-comment.tsx +++ b/web/components/issues/comment/add-comment.tsx @@ -7,7 +7,7 @@ import { FileService } from "services/file.service"; // components import { LiteTextEditorWithRef } from "@plane/lite-text-editor"; // ui -import { Button, Tooltip } from "@plane/ui"; +import { Button } from "@plane/ui"; import { Globe2, Lock } from "lucide-react"; // types @@ -72,35 +72,6 @@ export const AddComment: React.FC = ({ disabled = false, onSubmit, showAc
- {showAccessSpecifier && ( -
- ( -
- {commentAccess.map((access) => ( - - - - ))} -
- )} - /> -
- )} = ({