Merge pull request #7655 from Flaburgan/6721-fix-upload-progress-bar

Fix multiple photos upload progress bar
This commit is contained in:
Benjamin Neff 2018-07-11 01:36:35 +02:00
commit ea15403d57
No known key found for this signature in database
GPG key ID: 971464C3F1A90194
6 changed files with 84 additions and 84 deletions

View file

@ -6,6 +6,7 @@
* Enable Content-Security-Policy header by default [#7781](https://github.com/diaspora/diaspora/pull/7781) * Enable Content-Security-Policy header by default [#7781](https://github.com/diaspora/diaspora/pull/7781)
## Bug fixes ## Bug fixes
* Fix multiple photos upload progress bar [#7655](https://github.com/diaspora/diaspora/pull/7655)
## Features ## Features
* Add client-side cropping of profile image uploads [#7581](https://github.com/diaspora/diaspora/pull/7581) * Add client-side cropping of profile image uploads [#7581](https://github.com/diaspora/diaspora/pull/7581)

View file

@ -7,94 +7,82 @@
app.views.PublisherUploader = Backbone.View.extend({ app.views.PublisherUploader = Backbone.View.extend({
initialize: function(opts) { initialize: function(opts) {
this.publisher = opts.publisher; this.publisher = opts.publisher;
this.info = $("<div id=\"fileInfo\" />");
this.publisher.wrapperEl.before(this.info);
this.publisher.photozoneEl.on("click", ".x", _.bind(this._removePhoto, this)); this.publisher.photozoneEl.on("click", ".x", _.bind(this._removePhoto, this));
// Initialize the PostPhotoUploader and subscribe its events // Initialize the PostPhotoUploader and subscribe its events
this.uploader = new Diaspora.PostPhotoUploader(this.el); this.uploader = new Diaspora.PostPhotoUploader(this.el);
this.uploader.onUploadStarted = _.bind(this.uploadStartedHandler, this); this.uploader.onUploadStarted = _.bind(this.uploadStartedHandler, this);
this.uploader.onProgress = _.bind(this.progressHandler, this); this.uploader.onProgress = _.bind(this.progressHandler, this);
this.uploader.onUploadCompleted = _.bind(this.uploadCompleteHandler, this); this.uploader.onUploadCompleted = _.bind(this.uploadCompleteHandler, this);
}, },
// add photo placeholders to the publisher to indicate an upload in progress // add photo placeholders to the publisher to indicate an upload in progress
_addPhotoPlaceholder: function() { _addPhotoPlaceholder: function(id) {
var publisher = this.publisher; var publisher = this.publisher;
publisher.setButtonsEnabled(false); publisher.setButtonsEnabled(false);
publisher.wrapperEl.addClass("with_attachments"); publisher.wrapperEl.addClass("with_attachments");
publisher.photozoneEl.append( publisher.photozoneEl.append(
"<li class=\"publisher_photo loading\" style=\"position:relative;\">" + "<li id=\"upload-" + id + "\"class=\"publisher_photo loading\" style=\"position:relative;\">" +
" <div class=\"progress\">" + " <div class=\"progress\">" +
" <div class=\"progress-bar progress-bar-striped active\" role=\"progressbar\"></div>"+ " <div class=\"progress-bar progress-bar-striped active\" role=\"progressbar\"></div>"+
" </div>" + " </div>" +
" <img src=\"\"+Handlebars.helpers.imageUrl(\"ajax-loader2.gif\")+\"\" class=\"ajax-loader\" alt=\"\" />"+ " <div class=\"spinner\"></div>" +
"</li>" "</li>"
); );
}, },
uploadStartedHandler: function() { uploadStartedHandler: function(id) {
this.$el.addClass("loading"); this.$el.addClass("loading");
this._addPhotoPlaceholder(); this._addPhotoPlaceholder(id);
}, },
progressHandler: function(fileName, progress) { progressHandler: function(id, fileName, progress) {
this.info.text(fileName + " " + progress + "%").fadeTo(200, 1);
this.publisher.photozoneEl this.publisher.photozoneEl
.find("li.loading").first().find(".progress-bar") .find("li.loading#upload-" + id + " .progress-bar")
.width(progress + "%"); .width(progress + "%");
}, },
uploadCompleteHandler: function(_id, fileName, response) { uploadCompleteHandler: function(id, fileName, response) {
if (response.success){ if (response.success){
this.info.text(Diaspora.I18n.t("photo_uploader.completed", {file: fileName})).fadeTo(2000, 0); var photoId = response.data.photo.id,
var id = response.data.photo.id,
image = response.data.photo.unprocessed_image; image = response.data.photo.unprocessed_image;
this._addFinishedPhoto(id, image); this._addFinishedPhoto(id, photoId, image);
this.trigger("change"); this.trigger("change");
} else { } else {
this._cancelPhotoUpload(); this._cancelPhotoUpload(id);
this.trigger("change");
this.info.text(Diaspora.I18n.t("photo_uploader.error", {file: fileName}));
this.publisher.wrapperEl.find("#photodropzone_container").first().after( this.publisher.wrapperEl.find("#photodropzone_container").first().after(
"<div id=\"upload_error\">" + "<div id=\"upload_error\">" +
Diaspora.I18n.t("photo_uploader.error", {file: fileName}) + Diaspora.I18n.t("photo_uploader.error", {file: fileName}) +
"</div>" "</div>"
); );
this.trigger("change");
} }
}, },
// replace the first photo placeholder with the finished uploaded image and // replace the first photo placeholder with the finished uploaded image and
// add the id to the publishers form // add the id to the publishers form
_addFinishedPhoto: function(id, image) { _addFinishedPhoto: function(id, photoId, image) {
var publisher = this.publisher; var publisher = this.publisher;
// add form input element // add form input element
publisher.$(".content_creation form").append( publisher.$(".content_creation form").append(
"<input type=\"hidden\", value=\""+id+"\" name=\"photos[]\" />" "<input type=\"hidden\", value=\"" + photoId + "\" name=\"photos[]\" />"
); );
// replace placeholder // replace placeholder
var placeholder = publisher.photozoneEl.find("li.loading").first(); var placeholder = publisher.photozoneEl.find("li.loading#upload-" + id);
placeholder placeholder
.removeClass("loading")
.prepend( .prepend(
"<div class=\"x\"></div>" + "<div class=\"x\"></div>" +
"<div class=\"circle\"></div>" "<div class=\"circle\"></div>" +
) "<img src=\"" + image.url + "\" data-id=\"" + photoId + "\" alt=\"\" class=\"hidden\" />"
.find("img").attr( ).removeClass("loading");
{ placeholder.find("div.progress").remove();
"src": image.thumb_medium.url, placeholder.find("img").on("load", function(ev) {
"data-small": image.thumb_small.url, $(ev.target).removeClass("hidden");
"data-scaled": image.scaled_full.url, placeholder.find(".spinner").remove();
"data-id": id });
}).removeClass("ajax-loader");
placeholder
.find("div.progress").remove();
// no more placeholders? enable buttons // no more placeholders? enable buttons
if (publisher.photozoneEl.find("li.loading").length === 0) { if (publisher.photozoneEl.find("li.loading").length === 0) {
@ -103,12 +91,8 @@ app.views.PublisherUploader = Backbone.View.extend({
} }
}, },
_cancelPhotoUpload: function() { _cancelPhotoUpload: function(id) {
var publisher = this.publisher; this.publisher.photozoneEl.find("li.loading#upload-" + id).remove();
var placeholder = publisher.photozoneEl.find("li.loading").first();
placeholder
.removeClass("loading")
.find("img").remove();
}, },
// remove an already uploaded photo // remove an already uploaded photo
@ -135,9 +119,7 @@ app.views.PublisherUploader = Backbone.View.extend({
}); });
} }
}); });
return false; return false;
} }
}); });
// @license-end // @license-end

View file

@ -66,7 +66,7 @@ Diaspora.PostPhotoUploader = class {
onSubmit: (id, name) => this.onPictureSelected(id, name), onSubmit: (id, name) => this.onPictureSelected(id, name),
onUpload: (id, name) => (this.func(this.onUploadStarted) && this.onUploadStarted(id, name)), onUpload: (id, name) => (this.func(this.onUploadStarted) && this.onUploadStarted(id, name)),
onProgress: (id, fileName, loaded, total) => onProgress: (id, fileName, loaded, total) =>
(this.func(this.onProgress) && this.onProgress(fileName, Math.round(loaded / total * 100))), (this.func(this.onProgress) && this.onProgress(id, fileName, Math.round(loaded / total * 100))),
onComplete: (id, name, json) => (this.func(this.onUploadCompleted) && this.onUploadCompleted(id, name, json)), onComplete: (id, name, json) => (this.func(this.onUploadCompleted) && this.onUploadCompleted(id, name, json)),
onError: (id, name, errorReason) => this.showMessage("error", errorReason) onError: (id, name, errorReason) => this.showMessage("error", errorReason)
} }

View file

@ -119,7 +119,7 @@ class PhotosController < ApplicationController
file_name = params[:qqfile] file_name = params[:qqfile]
# get file content type # get file content type
att_content_type = request.content_type.to_s == "" ? "application/octet-stream" : request.content_type.to_s att_content_type = request.content_type.to_s == "" ? "application/octet-stream" : request.content_type.to_s
# create tempora##l file # create temporal file
file = Tempfile.new(file_name, encoding: "BINARY") file = Tempfile.new(file_name, encoding: "BINARY")
# put data into this file from raw post request # put data into this file from raw post request
file.print request.raw_post.force_encoding("BINARY") file.print request.raw_post.force_encoding("BINARY")

View file

@ -517,13 +517,16 @@ describe("app.views.Publisher", function() {
beforeEach(function() { beforeEach(function() {
jQuery.fx.off = true; jQuery.fx.off = true;
setFixtures( setFixtures(
'<div id="publisher">'+ "<div id=\"publisher\">" +
' <div class="content_creation"><form>'+ " <div class=\"content_creation\"><form>" +
' <div id="publisher-textarea-wrapper"></div>'+ " <div id=\"publisher-textarea-wrapper\">" +
' <div id="photodropzone"></div>'+ " <div id=\"photodropzone_container\">" +
' <input type="submit" />'+ " <ul id=\"photodropzone\"></ul>" +
' </form></div>'+ " </div>" +
'</div>' " </div>" +
" <input type=\"submit\" />" +
" </form></div>" +
"</div>"
); );
}); });
@ -542,25 +545,43 @@ describe("app.views.Publisher", function() {
var uploadView = this.view.viewUploader; var uploadView = this.view.viewUploader;
this.uploader = { this.uploader = {
onProgress: _.bind(uploadView.progressHandler, uploadView), onProgress: _.bind(uploadView.progressHandler, uploadView),
onUpload: _.bind(uploadView.uploadStartedHandler, uploadView), onUploadStarted: _.bind(uploadView.uploadStartedHandler, uploadView),
onComplete: _.bind(uploadView.uploadCompleteHandler, uploadView) onUploadCompleted: _.bind(uploadView.uploadCompleteHandler, uploadView)
}; };
uploadView.uploader = this.uploader; uploadView.uploader = this.uploader;
}); });
context("progress", function() { context("progress", function() {
it("shows progress in percent", function() { beforeEach(function() {
this.uploader.onProgress("test.jpg", 20); this.view.photozoneEl.append(
"<li id=\"upload-0\" class=\"publisher_photo loading\" style=\"position:relative;\">" +
" <div class=\"progress\">" +
" <div class=\"progress-bar progress-bar-striped active\" role=\"progressbar\"></div>" +
" </div>" +
" <div class=\"spinner\"></div>" +
"</li>");
this.view.photozoneEl.append(
"<li id=\"upload-1\" class=\"publisher_photo loading\" style=\"position:relative;\">" +
" <div class=\"progress\">" +
" <div class=\"progress-bar progress-bar-striped active\" role=\"progressbar\"></div>" +
" </div>" +
" <div class=\"spinner\"></div>" +
"</li>");
});
var info = this.view.viewUploader.info; it("shows progress in percent", function() {
expect(info.text()).toContain("test.jpg"); this.uploader.onProgress(0, "test.jpg", 20);
expect(info.text()).toContain("20%"); this.uploader.onProgress(1, "test2.jpg", 25);
var dropzone = $("#photodropzone");
expect(dropzone.find("li.loading#upload-0 .progress-bar").attr("style")).toBe("width: 20%;");
expect(dropzone.find("li.loading#upload-1 .progress-bar").attr("style")).toBe("width: 25%;");
}); });
}); });
context("submitting", function() { context("submitting", function() {
beforeEach(function() { beforeEach(function() {
this.uploader.onUpload(null, "test.jpg"); this.uploader.onUploadStarted(null, "test.jpg");
}); });
it("adds a placeholder", function() { it("adds a placeholder", function() {
@ -575,28 +596,24 @@ describe("app.views.Publisher", function() {
context('successful completion', function() { context('successful completion', function() {
beforeEach(function() { beforeEach(function() {
$('#photodropzone').html('<li class="publisher_photo loading"><img src="" /></li>'); $("#photodropzone").html("<li id='upload-0' class='publisher_photo loading'></li>");
/* eslint-disable camelcase */ /* eslint-disable camelcase */
this.uploader.onComplete(null, 'test.jpg', { this.uploader.onUploadCompleted(0, "test.jpg", {
data: { photo: { data: { photo: {
id: '987', id: '987',
unprocessed_image: { unprocessed_image: {
thumb_small: {url: "test.jpg"}, scaled_full: {url: "/uploads/images/scaled_full_test.jpg"},
thumb_medium: {url: "test.jpg"}, thumb_large: {url: "/uploads/images/thumb_large_test.jpg"},
thumb_large: {url: "test.jpg"}, thumb_medium: {url: "/uploads/images/thumb_medium_test.jpg"},
scaled_full: {url: "test.jpg"} thumb_small: {url: "/uploads/images/thumb_small_test.jpg"},
url: "/uploads/images/test.jpg"
} }
}}, }},
success: true }); success: true });
}); });
/* eslint-enable camelcase */ /* eslint-enable camelcase */
it('shows it in text form', function() {
var info = this.view.viewUploader.info;
expect(info.text()).toBe(Diaspora.I18n.t('photo_uploader.completed', {file: 'test.jpg'}));
});
it('adds a hidden input to the publisher', function() { it('adds a hidden input to the publisher', function() {
var input = this.view.$('input[type="hidden"][value="987"][name="photos[]"]'); var input = this.view.$('input[type="hidden"][value="987"][name="photos[]"]');
expect(input.length).toBe(1); expect(input.length).toBe(1);
@ -606,9 +623,9 @@ describe("app.views.Publisher", function() {
var li = this.view.photozoneEl.find("li"); var li = this.view.photozoneEl.find("li");
var img = li.find('img'); var img = li.find('img');
expect(li.attr('class')).not.toContain('loading'); expect(li).not.toHaveClass("loading");
expect(img.attr('src')).toBe('test.jpg'); expect(img.attr("src")).toBe("/uploads/images/test.jpg");
expect(img.attr('data-id')).toBe('987'); expect(img.attr("data-id")).toBe("987");
}); });
it('re-enables the buttons', function() { it('re-enables the buttons', function() {
@ -618,10 +635,10 @@ describe("app.views.Publisher", function() {
context('unsuccessful completion', function() { context('unsuccessful completion', function() {
beforeEach(function() { beforeEach(function() {
$('#photodropzone').html('<li class="publisher_photo loading"><img src="" /></li>'); $("#photodropzone").append("<li id='upload-0' class='publisher_photo loading'></li>");
/* eslint-disable camelcase */ /* eslint-disable camelcase */
this.uploader.onComplete(null, 'test.jpg', { this.uploader.onUploadCompleted(0, "test.jpg", {
data: { photo: { data: { photo: {
id: '987', id: '987',
unprocessed_image: { unprocessed_image: {
@ -635,8 +652,8 @@ describe("app.views.Publisher", function() {
}); });
/* eslint-enable camelcase */ /* eslint-enable camelcase */
it('shows error message', function() { it('shows error message', function() {
var info = this.view.viewUploader.info; expect($("#photodropzone li").length).toEqual(0);
expect(info.text()).toBe(Diaspora.I18n.t('photo_uploader.error', {file: 'test.jpg'})); expect($("#upload_error").text()).toBe(Diaspora.I18n.t("photo_uploader.error", {file: "test.jpg"}));
}); });
}); });
}); });