X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/fd20255387eccdcc9bf05f61131dca4835f1a57d..bef091f69353d5a1ec7ef6c4e84f81756023596b:/apps/workbench/app/assets/javascripts/upload_to_collection.js diff --git a/apps/workbench/app/assets/javascripts/upload_to_collection.js b/apps/workbench/app/assets/javascripts/upload_to_collection.js index 67c1b98f6a..d66be63853 100644 --- a/apps/workbench/app/assets/javascripts/upload_to_collection.js +++ b/apps/workbench/app/assets/javascripts/upload_to_collection.js @@ -1,3 +1,7 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + var app = angular.module('Workbench', ['Arvados']); app.controller('UploadToCollection', UploadToCollection); app.directive('arvUuid', arvUuid); @@ -23,13 +27,18 @@ function UploadToCollection($scope, $filter, $q, $timeout, // Angular binding doesn't work its usual magic for file // inputs, so we need to $scope.$apply() this update. $scope.$apply(function(){ - var i; - var insertAt; - for (insertAt=0; (insertAt<$scope.uploadQueue.length && - $scope.uploadQueue[insertAt].state !== 'Done'); - insertAt++); + var i, nItemsTodo; + // Add these new files after the items already waiting + // in the queue -- but before the items that are + // 'Done' and have therefore been pushed to the + // bottom. + for (nItemsTodo = 0; + (nItemsTodo < $scope.uploadQueue.length && + $scope.uploadQueue[nItemsTodo].state !== 'Done'); ) { + nItemsTodo++; + } for (i=0; i _startByte) { kBps = (bytesDone - _startByte) / (Date.now() - _startTime); @@ -290,21 +319,28 @@ function UploadToCollection($scope, $filter, $q, $timeout, new Date( Date.now() + (that.file.size - bytesDone) / kBps), 'shortTime') - } else { - that.statistics += ', finished ' + - $filter('date')(Date.now(), 'shortTime'); - _finishTime = Date.now(); } } else { that.statistics = that.state; } - _deferred.notify(); + if (that.state === 'Uploaded') { + // 'Uploaded' gets reported as 'finished', which is a + // little misleading because the collection hasn't + // been updated yet. But FileUploader's portion of the + // work (and the time when it makes sense to show + // speed and ETA) is finished. + that.statistics += ', finished ' + + $filter('date')(Date.now(), 'shortTime'); + _finishTime = Date.now(); + } + if (_deferred) + _deferred.notify(); } } function QueueUploader() { $.extend(this, { - state: 'Idle', + state: 'Idle', // Idle, Running, Stopped, Failed stateReason: null, statusSuccess: null, go: go, @@ -312,9 +348,11 @@ function UploadToCollection($scope, $filter, $q, $timeout, }); //////////////////////////////// var that = this; - var _deferred; + var _deferred; // the one we promise to go()'s caller + var _deferredAppend; // tracks current appendToCollection function go() { - if (that.state === 'Running') return _deferred.promise; + if (_deferred) return _deferred.promise(); + if (_deferredAppend) return _deferredAppend.promise(); _deferred = $.Deferred(); that.state = 'Running'; ArvadosClient.apiPromise( @@ -322,11 +360,16 @@ function UploadToCollection($scope, $filter, $q, $timeout, {filters: [['service_type','=','proxy']]}). then(doQueueWithProxy); onQueueProgress(); - return _deferred.promise(); + return _deferred.promise().always(function() { _deferred = null; }); } function stop() { + that.state = 'Stopped'; + if (_deferred) { + _deferred.reject({}); + } for (var i=0; i<$scope.uploadQueue.length; i++) $scope.uploadQueue[i].stop(); + onQueueProgress(); } function doQueueWithProxy(data) { keepProxy = data.items[0]; @@ -340,25 +383,35 @@ function UploadToCollection($scope, $filter, $q, $timeout, return doQueueWork(); } function doQueueWork() { - var i; - that.state = 'Running'; - that.stateReason = null; - // Push the done things to the bottom of the queue. - for (i=0; (i<$scope.uploadQueue.length && - $scope.uploadQueue[i].state === 'Done'); i++); - if (i>0) - $scope.uploadQueue.push.apply($scope.uploadQueue, $scope.uploadQueue.splice(0, i)); - // If anything is not-done, do it. + // If anything is not Done, do it. if ($scope.uploadQueue.length > 0 && $scope.uploadQueue[0].state !== 'Done') { - return $scope.uploadQueue[0].go(). - then(appendToCollection, null, onQueueProgress). - then(doQueueWork, onQueueReject); + if (_deferred) { + that.stateReason = null; + return $scope.uploadQueue[0].go(). + then(appendToCollection, null, onQueueProgress). + then(doQueueWork, onQueueReject); + } else { + // Queue work has been stopped. Just update the + // view. + onQueueProgress(); + return; + } } - // If everything is done, resolve the promise and clean up. - return onQueueResolve(); + // If everything is Done, resolve the promise and clean + // up. Note this can happen even after the _deferred + // promise has been rejected: specifically, when stop() is + // called too late to prevent completion of the last + // upload. In that case we want to update state to "Idle", + // rather than leave it at "Stopped". + onQueueResolve(); } function onQueueReject(reason) { + if (!_deferred) { + // Outcome has already been decided (by stop()). + return; + } + that.state = 'Failed'; that.stateReason = ( (reason.textStatus || 'Error') + @@ -366,7 +419,7 @@ function UploadToCollection($scope, $filter, $q, $timeout, ? (' (from ' + reason.xhr.options.url + ')') : '') + ': ' + - (reason.err || '')); + (reason.err || defaultErrorMessage)); if (reason.xhr && reason.xhr.responseText) that.stateReason += ' -- ' + reason.xhr.responseText; _deferred.reject(reason); @@ -375,7 +428,8 @@ function UploadToCollection($scope, $filter, $q, $timeout, function onQueueResolve() { that.state = 'Idle'; that.stateReason = 'Done!'; - _deferred.resolve(); + if (_deferred) + _deferred.resolve(); onQueueProgress(); } function onQueueProgress() { @@ -383,39 +437,58 @@ function UploadToCollection($scope, $filter, $q, $timeout, $timeout(function(){$scope.$apply();}); } function appendToCollection(uploads) { - var deferred = $q.defer(); - return ArvadosClient.apiPromise( + _deferredAppend = $.Deferred(); + ArvadosClient.apiPromise( 'collections', 'get', { uuid: $scope.uuid }). then(function(collection) { var manifestText = ''; - var upload, i; - for (i=0; i= 0) { + $scope.uploadQueue[i].state = 'Done'; + $scope.uploadQueue.push.apply( + $scope.uploadQueue, + $scope.uploadQueue.splice(i, 1)); + --i; + --qLen; + } } + }). + then(_deferredAppend.resolve, + _deferredAppend.reject); + return _deferredAppend.promise(). + always(function() { + _deferredAppend = null; }); - return deferred.promise.then(doQueueWork); } } }