[WIKI-181] refactor: invalid file handling #7139
This commit is contained in:
parent
b16a585102
commit
01b685ea57
3 changed files with 23 additions and 50 deletions
|
|
@ -86,6 +86,10 @@ export const CustomImageUploader = (props: CustomImageUploaderProps) => {
|
||||||
[editor]
|
[editor]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleInvalidFile = useCallback((_error: EFileError, _file: File, message: string) => {
|
||||||
|
alert(message);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
const { isUploading: isImageBeingUploaded, uploadFile } = useUploader({
|
const { isUploading: isImageBeingUploaded, uploadFile } = useUploader({
|
||||||
acceptedMimeTypes: ACCEPTED_IMAGE_MIME_TYPES,
|
acceptedMimeTypes: ACCEPTED_IMAGE_MIME_TYPES,
|
||||||
|
|
@ -94,18 +98,12 @@ export const CustomImageUploader = (props: CustomImageUploaderProps) => {
|
||||||
handleProgressStatus,
|
handleProgressStatus,
|
||||||
loadFileFromFileSystem: loadImageFromFileSystem,
|
loadFileFromFileSystem: loadImageFromFileSystem,
|
||||||
maxFileSize,
|
maxFileSize,
|
||||||
|
onInvalidFile: handleInvalidFile,
|
||||||
onUpload,
|
onUpload,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleInvalidFile = useCallback((_error: EFileError, message: string) => {
|
|
||||||
alert(message);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const { draggedInside, onDrop, onDragEnter, onDragLeave } = useDropZone({
|
const { draggedInside, onDrop, onDragEnter, onDragLeave } = useDropZone({
|
||||||
acceptedMimeTypes: ACCEPTED_IMAGE_MIME_TYPES,
|
|
||||||
editor,
|
editor,
|
||||||
maxFileSize,
|
|
||||||
onInvalidFile: handleInvalidFile,
|
|
||||||
pos: getPos(),
|
pos: getPos(),
|
||||||
type: "image",
|
type: "image",
|
||||||
uploader: uploadFile,
|
uploader: uploadFile,
|
||||||
|
|
@ -140,11 +138,8 @@ export const CustomImageUploader = (props: CustomImageUploaderProps) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await uploadFirstFileAndInsertRemaining({
|
await uploadFirstFileAndInsertRemaining({
|
||||||
acceptedMimeTypes: ACCEPTED_IMAGE_MIME_TYPES,
|
|
||||||
editor,
|
editor,
|
||||||
filesList,
|
filesList,
|
||||||
maxFileSize,
|
|
||||||
onInvalidFile: (_error, message) => alert(message),
|
|
||||||
pos: getPos(),
|
pos: getPos(),
|
||||||
type: "image",
|
type: "image",
|
||||||
uploader: uploadFile,
|
uploader: uploadFile,
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,11 @@ import { TEditorCommands } from "@/types";
|
||||||
|
|
||||||
type TUploaderArgs = {
|
type TUploaderArgs = {
|
||||||
acceptedMimeTypes: string[];
|
acceptedMimeTypes: string[];
|
||||||
editorCommand: (file: File) => Promise<string>;
|
editorCommand: (file: File) => Promise<string | undefined>;
|
||||||
handleProgressStatus?: (isUploading: boolean) => void;
|
handleProgressStatus?: (isUploading: boolean) => void;
|
||||||
loadFileFromFileSystem?: (file: string) => void;
|
loadFileFromFileSystem?: (file: string) => void;
|
||||||
maxFileSize: number;
|
maxFileSize: number;
|
||||||
onInvalidFile: (error: EFileError, message: string) => void;
|
onInvalidFile: (error: EFileError, file: File, message: string) => void;
|
||||||
onUpload: (url: string, file: File) => void;
|
onUpload: (url: string, file: File) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -38,7 +38,7 @@ export const useUploader = (args: TUploaderArgs) => {
|
||||||
acceptedMimeTypes,
|
acceptedMimeTypes,
|
||||||
file,
|
file,
|
||||||
maxFileSize,
|
maxFileSize,
|
||||||
onError: onInvalidFile,
|
onError: (error, message) => onInvalidFile(error, file, message),
|
||||||
});
|
});
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
handleProgressStatus?.(false);
|
handleProgressStatus?.(false);
|
||||||
|
|
@ -60,7 +60,7 @@ export const useUploader = (args: TUploaderArgs) => {
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
}
|
}
|
||||||
const url: string = await editorCommand(file);
|
const url = await editorCommand(file);
|
||||||
|
|
||||||
if (!url) {
|
if (!url) {
|
||||||
throw new Error("Something went wrong while uploading the file.");
|
throw new Error("Something went wrong while uploading the file.");
|
||||||
|
|
@ -89,17 +89,14 @@ export const useUploader = (args: TUploaderArgs) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
type TDropzoneArgs = {
|
type TDropzoneArgs = {
|
||||||
acceptedMimeTypes: string[];
|
|
||||||
editor: Editor;
|
editor: Editor;
|
||||||
maxFileSize: number;
|
|
||||||
onInvalidFile: (error: EFileError, message: string) => void;
|
|
||||||
pos: number;
|
pos: number;
|
||||||
type: Extract<TEditorCommands, "attachment" | "image">;
|
type: Extract<TEditorCommands, "attachment" | "image">;
|
||||||
uploader: (file: File) => Promise<void>;
|
uploader: (file: File) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useDropZone = (args: TDropzoneArgs) => {
|
export const useDropZone = (args: TDropzoneArgs) => {
|
||||||
const { acceptedMimeTypes, editor, maxFileSize, onInvalidFile, pos, type, uploader } = args;
|
const { editor, pos, type, uploader } = args;
|
||||||
// states
|
// states
|
||||||
const [isDragging, setIsDragging] = useState<boolean>(false);
|
const [isDragging, setIsDragging] = useState<boolean>(false);
|
||||||
const [draggedInside, setDraggedInside] = useState<boolean>(false);
|
const [draggedInside, setDraggedInside] = useState<boolean>(false);
|
||||||
|
|
@ -126,22 +123,21 @@ export const useDropZone = (args: TDropzoneArgs) => {
|
||||||
async (e: DragEvent<HTMLDivElement>) => {
|
async (e: DragEvent<HTMLDivElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setDraggedInside(false);
|
setDraggedInside(false);
|
||||||
if (e.dataTransfer.files.length === 0 || !editor.isEditable) {
|
const filesList = e.dataTransfer.files;
|
||||||
|
|
||||||
|
if (filesList.length === 0 || !editor.isEditable) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const filesList = e.dataTransfer.files;
|
|
||||||
await uploadFirstFileAndInsertRemaining({
|
await uploadFirstFileAndInsertRemaining({
|
||||||
acceptedMimeTypes,
|
|
||||||
editor,
|
editor,
|
||||||
filesList,
|
filesList,
|
||||||
maxFileSize,
|
|
||||||
onInvalidFile,
|
|
||||||
pos,
|
pos,
|
||||||
type,
|
type,
|
||||||
uploader,
|
uploader,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[acceptedMimeTypes, editor, maxFileSize, onInvalidFile, pos, type, uploader]
|
[editor, pos, type, uploader]
|
||||||
);
|
);
|
||||||
const onDragEnter = useCallback(() => setDraggedInside(true), []);
|
const onDragEnter = useCallback(() => setDraggedInside(true), []);
|
||||||
const onDragLeave = useCallback(() => setDraggedInside(false), []);
|
const onDragLeave = useCallback(() => setDraggedInside(false), []);
|
||||||
|
|
@ -156,11 +152,8 @@ export const useDropZone = (args: TDropzoneArgs) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
type TMultipleFileArgs = {
|
type TMultipleFileArgs = {
|
||||||
acceptedMimeTypes: string[];
|
|
||||||
editor: Editor;
|
editor: Editor;
|
||||||
filesList: FileList;
|
filesList: FileList;
|
||||||
maxFileSize: number;
|
|
||||||
onInvalidFile: (error: EFileError, message: string) => void;
|
|
||||||
pos: number;
|
pos: number;
|
||||||
type: Extract<TEditorCommands, "attachment" | "image">;
|
type: Extract<TEditorCommands, "attachment" | "image">;
|
||||||
uploader: (file: File) => Promise<void>;
|
uploader: (file: File) => Promise<void>;
|
||||||
|
|
@ -168,35 +161,18 @@ type TMultipleFileArgs = {
|
||||||
|
|
||||||
// Upload the first file and insert the remaining ones for uploading multiple files
|
// Upload the first file and insert the remaining ones for uploading multiple files
|
||||||
export const uploadFirstFileAndInsertRemaining = async (args: TMultipleFileArgs) => {
|
export const uploadFirstFileAndInsertRemaining = async (args: TMultipleFileArgs) => {
|
||||||
const { acceptedMimeTypes, editor, filesList, maxFileSize, onInvalidFile, pos, type, uploader } = args;
|
const { editor, filesList, pos, type, uploader } = args;
|
||||||
const filteredFiles: File[] = [];
|
const filesArray = Array.from(filesList);
|
||||||
for (let i = 0; i < filesList.length; i += 1) {
|
if (filesArray.length === 0) {
|
||||||
const file = filesList.item(i);
|
|
||||||
if (
|
|
||||||
file &&
|
|
||||||
isFileValid({
|
|
||||||
acceptedMimeTypes,
|
|
||||||
file,
|
|
||||||
maxFileSize,
|
|
||||||
onError: onInvalidFile,
|
|
||||||
})
|
|
||||||
) {
|
|
||||||
filteredFiles.push(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (filteredFiles.length !== filesList.length) {
|
|
||||||
console.warn("Some files were invalid and have been ignored.");
|
|
||||||
}
|
|
||||||
if (filteredFiles.length === 0) {
|
|
||||||
console.error("No files found to upload.");
|
console.error("No files found to upload.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload the first file
|
// Upload the first file
|
||||||
const firstFile = filteredFiles[0];
|
const firstFile = filesArray[0];
|
||||||
uploader(firstFile);
|
uploader(firstFile);
|
||||||
// Insert the remaining files
|
// Insert the remaining files
|
||||||
const remainingFiles = filteredFiles.slice(1);
|
const remainingFiles = filesArray.slice(1);
|
||||||
if (remainingFiles.length > 0) {
|
if (remainingFiles.length > 0) {
|
||||||
const docSize = editor.state.doc.content.size;
|
const docSize = editor.state.doc.content.size;
|
||||||
const posOfNextFileToBeInserted = Math.min(pos + 1, docSize);
|
const posOfNextFileToBeInserted = Math.min(pos + 1, docSize);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { Editor } from "@tiptap/core";
|
import { Editor } from "@tiptap/core";
|
||||||
import { EditorState, Plugin, PluginKey, Transaction } from "@tiptap/pm/state";
|
import { EditorState, Plugin, PluginKey, Transaction } from "@tiptap/pm/state";
|
||||||
|
// constants
|
||||||
|
import { CORE_EDITOR_META } from "@/constants/meta";
|
||||||
// plane editor imports
|
// plane editor imports
|
||||||
import { NODE_FILE_MAP } from "@/plane-editor/constants/utility";
|
import { NODE_FILE_MAP } from "@/plane-editor/constants/utility";
|
||||||
// types
|
// types
|
||||||
|
|
@ -32,7 +34,7 @@ export const TrackFileDeletionPlugin = (editor: Editor, deleteHandler: TFileHand
|
||||||
|
|
||||||
transactions.forEach((transaction) => {
|
transactions.forEach((transaction) => {
|
||||||
// if the transaction has meta of skipFileDeletion set to true, then return (like while clearing the editor content programmatically)
|
// if the transaction has meta of skipFileDeletion set to true, then return (like while clearing the editor content programmatically)
|
||||||
if (transaction.getMeta("skipFileDeletion")) return;
|
if (transaction.getMeta(CORE_EDITOR_META.SKIP_FILE_DELETION)) return;
|
||||||
|
|
||||||
const removedFiles: TFileNode[] = [];
|
const removedFiles: TFileNode[] = [];
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue