Path: blob/master/src/packages/frontend/editors/slate/upload.tsx
1691 views
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Transforms } from "slate";6import { SlateEditor } from "./editable-markdown";7import { useEffect, useMemo, useRef } from "react";8import { Dropzone, BlobUpload } from "@cocalc/frontend/file-upload";9import { getFocus } from "./format/commands";10import { useFrameContext } from "@cocalc/frontend/frame-editors/frame-tree/frame-context";1112export default function useUpload(13editor: SlateEditor,14body: React.JSX.Element,15): React.JSX.Element {16const dropzoneRef = useRef<Dropzone>(null);17const { actions, project_id, path } = useFrameContext();18const actionsRef = useRef<any>(actions);19actionsRef.current = actions;20const pathRef = useRef<string>(path);21pathRef.current = path;2223// We setup the slate "plugin" change to insertData here exactly once when24// the component is mounted, because otherwise we would have to save25// the dropzoneRef as an attribute on editor, which would make it not JSON-able.26// Also, this simplifies using upload in editable-markdown.27useEffect(() => {28const { insertData } = editor;2930editor.insertData = (data) => {31if (dropzoneRef?.current == null) {32// fallback33insertData(data);34return;35}36const items = data.items;37for (let i = 0; i < items.length; i++) {38const item = items[i];39if (item?.type.startsWith("image/")) {40const file = item.getAsFile();41if (file != null) {42const blob = file.slice(0, -1, item.type);43dropzoneRef?.current?.addFile(44new File([blob], `paste-${Math.random()}`, { type: item.type }),45);46}47return; // what if more than one ?48}49}50insertData(data);51};52}, []);5354// NOTE: when updloadEventHandlers function changes the FileUploadWrapper doesn't properly update55// to reflect that (it's wrapping a third party library). (For some reason this wasn't an issue with56// React 17, but is with React 18.) This is why we store what updloadEventHandlers57// depends on in a ref and only create it once.58const updloadEventHandlers = useMemo(() => {59return {60error: (_, message) => {61if (actions?.set_error != null) {62actions?.set_error(`${message}`);63} else {64console.warn("Error uploading file -- ", message);65}66},67sending: ({ name }) => {68actionsRef.current?.set_status?.(`Uploading ${name}...`);69},70complete: (file) => {71actionsRef.current?.set_status?.("");72const { url } = file;73if (!url) {74// probably an error75return;76}77let node;78const { dataURL, height, upload } = file;79if (!height && !dataURL?.startsWith("data:image")) {80node = {81type: "link",82isInline: true,83children: [{ text: upload.filename ? upload.filename : "file" }],84url,85} as const;86} else {87node = {88type: "image",89isInline: true,90isVoid: true,91src: url,92children: [{ text: "" }],93} as const;94}95Transforms.insertFragment(editor, [node], {96at: getFocus(editor),97});98},99};100}, []);101102return (103<BlobUpload104show_upload={false}105className="smc-vfill"106project_id={project_id}107event_handlers={updloadEventHandlers}108style={{ height: "100%", width: "100%" }}109dropzone_ref={dropzoneRef}110>111{body}112</BlobUpload>113);114}115116117