From 0c58f08207677eae93aad0ead17ef5a91d3dfb63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Fri, 11 Sep 2015 12:56:34 +0200 Subject: [PATCH] FIX: profile picture selector --- .../components/avatar-uploader.js.es6 | 17 +++++--- .../controllers/avatar-selector.js.es6 | 41 +++++++++++------- .../discourse/helpers/application.js.es6 | 15 +++---- .../javascripts/discourse/models/user.js.es6 | 34 +++++---------- .../discourse/routes/preferences.js.es6 | 43 ++++++++++--------- .../templates/modal/avatar_selector.hbs | 21 ++++----- .../discourse/views/avatar-selector.js.es6 | 12 ++++-- app/controllers/user_avatars_controller.rb | 5 ++- app/models/user.rb | 7 +-- app/serializers/user_serializer.rb | 22 ++++++++++ .../lib/avatar-template-test.js.es6 | 16 ------- 11 files changed, 116 insertions(+), 117 deletions(-) delete mode 100644 test/javascripts/lib/avatar-template-test.js.es6 diff --git a/app/assets/javascripts/discourse/components/avatar-uploader.js.es6 b/app/assets/javascripts/discourse/components/avatar-uploader.js.es6 index 8379606460b..539171bc9a1 100644 --- a/app/assets/javascripts/discourse/components/avatar-uploader.js.es6 +++ b/app/assets/javascripts/discourse/components/avatar-uploader.js.es6 @@ -1,3 +1,4 @@ +import computed from "ember-addons/ember-computed-decorators"; import UploadMixin from "discourse/mixins/upload"; export default Em.Component.extend(UploadMixin, { @@ -5,21 +6,23 @@ export default Em.Component.extend(UploadMixin, { tagName: "span", imageIsNotASquare: false, - uploadButtonText: function() { - return this.get("uploading") ? I18n.t("uploading") : I18n.t("user.change_avatar.upload_picture"); - }.property("uploading"), + @computed("uploading") + uploadButtonText(uploading) { + return uploading ? I18n.t("uploading") : I18n.t("user.change_avatar.upload_picture"); + }, uploadDone(upload) { this.setProperties({ imageIsNotASquare: upload.width !== upload.height, uploadedAvatarTemplate: upload.url, - custom_avatar_upload_id: upload.id, + uploadedAvatarId: upload.id, }); this.sendAction("done"); }, - data: function() { - return { user_id: this.get("user_id") }; - }.property("user_id") + @computed("user_id") + data(user_id) { + return { user_id }; + } }); diff --git a/app/assets/javascripts/discourse/controllers/avatar-selector.js.es6 b/app/assets/javascripts/discourse/controllers/avatar-selector.js.es6 index f0bb9fcb3f3..29aae8bf67d 100644 --- a/app/assets/javascripts/discourse/controllers/avatar-selector.js.es6 +++ b/app/assets/javascripts/discourse/controllers/avatar-selector.js.es6 @@ -1,21 +1,29 @@ -import ModalFunctionality from 'discourse/mixins/modal-functionality'; +import computed from "ember-addons/ember-computed-decorators"; +import ModalFunctionality from "discourse/mixins/modal-functionality"; export default Ember.Controller.extend(ModalFunctionality, { - uploadedAvatarTemplate: null, - saveDisabled: Em.computed.alias("uploading"), - hasUploadedAvatar: Em.computed.or('uploadedAvatarTemplate', 'custom_avatar_upload_id'), - - selectedUploadId: function() { - switch (this.get("selected")) { - case "system": return this.get("system_avatar_upload_id"); - case "gravatar": return this.get("gravatar_avatar_upload_id"); - default: return this.get("custom_avatar_upload_id"); + @computed("selected", "system_avatar_upload_id", "gravatar_avatar_upload_id", "custom_avatar_upload_id") + selectedUploadId(selected, system, gravatar, custom) { + switch (selected) { + case "system": return system; + case "gravatar": return gravatar; + default: return custom; } - }.property('selected', 'system_avatar_upload_id', 'gravatar_avatar_upload_id', 'custom_avatar_upload_id'), + }, - allowImageUpload: function() { + @computed("selected", "system_avatar_template", "gravatar_avatar_template", "custom_avatar_template") + selectedAvatarTemplate(selected, system, gravatar, custom) { + switch (selected) { + case "system": return system; + case "gravatar": return gravatar; + default: return custom; + } + }, + + @computed() + allowImageUpload() { return Discourse.Utilities.allowsImages(); - }.property(), + }, actions: { useUploadedAvatar() { this.set("selected", "uploaded"); }, @@ -25,8 +33,11 @@ export default Ember.Controller.extend(ModalFunctionality, { refreshGravatar() { this.set("gravatarRefreshDisabled", true); return Discourse - .ajax("/user_avatar/" + this.get("username") + "/refresh_gravatar.json", { method: 'POST' }) - .then(result => this.set("gravatar_avatar_upload_id", result.upload_id)) + .ajax(`/user_avatar/${this.get("username")}/refresh_gravatar.json`, { method: "POST" }) + .then(result => this.setProperties({ + gravatar_avatar_template: result.gravatar_avatar_template, + gravatar_upload_id: result.gravatar_upload_id, + })) .finally(() => this.set("gravatarRefreshDisabled", false)); } } diff --git a/app/assets/javascripts/discourse/helpers/application.js.es6 b/app/assets/javascripts/discourse/helpers/application.js.es6 index 5c72c6fc8bd..2f4ea292c81 100644 --- a/app/assets/javascripts/discourse/helpers/application.js.es6 +++ b/app/assets/javascripts/discourse/helpers/application.js.es6 @@ -3,32 +3,27 @@ import { longDate, autoUpdatingRelativeAge, number } from 'discourse/lib/formatt const safe = Handlebars.SafeString; -Em.Handlebars.helper('bound-avatar', function(user, size) { +Em.Handlebars.helper('bound-avatar', (user, size) => { if (Em.isEmpty(user)) { return new safe("
"); } const avatar = Em.get(user, 'avatar_template'); - return new safe(Discourse.Utilities.avatarImg({ size: size, avatarTemplate: avatar })); }, 'username', 'avatar_template'); /* * Used when we only have a template */ -Em.Handlebars.helper('bound-avatar-template', function(at, size) { +Em.Handlebars.helper('bound-avatar-template', (at, size) => { return new safe(Discourse.Utilities.avatarImg({ size: size, avatarTemplate: at })); }); -registerUnbound('raw-date', function(dt) { - return longDate(new Date(dt)); -}); +registerUnbound('raw-date', dt => longDate(new Date(dt))); -registerUnbound('age-with-tooltip', function(dt) { - return new safe(autoUpdatingRelativeAge(new Date(dt), {title: true})); -}); +registerUnbound('age-with-tooltip', dt => new safe(autoUpdatingRelativeAge(new Date(dt), {title: true}))); -registerUnbound('number', function(orig, params) { +registerUnbound('number', (orig, params) => { orig = parseInt(orig, 10); if (isNaN(orig)) { orig = 0; } diff --git a/app/assets/javascripts/discourse/models/user.js.es6 b/app/assets/javascripts/discourse/models/user.js.es6 index 4fab93f2de8..48b84011b48 100644 --- a/app/assets/javascripts/discourse/models/user.js.es6 +++ b/app/assets/javascripts/discourse/models/user.js.es6 @@ -256,47 +256,33 @@ const User = RestModel.extend({ }); }, - /* - Change avatar selection - */ - pickAvatar(uploadId) { + pickAvatar(upload_id, avatar_template) { return Discourse.ajax(`/users/${this.get("username_lower")}/preferences/avatar/pick`, { type: 'PUT', - data: { upload_id: uploadId } - }).then(() => this.set('uploaded_avatar_id', uploadId)); + data: { upload_id } + }).then(() => this.setProperties({ + avatar_template, + uploaded_avatar_id: upload_id + })); }, - /** - Determines whether the current user is allowed to upload a file. - - @method isAllowedToUploadAFile - @param {String} type The type of the upload (image, attachment) - @returns true if the current user is allowed to upload a file - **/ isAllowedToUploadAFile(type) { return this.get('staff') || this.get('trust_level') > 0 || Discourse.SiteSettings['newuser_max_' + type + 's'] > 0; }, - /** - Invite a user to the site - - @method createInvite - @param {String} email The email address of the user to invite to the site - @returns {Promise} the result of the server call - **/ - createInvite(email, groupNames) { + createInvite(email, group_names) { return Discourse.ajax('/invites', { type: 'POST', - data: {email: email, group_names: groupNames} + data: { email, group_names } }); }, - generateInviteLink(email, groupNames, topicId) { + generateInviteLink(email, group_names, topic_id) { return Discourse.ajax('/invites/link', { type: 'POST', - data: {email: email, group_names: groupNames, topic_id: topicId} + data: { email, group_names, topic_id } }); }, diff --git a/app/assets/javascripts/discourse/routes/preferences.js.es6 b/app/assets/javascripts/discourse/routes/preferences.js.es6 index d748689f40f..e1f9d597da7 100644 --- a/app/assets/javascripts/discourse/routes/preferences.js.es6 +++ b/app/assets/javascripts/discourse/routes/preferences.js.es6 @@ -18,50 +18,51 @@ export default RestrictedUserRoute.extend({ showModal('avatar-selector'); // all the properties needed for displaying the avatar selector modal - const controller = this.controllerFor('avatar-selector'), - props = this.modelFor('user').getProperties( + const props = this.modelFor('user').getProperties( 'id', 'email', 'username', - 'uploaded_avatar_id', + 'avatar_template', + 'system_avatar_template', + 'gravatar_avatar_template', + 'custom_avatar_template', 'system_avatar_upload_id', 'gravatar_avatar_upload_id', 'custom_avatar_upload_id' ); - switch (props.uploaded_avatar_id) { - case props.system_avatar_upload_id: + switch (props.avatar_template) { + case props.system_avatar_template: props.selected = "system"; break; - case props.gravatar_avatar_upload_id: + case props.gravatar_avatar_template: props.selected = "gravatar"; break; default: props.selected = "uploaded"; } - controller.setProperties(props); + this.controllerFor('avatar-selector').setProperties(props); }, saveAvatarSelection() { const user = this.modelFor('user'), - avatarSelector = this.controllerFor('avatar-selector'); + controller = this.controllerFor('avatar-selector'), + selectedUploadId = controller.get("selectedUploadId"), + selectedAvatarTemplate = controller.get("selectedAvatarTemplate"); - // sends the information to the server if it has changed - if (avatarSelector.get('selectedUploadId') !== user.get('uploaded_avatar_id')) { - user.pickAvatar(avatarSelector.get('selectedUploadId')) - .then(() => { - user.setProperties(avatarSelector.getProperties( - 'system_avatar_upload_id', - 'gravatar_avatar_upload_id', - 'custom_avatar_upload_id' - )); - bootbox.alert(I18n.t("user.change_avatar.cache_notice")); - }); - } + user.pickAvatar(selectedUploadId, selectedAvatarTemplate) + .then(() => { + user.setProperties(controller.getProperties( + 'system_avatar_template', + 'gravatar_avatar_template', + 'custom_avatar_template' + )); + bootbox.alert(I18n.t("user.change_avatar.cache_notice")); + }); // saves the data back - avatarSelector.send('closeModal'); + controller.send('closeModal'); }, } diff --git a/app/assets/javascripts/discourse/templates/modal/avatar_selector.hbs b/app/assets/javascripts/discourse/templates/modal/avatar_selector.hbs index 12f0309d6c8..e7d070d814a 100644 --- a/app/assets/javascripts/discourse/templates/modal/avatar_selector.hbs +++ b/app/assets/javascripts/discourse/templates/modal/avatar_selector.hbs @@ -2,32 +2,27 @@
- +
- + {{d-button action="refreshGravatar" title="user.change_avatar.refresh_gravatar_title" disabled=gravatarRefreshDisabled icon="refresh"}}
{{#if allowImageUpload}}
- {{avatar-uploader username=username - user_id=id - uploadedAvatarTemplate=uploadedAvatarTemplate - custom_avatar_upload_id=custom_avatar_upload_id + {{avatar-uploader user_id=id + uploadedAvatarTemplate=custom_avatar_template + uploadedAvatarId=custom_avatar_upload_id uploading=uploading done="useUploadedAvatar"}}
@@ -36,6 +31,6 @@
diff --git a/app/assets/javascripts/discourse/views/avatar-selector.js.es6 b/app/assets/javascripts/discourse/views/avatar-selector.js.es6 index 15b8541ef36..6fcc5c9bc49 100644 --- a/app/assets/javascripts/discourse/views/avatar-selector.js.es6 +++ b/app/assets/javascripts/discourse/views/avatar-selector.js.es6 @@ -1,3 +1,4 @@ +import { on, observes } from "ember-addons/ember-computed-decorators"; import ModalBodyView from "discourse/views/modal-body"; export default ModalBodyView.extend({ @@ -6,11 +7,14 @@ export default ModalBodyView.extend({ title: I18n.t('user.change_avatar.title'), // *HACK* used to select the proper radio button, because {{action}} stops the default behavior - selectedChanged: function() { + @on("didInsertElement") + @observes("controller.selected") + selectedChanged() { Em.run.next(() => $('input:radio[name="avatar"]').val([this.get('controller.selected')])); - }.observes('controller.selected').on("didInsertElement"), + }, - _focusSelectedButton: function() { + @on("didInsertElement") + _focusSelectedButton() { Em.run.next(() => $('input:radio[value="' + this.get('controller.selected') + '"]').focus()); - }.on("didInsertElement") + } }); diff --git a/app/controllers/user_avatars_controller.rb b/app/controllers/user_avatars_controller.rb index ed8f31e4af0..26664810702 100644 --- a/app/controllers/user_avatars_controller.rb +++ b/app/controllers/user_avatars_controller.rb @@ -13,7 +13,10 @@ class UserAvatarsController < ApplicationController user.create_user_avatar(user_id: user.id) unless user.user_avatar user.user_avatar.update_gravatar! - render json: { upload_id: user.user_avatar.gravatar_upload_id } + render json: { + gravatar_upload_id: user.user_avatar.gravatar_upload_id, + gravatar_avatar_template: User.avatar_template(user.username, user.user_avatar.gravatar_upload_id) + } else raise Discourse::NotFound end diff --git a/app/models/user.rb b/app/models/user.rb index 4c8e1d9aad5..b00ac21b3a2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -471,9 +471,8 @@ class User < ActiveRecord::Base def self.system_avatar_template(username) # TODO it may be worth caching this in a distributed cache, should be benched if SiteSetting.external_system_avatars_enabled - color = letter_avatar_color(username) url = SiteSetting.external_system_avatars_url.dup - url.gsub! "{color}", color + url.gsub! "{color}", letter_avatar_color(username) url.gsub! "{username}", username url.gsub! "{first_letter}", username[0].downcase url @@ -482,10 +481,6 @@ class User < ActiveRecord::Base end end - def letter_avatar_color - self.class.letter_avatar_color(username) - end - def self.letter_avatar_color(username) username = username || "" color = LetterAvatar::COLORS[Digest::MD5.hexdigest(username)[0...15].to_i(16) % LetterAvatar::COLORS.length] diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index 2d216a51502..334b20e2d24 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -93,8 +93,12 @@ class UserSerializer < BasicUserSerializer :watched_category_ids, :private_messages_stats, :disable_jump_reply, + :system_avatar_upload_id, + :system_avatar_template, :gravatar_avatar_upload_id, + :gravatar_avatar_template, :custom_avatar_upload_id, + :custom_avatar_template, :has_title_badges, :card_image_badge, :card_image_badge_id, @@ -278,14 +282,32 @@ class UserSerializer < BasicUserSerializer UserAction.private_messages_stats(object.id, scope) end + def system_avatar_upload_id + # should be left blank + end + + def system_avatar_template + User.system_avatar_template(object.username) + end + def gravatar_avatar_upload_id object.user_avatar.try(:gravatar_upload_id) end + def gravatar_avatar_template + return unless gravatar_upload_id = object.user_avatar.try(:gravatar_upload_id) + User.avatar_template(object.username, gravatar_upload_id) + end + def custom_avatar_upload_id object.user_avatar.try(:custom_upload_id) end + def custom_avatar_template + return unless custom_upload_id = object.user_avatar.try(:custom_upload_id) + User.avatar_template(object.username, custom_upload_id) + end + def has_title_badges object.badges.where(allow_title: true).count > 0 end diff --git a/test/javascripts/lib/avatar-template-test.js.es6 b/test/javascripts/lib/avatar-template-test.js.es6 deleted file mode 100644 index c2f004c8da2..00000000000 --- a/test/javascripts/lib/avatar-template-test.js.es6 +++ /dev/null @@ -1,16 +0,0 @@ -import avatarTemplate from 'discourse/lib/avatar-template'; - -module('lib:avatar-template'); - -test("avatarTemplate", function(){ - var oldCDN = Discourse.CDN; - var oldBase = Discourse.BaseUrl; - Discourse.BaseUrl = "frogs.com"; - - equal(avatarTemplate("sam", 1), "/user_avatar/frogs.com/sam/{size}/1.png"); - Discourse.CDN = "http://awesome.cdn.com"; - equal(avatarTemplate("sam", 1), "http://awesome.cdn.com/user_avatar/frogs.com/sam/{size}/1.png"); - Discourse.CDN = oldCDN; - Discourse.BaseUrl = oldBase; -}); -