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