Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add thumbnail to dropzone when pasting images in to comments #20147

Closed
wants to merge 10 commits into from
23 changes: 9 additions & 14 deletions web_src/js/features/common-global.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import createDropzone from './dropzone.js';
import {initCompColorPicker} from './comp/ColorPicker.js';
import {showGlobalErrorMessage} from '../bootstrap.js';
import {attachDropdownAria} from './aria.js';
import {removeUploadedFileFromEditor} from './comp/ImagePaste.js';
import {getAttachedEasyMDE} from './comp/EasyMDE.js';
import {handleGlobalEnterQuickSubmit} from './comp/QuickSubmit.js';

const {appUrl, csrfToken} = window.config;

Expand Down Expand Up @@ -53,20 +56,6 @@ export function initGlobalEnterQuickSubmit() {
});
}

export function handleGlobalEnterQuickSubmit(target) {
const $target = $(target);
const $form = $(target).closest('form');
if ($form.length) {
// here use the event to trigger the submit event (instead of calling `submit()` method directly)
// otherwise the `areYouSure` handler won't be executed, then there will be an annoying "confirm to leave" dialog
$form.trigger('submit');
} else {
// if no form, then the editor is for an AJAX request, dispatch an event to the target, let the target's event handler to do the AJAX request.
// the 'ce-' prefix means this is a CustomEvent
$target.trigger('ce-quick-submit');
}
}

export function initGlobalButtonClickOnEnter() {
$(document).on('keypress', '.ui.button', (e) => {
if (e.keyCode === 13 || e.keyCode === 32) { // enter key or space bar
Expand Down Expand Up @@ -208,6 +197,12 @@ export function initGlobalDropzone() {
$.post($dropzone.data('remove-url'), {
file: file.uuid,
_csrf: csrfToken,
}).then(() => {
// FIXME: the code is still very fragile, in the future, there should be stable relation between dropzone and its editor.
const easyMDE = getAttachedEasyMDE($dropzone.parent().parent().find('textarea'));
if (easyMDE) {
removeUploadedFileFromEditor(easyMDE, file.uuid);
}
});
}
});
Expand Down
2 changes: 1 addition & 1 deletion web_src/js/features/comp/EasyMDE.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import $ from 'jquery';
import attachTribute from '../tribute.js';
import {handleGlobalEnterQuickSubmit} from '../common-global.js';
import {handleGlobalEnterQuickSubmit} from './QuickSubmit.js';

/**
* @returns {EasyMDE}
Expand Down
28 changes: 25 additions & 3 deletions web_src/js/features/comp/ImagePaste.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import $ from 'jquery';

const {csrfToken} = window.config;

async function uploadFile(file, uploadUrl) {
async function uploadFile(file, uploadUrl, dropzone) {
const formData = new FormData();
formData.append('file', file, file.name);

Expand All @@ -11,7 +11,27 @@ async function uploadFile(file, uploadUrl) {
headers: {'X-Csrf-Token': csrfToken},
body: formData,
});
return await res.json();
const data = await res.json();
const upfile = {name: file.name, size: file.size, uuid: data.uuid};
dropzone.emit('addedfile', upfile);
dropzone.emit('thumbnail', upfile, `/attachments/${data.uuid}`);
dropzone.emit('complete', upfile);
dropzone.files.push(upfile);
return data;
}

/**
* @param editor{EasyMDE}
* @param fileUuid
*/
export function removeUploadedFileFromEditor(editor, fileUuid) {
// the raw regexp is: /!\[[^\]]*]\(\/attachments\/{uuid}\)/
const re = new RegExp(`!\\[[^\\]]*]\\(/attachments/${fileUuid}\\)`);
editor.value(editor.value().replace(re, '')); // at the moment, we assume the editor is an EasyMDE
if (editor.element) {
// when using "simple textarea" mode, the value of the textarea should be replaced too.
editor.element.value = editor.element.value.replace(re, '');
}
}

function clipboardPastedImages(e) {
Expand Down Expand Up @@ -89,6 +109,8 @@ class CodeMirrorEditor {


export function initEasyMDEImagePaste(easyMDE, $dropzone) {
if ($dropzone.length !== 1) throw new Error('invalid dropzone binding for editor');

const uploadUrl = $dropzone.attr('data-upload-url');
const $files = $dropzone.find('.files');

Expand All @@ -107,7 +129,7 @@ export function initEasyMDEImagePaste(easyMDE, $dropzone) {

const placeholder = `![${name}](uploading ...)`;
editor.insertPlaceholder(placeholder);
const data = await uploadFile(img, uploadUrl);
const data = await uploadFile(img, uploadUrl, $dropzone[0].dropzone);
editor.replacePlaceholder(placeholder, `![${name}](/attachments/${data.uuid})`);

const $input = $(`<input name="files" type="hidden">`).attr('id', data.uuid).val(data.uuid);
Expand Down
15 changes: 15 additions & 0 deletions web_src/js/features/comp/QuickSubmit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import $ from 'jquery';

export function handleGlobalEnterQuickSubmit(target) {
const $target = $(target);
const $form = $(target).closest('form');
if ($form.length) {
// here use the event to trigger the submit event (instead of calling `submit()` method directly)
// otherwise the `areYouSure` handler won't be executed, then there will be an annoying "confirm to leave" dialog
$form.trigger('submit');
} else {
// if no form, then the editor is for an AJAX request, dispatch an event to the target, let the target's event handler to do the AJAX request.
// the 'ce-' prefix means this is a CustomEvent
$target.trigger('ce-quick-submit');
}
}
18 changes: 14 additions & 4 deletions web_src/js/features/repo-legacy.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import $ from 'jquery';
import {createCommentEasyMDE, getAttachedEasyMDE} from './comp/EasyMDE.js';
import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js';
import {initEasyMDEImagePaste} from './comp/ImagePaste.js';
import {initEasyMDEImagePaste, removeUploadedFileFromEditor} from './comp/ImagePaste.js';
import {
initRepoIssueBranchSelect, initRepoIssueCodeCommentCancel,
initRepoIssueCommentDelete,
Expand Down Expand Up @@ -33,7 +33,7 @@ import initRepoPullRequestMergeForm from './repo-issue-pr-form.js';
const {csrfToken} = window.config;

export function initRepoCommentForm() {
const $commentForm = $('.comment.form');
const $commentForm = $('#comment-form, #new-issue'); // for issues and PRs
if ($commentForm.length === 0) {
return;
}
Expand Down Expand Up @@ -284,7 +284,8 @@ async function onEditContent(event) {
if ($dropzone.length === 1) {
$dropzone.data('saved', false);

const fileUuidDict = {};
let disableRemovedfileEvent = false; // when resetting the dropzone (removeAllFiles), disable the removedfile event
let fileUuidDict = {}; // if a comment has been saved, then the uploaded files won't be deleted from server when clicking the Remove in the dropzone
dz = await createDropzone($dropzone[0], {
url: $dropzone.data('upload-url'),
headers: {'X-Csrf-Token': csrfToken},
Expand All @@ -308,12 +309,18 @@ async function onEditContent(event) {
$dropzone.find('.files').append(input);
});
this.on('removedfile', (file) => {
if (disableRemovedfileEvent) return;
$(`#${file.uuid}`).remove();
if ($dropzone.data('remove-url') && !fileUuidDict[file.uuid].submitted) {
if ($dropzone.data('remove-url') && !fileUuidDict[file.uuid]?.submitted) {
$.post($dropzone.data('remove-url'), {
file: file.uuid,
_csrf: csrfToken,
}).then(() => {
removeUploadedFileFromEditor(easyMDE, file.uuid);
});
} else {
// for saved comment's attachment's removal, only remove the link in the editor
removeUploadedFileFromEditor(easyMDE, file.uuid);
}
});
this.on('submit', () => {
Expand All @@ -323,8 +330,11 @@ async function onEditContent(event) {
});
this.on('reload', () => {
$.getJSON($editContentZone.data('attachment-url'), (data) => {
disableRemovedfileEvent = true;
dz.removeAllFiles(true);
disableRemovedfileEvent = false;
$dropzone.find('.files').empty();
fileUuidDict = {};
$.each(data, function () {
const imgSrc = `${$dropzone.data('link-url')}/${this.uuid}`;
dz.emit('addedfile', this);
Expand Down