diff options
author | John Molakvoæ <skjnldsv@users.noreply.github.com> | 2019-02-14 14:24:20 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-02-14 14:24:20 +0100 |
commit | 6a3f4e4957eb803ed4b764f01d50727403d7a3ba (patch) | |
tree | f3a7e8772e2f4e61b751fe645408b5f47de31462 /apps/files/js | |
parent | e65f7f05de6443bd66b1c31325cbc3cbe149d1e5 (diff) | |
parent | 08919eb19335825dc1f2cd950f9529a2b9caf29b (diff) | |
download | nextcloud-server-6a3f4e4957eb803ed4b764f01d50727403d7a3ba.tar.gz nextcloud-server-6a3f4e4957eb803ed4b764f01d50727403d7a3ba.zip |
Merge pull request #12652 from tomasz-grobelny/operation_progress_improvements3
Operation progress improvements
Diffstat (limited to 'apps/files/js')
-rw-r--r-- | apps/files/js/file-upload.js | 62 | ||||
-rw-r--r-- | apps/files/js/filelist.js | 143 | ||||
-rw-r--r-- | apps/files/js/merged-index.json | 2 | ||||
-rw-r--r-- | apps/files/js/operationprogressbar.js | 79 | ||||
-rw-r--r-- | apps/files/js/semaphore.js | 37 | ||||
-rw-r--r-- | apps/files/js/templates.js | 16 | ||||
-rw-r--r-- | apps/files/js/templates/operationprogressbar.handlebars | 6 | ||||
-rw-r--r-- | apps/files/js/templates/operationprogressbarlabel.handlebars | 4 |
8 files changed, 243 insertions, 106 deletions
diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 79d266a300b..e0b274cdc76 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -429,6 +429,11 @@ OC.Uploader.prototype = _.extend({ fileList: null, /** + * @type OCA.Files.OperationProgressBar + */ + progressBar: null, + + /** * @type OC.Files.Client */ filesClient: null, @@ -759,14 +764,6 @@ OC.Uploader.prototype = _.extend({ callbacks.onNoConflicts(selection); }, - _hideProgressBar: function() { - var self = this; - $('#uploadprogresswrapper .stop').fadeOut(); - $('#uploadprogressbar').fadeOut(function() { - self.$uploadEl.trigger(new $.Event('resized')); - }); - }, - _updateProgressBarOnUploadStop: function() { if (this._pendingUploadDoneCount === 0) { // All the uploads ended and there is no pending operation, so hide @@ -780,17 +777,31 @@ OC.Uploader.prototype = _.extend({ return; } - $('#uploadprogressbar .label .mobile').text(t('core', '…')); - $('#uploadprogressbar .label .desktop').text(t('core', 'Processing files …')); + this._setProgressBarText(t('core', 'Processing files …'), t('core', '…')); // Nothing is being uploaded at this point, and the pending operations // can not be cancelled, so the cancel button should be hidden. - $('#uploadprogresswrapper .stop').fadeOut(); + this._hideCancelButton(); + }, + + _hideProgressBar: function() { + this.progressBar.hideProgressBar(); + }, + + _hideCancelButton: function() { + this.progressBar.hideCancelButton(); }, _showProgressBar: function() { - $('#uploadprogressbar').fadeIn(); - this.$uploadEl.trigger(new $.Event('resized')); + this.progressBar.showProgressBar(); + }, + + _setProgressBarValue: function(value) { + this.progressBar.setProgressBarValue(value); + }, + + _setProgressBarText: function(textDesktop, textMobile, title) { + this.progressBar.setProgressBarText(textDesktop, textMobile, title); }, /** @@ -826,6 +837,7 @@ OC.Uploader.prototype = _.extend({ options = options || {}; this.fileList = options.fileList; + this.progressBar = options.progressBar; this.filesClient = options.filesClient || OC.Files.getClient(); this.davClient = new OC.Files.Client({ host: this.filesClient.getHost(), @@ -839,7 +851,7 @@ OC.Uploader.prototype = _.extend({ this.$uploadEl = $uploadEl; if ($uploadEl.exists()) { - $('#uploadprogresswrapper .stop').on('click', function() { + this.progressBar.on('cancel', function() { self.cancelUploads(); }); @@ -1099,16 +1111,8 @@ OC.Uploader.prototype = _.extend({ // add progress handlers fileupload.on('fileuploadstart', function(e, data) { self.log('progress handle fileuploadstart', e, data); - $('#uploadprogresswrapper .stop').show(); - $('#uploadprogresswrapper .label').show(); - $('#uploadprogressbar').progressbar({value: 0}); - $('#uploadprogressbar .ui-progressbar-value'). - html('<em class="label inner"><span class="desktop">' - + t('files', 'Uploading …') - + '</span><span class="mobile">' - + t('files', '…') - + '</span></em>'); - $('#uploadprogressbar').tooltip({placement: 'bottom'}); + self._setProgressBarText(t('files', 'Uploading …'), t('files', '…')); + self._setProgressBarValue(0); self._showProgressBar(); // initial remaining time variables lastUpdate = new Date().getTime(); @@ -1158,16 +1162,12 @@ OC.Uploader.prototype = _.extend({ // show "Uploading ..." for durations longer than 4 hours h = t('files', 'Uploading …'); } - $('#uploadprogressbar .label .mobile').text(h); - $('#uploadprogressbar .label .desktop').text(h); - $('#uploadprogressbar').attr('original-title', - t('files', '{loadedSize} of {totalSize} ({bitrate})' , { + self._setProgressBarText(h, h, t('files', '{loadedSize} of {totalSize} ({bitrate})' , { loadedSize: humanFileSize(data.loaded), totalSize: humanFileSize(data.total), bitrate: humanFileSize(data.bitrate / 8) + '/s' - }) - ); - $('#uploadprogressbar').progressbar('value', progress); + })); + self._setProgressBarValue(progress); self.trigger('progressall', e, data); }); fileupload.on('fileuploadstop', function(e, data) { diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 363f81a1a73..868623d9a8a 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -386,11 +386,16 @@ }); } + this._operationProgressBar = new OCA.Files.OperationProgressBar(); + this._operationProgressBar.render(); + this.$el.find('#uploadprogresswrapper').replaceWith(this._operationProgressBar.$el); + if (options.enableUpload) { // TODO: auto-create this element var $uploadEl = this.$el.find('#file_upload_start'); if ($uploadEl.exists()) { this._uploader = new OC.Uploader($uploadEl, { + progressBar: this._operationProgressBar, fileList: this, filesClient: this.filesClient, dropZone: $('#content'), @@ -2206,21 +2211,39 @@ remove: function(name, options){ options = options || {}; var fileEl = this.findFileEl(name); - var fileId = fileEl.data('id'); - var index = fileEl.index(); - if (!fileEl.length) { - return null; + var fileData = _.findWhere(this.files, {name: name}); + if (!fileData) { + return; } + var fileId = fileData.id; if (this._selectedFiles[fileId]) { // remove from selection first this._selectFileEl(fileEl, false); this.updateSelectionSummary(); } + if (this._selectedFiles[fileId]) { + delete this._selectedFiles[fileId]; + this._selectionSummary.remove(fileData); + this.updateSelectionSummary(); + } + var index = this.files.findIndex(function(el){return el.name==name;}); + this.files.splice(index, 1); + + // TODO: improve performance on batch update + this.isEmpty = !this.files.length; + if (typeof(options.updateSummary) === 'undefined' || !!options.updateSummary) { + this.updateEmptyContent(); + this.fileSummary.remove({type: fileData.type, size: fileData.size}, true); + } + + if (!fileEl.length) { + return null; + } + if (this._dragOptions && (fileEl.data('permissions') & OC.PERMISSION_DELETE)) { // file is only draggable when delete permissions are set fileEl.find('td.filename').draggable('destroy'); } - this.files.splice(index, 1); if (this._currentFileModel && this._currentFileModel.get('id') === fileId) { // Note: in the future we should call destroy() directly on the model // and the model will take care of the deletion. @@ -2230,12 +2253,6 @@ this._updateDetailsView(null); } fileEl.remove(); - // TODO: improve performance on batch update - this.isEmpty = !this.files.length; - if (typeof(options.updateSummary) === 'undefined' || !!options.updateSummary) { - this.updateEmptyContent(); - this.fileSummary.remove({type: fileEl.attr('data-type'), size: fileEl.attr('data-size')}, true); - } var lastIndex = this.$fileList.children().length; // if there are less elements visible than one page @@ -2282,29 +2299,6 @@ fileNames = [fileNames]; } - function Semaphore(max) { - var counter = 0; - var waiting = []; - - this.acquire = function() { - if(counter < max) { - counter++; - return new Promise(function(resolve) { resolve(); }); - } else { - return new Promise(function(resolve) { waiting.push(resolve); }); - } - }; - - this.release = function() { - counter--; - if (waiting.length > 0 && counter < max) { - counter++; - var promise = waiting.shift(); - promise(); - } - }; - } - var moveFileFunction = function(fileName) { var $tr = self.findFileEl(fileName); self.showFileBusyState($tr, true); @@ -2316,7 +2310,7 @@ return self.filesClient.move(dir + fileName, targetPath + fileName) .done(function() { // if still viewing the same directory - if (OC.joinPaths(self.getCurrentDirectory(), '/') === dir) { + if (OC.joinPaths(self.getCurrentDirectory(), '/') === OC.joinPaths(dir, '/')) { // recalculate folder size var oldFile = self.findFileEl(target); var newFile = self.findFileEl(fileName); @@ -2325,7 +2319,6 @@ oldFile.data('size', newSize); oldFile.find('td.filesize').text(OC.Util.humanFileSize(newSize)); - // TODO: also update entry in FileList.files self.remove(fileName); } }) @@ -2345,22 +2338,33 @@ self.showFileBusyState($tr, false); }); }; + return this.reportOperationProgress(fileNames, moveFileFunction, callback); + }, + + _reflect: function (promise){ + return promise.then(function(v){ return {};}, function(e){ return {};}); + }, - var mcSemaphore = new Semaphore(10); + reportOperationProgress: function (fileNames, operationFunction, callback){ + var self = this; + self._operationProgressBar.showProgressBar(false); + var mcSemaphore = new OCA.Files.Semaphore(5); var counter = 0; var promises = _.map(fileNames, function(arg) { return mcSemaphore.acquire().then(function(){ - moveFileFunction(arg).then(function(){ + return operationFunction(arg).always(function(){ mcSemaphore.release(); counter++; + self._operationProgressBar.setProgressBarValue(100.0*counter/fileNames.length); }); }); }); - return Promise.all(promises).then(function(){ + return Promise.all(_.map(promises, self._reflect)).then(function(){ if (callback) { callback(); } + self._operationProgressBar.hideProgressBar(); }); }, @@ -2385,7 +2389,7 @@ if (!_.isArray(fileNames)) { fileNames = [fileNames]; } - _.each(fileNames, function(fileName) { + var copyFileFunction = function(fileName) { var $tr = self.findFileEl(fileName); self.showFileBusyState($tr, true); if (targetPath.charAt(targetPath.length - 1) !== '/') { @@ -2441,12 +2445,12 @@ } } } - self.filesClient.copy(dir + fileName, targetPathAndName) + return self.filesClient.copy(dir + fileName, targetPathAndName) .done(function () { filesToNotify.push(fileName); // if still viewing the same directory - if (OC.joinPaths(self.getCurrentDirectory(), '/') === dir) { + if (OC.joinPaths(self.getCurrentDirectory(), '/') === OC.joinPaths(dir, '/')) { // recalculate folder size var oldFile = self.findFileEl(target); var newFile = self.findFileEl(fileName); @@ -2513,11 +2517,8 @@ } } }); - }); - - if (callback) { - callback(); - } + }; + return this.reportOperationProgress(fileNames, copyFileFunction, callback); }, /** @@ -2939,9 +2940,6 @@ // delete all files in directory files = _.pluck(this.files, 'name'); } - if (files) { - this.showFileBusyState(files, true); - } // Finish any existing actions if (this.lastAction) { this.lastAction(); @@ -2949,43 +2947,38 @@ dir = dir || this.getCurrentDirectory(); - function removeFromList(file) { - var fileEl = self.remove(file, {updateSummary: false}); - // FIXME: not sure why we need this after the - // element isn't even in the DOM any more - fileEl.find('.selectCheckBox').prop('checked', false); - fileEl.removeClass('selected'); - self.fileSummary.remove({type: fileEl.attr('data-type'), size: fileEl.attr('data-size')}); - // TODO: this info should be returned by the ajax call! - self.updateEmptyContent(); - self.fileSummary.update(); - self.updateSelectionSummary(); - // FIXME: don't repeat this, do it once all files are done - self.updateStorageStatistics(); - self.updateStorageQuotas(); - } - - _.each(files, function(file) { - self.filesClient.remove(dir + '/' + file) + var removeFunction = function(fileName) { + var $tr = self.findFileEl(fileName); + self.showFileBusyState($tr, true); + return self.filesClient.remove(dir + '/' + fileName) .done(function() { - removeFromList(file); + if (OC.joinPaths(self.getCurrentDirectory(), '/') === OC.joinPaths(dir, '/')) { + self.remove(fileName); + } }) .fail(function(status) { if (status === 404) { // the file already did not exist, remove it from the list - removeFromList(file); + if (OC.joinPaths(self.getCurrentDirectory(), '/') === OC.joinPaths(dir, '/')) { + self.remove(fileName); + } } else { // only reset the spinner for that one file OC.Notification.show(t('files', 'Error deleting file "{fileName}".', - {fileName: file}), {type: 'error'} + {fileName: fileName}), {type: 'error'} ); - var deleteAction = self.findFileEl(file).find('.action.delete'); - deleteAction.removeClass('icon-loading-small').addClass('icon-delete'); - self.showFileBusyState(files, false); } + }) + .always(function() { + self.showFileBusyState($tr, false); }); - }); + }; + return this.reportOperationProgress(files, removeFunction).then(function(){ + self.updateStorageStatistics(); + self.updateStorageQuotas(); + }); }, + /** * Creates the file summary section */ diff --git a/apps/files/js/merged-index.json b/apps/files/js/merged-index.json index e891d10bdae..8d25daa6b3c 100644 --- a/apps/files/js/merged-index.json +++ b/apps/files/js/merged-index.json @@ -21,7 +21,9 @@ "sidebarpreviewmanager.js", "sidebarpreviewtext.js", "detailtabview.js", + "semaphore.js", "mainfileinfodetailview.js", + "operationprogressbar.js", "detailsview.js", "fileactions.js", "fileactionsmenu.js", diff --git a/apps/files/js/operationprogressbar.js b/apps/files/js/operationprogressbar.js new file mode 100644 index 00000000000..efeea4ad78f --- /dev/null +++ b/apps/files/js/operationprogressbar.js @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function() { + var OperationProgressBar = OC.Backbone.View.extend({ + tagName: 'div', + id: 'uploadprogresswrapper', + events: { + 'click button.stop': '_onClickCancel' + }, + + render: function() { + this.$el.html(OCA.Files.Templates['operationprogressbar']({ + textCancelButton: t('Cancel operation') + })); + this.setProgressBarText(t('Uploading …'), t('…')); + }, + + hideProgressBar: function() { + var self = this; + $('#uploadprogresswrapper .stop').fadeOut(); + $('#uploadprogressbar').fadeOut(function() { + self.$el.trigger(new $.Event('resized')); + }); + }, + + hideCancelButton: function() { + $('#uploadprogresswrapper .stop').fadeOut(function() { + this.$el.trigger(new $.Event('resized')); + }); + }, + + showProgressBar: function(showCancelButton) { + if (showCancelButton) { + showCancelButton = true; + } + $('#uploadprogressbar').progressbar({value: 0}); + if(showCancelButton) { + $('#uploadprogresswrapper .stop').show(); + } else { + $('#uploadprogresswrapper .stop').hide(); + } + $('#uploadprogresswrapper .label').show(); + $('#uploadprogressbar').fadeIn(); + this.$el.trigger(new $.Event('resized')); + }, + + setProgressBarValue: function(value) { + $('#uploadprogressbar').progressbar({value: value}); + }, + + setProgressBarText: function(textDesktop, textMobile, title) { + var labelHtml = OCA.Files.Templates['operationprogressbarlabel']({textDesktop: textDesktop, textMobile: textMobile}); + $('#uploadprogressbar .ui-progressbar-value').html(labelHtml); + $('#uploadprogressbar .ui-progressbar-value>em').addClass('inner'); + $('#uploadprogressbar>em').replaceWith(labelHtml); + $('#uploadprogressbar>em').addClass('outer'); + $('#uploadprogressbar').tooltip({placement: 'bottom'}); + if(title) { + $('#uploadprogressbar').attr('original-title', title); + } + $('#uploadprogresswrapper .stop').show(); + }, + + _onClickCancel: function (event) { + this.trigger('cancel'); + return false; + } + }); + + OCA.Files.OperationProgressBar = OperationProgressBar; +})(OC, OCA); diff --git a/apps/files/js/semaphore.js b/apps/files/js/semaphore.js new file mode 100644 index 00000000000..044f0af23f3 --- /dev/null +++ b/apps/files/js/semaphore.js @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +(function(){ + var Semaphore = function(max) { + var counter = 0; + var waiting = []; + + this.acquire = function() { + if(counter < max) { + counter++; + return new Promise(function(resolve) { resolve(); }); + } else { + return new Promise(function(resolve) { waiting.push(resolve); }); + } + }; + + this.release = function() { + counter--; + if (waiting.length > 0 && counter < max) { + counter++; + var promise = waiting.shift(); + promise(); + } + }; + }; + + OCA.Files.Semaphore = Semaphore; + +})(); diff --git a/apps/files/js/templates.js b/apps/files/js/templates.js index a2bdae5b3c4..be6b255d594 100644 --- a/apps/files/js/templates.js +++ b/apps/files/js/templates.js @@ -245,6 +245,22 @@ templates['newfilemenu_filename_form'] = template({"compiler":[7,">= 4.0.0"],"ma + alias4(((helper = (helper = helpers.fileName || (depth0 != null ? depth0.fileName : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"fileName","hash":{},"data":data}) : helper))) + "\" autocomplete=\"off\" autocapitalize=\"off\">\n <input type=\"submit\" value=\" \" class=\"icon-confirm\" />\n</form>\n"; },"useData":true}); +templates['operationprogressbar'] = template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { + var helper; + + return "<div id=\"uploadprogressbar\">\n <em class=\"label outer\" style=\"display:none\"></em>\n</div>\n<button class=\"stop icon-close\" style=\"display:none\">\n <span class=\"hidden-visually\">" + + container.escapeExpression(((helper = (helper = helpers.textCancelButton || (depth0 != null ? depth0.textCancelButton : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"textCancelButton","hash":{},"data":data}) : helper))) + + "</span>\n</button>\n"; +},"useData":true}); +templates['operationprogressbarlabel'] = template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { + var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression; + + return "<em class=\"label\">\n <span class=\"desktop\">" + + alias4(((helper = (helper = helpers.textDesktop || (depth0 != null ? depth0.textDesktop : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"textDesktop","hash":{},"data":data}) : helper))) + + "</span>\n <span class=\"mobile\">" + + alias4(((helper = (helper = helpers.textMobile || (depth0 != null ? depth0.textMobile : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"textMobile","hash":{},"data":data}) : helper))) + + "</span>\n</em>\n"; +},"useData":true}); templates['template_addbutton'] = template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression; diff --git a/apps/files/js/templates/operationprogressbar.handlebars b/apps/files/js/templates/operationprogressbar.handlebars new file mode 100644 index 00000000000..c02ac623fab --- /dev/null +++ b/apps/files/js/templates/operationprogressbar.handlebars @@ -0,0 +1,6 @@ +<div id="uploadprogressbar"> + <em class="label outer" style="display:none"></em> +</div> +<button class="stop icon-close" style="display:none"> + <span class="hidden-visually">{{textCancelButton}}</span> +</button> diff --git a/apps/files/js/templates/operationprogressbarlabel.handlebars b/apps/files/js/templates/operationprogressbarlabel.handlebars new file mode 100644 index 00000000000..24ac66994da --- /dev/null +++ b/apps/files/js/templates/operationprogressbarlabel.handlebars @@ -0,0 +1,4 @@ +<em class="label"> + <span class="desktop">{{textDesktop}}</span> + <span class="mobile">{{textMobile}}</span> +</em> |