From 4088fba4f219aeef3d329ad26c90f16253e8b914 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 26 Jun 2014 11:43:44 -0400 Subject: [PATCH] REFACTOR: Convert profile background uploader to be an ember component --- .../components/image-uploader.js.es6 | 61 +++++++++++++++++++ .../discourse/controllers/preferences.js.es6 | 7 +++ .../javascripts/discourse/models/user.js | 2 - .../discourse/routes/preferences_routes.js | 6 -- .../components/image-uploader.js.handlebars | 10 +++ .../modal/edit-category.js.handlebars | 1 + .../templates/user/preferences.js.handlebars | 16 ++--- .../views/modal/avatar_selector_view.js | 2 +- .../discourse/views/user/preferences_view.js | 53 +--------------- app/assets/stylesheets/desktop/upload.scss | 11 ++++ app/assets/stylesheets/desktop/user.scss | 13 ---- app/assets/stylesheets/mobile/upload.scss | 7 +++ app/assets/stylesheets/mobile/user.scss | 7 --- app/controllers/users_controller.rb | 6 +- config/locales/client.en.yml | 1 + spec/controllers/users_controller_spec.rb | 22 +++---- 16 files changed, 118 insertions(+), 107 deletions(-) create mode 100644 app/assets/javascripts/discourse/components/image-uploader.js.es6 create mode 100644 app/assets/javascripts/discourse/templates/components/image-uploader.js.handlebars diff --git a/app/assets/javascripts/discourse/components/image-uploader.js.es6 b/app/assets/javascripts/discourse/components/image-uploader.js.es6 new file mode 100644 index 000000000..5db2fd235 --- /dev/null +++ b/app/assets/javascripts/discourse/components/image-uploader.js.es6 @@ -0,0 +1,61 @@ +export default Em.Component.extend({ + uploading: false, + uploadProgress: 0, + + backgroundStyle: function() { + var imageUrl = this.get('imageUrl'); + if (Em.isNone(imageUrl)) { return; } + + return "background-image: url(" + imageUrl + ")"; + }.property('imageUrl'), + + _initializeUploader: function() { + var $upload = this.$('input[type=file]'), // note: we can't cache this as fileupload replaces the input after upload + self = this; + + $upload.fileupload({ + url: this.get('uploadUrl'), + dataType: "json", + fileInput: $upload, + formData: { image_type: this.get('type') } + }); + + $upload.on('fileuploadsubmit', function (e, data) { + var result = Discourse.Utilities.validateUploadedFiles(data.files, true); + self.setProperties({ uploadProgress: 0, uploading: result }); + return result; + }); + $upload.on("fileuploadprogressall", function(e, data) { + var progress = parseInt(data.loaded / data.total * 100, 10); + self.set("uploadProgress", progress); + }); + $upload.on("fileuploaddone", function(e, data) { + if(data.result.url) { + self.set('imageUrl', data.result.url); + } else { + bootbox.alert(I18n.t('post.errors.upload')); + } + }); + $upload.on("fileuploadfail", function(e, data) { + Discourse.Utilities.displayErrorForUpload(data); + }); + $upload.on("fileuploadalways", function() { + self.setProperties({ uploading: false, uploadProgress: 0}); + }); + }.on('didInsertElement'), + + _destroyUploader: function() { + this.$('input[type=file]').fileupload('destroy'); + }.on('willDestroyElement'), + + actions: { + selectFile: function() { + this.$('input[type=file]').click(); + }, + + trash: function() { + this.set('imageUrl', null); + this.sendAction('clear'); + } + } +}); diff --git a/app/assets/javascripts/discourse/controllers/preferences.js.es6 b/app/assets/javascripts/discourse/controllers/preferences.js.es6 index b7c8ee462..cde0cadc5 100644 --- a/app/assets/javascripts/discourse/controllers/preferences.js.es6 +++ b/app/assets/javascripts/discourse/controllers/preferences.js.es6 @@ -66,7 +66,14 @@ export default Discourse.ObjectController.extend({ return this.get('saving') ? I18n.t('saving') : I18n.t('save'); }.property('saving'), + imageUploadUrl: Discourse.computed.url('username', '/users/%@/preferences/user_image'), + actions: { + + clearProfileBackground: function() { + this.get('model').clearProfileBackground(); + }, + save: function() { var self = this; this.setProperties({ saving: true, saved: false }); diff --git a/app/assets/javascripts/discourse/models/user.js b/app/assets/javascripts/discourse/models/user.js index d25c44a2e..bc01551b7 100644 --- a/app/assets/javascripts/discourse/models/user.js +++ b/app/assets/javascripts/discourse/models/user.js @@ -354,8 +354,6 @@ Discourse.User = Discourse.Model.extend({ return Discourse.ajax("/users/" + this.get("username_lower") + "/preferences/profile_background/clear", { type: 'PUT', data: { } - }).then(function() { - user.set('profile_background', null); }); }, diff --git a/app/assets/javascripts/discourse/routes/preferences_routes.js b/app/assets/javascripts/discourse/routes/preferences_routes.js index 2c68967e9..d38b7aa2d 100644 --- a/app/assets/javascripts/discourse/routes/preferences_routes.js +++ b/app/assets/javascripts/discourse/routes/preferences_routes.js @@ -63,12 +63,6 @@ Discourse.PreferencesRoute = Discourse.RestrictedUserRoute.extend({ avatarSelector.send('closeModal'); }, - showProfileBackgroundFileSelector: function() { - $("#profile-background-input").click(); - }, - clearProfileBackground: function() { - this.modelFor('user').clearProfileBackground(); - } } }); diff --git a/app/assets/javascripts/discourse/templates/components/image-uploader.js.handlebars b/app/assets/javascripts/discourse/templates/components/image-uploader.js.handlebars new file mode 100644 index 000000000..9f492495a --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/image-uploader.js.handlebars @@ -0,0 +1,10 @@ + +
+
+ + {{#if backgroundStyle}} + + {{/if}} + {{i18n upload_selector.uploading}} {{uploadProgress}}% +
+
diff --git a/app/assets/javascripts/discourse/templates/modal/edit-category.js.handlebars b/app/assets/javascripts/discourse/templates/modal/edit-category.js.handlebars index e40e61f55..9be16aa9c 100644 --- a/app/assets/javascripts/discourse/templates/modal/edit-category.js.handlebars +++ b/app/assets/javascripts/discourse/templates/modal/edit-category.js.handlebars @@ -4,6 +4,7 @@ {{#unless isUncategorizedCategory}} {{edit-category-tab selectedTab=selectedTab tab="security"}} {{edit-category-tab selectedTab=selectedTab tab="settings"}} + {{edit-category-tab selectedTab=selectedTab tab="images"}} {{/unless}} diff --git a/app/assets/javascripts/discourse/templates/user/preferences.js.handlebars b/app/assets/javascripts/discourse/templates/user/preferences.js.handlebars index ffc7d62c4..87122f1ff 100644 --- a/app/assets/javascripts/discourse/templates/user/preferences.js.handlebars +++ b/app/assets/javascripts/discourse/templates/user/preferences.js.handlebars @@ -92,18 +92,10 @@
- -
-
- - {{#if profileBackground}} - - {{/if}} - {{#if view.uploading}} - {{i18n upload_selector.uploading}} {{view.uploadProgress}}% - {{/if}} -
-
+ {{image-uploader uploadUrl=imageUploadUrl + imageUrl=profile_background + type="profile_background" + clear="clearProfileBackground"}}
{{/if}} diff --git a/app/assets/javascripts/discourse/views/modal/avatar_selector_view.js b/app/assets/javascripts/discourse/views/modal/avatar_selector_view.js index 01322d927..25dcb912c 100644 --- a/app/assets/javascripts/discourse/views/modal/avatar_selector_view.js +++ b/app/assets/javascripts/discourse/views/modal/avatar_selector_view.js @@ -37,7 +37,7 @@ Discourse.AvatarSelectorView = Discourse.ModalBodyView.extend({ url: Discourse.getURL("/users/" + this.get("controller.username") + "/preferences/user_image"), dataType: "json", fileInput: $upload, - formData: { user_image_type: "avatar" } + formData: { image_type: "avatar" } }); // when a file has been selected diff --git a/app/assets/javascripts/discourse/views/user/preferences_view.js b/app/assets/javascripts/discourse/views/user/preferences_view.js index d0728d46f..de34a7cbf 100644 --- a/app/assets/javascripts/discourse/views/user/preferences_view.js +++ b/app/assets/javascripts/discourse/views/user/preferences_view.js @@ -1,55 +1,4 @@ -/** - This view handles rendering of a user's preferences - - @class PreferencesView - @extends Discourse.View - @namespace Discourse - @module Discourse -**/ Discourse.PreferencesView = Discourse.View.extend({ templateName: 'user/preferences', - classNames: ['user-preferences'], - - uploading: false, - uploadProgress: 0, - - didInsertElement: function() { - var self = this; - var $upload = $("#profile-background-input"); - - this._super(); - - $upload.fileupload({ - url: Discourse.getURL("/users/" + this.get('controller.model.username') + "/preferences/user_image"), - dataType: "json", - fileInput: $upload, - formData: { user_image_type: "profile_background" } - }); - - $upload.on('fileuploadsubmit', function (e, data) { - var result = Discourse.Utilities.validateUploadedFiles(data.files, true); - self.setProperties({ uploadProgress: 0, uploading: result }); - return result; - }); - $upload.on("fileuploadprogressall", function(e, data) { - var progress = parseInt(data.loaded / data.total * 100, 10); - self.set("uploadProgress", progress); - }); - $upload.on("fileuploaddone", function(e, data) { - if(data.result.url) { - self.set("controller.model.profile_background", data.result.url); - } else { - bootbox.alert(I18n.t('post.errors.upload')); - } - }); - $upload.on("fileuploadfail", function(e, data) { - Discourse.Utilities.displayErrorForUpload(data); - }); - $upload.on("fileuploadalways", function() { - self.setProperties({ uploading: false, uploadProgress: 0}); - }); - }, - willDestroyElement: function() { - $("#profile-background-input").fileupload("destroy"); - } + classNames: ['user-preferences'] }); diff --git a/app/assets/stylesheets/desktop/upload.scss b/app/assets/stylesheets/desktop/upload.scss index a77838669..5682dae85 100644 --- a/app/assets/stylesheets/desktop/upload.scss +++ b/app/assets/stylesheets/desktop/upload.scss @@ -29,3 +29,14 @@ height: 20px; } } + +.uploaded-image-preview { + height: 270px; + background-position: center center; + background-size: cover; + background-color: $primary; +} + +.image-upload-controls { + padding: 10px; +} diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss index 77e18889a..4ff58176e 100644 --- a/app/assets/stylesheets/desktop/user.scss +++ b/app/assets/stylesheets/desktop/user.scss @@ -43,19 +43,6 @@ display: none; } - #profile-background-preview { - height: 270px; - - background-position: center center; - background-size: cover; - - background-color: $primary; - } - - #profile-background-controls { - padding: 10px; - } - .static { color: $primary; display: inline-block; diff --git a/app/assets/stylesheets/mobile/upload.scss b/app/assets/stylesheets/mobile/upload.scss index e2dfd7ab9..94270eec5 100644 --- a/app/assets/stylesheets/mobile/upload.scss +++ b/app/assets/stylesheets/mobile/upload.scss @@ -10,3 +10,10 @@ color: scale-color($primary, $lightness: 50%); } } + +.uploaded-image-preview { + height: 150px; + background-position: center center; + background-size: cover; + background-color: $primary; +} diff --git a/app/assets/stylesheets/mobile/user.scss b/app/assets/stylesheets/mobile/user.scss index 90919d3cc..3eddc52e1 100644 --- a/app/assets/stylesheets/mobile/user.scss +++ b/app/assets/stylesheets/mobile/user.scss @@ -63,13 +63,6 @@ padding: 5px 8px; } - #profile-background-preview { - height: 150px; - background-position: center center; - background-size: cover; - background-color: $primary; - } - .bio-composer #wmd-quote-post { display: none; } diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 0c2bb9a00..420307454 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -333,12 +333,12 @@ class UsersController < ApplicationController # LEGACY: used by the API def upload_avatar - params[:user_image_type] = "avatar" + params[:image_type] = "avatar" upload_user_image end def upload_user_image - params.require(:user_image_type) + params.require(:image_type) user = fetch_user_from_params guardian.ensure_can_edit!(user) @@ -353,7 +353,7 @@ class UsersController < ApplicationController upload = Upload.create_for(user.id, image.file, image.filename, image.filesize) if upload.errors.empty? - case params[:user_image_type] + case params[:image_type] when "avatar" upload_avatar_for(user, upload) when "profile_background" diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 85e585bb3..214826e66 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1158,6 +1158,7 @@ en: change_in_category_topic: "Edit Description" already_used: 'This color has been used by another category' security: "Security" + images: "Images" auto_close_label: "Auto-close topics after:" auto_close_units: "hours" email_in: "Custom incoming email address:" diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index f9cd9e78c..9018b8148 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -1126,7 +1126,7 @@ describe UsersController do ActionDispatch::Http::UploadedFile.new({ filename: 'logo.png', tempfile: logo }) end - it 'raises an error without a user_image_type param' do + it 'raises an error without a image_type param' do lambda { xhr :put, :upload_user_image, username: user.username }.should raise_error(ActionController::ParameterMissing) end @@ -1134,19 +1134,19 @@ describe UsersController do it 'raises an error when you don\'t have permission to upload an user image' do Guardian.any_instance.expects(:can_edit?).with(user).returns(false) - xhr :post, :upload_user_image, username: user.username, user_image_type: "avatar" + xhr :post, :upload_user_image, username: user.username, image_type: "avatar" response.should be_forbidden end it 'rejects large images' do SiteSetting.stubs(:max_image_size_kb).returns(1) - xhr :post, :upload_user_image, username: user.username, file: user_image, user_image_type: "avatar" + xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "avatar" response.status.should eq 422 end it 'rejects unauthorized images' do SiteSetting.stubs(:authorized_extensions).returns(".txt") - xhr :post, :upload_user_image, username: user.username, file: user_image, user_image_type: "avatar" + xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "avatar" response.status.should eq 422 end @@ -1154,7 +1154,7 @@ describe UsersController do upload = Fabricate(:upload) Upload.expects(:create_for).returns(upload) # enqueues the user_image generator job - xhr :post, :upload_user_image, username: user.username, file: user_image, user_image_type: "avatar" + xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "avatar" # returns the url, width and height of the uploaded image json = JSON.parse(response.body) json['url'].should == "/uploads/default/1/1234567890123456.png" @@ -1166,7 +1166,7 @@ describe UsersController do it 'is successful for profile backgrounds' do upload = Fabricate(:upload) Upload.expects(:create_for).returns(upload) - xhr :post, :upload_user_image, username: user.username, file: user_image, user_image_type: "profile_background" + xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "profile_background" user.reload user.user_profile.profile_background.should == "/uploads/default/1/1234567890123456.png" @@ -1191,13 +1191,13 @@ describe UsersController do it 'rejects large images' do SiteSetting.stubs(:max_image_size_kb).returns(1) - xhr :post, :upload_user_image, username: user.username, file: user_image_url, user_image_type: "profile_background" + xhr :post, :upload_user_image, username: user.username, file: user_image_url, image_type: "profile_background" response.status.should eq 422 end it 'rejects unauthorized images' do SiteSetting.stubs(:authorized_extensions).returns(".txt") - xhr :post, :upload_user_image, username: user.username, file: user_image_url, user_image_type: "profile_background" + xhr :post, :upload_user_image, username: user.username, file: user_image_url, image_type: "profile_background" response.status.should eq 422 end @@ -1205,7 +1205,7 @@ describe UsersController do upload = Fabricate(:upload) Upload.expects(:create_for).returns(upload) # enqueues the user_image generator job - xhr :post, :upload_avatar, username: user.username, file: user_image_url, user_image_type: "avatar" + xhr :post, :upload_avatar, username: user.username, file: user_image_url, image_type: "avatar" json = JSON.parse(response.body) json['url'].should == "/uploads/default/1/1234567890123456.png" json['width'].should == 100 @@ -1216,7 +1216,7 @@ describe UsersController do it 'is successful for profile backgrounds' do upload = Fabricate(:upload) Upload.expects(:create_for).returns(upload) - xhr :post, :upload_user_image, username: user.username, file: user_image_url, user_image_type: "profile_background" + xhr :post, :upload_user_image, username: user.username, file: user_image_url, image_type: "profile_background" user.reload user.user_profile.profile_background.should == "/uploads/default/1/1234567890123456.png" @@ -1229,7 +1229,7 @@ describe UsersController do end it "should handle malformed urls" do - xhr :post, :upload_user_image, username: user.username, file: "foobar", user_image_type: "profile_background" + xhr :post, :upload_user_image, username: user.username, file: "foobar", image_type: "profile_background" response.status.should eq 422 end