mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-15 18:26:15 +01:00
feat: add upload helper module for drag&drop, paste, closes #6388
fix chat input not getting focused
This commit is contained in:
@@ -173,3 +173,12 @@
|
|||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.imagedrop {
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 24px;
|
||||||
|
color: @gray-light;
|
||||||
|
width: 100%;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
@@ -14,11 +14,12 @@ define('forum/chats', [
|
|||||||
'alerts',
|
'alerts',
|
||||||
'chat',
|
'chat',
|
||||||
'api',
|
'api',
|
||||||
|
'uploadHelpers',
|
||||||
], function (
|
], function (
|
||||||
components, translator, mousetrap,
|
components, translator, mousetrap,
|
||||||
recentChats, search, messages,
|
recentChats, search, messages,
|
||||||
autocomplete, hooks, bootbox, alerts, chatModule,
|
autocomplete, hooks, bootbox, alerts, chatModule,
|
||||||
api
|
api, uploadHelpers
|
||||||
) {
|
) {
|
||||||
const Chats = {
|
const Chats = {
|
||||||
initialised: false,
|
initialised: false,
|
||||||
@@ -69,12 +70,35 @@ define('forum/chats', [
|
|||||||
Chats.addCharactersLeftHandler($('[component="chat/main-wrapper"]'));
|
Chats.addCharactersLeftHandler($('[component="chat/main-wrapper"]'));
|
||||||
Chats.addIPHandler($('[component="chat/main-wrapper"]'));
|
Chats.addIPHandler($('[component="chat/main-wrapper"]'));
|
||||||
Chats.createAutoComplete($('[component="chat/input"]'));
|
Chats.createAutoComplete($('[component="chat/input"]'));
|
||||||
|
Chats.addUploadHandler({
|
||||||
|
dragDropAreaEl: $('.chats-full'),
|
||||||
|
pasteEl: $('[component="chat/input"]'),
|
||||||
|
uploadFormEl: $('[component="chat/upload"]'),
|
||||||
|
inputEl: $('[component="chat/input"]'),
|
||||||
|
});
|
||||||
|
|
||||||
$('[data-action="close"]').on('click', function () {
|
$('[data-action="close"]').on('click', function () {
|
||||||
Chats.switchChat();
|
Chats.switchChat();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Chats.addUploadHandler = function (options) {
|
||||||
|
uploadHelpers.init({
|
||||||
|
dragDropAreaEl: options.dragDropAreaEl,
|
||||||
|
pasteEl: options.pasteEl,
|
||||||
|
uploadFormEl: options.uploadFormEl,
|
||||||
|
route: '/api/post/upload', // using same route as post uploads
|
||||||
|
callback: function (uploads) {
|
||||||
|
const inputEl = options.inputEl;
|
||||||
|
let text = inputEl.val();
|
||||||
|
uploads.forEach((upload) => {
|
||||||
|
text = text + (text ? '\n' : '') + (upload.isImage ? '!' : '') + `[${upload.filename}](${upload.url})`;
|
||||||
|
});
|
||||||
|
inputEl.val(text);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Chats.addIPHandler = function (container) {
|
Chats.addIPHandler = function (container) {
|
||||||
container.on('click', '.chat-ip-button', function () {
|
container.on('click', '.chat-ip-button', function () {
|
||||||
const ipEl = $(this).parent();
|
const ipEl = $(this).parent();
|
||||||
|
|||||||
@@ -89,8 +89,8 @@ define('alerts', ['translator', 'components', 'hooks'], function (translator, co
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateAlert(alert, params) {
|
function updateAlert(alert, params) {
|
||||||
alert.find('strong').html(params.title);
|
alert.find('strong').translateHtml(params.title);
|
||||||
alert.find('p').html(params.message);
|
alert.find('p').translateHtml(params.message);
|
||||||
alert.attr('class', 'alert alert-dismissable alert-' + params.type + ' clearfix');
|
alert.attr('class', 'alert alert-dismissable alert-' + params.type + ' clearfix');
|
||||||
|
|
||||||
clearTimeout(parseInt(alert.attr('timeoutId'), 10));
|
clearTimeout(parseInt(alert.attr('timeoutId'), 10));
|
||||||
@@ -98,12 +98,7 @@ define('alerts', ['translator', 'components', 'hooks'], function (translator, co
|
|||||||
startTimeout(alert, params);
|
startTimeout(alert, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
alert.children().fadeOut(100);
|
hooks.fire('action:alert.update', { alert, params });
|
||||||
translator.translate(alert.html(), function (translatedHTML) {
|
|
||||||
alert.children().fadeIn(100);
|
|
||||||
alert.html(translatedHTML);
|
|
||||||
hooks.fire('action:alert.update', { alert, params });
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle changes in the clickfn
|
// Handle changes in the clickfn
|
||||||
alert.off('click').removeClass('pointer');
|
alert.off('click').removeClass('pointer');
|
||||||
|
|||||||
@@ -196,7 +196,9 @@ define('chat', [
|
|||||||
|
|
||||||
module.createModal = function (data, callback) {
|
module.createModal = function (data, callback) {
|
||||||
callback = callback || function () {};
|
callback = callback || function () {};
|
||||||
require(['scrollStop', 'forum/chats', 'forum/chats/messages'], function (scrollStop, Chats, ChatsMessages) {
|
require([
|
||||||
|
'scrollStop', 'forum/chats', 'forum/chats/messages',
|
||||||
|
], function (scrollStop, Chats, ChatsMessages) {
|
||||||
app.parseAndTranslate('chat', data, function (chatModal) {
|
app.parseAndTranslate('chat', data, function (chatModal) {
|
||||||
if (module.modalExists(data.roomId)) {
|
if (module.modalExists(data.roomId)) {
|
||||||
return callback(module.getModal(data.roomId));
|
return callback(module.getModal(data.roomId));
|
||||||
@@ -233,7 +235,7 @@ define('chat', [
|
|||||||
taskbar.updateActive(uuid);
|
taskbar.updateActive(uuid);
|
||||||
},
|
},
|
||||||
stop: function () {
|
stop: function () {
|
||||||
chatModal.find('#chat-message-input').focus();
|
module.focusInput(chatModal);
|
||||||
},
|
},
|
||||||
distance: 10,
|
distance: 10,
|
||||||
handle: '.modal-header',
|
handle: '.modal-header',
|
||||||
@@ -297,6 +299,14 @@ define('chat', [
|
|||||||
|
|
||||||
Chats.addCharactersLeftHandler(chatModal);
|
Chats.addCharactersLeftHandler(chatModal);
|
||||||
Chats.addIPHandler(chatModal);
|
Chats.addIPHandler(chatModal);
|
||||||
|
|
||||||
|
Chats.addUploadHandler({
|
||||||
|
dragDropAreaEl: chatModal.find('.modal-content'),
|
||||||
|
pasteEl: chatModal,
|
||||||
|
uploadFormEl: chatModal.find('[component="chat/upload"]'),
|
||||||
|
inputEl: chatModal.find('[component="chat/input"]'),
|
||||||
|
});
|
||||||
|
|
||||||
ChatsMessages.addSocketListeners();
|
ChatsMessages.addSocketListeners();
|
||||||
|
|
||||||
taskbar.push('chat', chatModal.attr('data-uuid'), {
|
taskbar.push('chat', chatModal.attr('data-uuid'), {
|
||||||
@@ -318,7 +328,9 @@ define('chat', [
|
|||||||
};
|
};
|
||||||
|
|
||||||
module.focusInput = function (chatModal) {
|
module.focusInput = function (chatModal) {
|
||||||
chatModal.find('[component="chat/input"]').focus();
|
setTimeout(function () {
|
||||||
|
chatModal.find('[component="chat/input"]').focus();
|
||||||
|
}, 20);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.close = function (chatModal) {
|
module.close = function (chatModal) {
|
||||||
|
|||||||
199
public/src/modules/uploadHelpers.js
Normal file
199
public/src/modules/uploadHelpers.js
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
|
define('uploadHelpers', ['alerts'], function (alerts) {
|
||||||
|
const uploadHelpers = {};
|
||||||
|
|
||||||
|
uploadHelpers.init = function (options) {
|
||||||
|
const formEl = options.uploadFormEl;
|
||||||
|
if (!formEl.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
formEl.attr('action', config.relative_path + options.route);
|
||||||
|
|
||||||
|
if (options.dragDropAreaEl) {
|
||||||
|
uploadHelpers.handleDragDrop({
|
||||||
|
container: options.dragDropAreaEl,
|
||||||
|
callback: function (upload) {
|
||||||
|
uploadHelpers.ajaxSubmit({
|
||||||
|
uploadForm: formEl,
|
||||||
|
upload: upload,
|
||||||
|
callback: options.callback,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.pasteEl) {
|
||||||
|
uploadHelpers.handlePaste({
|
||||||
|
container: options.pasteEl,
|
||||||
|
callback: function (upload) {
|
||||||
|
uploadHelpers.ajaxSubmit({
|
||||||
|
uploadForm: formEl,
|
||||||
|
upload: upload,
|
||||||
|
callback: options.callback,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
uploadHelpers.handleDragDrop = function (options) {
|
||||||
|
let draggingDocument = false;
|
||||||
|
const postContainer = options.container;
|
||||||
|
const drop = options.container.find('.imagedrop');
|
||||||
|
|
||||||
|
postContainer.on('dragenter', function onDragEnter() {
|
||||||
|
if (draggingDocument) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
drop.css('top', '0px');
|
||||||
|
drop.css('height', postContainer.height() + 'px');
|
||||||
|
drop.css('line-height', postContainer.height() + 'px');
|
||||||
|
drop.show();
|
||||||
|
|
||||||
|
drop.on('dragleave', function () {
|
||||||
|
drop.hide();
|
||||||
|
drop.off('dragleave');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
drop.on('drop', function onDragDrop(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const files = e.originalEvent.dataTransfer.files;
|
||||||
|
|
||||||
|
if (files.length) {
|
||||||
|
let formData;
|
||||||
|
if (window.FormData) {
|
||||||
|
formData = new FormData();
|
||||||
|
for (var i = 0; i < files.length; ++i) {
|
||||||
|
formData.append('files[]', files[i], files[i].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
options.callback({
|
||||||
|
files: files,
|
||||||
|
formData: formData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
drop.hide();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
function cancel(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document)
|
||||||
|
.off('dragstart')
|
||||||
|
.on('dragstart', function () {
|
||||||
|
draggingDocument = true;
|
||||||
|
})
|
||||||
|
.off('dragend')
|
||||||
|
.on('dragend', function () {
|
||||||
|
draggingDocument = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
drop.on('dragover', cancel);
|
||||||
|
drop.on('dragenter', cancel);
|
||||||
|
};
|
||||||
|
|
||||||
|
uploadHelpers.handlePaste = function (options) {
|
||||||
|
const container = options.container;
|
||||||
|
container.on('paste', function (event) {
|
||||||
|
const items = (event.clipboardData || event.originalEvent.clipboardData || {}).items;
|
||||||
|
const files = [];
|
||||||
|
const fileNames = [];
|
||||||
|
let formData = null;
|
||||||
|
if (window.FormData) {
|
||||||
|
formData = new FormData();
|
||||||
|
}
|
||||||
|
[].forEach.call(items, function (item) {
|
||||||
|
const file = item.getAsFile();
|
||||||
|
if (file) {
|
||||||
|
const fileName = utils.generateUUID() + '-' + file.name;
|
||||||
|
if (formData) {
|
||||||
|
formData.append('files[]', file, fileName);
|
||||||
|
}
|
||||||
|
files.push(file);
|
||||||
|
fileNames.push(fileName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (files.length) {
|
||||||
|
options.callback({
|
||||||
|
files: files,
|
||||||
|
fileNames: fileNames,
|
||||||
|
formData: formData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
uploadHelpers.ajaxSubmit = function (options) {
|
||||||
|
const files = [...options.upload.files];
|
||||||
|
|
||||||
|
for (let i = 0; i < files.length; ++i) {
|
||||||
|
const isImage = files[i].type.match(/image./);
|
||||||
|
if ((isImage && !app.user.privileges['upload:post:image']) || (!isImage && !app.user.privileges['upload:post:file'])) {
|
||||||
|
return alerts.error('[[error:no-privileges]]');
|
||||||
|
}
|
||||||
|
if (files[i].size > parseInt(config.maximumFileSize, 10) * 1024) {
|
||||||
|
options.uploadForm[0].reset();
|
||||||
|
return alerts.error('[[error:file-too-big, ' + config.maximumFileSize + ']]');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const alert_id = Date.now();
|
||||||
|
options.uploadForm.off('submit').on('submit', function () {
|
||||||
|
$(this).ajaxSubmit({
|
||||||
|
headers: {
|
||||||
|
'x-csrf-token': config.csrf_token,
|
||||||
|
},
|
||||||
|
resetForm: true,
|
||||||
|
clearForm: true,
|
||||||
|
formData: options.upload.formData,
|
||||||
|
error: function (xhr) {
|
||||||
|
let errorMsg = (xhr.responseJSON &&
|
||||||
|
(xhr.responseJSON.error || (xhr.responseJSON.status && xhr.responseJSON.status.message))) ||
|
||||||
|
'[[error:parse-error]]';
|
||||||
|
|
||||||
|
if (xhr && xhr.status === 413) {
|
||||||
|
errorMsg = xhr.statusText || 'Request Entity Too Large';
|
||||||
|
}
|
||||||
|
alerts.error(errorMsg);
|
||||||
|
alerts.remove(alert_id);
|
||||||
|
},
|
||||||
|
|
||||||
|
uploadProgress: function (event, position, total, percent) {
|
||||||
|
alerts.alert({
|
||||||
|
alert_id: alert_id,
|
||||||
|
message: '[[modules:composer.uploading, ' + percent + '%]]',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
success: function (res) {
|
||||||
|
const uploads = res.response.images;
|
||||||
|
if (uploads && uploads.length) {
|
||||||
|
for (var i = 0; i < uploads.length; ++i) {
|
||||||
|
uploads[i].filename = files[i].name;
|
||||||
|
uploads[i].isImage = /image./.test(files[i].type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
options.callback(uploads);
|
||||||
|
},
|
||||||
|
|
||||||
|
complete: function () {
|
||||||
|
options.uploadForm[0].reset();
|
||||||
|
setTimeout(alerts.remove, 100, alert_id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
options.uploadForm.submit();
|
||||||
|
};
|
||||||
|
|
||||||
|
return uploadHelpers;
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user