halo/ui/src/components/editor/utils/upload.ts

100 lines
2.8 KiB
TypeScript

// image drag and paste upload
import { CoreEditor } from "@halo-dev/richtext-editor";
import type { Attachment } from "@halo-dev/api-client";
import Image from "../extensions/image";
import type { AxiosRequestConfig } from "axios";
export interface FileProps {
file: File;
editor: CoreEditor;
}
/**
* Handles file events, determining if the file is an image and triggering the appropriate upload process.
*
* @param {FileProps} { file, editor } - File and editor instances
* @returns {boolean} - True if a file is handled, otherwise false
*/
export const handleFileEvent = ({ file, editor }: FileProps) => {
if (!file) {
return false;
}
if (file.type.startsWith("image/")) {
uploadImage({ file, editor });
return true;
}
return true;
};
/**
* Uploads an image file and inserts it into the editor.
*
* @param {FileProps} { file, editor } - File to be uploaded and the editor instance
*/
export const uploadImage = ({ file, editor }: FileProps) => {
const { view } = editor;
const node = view.props.state.schema.nodes[Image.name].create({
file: file,
});
editor.view.dispatch(editor.view.state.tr.replaceSelectionWith(node));
};
export interface UploadFetchResponse {
controller: AbortController;
onUploadProgress: (progress: number) => void;
onFinish: (attachment?: Attachment) => void;
onError: (error: Error) => void;
}
/**
* Uploads a file with progress monitoring, cancellation support, and callbacks for completion and errors.
*
* @param {File} file - File to be uploaded
* @param {Function} upload - Function to handle the file upload, should return a Promise
* @returns {Promise<UploadFetchResponse>} - Returns an object with control and callback methods
*/
export const uploadFile = async (
file: File,
upload: (file: File, options?: AxiosRequestConfig) => Promise<Attachment>,
uploadResponse: UploadFetchResponse
) => {
const { signal } = uploadResponse.controller;
upload(file, {
signal,
onUploadProgress(progressEvent) {
const progress = Math.round(
(progressEvent.loaded * 100) / (progressEvent.total || 0)
);
uploadResponse.onUploadProgress(progress);
},
})
.then((attachment) => {
uploadResponse.onFinish(attachment);
})
.catch((error) => {
uploadResponse.onError(error);
});
};
/**
* Converts a file to a Base64 string.
*
* @param {File} file - File to be converted
* @returns {Promise<string>} - A promise that resolves with the Base64 string
*/
export function fileToBase64(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function () {
resolve(reader.result as string);
};
reader.onerror = function (error) {
reject(error);
};
reader.readAsDataURL(file);
});
}