Add attachment support for code review comments (#29220)
Fixes #27960, #24411, #12183 --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
parent
590f86796b
commit
f95fb8cc44
15 changed files with 227 additions and 74 deletions
|
@ -200,65 +200,68 @@ export function initGlobalCommon() {
|
|||
}
|
||||
|
||||
export function initGlobalDropzone() {
|
||||
// Dropzone
|
||||
for (const el of document.querySelectorAll('.dropzone')) {
|
||||
const $dropzone = $(el);
|
||||
const _promise = createDropzone(el, {
|
||||
url: $dropzone.data('upload-url'),
|
||||
headers: {'X-Csrf-Token': csrfToken},
|
||||
maxFiles: $dropzone.data('max-file'),
|
||||
maxFilesize: $dropzone.data('max-size'),
|
||||
acceptedFiles: (['*/*', ''].includes($dropzone.data('accepts'))) ? null : $dropzone.data('accepts'),
|
||||
addRemoveLinks: true,
|
||||
dictDefaultMessage: $dropzone.data('default-message'),
|
||||
dictInvalidFileType: $dropzone.data('invalid-input-type'),
|
||||
dictFileTooBig: $dropzone.data('file-too-big'),
|
||||
dictRemoveFile: $dropzone.data('remove-file'),
|
||||
timeout: 0,
|
||||
thumbnailMethod: 'contain',
|
||||
thumbnailWidth: 480,
|
||||
thumbnailHeight: 480,
|
||||
init() {
|
||||
this.on('success', (file, data) => {
|
||||
file.uuid = data.uuid;
|
||||
const input = $(`<input id="${data.uuid}" name="files" type="hidden">`).val(data.uuid);
|
||||
$dropzone.find('.files').append(input);
|
||||
// Create a "Copy Link" element, to conveniently copy the image
|
||||
// or file link as Markdown to the clipboard
|
||||
const copyLinkElement = document.createElement('div');
|
||||
copyLinkElement.className = 'gt-text-center';
|
||||
// The a element has a hardcoded cursor: pointer because the default is overridden by .dropzone
|
||||
copyLinkElement.innerHTML = `<a href="#" style="cursor: pointer;">${svg('octicon-copy', 14, 'copy link')} Copy link</a>`;
|
||||
copyLinkElement.addEventListener('click', async (e) => {
|
||||
e.preventDefault();
|
||||
let fileMarkdown = `[${file.name}](/attachments/${file.uuid})`;
|
||||
if (file.type.startsWith('image/')) {
|
||||
fileMarkdown = `!${fileMarkdown}`;
|
||||
} else if (file.type.startsWith('video/')) {
|
||||
fileMarkdown = `<video src="/attachments/${file.uuid}" title="${htmlEscape(file.name)}" controls></video>`;
|
||||
}
|
||||
const success = await clippie(fileMarkdown);
|
||||
showTemporaryTooltip(e.target, success ? i18n.copy_success : i18n.copy_error);
|
||||
});
|
||||
file.previewTemplate.append(copyLinkElement);
|
||||
});
|
||||
this.on('removedfile', (file) => {
|
||||
$(`#${file.uuid}`).remove();
|
||||
if ($dropzone.data('remove-url')) {
|
||||
POST($dropzone.data('remove-url'), {
|
||||
data: new URLSearchParams({file: file.uuid}),
|
||||
});
|
||||
}
|
||||
});
|
||||
this.on('error', function (file, message) {
|
||||
showErrorToast(message);
|
||||
this.removeFile(file);
|
||||
});
|
||||
},
|
||||
});
|
||||
initDropzone(el);
|
||||
}
|
||||
}
|
||||
|
||||
export function initDropzone(el) {
|
||||
const $dropzone = $(el);
|
||||
const _promise = createDropzone(el, {
|
||||
url: $dropzone.data('upload-url'),
|
||||
headers: {'X-Csrf-Token': csrfToken},
|
||||
maxFiles: $dropzone.data('max-file'),
|
||||
maxFilesize: $dropzone.data('max-size'),
|
||||
acceptedFiles: (['*/*', ''].includes($dropzone.data('accepts'))) ? null : $dropzone.data('accepts'),
|
||||
addRemoveLinks: true,
|
||||
dictDefaultMessage: $dropzone.data('default-message'),
|
||||
dictInvalidFileType: $dropzone.data('invalid-input-type'),
|
||||
dictFileTooBig: $dropzone.data('file-too-big'),
|
||||
dictRemoveFile: $dropzone.data('remove-file'),
|
||||
timeout: 0,
|
||||
thumbnailMethod: 'contain',
|
||||
thumbnailWidth: 480,
|
||||
thumbnailHeight: 480,
|
||||
init() {
|
||||
this.on('success', (file, data) => {
|
||||
file.uuid = data.uuid;
|
||||
const input = $(`<input id="${data.uuid}" name="files" type="hidden">`).val(data.uuid);
|
||||
$dropzone.find('.files').append(input);
|
||||
// Create a "Copy Link" element, to conveniently copy the image
|
||||
// or file link as Markdown to the clipboard
|
||||
const copyLinkElement = document.createElement('div');
|
||||
copyLinkElement.className = 'gt-text-center';
|
||||
// The a element has a hardcoded cursor: pointer because the default is overridden by .dropzone
|
||||
copyLinkElement.innerHTML = `<a href="#" style="cursor: pointer;">${svg('octicon-copy', 14, 'copy link')} Copy link</a>`;
|
||||
copyLinkElement.addEventListener('click', async (e) => {
|
||||
e.preventDefault();
|
||||
let fileMarkdown = `[${file.name}](/attachments/${file.uuid})`;
|
||||
if (file.type.startsWith('image/')) {
|
||||
fileMarkdown = `!${fileMarkdown}`;
|
||||
} else if (file.type.startsWith('video/')) {
|
||||
fileMarkdown = `<video src="/attachments/${file.uuid}" title="${htmlEscape(file.name)}" controls></video>`;
|
||||
}
|
||||
const success = await clippie(fileMarkdown);
|
||||
showTemporaryTooltip(e.target, success ? i18n.copy_success : i18n.copy_error);
|
||||
});
|
||||
file.previewTemplate.append(copyLinkElement);
|
||||
});
|
||||
this.on('removedfile', (file) => {
|
||||
$(`#${file.uuid}`).remove();
|
||||
if ($dropzone.data('remove-url')) {
|
||||
POST($dropzone.data('remove-url'), {
|
||||
data: new URLSearchParams({file: file.uuid}),
|
||||
});
|
||||
}
|
||||
});
|
||||
this.on('error', function (file, message) {
|
||||
showErrorToast(message);
|
||||
this.removeFile(file);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function linkAction(e) {
|
||||
// A "link-action" can post AJAX request to its "data-url"
|
||||
// Then the browser is redirected to: the "redirect" in response, or "data-redirect" attribute, or current URL by reloading.
|
||||
|
|
|
@ -5,6 +5,7 @@ import {hideElem, showElem, toggleElem} from '../utils/dom.js';
|
|||
import {setFileFolding} from './file-fold.js';
|
||||
import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js';
|
||||
import {toAbsoluteUrl} from '../utils.js';
|
||||
import {initDropzone} from './common-global.js';
|
||||
|
||||
const {appSubUrl, csrfToken} = window.config;
|
||||
|
||||
|
@ -382,6 +383,11 @@ export async function handleReply($el) {
|
|||
const $textarea = form.find('textarea');
|
||||
let editor = getComboMarkdownEditor($textarea);
|
||||
if (!editor) {
|
||||
// FIXME: the initialization of the dropzone is not consistent.
|
||||
// When the page is loaded, the dropzone is initialized by initGlobalDropzone, but the editor is not initialized.
|
||||
// When the form is submitted and partially reload, none of them is initialized.
|
||||
const dropzone = form.find('.dropzone')[0];
|
||||
if (!dropzone.dropzone) initDropzone(dropzone);
|
||||
editor = await initComboMarkdownEditor(form.find('.combo-markdown-editor'));
|
||||
}
|
||||
editor.focus();
|
||||
|
@ -511,6 +517,7 @@ export function initRepoPullRequestReview() {
|
|||
td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed');
|
||||
td.find("input[name='path']").val(path);
|
||||
|
||||
initDropzone(td.find('.dropzone')[0]);
|
||||
const editor = await initComboMarkdownEditor(td.find('.combo-markdown-editor'));
|
||||
editor.focus();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue