From 8ece95fd2f3336261150b20891fda26d3972913b Mon Sep 17 00:00:00 2001 From: Jannik Stehle Date: Tue, 7 Jun 2022 16:38:23 +0200 Subject: [PATCH 1/3] Implement an upload progress estimation --- .../web-runtime/src/components/UploadInfo.vue | 89 +++++++++++++++++-- .../web-runtime/src/services/uppyService.ts | 4 + 2 files changed, 84 insertions(+), 9 deletions(-) diff --git a/packages/web-runtime/src/components/UploadInfo.vue b/packages/web-runtime/src/components/UploadInfo.vue index 19fa25120c6..b57574df2d4 100644 --- a/packages/web-runtime/src/components/UploadInfo.vue +++ b/packages/web-runtime/src/components/UploadInfo.vue @@ -18,14 +18,11 @@ -
- +
+
+ + +
{ this.runningUploads -= 1 + + if (!this.runningUploads) { + this.resetProgress() + } }) this.$uppyService.subscribe('progress', (value) => { this.totalProgress = value }) + this.$uppyService.subscribe('upload-progress', ({ file, progress }) => { + if (!Object.keys(this.filesInEstimation).length) { + this.timeStarted = new Date() + this.remainingTime = this.$gettext('Calculating estimated time...') + } + + if (this.filesInEstimation[file.meta.uploadId] === undefined) { + this.filesInEstimation[file.meta.uploadId] = 0 + this.bytesTotal += progress.bytesTotal + } + + const byteIncrease = progress.bytesUploaded - this.filesInEstimation[file.meta.uploadId] + this.bytesUploaded += byteIncrease + this.filesInEstimation[file.meta.uploadId] = progress.bytesUploaded + + const timeElapsed = new Date() - this.timeStarted + const progressPercent = (100 * this.bytesUploaded) / this.bytesTotal + const totalTimeNeededInMilliseconds = (timeElapsed / progressPercent) * 100 + const remainingMilliseconds = totalTimeNeededInMilliseconds - timeElapsed + + this.remainingTime = this.getRemainingTime(remainingMilliseconds) + }) this.$uppyService.subscribe('uploadError', (file) => { if (this.errors.includes(file.meta.uploadId)) { return @@ -301,6 +330,38 @@ export default { }) }, methods: { + getRemainingTime(remainingMilliseconds) { + const roundedRemainingSeconds = Math.round(remainingMilliseconds / 1000) + if (roundedRemainingSeconds >= 30 && roundedRemainingSeconds < 60) { + return this.$gettext('1 minute left') + } + + const roundedRemainingMinutes = Math.round(remainingMilliseconds / 1000 / 60) + if (roundedRemainingMinutes >= 1 && roundedRemainingMinutes < 60) { + return this.$gettextInterpolate( + this.$ngettext( + '%{ roundedRemainingMinutes } minute left', + '%{ roundedRemainingMinutes } minutes left', + roundedRemainingMinutes + ), + { roundedRemainingMinutes } + ) + } + + const roundedRemainingHours = Math.round(remainingMilliseconds / 1000 / 60 / 60) + if (roundedRemainingHours > 0) { + return this.$gettextInterpolate( + this.$ngettext( + '%{ roundedRemainingHours } hour left', + '%{ roundedRemainingHours } hours left', + roundedRemainingHours + ), + { roundedRemainingHours } + ) + } + + return this.$gettext('Few seconds left') + }, handleTopLevelFolderUpdate(file, status) { const topLevelFolder = this.uploads[file.meta.topLevelFolderId] if (status === 'success') { @@ -318,6 +379,7 @@ export default { this.showInfo = false this.infoExpanded = false this.cleanOverlay() + this.resetProgress() }, cleanOverlay() { this.uploadsCancelled = false @@ -327,6 +389,14 @@ export default { this.filesInProgressCount = 0 this.runningUploads = 0 }, + resetProgress() { + this.bytesTotal = 0 + this.bytesUploaded = 0 + this.filesInEstimation = {} + this.estimatedTime = 0 + this.timeStarted = null + this.remainingTime = undefined + }, displayFileAsResource(file) { return !!file.targetRoute }, @@ -402,6 +472,7 @@ export default { this.uploadsCancelled = true this.filesInProgressCount = 0 this.runningUploads = 0 + this.resetProgress() this.$uppyService.cancelAllUploads() const runningUploads = Object.values(this.uploads).filter( (u) => u.status !== 'success' && u.status !== 'error' diff --git a/packages/web-runtime/src/services/uppyService.ts b/packages/web-runtime/src/services/uppyService.ts index 219edf37fa8..535c8196f57 100644 --- a/packages/web-runtime/src/services/uppyService.ts +++ b/packages/web-runtime/src/services/uppyService.ts @@ -17,6 +17,7 @@ type UppyServiceTopics = | 'filesSelected' | 'progress' | 'addedForUpload' + | 'upload-progress' export class UppyService { uppy: Uppy @@ -137,6 +138,9 @@ export class UppyService { this.uppy.on('progress', (value) => { this.publish('progress', value) }) + this.uppy.on('upload-progress', (file, progress) => { + this.publish('upload-progress', { file, progress }) + }) this.uppy.on('cancel-all', () => { this.publish('uploadCancelled') }) From 373c22b0c3385f345547c823bb0164131775661d Mon Sep 17 00:00:00 2001 From: Jannik Stehle Date: Wed, 8 Jun 2022 11:16:48 +0200 Subject: [PATCH 2/3] Minor improvements and layout adjustments --- .../enhancement-upload-time-estimation | 6 ++++ .../web-runtime/src/components/UploadInfo.vue | 35 +++++++++++-------- 2 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 changelog/unreleased/enhancement-upload-time-estimation diff --git a/changelog/unreleased/enhancement-upload-time-estimation b/changelog/unreleased/enhancement-upload-time-estimation new file mode 100644 index 00000000000..732d30b5117 --- /dev/null +++ b/changelog/unreleased/enhancement-upload-time-estimation @@ -0,0 +1,6 @@ +Enhancement: Upload time estimation + +The estimated remaining time for an upload will now be displayed in the upload overlay. + +https://github.com/owncloud/web/pull/7088 +https://github.com/owncloud/web/issues/7066 diff --git a/packages/web-runtime/src/components/UploadInfo.vue b/packages/web-runtime/src/components/UploadInfo.vue index b57574df2d4..dfd255e858b 100644 --- a/packages/web-runtime/src/components/UploadInfo.vue +++ b/packages/web-runtime/src/components/UploadInfo.vue @@ -18,10 +18,15 @@
-
-
+
+
+ -
+
+ +
  • { + if (!this.remainingTime) { + this.remainingTime = this.$gettext('Calculating estimated time...') + } + // No upload in progress -> clean overlay if (!this.runningUploads && this.showInfo) { this.cleanOverlay() @@ -243,9 +254,8 @@ export default { this.totalProgress = value }) this.$uppyService.subscribe('upload-progress', ({ file, progress }) => { - if (!Object.keys(this.filesInEstimation).length) { + if (!this.timeStarted) { this.timeStarted = new Date() - this.remainingTime = this.$gettext('Calculating estimated time...') } if (this.filesInEstimation[file.meta.uploadId] === undefined) { @@ -259,6 +269,9 @@ export default { const timeElapsed = new Date() - this.timeStarted const progressPercent = (100 * this.bytesUploaded) / this.bytesTotal + if (progressPercent === 0) { + return + } const totalTimeNeededInMilliseconds = (timeElapsed / progressPercent) * 100 const remainingMilliseconds = totalTimeNeededInMilliseconds - timeElapsed @@ -331,11 +344,6 @@ export default { }, methods: { getRemainingTime(remainingMilliseconds) { - const roundedRemainingSeconds = Math.round(remainingMilliseconds / 1000) - if (roundedRemainingSeconds >= 30 && roundedRemainingSeconds < 60) { - return this.$gettext('1 minute left') - } - const roundedRemainingMinutes = Math.round(remainingMilliseconds / 1000 / 60) if (roundedRemainingMinutes >= 1 && roundedRemainingMinutes < 60) { return this.$gettextInterpolate( @@ -393,7 +401,6 @@ export default { this.bytesTotal = 0 this.bytesUploaded = 0 this.filesInEstimation = {} - this.estimatedTime = 0 this.timeStarted = null this.remainingTime = undefined }, @@ -462,6 +469,7 @@ export default { togglePauseUploads() { if (this.uploadsPaused) { this.$uppyService.resumeAllUploads() + this.timeStarted = null } else { this.$uppyService.pauseAllUploads() } @@ -524,9 +532,6 @@ export default { overflow-y: auto; } - .upload-info-progress { - width: 50%; - } .upload-info-danger { color: var(--oc-color-swatch-danger-default); } From 5c1842eb77c5412f1b1994c7016f47c8ac89ce1e Mon Sep 17 00:00:00 2001 From: Jannik Stehle Date: Wed, 8 Jun 2022 11:22:43 +0200 Subject: [PATCH 3/3] Add unit tests --- .../tests/unit/components/UploadInfo.spec.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/web-runtime/tests/unit/components/UploadInfo.spec.js b/packages/web-runtime/tests/unit/components/UploadInfo.spec.js index 96a393780a0..409a03235c7 100644 --- a/packages/web-runtime/tests/unit/components/UploadInfo.spec.js +++ b/packages/web-runtime/tests/unit/components/UploadInfo.spec.js @@ -130,6 +130,18 @@ describe('UploadInfo component', () => { expect(uploadedItems.length).toBe(2) }) }) + describe('getRemainingTime method', () => { + it.each([ + { ms: 1000, expected: 'Few seconds left' }, + { ms: 1000 * 60 * 30, expected: '30 minutes left' }, + { ms: 1000 * 60 * 60, expected: '1 hour left' }, + { ms: 1000 * 60 * 60 * 2, expected: '2 hours left' } + ])('should return the proper string', ({ ms, expected }) => { + const wrapper = getShallowWrapper() + const estimatedTime = wrapper.vm.getRemainingTime(ms) + expect(estimatedTime).toBe(expected) + }) + }) }) function createStore() {