mirror of
https://github.com/go-gitea/gitea.git
synced 2024-09-26 19:21:09 +02:00
Improve attachment upload methods
This commit is contained in:
parent
3b045ee165
commit
ce5745233c
@ -5,12 +5,13 @@ import {createDropzone} from './dropzone.js';
|
||||
import {showGlobalErrorMessage} from '../bootstrap.js';
|
||||
import {handleGlobalEnterQuickSubmit} from './comp/QuickSubmit.js';
|
||||
import {svg} from '../svg.js';
|
||||
import {hideElem, showElem, toggleElem, initSubmitEventPolyfill, submitEventSubmitter} from '../utils/dom.js';
|
||||
import {hideElem, showElem, toggleElem, initSubmitEventPolyfill, submitEventSubmitter, getComboMarkdownEditor} from '../utils/dom.js';
|
||||
import {htmlEscape} from 'escape-goat';
|
||||
import {showTemporaryTooltip} from '../modules/tippy.js';
|
||||
import {confirmModal} from './comp/ConfirmModal.js';
|
||||
import {showErrorToast} from '../modules/toast.js';
|
||||
import {request, POST, GET} from '../modules/fetch.js';
|
||||
import {removeLinksInTextarea} from './comp/ComboMarkdownEditor.js';
|
||||
import '../htmx.js';
|
||||
|
||||
const {appUrl, appSubUrl, csrfToken, i18n} = window.config;
|
||||
@ -249,12 +250,13 @@ export function initDropzone(el) {
|
||||
});
|
||||
file.previewTemplate.append(copyLinkElement);
|
||||
});
|
||||
this.on('removedfile', (file) => {
|
||||
$(`#${file.uuid}`).remove();
|
||||
this.on('removedfile', async (file) => {
|
||||
document.getElementById(file.uuid)?.remove();
|
||||
if ($dropzone.data('remove-url')) {
|
||||
POST($dropzone.data('remove-url'), {
|
||||
await POST($dropzone.data('remove-url'), {
|
||||
data: new URLSearchParams({file: file.uuid}),
|
||||
});
|
||||
removeLinksInTextarea(getComboMarkdownEditor(el.closest('form').querySelector('.combo-markdown-editor')), file);
|
||||
}
|
||||
});
|
||||
this.on('error', function (file, message) {
|
||||
|
@ -296,11 +296,6 @@ class ComboMarkdownEditor {
|
||||
}
|
||||
}
|
||||
|
||||
export function getComboMarkdownEditor(el) {
|
||||
if (el instanceof $) el = el[0];
|
||||
return el?._giteaComboMarkdownEditor;
|
||||
}
|
||||
|
||||
export async function initComboMarkdownEditor(container, options = {}) {
|
||||
if (container instanceof $) {
|
||||
if (container.length !== 1) {
|
||||
@ -315,3 +310,9 @@ export async function initComboMarkdownEditor(container, options = {}) {
|
||||
await editor.init();
|
||||
return editor;
|
||||
}
|
||||
|
||||
export function removeLinksInTextarea(editor, file) {
|
||||
const fileName = file.name.slice(0, file.name.lastIndexOf('.'));
|
||||
const fileText = `\\[${fileName}\\]\\(/attachments/${file.uuid}\\)`;
|
||||
editor.value(editor.value().replace(new RegExp(`<img [\\s\\w"=]+ alt="${fileName}" src="/attachments/${file.uuid}">`, 'g'), '').replace(new RegExp(`\\!${fileText}`, 'g'), '').replace(new RegExp(fileText, 'g'), ''));
|
||||
}
|
||||
|
@ -82,35 +82,48 @@ class CodeMirrorEditor {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleClipboardImages(editor, dropzone, images, e) {
|
||||
async function handleClipboardFiles(editor, dropzone, files, e) {
|
||||
const uploadUrl = dropzone.getAttribute('data-upload-url');
|
||||
const filesContainer = dropzone.querySelector('.files');
|
||||
|
||||
if (!dropzone || !uploadUrl || !filesContainer || !images.length) return;
|
||||
if (!dropzone || !uploadUrl || !filesContainer || !files.length) return;
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
for (const img of images) {
|
||||
const name = img.name.slice(0, img.name.lastIndexOf('.'));
|
||||
for (const file of files) {
|
||||
if (!file) continue;
|
||||
const name = file.name.slice(0, file.name.lastIndexOf('.'));
|
||||
|
||||
const placeholder = `![${name}](uploading ...)`;
|
||||
editor.insertPlaceholder(placeholder);
|
||||
|
||||
const {uuid} = await uploadFile(img, uploadUrl);
|
||||
const {width, dppx} = await imageInfo(img);
|
||||
const {uuid} = await uploadFile(file, uploadUrl);
|
||||
const {width, dppx} = await imageInfo(file);
|
||||
|
||||
const url = `/attachments/${uuid}`;
|
||||
let text;
|
||||
if (width > 0 && dppx > 1) {
|
||||
// Scale down images from HiDPI monitors. This uses the <img> tag because it's the only
|
||||
// method to change image size in Markdown that is supported by all implementations.
|
||||
text = `<img width="${Math.round(width / dppx)}" alt="${htmlEscape(name)}" src="${htmlEscape(url)}">`;
|
||||
if (file.type?.startsWith('image/')) {
|
||||
if (width > 0 && dppx > 1) {
|
||||
// Scale down images from HiDPI monitors. This uses the <img> tag because it's the only
|
||||
// method to change image size in Markdown that is supported by all implementations.
|
||||
text = `<img width="${Math.round(width / dppx)}" alt="${htmlEscape(name)}" src="${htmlEscape(url)}">`;
|
||||
} else {
|
||||
text = `![${name}](${url})`;
|
||||
}
|
||||
} else {
|
||||
text = `![${name}](${url})`;
|
||||
text = `[${name}](${url})`;
|
||||
}
|
||||
editor.replacePlaceholder(placeholder, text);
|
||||
|
||||
file.uuid = uuid;
|
||||
dropzone.dropzone.emit('addedfile', file);
|
||||
if (/\.(jpg|jpeg|png|gif|bmp)$/i.test(file.name)) {
|
||||
const imgSrc = `/attachments/${file.uuid}`;
|
||||
dropzone.dropzone.emit('thumbnail', file, imgSrc);
|
||||
dropzone.querySelector(`img[src='${imgSrc}']`).style.maxWidth = '100%';
|
||||
}
|
||||
dropzone.dropzone.emit('complete', file);
|
||||
const input = document.createElement('input');
|
||||
input.setAttribute('name', 'files');
|
||||
input.setAttribute('type', 'hidden');
|
||||
@ -134,21 +147,25 @@ function handleClipboardText(textarea, text, e) {
|
||||
}
|
||||
|
||||
export function initEasyMDEPaste(easyMDE, dropzone) {
|
||||
easyMDE.codemirror.on('paste', (_, e) => {
|
||||
const {images} = getPastedContent(e);
|
||||
if (images.length) {
|
||||
handleClipboardImages(new CodeMirrorEditor(easyMDE.codemirror), dropzone, images, e);
|
||||
const pasteFunc = (e) => {
|
||||
const {files} = getPastedContent(e);
|
||||
if (files.length) {
|
||||
handleClipboardFiles(new CodeMirrorEditor(easyMDE.codemirror), dropzone, files, e);
|
||||
}
|
||||
});
|
||||
};
|
||||
easyMDE.codemirror.on('paste', (_, e) => pasteFunc(e));
|
||||
easyMDE.codemirror.on('drop', (_, e) => pasteFunc(e));
|
||||
}
|
||||
|
||||
export function initTextareaPaste(textarea, dropzone) {
|
||||
textarea.addEventListener('paste', (e) => {
|
||||
const {images, text} = getPastedContent(e);
|
||||
if (images.length) {
|
||||
handleClipboardImages(new TextareaEditor(textarea), dropzone, images, e);
|
||||
const pasteFunc = (e) => {
|
||||
const {files, text} = getPastedContent(e);
|
||||
if (files.length) {
|
||||
handleClipboardFiles(new TextareaEditor(textarea), dropzone, files, e);
|
||||
} else if (text) {
|
||||
handleClipboardText(textarea, text, e);
|
||||
}
|
||||
});
|
||||
};
|
||||
textarea.addEventListener('paste', (e) => pasteFunc(e));
|
||||
textarea.addEventListener('drop', (e) => pasteFunc(e));
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import $ from 'jquery';
|
||||
import {handleReply} from './repo-issue.js';
|
||||
import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js';
|
||||
import {initComboMarkdownEditor, removeLinksInTextarea} from './comp/ComboMarkdownEditor.js';
|
||||
import {createDropzone} from './dropzone.js';
|
||||
import {GET, POST} from '../modules/fetch.js';
|
||||
import {hideElem, showElem} from '../utils/dom.js';
|
||||
import {hideElem, showElem, getComboMarkdownEditor} from '../utils/dom.js';
|
||||
import {attachRefIssueContextPopup} from './contextpopup.js';
|
||||
import {initCommentContent, initMarkupContent} from '../markup/content.js';
|
||||
|
||||
@ -26,7 +26,6 @@ async function onEditContent(event) {
|
||||
if (!dropzone) return null;
|
||||
|
||||
let disableRemovedfileEvent = false; // when resetting the dropzone (removeAllFiles), disable the "removedfile" event
|
||||
let fileUuidDict = {}; // to record: if a comment has been saved, then the uploaded files won't be deleted from server when clicking the Remove in the dropzone
|
||||
const dz = await createDropzone(dropzone, {
|
||||
url: dropzone.getAttribute('data-upload-url'),
|
||||
headers: {'X-Csrf-Token': csrfToken},
|
||||
@ -45,7 +44,6 @@ async function onEditContent(event) {
|
||||
init() {
|
||||
this.on('success', (file, data) => {
|
||||
file.uuid = data.uuid;
|
||||
fileUuidDict[file.uuid] = {submitted: false};
|
||||
const input = document.createElement('input');
|
||||
input.id = data.uuid;
|
||||
input.name = 'files';
|
||||
@ -56,19 +54,15 @@ async function onEditContent(event) {
|
||||
this.on('removedfile', async (file) => {
|
||||
document.getElementById(file.uuid)?.remove();
|
||||
if (disableRemovedfileEvent) return;
|
||||
if (dropzone.getAttribute('data-remove-url') && !fileUuidDict[file.uuid].submitted) {
|
||||
if (dropzone.getAttribute('data-remove-url')) {
|
||||
try {
|
||||
await POST(dropzone.getAttribute('data-remove-url'), {data: new URLSearchParams({file: file.uuid})});
|
||||
removeLinksInTextarea(getComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor')), file);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.on('submit', () => {
|
||||
for (const fileUuid of Object.keys(fileUuidDict)) {
|
||||
fileUuidDict[fileUuid].submitted = true;
|
||||
}
|
||||
});
|
||||
this.on('reload', async () => {
|
||||
try {
|
||||
const response = await GET(editContentZone.getAttribute('data-attachment-url'));
|
||||
@ -78,16 +72,16 @@ async function onEditContent(event) {
|
||||
dz.removeAllFiles(true);
|
||||
dropzone.querySelector('.files').innerHTML = '';
|
||||
for (const el of dropzone.querySelectorAll('.dz-preview')) el.remove();
|
||||
fileUuidDict = {};
|
||||
disableRemovedfileEvent = false;
|
||||
|
||||
for (const attachment of data) {
|
||||
const imgSrc = `${dropzone.getAttribute('data-link-url')}/${attachment.uuid}`;
|
||||
dz.emit('addedfile', attachment);
|
||||
dz.emit('thumbnail', attachment, imgSrc);
|
||||
if (/\.(jpg|jpeg|png|gif|bmp)$/i.test(attachment.name)) {
|
||||
const imgSrc = `${dropzone.getAttribute('data-link-url')}/${attachment.uuid}`;
|
||||
dz.emit('thumbnail', attachment, imgSrc);
|
||||
dropzone.querySelector(`img[src='${imgSrc}']`).style.maxWidth = '100%';
|
||||
}
|
||||
dz.emit('complete', attachment);
|
||||
fileUuidDict[attachment.uuid] = {submitted: true};
|
||||
dropzone.querySelector(`img[src='${imgSrc}']`).style.maxWidth = '100%';
|
||||
const input = document.createElement('input');
|
||||
input.id = attachment.uuid;
|
||||
input.name = 'files';
|
||||
|
@ -1,9 +1,9 @@
|
||||
import $ from 'jquery';
|
||||
import {htmlEscape} from 'escape-goat';
|
||||
import {showTemporaryTooltip, createTippy} from '../modules/tippy.js';
|
||||
import {hideElem, showElem, toggleElem} from '../utils/dom.js';
|
||||
import {hideElem, showElem, toggleElem, getComboMarkdownEditor} from '../utils/dom.js';
|
||||
import {setFileFolding} from './file-fold.js';
|
||||
import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js';
|
||||
import {initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js';
|
||||
import {toAbsoluteUrl} from '../utils.js';
|
||||
import {initDropzone} from './common-global.js';
|
||||
import {POST, GET} from '../modules/fetch.js';
|
||||
|
@ -258,16 +258,27 @@ export function isElemVisible(element) {
|
||||
return Boolean(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
|
||||
}
|
||||
|
||||
export function getComboMarkdownEditor(el) {
|
||||
if (el.jquery) el = el[0];
|
||||
return el?._giteaComboMarkdownEditor;
|
||||
}
|
||||
|
||||
// extract text and images from "paste" event
|
||||
export function getPastedContent(e) {
|
||||
const images = [];
|
||||
for (const item of e.clipboardData?.items ?? []) {
|
||||
if (item.type?.startsWith('image/')) {
|
||||
images.push(item.getAsFile());
|
||||
const acceptedFiles = getComboMarkdownEditor(e.currentTarget).dropzone.getAttribute('data-accepts');
|
||||
const files = [];
|
||||
const data = e.clipboardData?.items || e.dataTransfer?.items;
|
||||
for (const item of data ?? []) {
|
||||
if (!item.type?.startsWith('text/')) {
|
||||
const file = item.getAsFile();
|
||||
const extName = file.name.slice(file.name.lastIndexOf('.'), file.name.length);
|
||||
if (acceptedFiles.includes(extName)) {
|
||||
files.push(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
const text = e.clipboardData?.getData?.('text') ?? '';
|
||||
return {text, images};
|
||||
return {text, files};
|
||||
}
|
||||
|
||||
// replace selected text in a textarea while preserving editor history, e.g. CTRL-Z works after this
|
||||
|
Loading…
Reference in New Issue
Block a user