Merge pull request #4641 from mfilser/attachment_upload_progress_bar

Attachment upload progress bar + multiple files upload
This commit is contained in:
Lauri Ojansivu 2022-08-17 00:01:16 +03:00 committed by GitHub
commit f320069ec8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 139 additions and 60 deletions

View file

@ -1,3 +1,7 @@
.attachment-upload {
text-align: center;
font-weight: bold;
}
.attachments-galery {
display: flex;
flex-wrap: wrap;

View file

@ -1,10 +1,25 @@
template(name="cardAttachmentsPopup")
ul.pop-over-list
li
input.js-attach-file.hide(type="file" name="file" multiple)
a.js-computer-upload {{_ 'computer'}}
li
a.js-upload-clipboard-image {{_ 'clipboard'}}
if $gt uploads.length 0
.attachment-upload {{_ 'uploading'}}
table
tr
th.upload-file-name-descr {{_ 'name'}}
th.upload-progress-descr {{_ 'progress'}}
th.upload-remaining-descr {{_ 'remaining_time'}}
th.upload-speed-descr {{_ 'speed'}}
each upload in uploads
tr
td.upload-file-name-value {{upload.file.name}}
td.upload-progress-value {{upload.progress.get}}%
td.upload-remaining-value {{getEstimateTime upload}}
td.upload-speed-value {{getEstimateSpeed upload}}
else
ul.pop-over-list
li
input.js-attach-file.hide(type="file" name="file" multiple)
a.js-computer-upload {{_ 'computer'}}
li
a.js-upload-clipboard-image {{_ 'clipboard'}}
template(name="previewClipboardImagePopup")
p <kbd>Ctrl</kbd>+<kbd>V</kbd> {{_ "paste-or-dragdrop"}}
@ -37,11 +52,9 @@ template(name="attachmentsGalery")
source(src="{{link}}" type="video/mp4")
else
span.attachment-thumbnail-ext= extension
else
+spinner
p.attachment-details
= name
span.file-size ({{fileSize size}} KB)
span.file-size ({{fileSize size}})
span.attachment-details-actions
a.js-download(href="{{link}}?download=true", download="{{name}}")
i.fa.fa-download

View file

@ -1,3 +1,6 @@
const filesize = require('filesize');
const prettyMilliseconds = require('pretty-ms');
Template.attachmentsGalery.events({
'click .js-add-attachment': Popup.open('cardAttachments'),
// If we let this event bubble, FlowRouter will handle it and empty the page
@ -13,37 +16,68 @@ Template.attachmentsGalery.helpers({
return Meteor.user().isBoardAdmin();
},
fileSize(size) {
return Math.round(size / 1024);
const ret = filesize(size);
return ret;
},
});
Template.cardAttachmentsPopup.onCreated(function() {
this.uploads = new ReactiveVar([]);
});
Template.cardAttachmentsPopup.helpers({
getEstimateTime(upload) {
const ret = prettyMilliseconds(upload.estimateTime.get());
return ret;
},
getEstimateSpeed(upload) {
const ret = filesize(upload.estimateSpeed.get(), {round: 0}) + "/s";
return ret;
},
uploads() {
return Template.instance().uploads.get();
}
});
Template.cardAttachmentsPopup.events({
'change .js-attach-file'(event) {
'change .js-attach-file'(event, templateInstance) {
const card = this;
if (event.currentTarget.files && event.currentTarget.files[0]) {
const fileId = Random.id();
const config = {
file: event.currentTarget.files[0],
fileId: fileId,
meta: Utils.getCommonAttachmentMetaFrom(card),
chunkSize: 'dynamic',
};
config.meta.fileId = fileId;
const uploader = Attachments.insert(
config,
false,
);
uploader.on('uploaded', (error, fileRef) => {
if (!error) {
if (fileRef.isImage) {
card.setCover(fileRef._id);
const files = event.currentTarget.files;
if (files) {
let uploads = [];
for (const file of files) {
const fileId = Random.id();
const config = {
file: file,
fileId: fileId,
meta: Utils.getCommonAttachmentMetaFrom(card),
chunkSize: 'dynamic',
};
config.meta.fileId = fileId;
const uploader = Attachments.insert(
config,
false,
);
uploader.on('start', function() {
uploads.push(this);
templateInstance.uploads.set(uploads);
});
uploader.on('uploaded', (error, fileRef) => {
if (!error) {
if (fileRef.isImage) {
card.setCover(fileRef._id);
}
}
}
});
uploader.on('end', (error, fileRef) => {
Popup.back();
});
uploader.start();
});
uploader.on('end', (error, fileRef) => {
uploads = uploads.filter(_upload => _upload.config.fileId != fileRef._id);
templateInstance.uploads.set(uploads);
if (uploads.length == 0 ) {
Popup.back();
}
});
uploader.start();
}
}
},
'click .js-computer-upload'(event, templateInstance) {

View file

@ -88,7 +88,7 @@ template(name="filesReport")
each att in results
tr
td {{ att.name }}
td.right {{fileSize att.size }}
td.right {{ fileSize att.size }}
td {{ att.type }}
td {{ att._id }}
td {{ att.meta.boardId }}

View file

@ -4,6 +4,7 @@ import { CardSearchPagedComponent } from '/client/lib/cardSearch';
import SessionData from '/models/usersessiondata';
import { QueryParams } from '/config/query-classes';
import { OPERATOR_LIMIT } from '/config/search-const';
const filesize = require('filesize');
BlazeComponent.extendComponent({
subscription: null,
@ -114,7 +115,8 @@ class AdminReport extends BlazeComponent {
}
fileSize(size) {
return Math.round(size / 1024);
const ret = filesize(size);
return ret;
}
abbreviate(text) {

View file

@ -52,7 +52,7 @@ template(name="moveBoardAttachments")
th {{_ 'name'}}
th {{_ 'path'}}
th {{_ 'version-name'}}
th {{_ 'size'}} (B)
th {{_ 'size'}}
th GridFsFileId
th {{_ 'storage'}}
th {{_ 'action'}}
@ -68,7 +68,7 @@ template(name="moveAttachment")
td {{ name }}
td {{ version.path }}
td {{ version.versionName }}
td {{ version.size }}
td {{ fileSize version.size }}
td {{ version.meta.gridFsFileId }}
td {{ version.storageName }}
td

View file

@ -1,4 +1,5 @@
import Attachments, { fileStoreStrategyFactory } from '/models/attachments';
const filesize = require('filesize');
BlazeComponent.extendComponent({
subscription: null,
@ -108,6 +109,10 @@ BlazeComponent.extendComponent({
}).register('moveBoardAttachments');
BlazeComponent.extendComponent({
fileSize(size) {
const ret = filesize(size);
return ret;
},
events() {
return [
{

View file

@ -58,38 +58,38 @@ template(name='statistics')
td {{numFormat statistics.os.loadavg.[0]}}, {{numFormat statistics.os.loadavg.[1]}}, {{numFormat statistics.os.loadavg.[2]}}
tr
th {{_ 'OS_Totalmem'}}
td {{bytesToSize statistics.os.totalmem}}
td {{fileSize statistics.os.totalmem}}
tr
th {{_ 'OS_Freemem'}}
td {{bytesToSize statistics.os.freemem}}
td {{fileSize statistics.os.freemem}}
tr
th {{_ 'OS_Cpus'}}
td {{statistics.os.cpus.length}}
unless isSandstorm
tr
th {{_ 'Node_heap_total_heap_size'}}
td {{bytesToSize statistics.nodeHeapStats.totalHeapSize}}
td {{fileSize statistics.nodeHeapStats.totalHeapSize}}
tr
th {{_ 'Node_heap_total_heap_size_executable'}}
td {{bytesToSize statistics.nodeHeapStats.totalHeapSizeExecutable}}
td {{fileSize statistics.nodeHeapStats.totalHeapSizeExecutable}}
tr
th {{_ 'Node_heap_total_physical_size'}}
td {{bytesToSize statistics.nodeHeapStats.totalPhysicalSize}}
td {{fileSize statistics.nodeHeapStats.totalPhysicalSize}}
tr
th {{_ 'Node_heap_total_available_size'}}
td {{bytesToSize statistics.nodeHeapStats.totalAvailableSize}}
td {{fileSize statistics.nodeHeapStats.totalAvailableSize}}
tr
th {{_ 'Node_heap_used_heap_size'}}
td {{bytesToSize statistics.nodeHeapStats.usedHeapSize}}
td {{fileSize statistics.nodeHeapStats.usedHeapSize}}
tr
th {{_ 'Node_heap_heap_size_limit'}}
td {{bytesToSize statistics.nodeHeapStats.heapSizeLimit}}
td {{fileSize statistics.nodeHeapStats.heapSizeLimit}}
tr
th {{_ 'Node_heap_malloced_memory'}}
td {{bytesToSize statistics.nodeHeapStats.mallocedMemory}}
td {{fileSize statistics.nodeHeapStats.mallocedMemory}}
tr
th {{_ 'Node_heap_peak_malloced_memory'}}
td {{bytesToSize statistics.nodeHeapStats.peakMallocedMemory}}
td {{fileSize statistics.nodeHeapStats.peakMallocedMemory}}
tr
th {{_ 'Node_heap_does_zap_garbage'}}
td {{statistics.nodeHeapStats.doesZapGarbage}}
@ -101,13 +101,13 @@ template(name='statistics')
td {{statistics.nodeHeapStats.numberOfDetachedContexts}}
tr
th {{_ 'Node_memory_usage_rss'}}
td {{bytesToSize statistics.nodeMemoryUsage.rss}}
td {{fileSize statistics.nodeMemoryUsage.rss}}
tr
th {{_ 'Node_memory_usage_heap_total'}}
td {{bytesToSize statistics.nodeMemoryUsage.heapTotal}}
td {{fileSize statistics.nodeMemoryUsage.heapTotal}}
tr
th {{_ 'Node_memory_usage_heap_used'}}
td {{bytesToSize statistics.nodeMemoryUsage.heapUsed}}
td {{fileSize statistics.nodeMemoryUsage.heapUsed}}
tr
th {{_ 'Node_memory_usage_external'}}
td {{bytesToSize statistics.nodeMemoryUsage.external}}
td {{fileSize statistics.nodeMemoryUsage.external}}

View file

@ -1,4 +1,5 @@
import { TAPi18n } from '/imports/i18n';
const filesize = require('filesize');
BlazeComponent.extendComponent({
onCreated() {
@ -39,12 +40,8 @@ BlazeComponent.extendComponent({
return parseFloat(number).toFixed(2);
},
bytesToSize(bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes === 0) {
return '0 Byte';
}
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10);
return `${Math.round(bytes / Math.pow(1024, i), 2)} ${sizes[i]}`;
fileSize(size) {
const ret = filesize(size);
return ret;
},
}).register('statistics');

View file

@ -1181,5 +1181,9 @@
"storage": "Storage",
"action": "Action",
"board-title": "Board Title",
"attachmentRenamePopup-title": "Rename"
"attachmentRenamePopup-title": "Rename",
"uploading": "Uploading",
"remaining_time": "Remaining time",
"speed": "Speed",
"progress": "Progress"
}

18
package-lock.json generated
View file

@ -1791,6 +1791,11 @@
"token-types": "^4.1.1"
}
},
"filesize": {
"version": "8.0.7",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz",
"integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ=="
},
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
@ -3624,6 +3629,11 @@
"parse5": "^7.0.0"
}
},
"parse-ms": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz",
"integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA=="
},
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@ -3671,6 +3681,14 @@
"resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz",
"integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ=="
},
"pretty-ms": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz",
"integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==",
"requires": {
"parse-ms": "^2.1.0"
}
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",

View file

@ -37,6 +37,7 @@
"exceljs": "^4.2.1",
"fibers": "^5.0.0",
"file-type": "^16.5.4",
"filesize": "^8.0.7",
"i18next": "^21.6.16",
"i18next-sprintf-postprocessor": "^0.2.2",
"jquery": "^2.2.4",
@ -55,6 +56,7 @@
"os": "^0.1.2",
"page": "^1.11.6",
"papaparse": "^5.3.1",
"pretty-ms": "^7.0.1",
"qs": "^6.10.1",
"simpl-schema": "^1.12.0",
"source-map-support": "^0.5.20",