fix: Cannot upload multiple files at once from editor command menu (#4957)

This commit is contained in:
Tom Moor
2023-02-28 19:52:07 -05:00
committed by GitHub
parent e4ec1681d5
commit e90e111139
2 changed files with 38 additions and 25 deletions

View File

@@ -290,7 +290,7 @@ class CommandMenu<T extends MenuItem> extends React.Component<Props<T>, State> {
this.setState({ insertItem: item }); this.setState({ insertItem: item });
}; };
handleFilePicked = (event: React.ChangeEvent<HTMLInputElement>) => { handleFilesPicked = (event: React.ChangeEvent<HTMLInputElement>) => {
const files = getEventFiles(event); const files = getEventFiles(event);
const { const {
@@ -581,7 +581,8 @@ class CommandMenu<T extends MenuItem> extends React.Component<Props<T>, State> {
<input <input
type="file" type="file"
ref={this.inputRef} ref={this.inputRef}
onChange={this.handleFilePicked} onChange={this.handleFilesPicked}
multiple
/> />
</label> </label>
</VisuallyHidden> </VisuallyHidden>

View File

@@ -57,50 +57,62 @@ const insertFiles = function (
// we'll use this to track of how many files have succeeded or failed // we'll use this to track of how many files have succeeded or failed
let complete = 0; let complete = 0;
let attachmentPlaceholdersSet = false;
const filesToUpload = files.map((file) => ({
id: `upload-${uuidv4()}`,
isImage: file.type.startsWith("image/") && !options.isAttachment,
file,
}));
// the user might have dropped multiple files at once, we need to loop // the user might have dropped multiple files at once, we need to loop
for (const file of files) { for (const upload of filesToUpload) {
const id = `upload-${uuidv4()}`;
const isImage = file.type.startsWith("image/") && !options.isAttachment;
const { tr } = view.state; const { tr } = view.state;
if (isImage) { if (upload.isImage) {
// insert a placeholder at this position, or mark an existing file as being // insert a placeholder at this position, or mark an existing file as being
// replaced // replaced
tr.setMeta(uploadPlaceholderPlugin, { tr.setMeta(uploadPlaceholderPlugin, {
add: { add: {
id, id: upload.id,
file, file: upload.file,
pos, pos,
isImage, isImage: true,
replaceExisting: options.replaceExisting, replaceExisting: options.replaceExisting,
}, },
}); });
view.dispatch(tr); view.dispatch(tr);
} else { } else if (!attachmentPlaceholdersSet) {
const $pos = tr.doc.resolve(pos); const $pos = tr.doc.resolve(pos);
const attachmentsToUpload = filesToUpload.filter(
(i) => i.isImage === false
);
view.dispatch( view.dispatch(
view.state.tr.replaceWith( view.state.tr.replaceWith(
$pos.pos, $pos.pos,
$pos.pos + ($pos.nodeAfter?.nodeSize || 0), $pos.pos + ($pos.nodeAfter?.nodeSize || 0),
schema.nodes.attachment.create({ attachmentsToUpload.map((attachment) =>
id, schema.nodes.attachment.create({
title: file.name ?? "Untitled", id: attachment.id,
size: file.size, title: attachment.file.name ?? "Untitled",
}) size: attachment.file.size,
})
)
) )
); );
attachmentPlaceholdersSet = true;
} }
// start uploading the file to the server. Using "then" syntax // start uploading the file to the server. Using "then" syntax
// to allow all placeholders to be entered at once with the uploads // to allow all placeholders to be entered at once with the uploads
// happening in the background in parallel. // happening in the background in parallel.
uploadFile(file) uploadFile(upload.file)
.then((src) => { .then((src) => {
if (isImage) { if (upload.isImage) {
const newImg = new Image(); const newImg = new Image();
newImg.onload = () => { newImg.onload = () => {
const result = findPlaceholder(view.state, id); const result = findPlaceholder(view.state, upload.id);
// if the content around the placeholder has been deleted // if the content around the placeholder has been deleted
// then forget about inserting this file // then forget about inserting this file
@@ -116,7 +128,7 @@ const insertFiles = function (
to || from, to || from,
schema.nodes.image.create({ src, width: options.width }) schema.nodes.image.create({ src, width: options.width })
) )
.setMeta(uploadPlaceholderPlugin, { remove: { id } }) .setMeta(uploadPlaceholderPlugin, { remove: { id: upload.id } })
); );
// If the users selection is still at the file then make sure to select // If the users selection is still at the file then make sure to select
@@ -137,7 +149,7 @@ const insertFiles = function (
newImg.src = src; newImg.src = src;
} else { } else {
const result = findAttachmentById(view.state, id); const result = findAttachmentById(view.state, upload.id);
// if the attachment has been deleted then forget about updating it // if the attachment has been deleted then forget about updating it
if (result === null) { if (result === null) {
@@ -151,8 +163,8 @@ const insertFiles = function (
to || from, to || from,
schema.nodes.attachment.create({ schema.nodes.attachment.create({
href: src, href: src,
title: file.name ?? "Untitled", title: upload.file.name ?? "Untitled",
size: file.size, size: upload.file.size,
}) })
) )
); );
@@ -173,14 +185,14 @@ const insertFiles = function (
Sentry.captureException(error); Sentry.captureException(error);
// cleanup the placeholder if there is a failure // cleanup the placeholder if there is a failure
if (isImage) { if (upload.isImage) {
view.dispatch( view.dispatch(
view.state.tr.setMeta(uploadPlaceholderPlugin, { view.state.tr.setMeta(uploadPlaceholderPlugin, {
remove: { id }, remove: { id: upload.id },
}) })
); );
} else { } else {
const result = findAttachmentById(view.state, id); const result = findAttachmentById(view.state, upload.id);
// if the attachment has been deleted then forget about updating it // if the attachment has been deleted then forget about updating it
if (result === null) { if (result === null) {