mirror of
https://github.com/nextcloud/server.git
synced 2026-06-13 18:50:47 -04:00
Merge pull request #39912 from nextcloud/fix/improved-upload-view
Improve upload progress visualization
This commit is contained in:
commit
da3c94161b
4 changed files with 60 additions and 20 deletions
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
#upload{box-sizing:border-box;height:36px;width:39px;padding:0 !important;margin-left:3px;overflow:hidden;vertical-align:top;position:relative;z-index:-20}#upload .icon-upload{position:relative;display:block;width:100%;height:44px;width:44px;margin:-5px -3px;cursor:pointer;z-index:10;opacity:.65}.file_upload_target{display:none}.file_upload_form{display:inline;float:left;margin:0;padding:0;cursor:pointer;overflow:visible}.uploadprogresswrapper,.uploadprogresswrapper *{box-sizing:border-box}.uploadprogresswrapper{display:inline-block;vertical-align:top;height:36px;margin-left:3px}.uploadprogresswrapper>input[type=button]{height:36px;margin-left:3px}#uploadprogressbar{border-color:var(--color-border-dark);border-radius:var(--border-radius-pill) 0 0 var(--border-radius-pill);border-right:0;position:relative;float:left;width:200px;height:44px;display:inline-block;text-align:center}#uploadprogressbar .ui-progressbar-value{margin-top:.1em}#uploadprogressbar .ui-progressbar-value.ui-widget-header.ui-corner-left{height:calc(100% + 2px);top:-1px;left:-1px;position:absolute;overflow:hidden;background-color:var(--color-primary-element)}#uploadprogressbar .label{top:8px;opacity:1;overflow:hidden;white-space:nowrap;font-weight:normal}#uploadprogressbar .label.inner{color:var(--color-primary-element-text);position:absolute;display:block;width:200px}#uploadprogressbar .label.outer{position:relative;color:var(--color-main-text)}#uploadprogressbar .desktop{display:block}#uploadprogressbar .mobile{display:none}#uploadprogressbar+.stop{border-top-left-radius:0;border-bottom-left-radius:0}.oc-dialog .fileexists{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;margin-bottom:30px}.oc-dialog .fileexists .conflict .filename,.oc-dialog .fileexists .conflict .mtime,.oc-dialog .fileexists .conflict .size{-webkit-touch-callout:initial;-webkit-user-select:initial;-khtml-user-select:initial;-moz-user-select:initial;-ms-user-select:initial;user-select:initial}.oc-dialog .fileexists .conflict .message{color:#e9322d}.oc-dialog .fileexists table{width:100%}.oc-dialog .fileexists th{padding-left:0;padding-right:0}.oc-dialog .fileexists th input[type=checkbox]{margin-right:3px}.oc-dialog .fileexists th:first-child{width:225px}.oc-dialog .fileexists th label{font-weight:normal;color:var(--color-main-text)}.oc-dialog .fileexists th .count{margin-left:3px}.oc-dialog .fileexists .conflicts .template{display:none}.oc-dialog .fileexists .conflict{width:100%;height:85px}.oc-dialog .fileexists .conflict .filename{color:#777;word-break:break-all;clear:left}.oc-dialog .fileexists .icon{width:64px;height:64px;margin:0px 5px 5px 5px;background-repeat:no-repeat;background-size:64px 64px;float:left}.oc-dialog .fileexists .original,.oc-dialog .fileexists .replacement{float:left;width:50%}.oc-dialog .fileexists .conflicts{overflow-y:auto;max-height:225px}.oc-dialog .fileexists .conflict input[type=checkbox]{float:left}.oc-dialog .fileexists #allfileslabel{float:right}.oc-dialog .fileexists #allfiles{vertical-align:bottom;position:relative;top:-3px}.oc-dialog .fileexists #allfiles+span{vertical-align:bottom}.oc-dialog .oc-dialog-buttonrow{width:100%;text-align:right}.oc-dialog .oc-dialog-buttonrow .cancel{float:left}.highlightUploaded{-webkit-animation:highlightAnimation 2s 1;-moz-animation:highlightAnimation 2s 1;-o-animation:highlightAnimation 2s 1;animation:highlightAnimation 2s 1}@-webkit-keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}@-moz-keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}@-o-keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}@keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}/*# sourceMappingURL=upload.css.map */
|
||||
#upload{box-sizing:border-box;height:36px;width:39px;padding:0 !important;margin-left:3px;overflow:hidden;vertical-align:top;position:relative;z-index:-20}#upload .icon-upload{position:relative;display:block;width:100%;height:44px;width:44px;margin:-5px -3px;cursor:pointer;z-index:10;opacity:.65}.file_upload_target{display:none}.file_upload_form{display:inline;float:left;margin:0;padding:0;cursor:pointer;overflow:visible}.uploadprogresswrapper,.uploadprogresswrapper *{box-sizing:border-box}.uploadprogresswrapper{display:inline-block;vertical-align:top;height:36px;margin-left:3px}.uploadprogresswrapper>input[type=button]{height:36px;margin-left:3px}#uploadprogressbar{border-color:var(--color-border-dark);border-radius:var(--border-radius-pill) 0 0 var(--border-radius-pill);border-right:0;position:relative;float:left;width:200px;height:44px;display:inline-block;text-align:center}#uploadprogressbar .ui-progressbar-value{margin-top:.1em}#uploadprogressbar .ui-progressbar-value.ui-widget-header.ui-corner-left{height:calc(100% + 2px);top:-2px;left:-1px;position:absolute;overflow:hidden;background-color:var(--color-primary-element)}#uploadprogressbar .label{top:8px;opacity:1;overflow:hidden;white-space:nowrap;font-weight:normal}#uploadprogressbar .label.inner{color:var(--color-primary-element-text);position:absolute;display:block;width:200px}#uploadprogressbar .label.outer{position:relative;color:var(--color-main-text)}#uploadprogressbar .desktop{display:block}#uploadprogressbar .mobile{display:none}#uploadprogressbar+.stop{border-top-left-radius:0;border-bottom-left-radius:0}.oc-dialog .fileexists{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;margin-bottom:30px}.oc-dialog .fileexists .conflict .filename,.oc-dialog .fileexists .conflict .mtime,.oc-dialog .fileexists .conflict .size{-webkit-touch-callout:initial;-webkit-user-select:initial;-khtml-user-select:initial;-moz-user-select:initial;-ms-user-select:initial;user-select:initial}.oc-dialog .fileexists .conflict .message{color:#e9322d}.oc-dialog .fileexists table{width:100%}.oc-dialog .fileexists th{padding-left:0;padding-right:0}.oc-dialog .fileexists th input[type=checkbox]{margin-right:3px}.oc-dialog .fileexists th:first-child{width:225px}.oc-dialog .fileexists th label{font-weight:normal;color:var(--color-main-text)}.oc-dialog .fileexists th .count{margin-left:3px}.oc-dialog .fileexists .conflicts .template{display:none}.oc-dialog .fileexists .conflict{width:100%;height:85px}.oc-dialog .fileexists .conflict .filename{color:#777;word-break:break-all;clear:left}.oc-dialog .fileexists .icon{width:64px;height:64px;margin:0px 5px 5px 5px;background-repeat:no-repeat;background-size:64px 64px;float:left}.oc-dialog .fileexists .original,.oc-dialog .fileexists .replacement{float:left;width:50%}.oc-dialog .fileexists .conflicts{overflow-y:auto;max-height:225px}.oc-dialog .fileexists .conflict input[type=checkbox]{float:left}.oc-dialog .fileexists #allfileslabel{float:right}.oc-dialog .fileexists #allfiles{vertical-align:bottom;position:relative;top:-3px}.oc-dialog .fileexists #allfiles+span{vertical-align:bottom}.oc-dialog .oc-dialog-buttonrow{width:100%;text-align:right}.oc-dialog .oc-dialog-buttonrow .cancel{float:left}.highlightUploaded{-webkit-animation:highlightAnimation 2s 1;-moz-animation:highlightAnimation 2s 1;-o-animation:highlightAnimation 2s 1;animation:highlightAnimation 2s 1}@-webkit-keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}@-moz-keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}@-o-keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}@keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}/*# sourceMappingURL=upload.css.map */
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
}
|
||||
#uploadprogressbar .ui-progressbar-value.ui-widget-header.ui-corner-left {
|
||||
height: calc(100% + 2px);
|
||||
top: -1px;
|
||||
top: -2px;
|
||||
left: -1px;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
|
|
|
|||
|
|
@ -618,8 +618,10 @@ OC.Uploader.prototype = _.extend({
|
|||
});
|
||||
if (!self._uploading) {
|
||||
self.totalToUpload = 0;
|
||||
self.totalUploadCount = 0;
|
||||
}
|
||||
self.totalToUpload += _.reduce(uploads, function(memo, upload) { return memo+upload.getFile().size; }, 0);
|
||||
self.totalUploadCount += uploads.length;
|
||||
var semaphore = new OCA.Files.Semaphore(5);
|
||||
var promises = _.map(uploads, function(upload) {
|
||||
return semaphore.acquire().then(function(){
|
||||
|
|
@ -726,8 +728,9 @@ OC.Uploader.prototype = _.extend({
|
|||
},
|
||||
|
||||
showUploadCancelMessage: _.debounce(function() {
|
||||
OC.Notification.show(t('files', 'Upload cancelled.'), {timeout : 7, type: 'error'});
|
||||
OC.Notification.show(t('files', 'Upload cancelled.'), { timeout : 7000, type: 'error' });
|
||||
}, 500),
|
||||
|
||||
/**
|
||||
* callback for the conflicts dialog
|
||||
*/
|
||||
|
|
@ -955,6 +958,7 @@ OC.Uploader.prototype = _.extend({
|
|||
type: 'PUT',
|
||||
dropZone: options.dropZone, // restrict dropZone to content div
|
||||
autoUpload: false,
|
||||
progressInterval: 300, // increased from the default of 100ms for more stable behvaviour when predicting remaining time
|
||||
sequentialUploads: false,
|
||||
limitConcurrentUploads: 4,
|
||||
/**
|
||||
|
|
@ -1197,7 +1201,7 @@ OC.Uploader.prototype = _.extend({
|
|||
|
||||
if (this._supportAjaxUploadWithProgress()) {
|
||||
//remaining time
|
||||
var lastUpdate, lastSize, bufferSize, buffer, bufferIndex, bufferIndex2, bufferTotal;
|
||||
var lastUpdate, lastSize, bufferSize, buffer, bufferIndex, bufferTotal, smoothRemainingSeconds, smoothBitrate;
|
||||
|
||||
var dragging = false;
|
||||
|
||||
|
|
@ -1215,11 +1219,15 @@ OC.Uploader.prototype = _.extend({
|
|||
// initial remaining time variables
|
||||
lastUpdate = new Date().getTime();
|
||||
lastSize = 0;
|
||||
bufferSize = 20;
|
||||
bufferSize = 20; // length of the ring buffer
|
||||
buffer = [];
|
||||
bufferIndex = 0;
|
||||
bufferIndex2 = 0;
|
||||
bufferIndex = 0; // index of the ring buffer, runs from 0 to bufferSize continuously
|
||||
bufferTotal = 0;
|
||||
newTotal = 0;
|
||||
smoothing = 0.02; // smoothing factor for EMA
|
||||
h = '';
|
||||
bufferFilled = false;
|
||||
|
||||
for(var i = 0; i < bufferSize; i++){
|
||||
buffer[i] = 0;
|
||||
}
|
||||
|
|
@ -1238,33 +1246,65 @@ OC.Uploader.prototype = _.extend({
|
|||
var diffUpdate = (thisUpdate - lastUpdate)/1000; // eg. 2s
|
||||
lastUpdate = thisUpdate;
|
||||
var diffSize = data.loaded - lastSize;
|
||||
if (diffSize <= 0) {
|
||||
diffSize = lastSize;
|
||||
}
|
||||
lastSize = data.loaded;
|
||||
diffSize = diffSize / diffUpdate; // apply timing factor, eg. 1MiB/2s = 0.5MiB/s, unit is byte per second
|
||||
var remainingSeconds = ((total - data.loaded) / diffSize);
|
||||
|
||||
if(remainingSeconds >= 0) {
|
||||
// bufferTotal holds the sum of all entries in the buffer, initially 0 like the entries itself
|
||||
// substract current entry from total and add the current value to total
|
||||
bufferTotal = bufferTotal - (buffer[bufferIndex]) + remainingSeconds;
|
||||
// put current value to the entry
|
||||
buffer[bufferIndex] = remainingSeconds; //buffer to make it smoother
|
||||
|
||||
bufferIndex = (bufferIndex + 1) % bufferSize;
|
||||
bufferIndex2++;
|
||||
}
|
||||
var smoothRemainingSeconds;
|
||||
if (bufferIndex2 > 0 && bufferIndex2 < 20) {
|
||||
smoothRemainingSeconds = bufferTotal / bufferIndex2;
|
||||
} else if (bufferSize > 0) {
|
||||
smoothRemainingSeconds = bufferTotal / bufferSize;
|
||||
if (bufferIndex === bufferSize - 1) {
|
||||
bufferFilled = true;
|
||||
}
|
||||
//console.log('#', ' idx: ',bufferIndex, ' Total: ', bufferTotal, ' remainSeconds: ', remainingSeconds, ' during: ', diffUpdate);
|
||||
|
||||
if (smoothRemainingSeconds) {
|
||||
smoothRemainingSeconds = smoothing * (bufferTotal / bufferSize) + ((1-smoothing) * smoothRemainingSeconds);
|
||||
} else {
|
||||
smoothRemainingSeconds = 1;
|
||||
smoothRemainingSeconds = bufferTotal / bufferSize;
|
||||
}
|
||||
|
||||
var h = moment.duration(smoothRemainingSeconds, "seconds").humanize();
|
||||
if (!(smoothRemainingSeconds >= 0 && smoothRemainingSeconds < 14400)) {
|
||||
// show "Uploading ..." for durations longer than 4 hours
|
||||
h = t('files', 'Uploading …');
|
||||
// the number of currently running uploads
|
||||
const runningUploads = Object.keys(self._uploads).length;
|
||||
|
||||
// Only show remaining time if enough buffer information is available and debounce to 1/4
|
||||
if (bufferFilled && bufferIndex % 4 === 0) {
|
||||
h = moment.duration(smoothRemainingSeconds, "seconds").humanize({m: 50, h: 50});
|
||||
if (self.totalUploadCount > 1) {
|
||||
h = t('files', '{remainingTime} ({currentNumber}/{total})', { remainingTime: h, currentNumber: self.totalUploadCount - runningUploads + 1, total: self.totalUploadCount });
|
||||
}
|
||||
}
|
||||
|
||||
// wait for the buffer to be filled and also show "Uploading ..." for durations longer than 4 hours
|
||||
if (!bufferFilled || !(smoothRemainingSeconds >= 0 && smoothRemainingSeconds < 14400)) {
|
||||
// Do not show file index when there is just one
|
||||
if (self.totalUploadCount > 1) {
|
||||
h = t('files', 'Uploading … ({currentNumber}/{total})', { currentNumber: self.totalUploadCount - runningUploads + 1, total: self.totalUploadCount });
|
||||
} else {
|
||||
h = t('files', 'Uploading …');
|
||||
}
|
||||
}
|
||||
|
||||
// smooth bitrate
|
||||
if (smoothBitrate) {
|
||||
smoothBitrate = smoothing * data.bitrate + ((1-smoothing) * smoothBitrate);
|
||||
} else {
|
||||
smoothBitrate = data.bitrate;
|
||||
}
|
||||
|
||||
self._setProgressBarText(h, h, t('files', '{loadedSize} of {totalSize} ({bitrate})' , {
|
||||
loadedSize: OC.Util.humanFileSize(data.loaded),
|
||||
totalSize: OC.Util.humanFileSize(total),
|
||||
bitrate: OC.Util.humanFileSize(data.bitrate / 8) + '/s'
|
||||
bitrate: OC.Util.humanFileSize(smoothBitrate / 8) + '/s'
|
||||
}));
|
||||
self._setProgressBarValue(progress);
|
||||
self.trigger('progressall', e, data);
|
||||
|
|
|
|||
Loading…
Reference in a new issue