refactored editor to not require workspace slug

This commit is contained in:
Palanikannan1437 2023-09-28 16:05:52 +05:30
parent d639a0126d
commit 4298b0500e
10 changed files with 63 additions and 62 deletions

View File

@ -6,7 +6,10 @@
"web", "web",
"space", "space",
"packages/editor/*", "packages/editor/*",
"packages/*" "packages/eslint-config-custom",
"packages/tailwind-config-custom",
"packages/tsconfig",
"packages/ui"
], ],
"scripts": { "scripts": {
"build": "turbo run build", "build": "turbo run build",

View File

@ -1 +1 @@
export type UploadImage = (workspaceSlug: string, formData: FormData) => Promise<any>; export type UploadImage = (file: File) => Promise<string>;

View File

@ -32,7 +32,6 @@ import "highlight.js/styles/github-dark.css";
lowlight.registerLanguage("ts", ts); lowlight.registerLanguage("ts", ts);
export const TiptapExtensions = ( export const TiptapExtensions = (
workspaceSlug: string,
uploadFile: UploadImage, uploadFile: UploadImage,
deleteFile: DeleteImage, deleteFile: DeleteImage,
setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void
@ -126,7 +125,7 @@ export const TiptapExtensions = (
}, },
includeChildren: true, includeChildren: true,
}), }),
SlashCommand(workspaceSlug, uploadFile, setIsSubmitting), SlashCommand(uploadFile, setIsSubmitting),
TiptapUnderline, TiptapUnderline,
TextStyle, TextStyle,
Color, Color,

View File

@ -59,7 +59,6 @@ const Command = Extension.create({
const getSuggestionItems = const getSuggestionItems =
( (
workspaceSlug: string,
uploadFile: UploadImage, uploadFile: UploadImage,
setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void
) => ) =>
@ -185,7 +184,7 @@ const getSuggestionItems =
if (input.files?.length) { if (input.files?.length) {
const file = input.files[0]; const file = input.files[0];
const pos = editor.view.state.selection.from; const pos = editor.view.state.selection.from;
startImageUpload(file, editor.view, pos, workspaceSlug, uploadFile, setIsSubmitting); startImageUpload(file, editor.view, pos, uploadFile, setIsSubmitting);
} }
}; };
input.click(); input.click();
@ -351,13 +350,12 @@ const renderItems = () => {
}; };
export const SlashCommand = ( export const SlashCommand = (
workspaceSlug: string,
uploadFile: UploadImage, uploadFile: UploadImage,
setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void
) => ) =>
Command.configure({ Command.configure({
suggestion: { suggestion: {
items: getSuggestionItems(workspaceSlug, uploadFile, setIsSubmitting), items: getSuggestionItems(uploadFile, setIsSubmitting),
render: renderItems, render: renderItems,
}, },
}); });

View File

@ -24,7 +24,6 @@ interface ITiptapEditor {
onChange?: (json: any, html: string) => void; onChange?: (json: any, html: string) => void;
setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void; setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void;
setShouldShowAlert?: (showAlert: boolean) => void; setShouldShowAlert?: (showAlert: boolean) => void;
workspaceSlug: string;
editable?: boolean; editable?: boolean;
forwardedRef?: any; forwardedRef?: any;
debouncedUpdatesEnabled?: boolean; debouncedUpdatesEnabled?: boolean;
@ -59,7 +58,6 @@ const TiptapEditor = ({
uploadFile, uploadFile,
deleteFile, deleteFile,
noBorder, noBorder,
workspaceSlug,
borderOnFocus, borderOnFocus,
customClassName, customClassName,
forwardedRef, forwardedRef,
@ -69,9 +67,9 @@ const TiptapEditor = ({
}: TiptapProps) => { }: TiptapProps) => {
const editor = useEditor({ const editor = useEditor({
editable: editable ?? true, editable: editable ?? true,
editorProps: TiptapEditorProps(workspaceSlug, uploadFile, setIsSubmitting), editorProps: TiptapEditorProps(uploadFile, setIsSubmitting),
// @ts-expect-err // @ts-expect-err
extensions: TiptapExtensions(workspaceSlug, uploadFile, deleteFile, setIsSubmitting), extensions: TiptapExtensions(uploadFile, deleteFile, setIsSubmitting),
content: (typeof value === "string" && value.trim() !== "") ? value : "<p></p>", content: (typeof value === "string" && value.trim() !== "") ? value : "<p></p>",
onUpdate: async ({ editor }) => { onUpdate: async ({ editor }) => {
// for instant feedback loop // for instant feedback loop

View File

@ -57,7 +57,6 @@ export async function startImageUpload(
file: File, file: File,
view: EditorView, view: EditorView,
pos: number, pos: number,
workspaceSlug: string,
uploadFile: UploadImage, uploadFile: UploadImage,
setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void
) { ) {
@ -83,11 +82,8 @@ export async function startImageUpload(
view.dispatch(tr); view.dispatch(tr);
}; };
if (!workspaceSlug) {
return;
}
setIsSubmitting?.("submitting"); setIsSubmitting?.("submitting");
const src = await UploadImageHandler(file, workspaceSlug, uploadFile); const src = await UploadImageHandler(file, uploadFile);
const { schema } = view.state; const { schema } = view.state;
pos = findPlaceholder(view.state, id); pos = findPlaceholder(view.state, id);
@ -101,21 +97,13 @@ export async function startImageUpload(
view.dispatch(transaction); view.dispatch(transaction);
} }
const UploadImageHandler = (file: File, workspaceSlug: string, const UploadImageHandler = (file: File,
uploadFile: UploadImage uploadFile: UploadImage
): Promise<string> => { ): Promise<string> => {
if (!workspaceSlug) {
return Promise.reject("Workspace slug is missing");
}
try { try {
const formData = new FormData();
formData.append("asset", file);
formData.append("attributes", JSON.stringify({}));
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
const imageUrl = await uploadFile(workspaceSlug, formData) const imageUrl = await uploadFile(file)
.then((response: { asset: string }) => response.asset);
const image = new Image(); const image = new Image();
image.src = imageUrl; image.src = imageUrl;

View File

@ -4,7 +4,6 @@ import { startImageUpload } from "@/ui/plugins/upload-image";
import { UploadImage } from "@/types/upload-image"; import { UploadImage } from "@/types/upload-image";
export function TiptapEditorProps( export function TiptapEditorProps(
workspaceSlug: string,
uploadFile: UploadImage, uploadFile: UploadImage,
setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void
): EditorProps { ): EditorProps {
@ -37,7 +36,7 @@ export function TiptapEditorProps(
event.preventDefault(); event.preventDefault();
const file = event.clipboardData.files[0]; const file = event.clipboardData.files[0];
const pos = view.state.selection.from; const pos = view.state.selection.from;
startImageUpload(file, view, pos, workspaceSlug, uploadFile, setIsSubmitting); startImageUpload(file, view, pos, uploadFile, setIsSubmitting);
return true; return true;
} }
return false; return false;
@ -61,7 +60,7 @@ export function TiptapEditorProps(
}); });
// here we deduct 1 from the pos or else the image will create an extra node // here we deduct 1 from the pos or else the image will create an extra node
if (coordinates) { if (coordinates) {
startImageUpload(file, view, coordinates.pos - 1, workspaceSlug, uploadFile, setIsSubmitting); startImageUpload(file, view, coordinates.pos - 1, uploadFile, setIsSubmitting);
} }
return true; return true;
} }

View File

@ -1,35 +1,35 @@
import { import {
useEditor as useEditorCore, useEditor as useEditorCore,
} from "@tiptap/react"; } from "@tiptap/react";
import { findTableAncestor } from "@/lib/utils"; import { findTableAncestor } from "@/lib/utils";
export const useEditor = (props: any) => useEditorCore({ export const useEditor = (props: any) => useEditorCore({
editorProps: { editorProps: {
attributes: { attributes: {
class: `prose prose-brand max-w-full prose-headings:font-display font-default focus:outline-none`, class: `prose prose-brand max-w-full prose-headings:font-display font-default focus:outline-none`,
}, },
handleDOMEvents: { handleDOMEvents: {
keydown: (_view, event) => { keydown: (_view, event) => {
// prevent default event listeners from firing when slash command is active // prevent default event listeners from firing when slash command is active
if (["ArrowUp", "ArrowDown", "Enter"].includes(event.key)) { if (["ArrowUp", "ArrowDown", "Enter"].includes(event.key)) {
const slashCommand = document.querySelector("#slash-command"); const slashCommand = document.querySelector("#slash-command");
if (slashCommand) { if (slashCommand) {
return true; return true;
}
}
},
},
handlePaste: () => {
if (typeof window !== "undefined") {
const selection: any = window?.getSelection();
if (selection.rangeCount !== 0) {
const range = selection.getRangeAt(0);
if (findTableAncestor(range.startContainer)) {
return;
}
}
} }
}, }
}, },
...props, },
}); handlePaste: () => {
if (typeof window !== "undefined") {
const selection: any = window?.getSelection();
if (selection.rangeCount !== 0) {
const range = selection.getRangeAt(0);
if (findTableAncestor(range.startContainer)) {
return;
}
}
}
},
},
...props,
});

View File

@ -135,10 +135,9 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({
return ( return (
<TiptapEditor <TiptapEditor
uploadFile={fileService.uploadFile} uploadFile={fileService.getUploadFileFunction(workspaceSlug)}
deleteFile={fileService.deleteImage} deleteFile={fileService.deleteImage}
value={value} value={value}
workspaceSlug={workspaceSlug}
debouncedUpdatesEnabled={true} debouncedUpdatesEnabled={true}
setShouldShowAlert={setShowAlert} setShouldShowAlert={setShowAlert}
setIsSubmitting={setIsSubmitting} setIsSubmitting={setIsSubmitting}

View File

@ -34,13 +34,29 @@ class FileService extends APIService {
} }
async uploadFile(workspaceSlug: string, file: FormData): Promise<any> { async uploadFile(workspaceSlug: string, file: FormData): Promise<any> {
return this.mediaUpload(`/api/workspaces/${workspaceSlug}/file-assets/`, file) return this.post(`/api/workspaces/${workspaceSlug}/file-assets/`, file, {
headers: {
...this.getHeaders(),
"Content-Type": "multipart/form-data",
},
})
.then((response) => response?.data) .then((response) => response?.data)
.catch((error) => { .catch((error) => {
throw error?.response?.data; throw error?.response?.data;
}); });
} }
getUploadFileFunction(workspaceSlug: string): (file: File) => Promise<string> {
return async (file: File) => {
const formData = new FormData();
formData.append("asset", file);
formData.append("attributes", JSON.stringify({}));
const data = await this.uploadFile(workspaceSlug, formData);
return data.asset;
};
}
async deleteImage(assetUrlWithWorkspaceId: string): Promise<any> { async deleteImage(assetUrlWithWorkspaceId: string): Promise<any> {
return this.delete(`/api/workspaces/file-assets/${assetUrlWithWorkspaceId}/`) return this.delete(`/api/workspaces/file-assets/${assetUrlWithWorkspaceId}/`)
.then((response) => response?.status) .then((response) => response?.status)
@ -59,6 +75,7 @@ class FileService extends APIService {
throw error?.response?.data; throw error?.response?.data;
}); });
} }
async uploadUserFile(file: FormData): Promise<any> { async uploadUserFile(file: FormData): Promise<any> {
return this.mediaUpload(`/api/users/file-assets/`, file) return this.mediaUpload(`/api/users/file-assets/`, file)
.then((response) => response?.data) .then((response) => response?.data)