From 168e54a17a86c327f51eb5fad446d6e2f41d7711 Mon Sep 17 00:00:00 2001 From: Felix Hammerl Date: Tue, 2 Feb 2016 19:16:25 +0100 Subject: Display progress bar when an upload is in progress Displays a progress bar when an upload is in progress and prohibits uploading multiple attachments in parallel. --- web-ui/app/js/mail_view/ui/attachment_icon.js | 19 ++++- web-ui/app/js/mail_view/ui/attachment_list.js | 73 +++++++++++-------- web-ui/app/js/views/templates.js | 1 + web-ui/app/scss/_compose.scss | 23 +++++- web-ui/app/scss/_read.scss | 1 + web-ui/app/scss/styles.scss | 8 ++- web-ui/app/templates/compose/attachments_list.hbs | 14 ++-- .../test/spec/mail_view/ui/attachment_icon.spec.js | 25 ++++++- .../test/spec/mail_view/ui/attachment_list.spec.js | 82 +++++++++++++--------- 9 files changed, 173 insertions(+), 73 deletions(-) (limited to 'web-ui') diff --git a/web-ui/app/js/mail_view/ui/attachment_icon.js b/web-ui/app/js/mail_view/ui/attachment_icon.js index 478853b0..6b9cb31c 100644 --- a/web-ui/app/js/mail_view/ui/attachment_icon.js +++ b/web-ui/app/js/mail_view/ui/attachment_icon.js @@ -34,12 +34,27 @@ define( this.trigger(document, events.mail.startUploadAttachment); }; + this.uploadInProgress = function (ev, data) { + this.attr.busy = true; + this.$node.addClass('busy'); + }; + + this.uploadFinished = function (ev, data) { + this.attr.busy = false; + this.$node.removeClass('busy'); + }; + this.after('initialize', function () { if (features.isEnabled('attachment')) { this.render(); + this.on(document, events.mail.uploadingAttachment, this.uploadInProgress); + this.on(document, events.mail.uploadedAttachment, this.uploadFinished); } - this.on(this.$node, 'click', this.triggerUploadAttachment); + this.on(this.$node, 'click', function() { + if (!this.attr.busy) { + this.triggerUploadAttachment(); + } + }); }); - }); }); diff --git a/web-ui/app/js/mail_view/ui/attachment_list.js b/web-ui/app/js/mail_view/ui/attachment_list.js index 3def6870..19a48a8d 100644 --- a/web-ui/app/js/mail_view/ui/attachment_list.js +++ b/web-ui/app/js/mail_view/ui/attachment_list.js @@ -29,7 +29,8 @@ define( this.defaultAttrs({ inputFileUpload: '#fileupload', attachmentListItem: '#attachment-list-item', - progressBar: '#progress .progress-bar', + attachmentUploadItem: '#attachment-upload-item', + attachmentUploadItemProgress: '#attachment-upload-item-progress', attachmentBaseUrl: '/attachment', attachments: [], closeIcon: '.close-icon', @@ -74,57 +75,73 @@ define( return element; }; - this.checkAttachmentSize = function(e, data) { - var self = this; - var uploadError = self.select('uploadError'); + this.performPreUploadCheck = function(e, data) { + if (data.originalFiles[0].size > ATTACHMENT_SIZE_LIMIT) { + return false; + } + + return true; + }; + + this.removeUploadError = function() { + var uploadError = this.select('uploadError'); if (uploadError) { uploadError.remove(); } + }; - var uploadErrors = []; + this.showUploadError = function () { + var self = this; - var showUploadFailed = function () { - var html = $(templates.compose.uploadAttachmentFailed()); - html.insertAfter(self.select('attachmentListItem')); + var html = $(templates.compose.uploadAttachmentFailed()); + html.insertAfter(self.select('attachmentListItem')); - self.on(self.select('closeIcon'), 'click', dismissUploadFailed); - self.on(self.select('dismissButton'), 'click', dismissUploadFailed); - self.on(self.select('uploadFileButton'), 'click', uploadAnotherFile); - }; + self.on(self.select('closeIcon'), 'click', dismissUploadFailed); + self.on(self.select('dismissButton'), 'click', dismissUploadFailed); + self.on(self.select('uploadFileButton'), 'click', uploadAnotherFile); - var dismissUploadFailed = function (event) { + function dismissUploadFailed(event) { event.preventDefault(); self.select('uploadError').remove(); - }; + } - var uploadAnotherFile = function (event) { + function uploadAnotherFile(event) { event.preventDefault(); self.trigger(document, events.mail.startUploadAttachment); - }; - - if (data.originalFiles[0].size > ATTACHMENT_SIZE_LIMIT) { - uploadErrors.push('Filesize is too big'); - } - if (uploadErrors.length > 0) { - showUploadFailed(); - } else { - data.submit(); } }; + this.showUploadProgressBar = function() { + this.select('attachmentUploadItem').show(); + }; + + this.hideUploadProgressBar = function() { + this.select('attachmentUploadItem').hide(); + }; + this.addJqueryFileUploadConfig = function() { var self = this; + + self.removeUploadError(); + this.select('inputFileUpload').fileupload({ - add: function(e, data) { self.checkAttachmentSize(e, data); }, + add: function(e, data) { + if (self.performPreUploadCheck(e, data)) { + self.showUploadProgressBar(); + data.submit(); + } else { + self.showUploadError(); + } + }, url: self.attr.attachmentBaseUrl, dataType: 'json', done: function (e, response) { - var data = response.result; - self.trigger(document, events.mail.uploadedAttachment, data); + self.hideUploadProgressBar(); + self.trigger(document, events.mail.uploadedAttachment, response.result); }, progressall: function (e, data) { var progressRate = parseInt(data.loaded / data.total * 100, 10); - self.select('progressBar').css('width', progressRate + '%'); + self.select('attachmentUploadItemProgress').css('width', progressRate + '%'); } }).bind('fileuploadstart', function (e) { self.trigger(document, events.mail.uploadingAttachment); diff --git a/web-ui/app/js/views/templates.js b/web-ui/app/js/views/templates.js index e5a3c435..f9d944b8 100644 --- a/web-ui/app/js/views/templates.js +++ b/web-ui/app/js/views/templates.js @@ -75,6 +75,7 @@ define(['hbs/templates'], function (templates) { Handlebars.registerPartial('tag_inner', Templates.tags.tagInner); Handlebars.registerPartial('recipients', Templates.compose.recipients); Handlebars.registerPartial('attachments_list', Templates.compose.attachmentsList); + Handlebars.registerPartial('attachments_upload', Templates.compose.attachmentsList); Handlebars.registerPartial('attachment_item', Templates.compose.attachmentItem); Handlebars.registerPartial('uploadAttachmentFailed', Templates.compose.uploadAttachmentFailed); diff --git a/web-ui/app/scss/_compose.scss b/web-ui/app/scss/_compose.scss index 91faf0bd..dfde3d19 100644 --- a/web-ui/app/scss/_compose.scss +++ b/web-ui/app/scss/_compose.scss @@ -1,4 +1,4 @@ -// COMPOSE BUTTON +// COMPOSE BUTTON #compose { margin-bottom: 5px; padding-right: 4px; @@ -44,7 +44,7 @@ } span { - padding: 3px; + padding: 3px; &.attachment-size { color: $attachment_size; @@ -127,6 +127,25 @@ margin-top: 0px; } + #attachment-upload-item { + display: none; + + .progress { + width: 0%; + position: absolute; + right: 0; + left: 0; + top: 0; + bottom: 0; + min-height: 100%; + + .progress-bar { + height: 100%; + background-color: rgba($light_blue, 0.3); + } + } + } + .attachmentsAreaWrap { padding: 0; diff --git a/web-ui/app/scss/_read.scss b/web-ui/app/scss/_read.scss index 1b6f8334..4a11ca76 100644 --- a/web-ui/app/scss/_read.scss +++ b/web-ui/app/scss/_read.scss @@ -76,6 +76,7 @@ padding: 10px 0 0; li { + position: relative; display: block; border: 1px solid #D9D9D9; border-radius: 2px; diff --git a/web-ui/app/scss/styles.scss b/web-ui/app/scss/styles.scss index 92cc32f2..a3fd2202 100644 --- a/web-ui/app/scss/styles.scss +++ b/web-ui/app/scss/styles.scss @@ -33,7 +33,7 @@ padding: 10px 16px 10px 18px; background-color: rgba(#3E3A37, 0.9); min-width: 230px; - + &.extra-bottom-space{ bottom: 33px } @@ -895,6 +895,12 @@ div.side-nav-bottom { i.fa-paperclip { font-size: 1.7em; } + + &.busy { + color: lighten($recipients_font_color, 10%); + cursor: progress; + } } + @import "mascot.scss"; diff --git a/web-ui/app/templates/compose/attachments_list.hbs b/web-ui/app/templates/compose/attachments_list.hbs index 936db2c9..639c395a 100644 --- a/web-ui/app/templates/compose/attachments_list.hbs +++ b/web-ui/app/templates/compose/attachments_list.hbs @@ -3,19 +3,23 @@ - -
-
-
    {{#each attachments }} - {{> attachment_item this }} + {{> attachment_item this }} {{/each }}
+
diff --git a/web-ui/test/spec/mail_view/ui/attachment_icon.spec.js b/web-ui/test/spec/mail_view/ui/attachment_icon.spec.js index c60d6f7e..1b2f182e 100644 --- a/web-ui/test/spec/mail_view/ui/attachment_icon.spec.js +++ b/web-ui/test/spec/mail_view/ui/attachment_icon.spec.js @@ -11,11 +11,34 @@ describeComponent('mail_view/ui/attachment_icon', function () { expect(this.$node.html()).toMatch(''); }); - it('should trigger starts of attachment upload process', function () { + it('should be busy', function() { + this.component.uploadInProgress(); + + expect(this.component.attr.busy).toBe(true); + }); + + it('should not be busy', function() { + this.component.uploadFinished(); + + expect(this.component.attr.busy).toBe(false); + }); + + it('should trigger start of attachment upload process', function () { var triggerUploadAttachment = spyOnEvent(document, Pixelated.events.mail.startUploadAttachment); + this.$node.click(); + expect(triggerUploadAttachment).toHaveBeenTriggeredOn(document); }); + it('should not trigger attachment upload when busy', function () { + this.component.uploadInProgress(); + var triggerUploadAttachment = spyOnEvent(document, Pixelated.events.mail.startUploadAttachment); + + this.$node.click(); + + expect(triggerUploadAttachment).not.toHaveBeenTriggeredOn(document); + }); + }); }); diff --git a/web-ui/test/spec/mail_view/ui/attachment_list.spec.js b/web-ui/test/spec/mail_view/ui/attachment_list.spec.js index 20f82704..1b351d36 100644 --- a/web-ui/test/spec/mail_view/ui/attachment_list.spec.js +++ b/web-ui/test/spec/mail_view/ui/attachment_list.spec.js @@ -5,6 +5,7 @@ describeMixin('mail_view/ui/attachment_list', function () { beforeEach(function () { this.setupComponent('
' + '' + + '' + '
'); }); @@ -39,58 +40,71 @@ describeMixin('mail_view/ui/attachment_list', function () { expect(this.component.select('attachmentListItem').html()).toContain('(4.39 Kb'); }); - describe('Upload files -- max file size -- ', function (){ - var ONE_MEGABYTE = 1024*1024; - var submitFile = 'file not submitted', submitted = 'file submitted'; - var mockSubmit = function (){ submitFile = submitted; }; - var largeAttachment = {originalFiles: [{size: ONE_MEGABYTE+1}], submit: mockSubmit}; - var dummyEvent = 'whatever, not used'; + describe('Upload', function() { - it('should show error messages on the dom, when uploading files larger than 1MB', function () { - this.component.checkAttachmentSize(dummyEvent, largeAttachment); + describe('Progress Bar', function () { + it('should show progress bar', function() { + this.component.showUploadProgressBar(); - expect(this.component.select('uploadError').html()).toContain('Upload failed. This file exceeds the 1MB limit.'); - }); - - it('should dismiss upload failed message when clicking close icon', function () { - this.component.checkAttachmentSize(dummyEvent, largeAttachment); + expect(this.component.select('attachmentUploadItem').html()).toContain('Uploading...'); + expect(this.component.select('attachmentUploadItem').css('display')).toEqual('block'); + }); - this.component.select('closeIcon').click(); + it('should hide progress bar', function() { + this.component.hideUploadProgressBar(); - expect(this.component.select('uploadError').html()).toBe(undefined); + expect(this.component.select('attachmentUploadItem').css('display')).toEqual('none'); + }); }); - it('should dismiss upload failed message when clicking dismiss button', function () { - this.component.checkAttachmentSize(dummyEvent, largeAttachment); + describe('Error', function() { + it('should show error message', function () { + this.component.showUploadError(); - this.component.select('dismissButton').click(); + expect(this.component.select('uploadError').html()).toContain('Upload failed. This file exceeds the 1MB limit.'); + }); - expect(this.component.select('uploadError').html()).toBe(undefined); - }); + it('should dismiss upload failed message when clicking close icon', function () { + this.component.showUploadError(); - it('should start file upload when clicking Choose another file button', function () { - this.component.checkAttachmentSize(dummyEvent, largeAttachment); + this.component.select('closeIcon').click(); - var triggerUploadAttachment = spyOnEvent(document, Pixelated.events.mail.startUploadAttachment); + expect(this.component.select('uploadError').html()).toBe(undefined); + }); - this.component.select('uploadFileButton').click(); + it('should dismiss upload failed message when clicking dismiss button', function () { + this.component.showUploadError(); - expect(triggerUploadAttachment).toHaveBeenTriggeredOn(document); - }); + this.component.select('dismissButton').click(); - it('should not upload files larger than 1MB', function () { - spyOn(largeAttachment, 'submit'); + expect(this.component.select('uploadError').html()).toBe(undefined); + }); - this.component.checkAttachmentSize(dummyEvent, largeAttachment); + it('should start file upload when clicking Choose another file button', function () { + this.component.showUploadError(); + var triggerUploadAttachment = spyOnEvent(document, Pixelated.events.mail.startUploadAttachment); - expect(largeAttachment.submit).not.toHaveBeenCalled(); + this.component.select('uploadFileButton').click(); + + expect(triggerUploadAttachment).toHaveBeenTriggeredOn(document); + }); }); - it('should upload files less or equal 1MB', function () { - var smallAttachment = {originalFiles: [{size: ONE_MEGABYTE}], submit: mockSubmit}; - this.component.checkAttachmentSize(dummyEvent, smallAttachment); + describe('File size check', function (){ + var ONE_MEGABYTE = 1024*1024; + var largeAttachment = {originalFiles: [{size: ONE_MEGABYTE+1}]}; + + it('should reject files larger than 1MB', function () { + var uploadAccepted = this.component.performPreUploadCheck(null, largeAttachment); + expect(uploadAccepted).toBe(false); + }); + + it('should accept files less or equal 1MB', function () { + var smallAttachment = {originalFiles: [{size: ONE_MEGABYTE}]}; + var uploadAccepted = this.component.performPreUploadCheck(null, smallAttachment); - expect(submitFile).toEqual(submitted); + expect(uploadAccepted).toBe(true); + }); }); }); }); -- cgit v1.2.3