From 5c290e1302bfd3d63b824c7ccee475cf4a88d6fa Mon Sep 17 00:00:00 2001 From: Palanikannan1437 <73993394+Palanikannan1437@users.noreply.github.com> Date: Wed, 9 Aug 2023 09:42:57 +0530 Subject: [PATCH] Integrated tiptap with Issue Modal --- apps/app/components/issues/form.tsx | 52 +++++++++++-------- .../components/tiptap/extensions/index.tsx | 6 +-- .../tiptap/hooks/useDebouncedUpdates.tsx | 18 +++++++ .../tiptap/hooks/useNodeDeletion.tsx | 51 ++++++++++++++++++ apps/app/components/tiptap/index.tsx | 13 ++--- 5 files changed, 104 insertions(+), 36 deletions(-) create mode 100644 apps/app/components/tiptap/hooks/useDebouncedUpdates.tsx create mode 100644 apps/app/components/tiptap/hooks/useNodeDeletion.tsx diff --git a/apps/app/components/issues/form.tsx b/apps/app/components/issues/form.tsx index e16bd317f..8d66edd21 100644 --- a/apps/app/components/issues/form.tsx +++ b/apps/app/components/issues/form.tsx @@ -36,6 +36,7 @@ import { import { SparklesIcon, XMarkIcon } from "@heroicons/react/24/outline"; // types import type { ICurrentUserResponse, IIssue, ISearchIssueResponse } from "types"; +import Tiptap from "components/tiptap"; // rich-text-editor // const RemirrorRichTextEditor = dynamic(() => import("components/rich-text-editor"), { // ssr: false, @@ -145,6 +146,8 @@ export const IssueForm: FC = ({ reValidateMode: "onChange", }); + console.log("values", getValues()); + const issueName = watch("name"); const handleCreateUpdateIssue = async (formData: Partial) => { @@ -338,9 +341,8 @@ export const IssueForm: FC = ({ {issueName && issueName !== "" && ( - {/* ( */} - {/* setValue("description", jsonValue)} */} - {/* onHTMLChange={(htmlValue) => setValue("description_html", htmlValue)} */} - {/* placeholder="Description" */} - {/* ref={editorRef} */} - {/* /> */} - {/* )} */} - {/* /> */} + { + if (!value && !watch("description_html")) return <>; + + return ( + { + onChange(description_html); + setValue("description", description); + }} + /> + ); + }} + /> { @@ -523,7 +529,7 @@ export const IssueForm: FC = ({ onClick={() => setCreateMore((prevData) => !prevData)} > Create more - {}} size="md" /> + { }} size="md" />
Discard @@ -533,8 +539,8 @@ export const IssueForm: FC = ({ ? "Updating Issue..." : "Update Issue" : isSubmitting - ? "Adding Issue..." - : "Add Issue"} + ? "Adding Issue..." + : "Add Issue"}
diff --git a/apps/app/components/tiptap/extensions/index.tsx b/apps/app/components/tiptap/extensions/index.tsx index 81a6aa6eb..14d7106eb 100644 --- a/apps/app/components/tiptap/extensions/index.tsx +++ b/apps/app/components/tiptap/extensions/index.tsx @@ -54,7 +54,7 @@ export const TiptapExtensions = [ code: { HTMLAttributes: { class: - "rounded-md bg-stone-200 px-1 py-1 font-mono font-medium text-stone-900", + "rounded-md bg-custom-bg-1000 px-1 py-1 font-mono font-medium text-stone-900", spellcheck: "false", }, }, @@ -62,7 +62,7 @@ export const TiptapExtensions = [ horizontalRule: false, dropcursor: { color: "#DBEAFE", - width: 4, + width: 2, }, gapcursor: false, }), @@ -117,7 +117,7 @@ export const TiptapExtensions = [ includeChildren: true, }), UniqueID.configure({ - types: ['heading', 'paragraph', 'image'], + types: ['image'], }), SlashCommand, TiptapUnderline, diff --git a/apps/app/components/tiptap/hooks/useDebouncedUpdates.tsx b/apps/app/components/tiptap/hooks/useDebouncedUpdates.tsx new file mode 100644 index 000000000..0fc835a98 --- /dev/null +++ b/apps/app/components/tiptap/hooks/useDebouncedUpdates.tsx @@ -0,0 +1,18 @@ +import { useDebouncedCallback } from 'use-debounce'; +import { Editor as CoreEditor } from "@tiptap/core"; + +type DebouncedUpdatesProps = { + onChange?: (json: any, html: string) => void; + editor: CoreEditor | null; +}; + +export const useDebouncedUpdates = (props: DebouncedUpdatesProps) => + useDebouncedCallback(async () => { + setTimeout(async () => { + if (props.onChange) { + props.onChange(props.editor.getJSON(), props.editor.getHTML()); + } + }, 500); + }, 1000); +; + diff --git a/apps/app/components/tiptap/hooks/useNodeDeletion.tsx b/apps/app/components/tiptap/hooks/useNodeDeletion.tsx new file mode 100644 index 000000000..9370947b6 --- /dev/null +++ b/apps/app/components/tiptap/hooks/useNodeDeletion.tsx @@ -0,0 +1,51 @@ +import { useCallback, useRef } from 'react'; +import { Node } from "@tiptap/pm/model"; +import { Editor as CoreEditor } from "@tiptap/core"; +import { EditorState } from '@tiptap/pm/state'; +import fileService from 'services/file.service'; + +export const useNodeDeletion = () => { + const previousState = useRef(); + + const onNodeDeleted = useCallback( + async (node: Node) => { + if (node.type.name === 'image') { + const assetUrlWithWorkspaceId = new URL(node.attrs.src).pathname.substring(1); + const resStatus = await fileService.deleteFile(assetUrlWithWorkspaceId); + if (resStatus === 204) { + console.log("file deleted successfully"); + } + } + }, + [], + ); + + const checkForNodeDeletions = useCallback( + (editor: CoreEditor) => { + const prevNodesById: Record = {}; + previousState.current?.doc.forEach((node) => { + if (node.attrs.id) { + prevNodesById[node.attrs.id] = node; + } + }); + + const nodesById: Record = {}; + editor.state?.doc.forEach((node) => { + if (node.attrs.id) { + nodesById[node.attrs.id] = node; + } + }); + + previousState.current = editor.state; + + for (const [id, node] of Object.entries(prevNodesById)) { + if (nodesById[id] === undefined) { + onNodeDeleted(node); + } + } + }, + [onNodeDeleted], + ); + + return { checkForNodeDeletions }; +}; diff --git a/apps/app/components/tiptap/index.tsx b/apps/app/components/tiptap/index.tsx index d0365769d..d2103d506 100644 --- a/apps/app/components/tiptap/index.tsx +++ b/apps/app/components/tiptap/index.tsx @@ -15,7 +15,7 @@ type TiptapProps = { borderOnFocus?: boolean; customClassName?: string; onChange?: (json: any, html: string) => void; - setIsSubmitting: (isSubmitting: boolean) => void; + setIsSubmitting?: (isSubmitting: boolean) => void; } const Tiptap = ({ onChange, setIsSubmitting, value, noBorder, borderOnFocus, customClassName }: TiptapProps) => { @@ -24,7 +24,7 @@ const Tiptap = ({ onChange, setIsSubmitting, value, noBorder, borderOnFocus, cus extensions: TiptapExtensions, content: value, onUpdate: async ({ editor }) => { - setIsSubmitting(true); + setIsSubmitting?.(true); checkForNodeDeletions(editor) debouncedUpdates({ onChange, editor }); } @@ -32,13 +32,6 @@ const Tiptap = ({ onChange, setIsSubmitting, value, noBorder, borderOnFocus, cus const previousState = useRef(); - const extractPath = useCallback((url: string, searchString: string) => { - if (url.startsWith(searchString)) { - console.log("chala", url, searchString) - return url.substring(searchString.length); - } - }, []); - const onNodeDeleted = useCallback( async (node: Node) => { if (node.type.name === 'image') { @@ -100,7 +93,7 @@ const Tiptap = ({ onChange, setIsSubmitting, value, noBorder, borderOnFocus, cus className={`tiptap-editor-container relative min-h-[150px] ${editorClassNames}`} > {editor && } -
+