diff --git a/core/client/app/components/gh-profile-image.js b/core/client/app/components/gh-profile-image.js index d468e83606..9a53faa69f 100644 --- a/core/client/app/components/gh-profile-image.js +++ b/core/client/app/components/gh-profile-image.js @@ -5,36 +5,52 @@ import Ember from 'ember'; * A component to manage a user profile image. By default it just handles picture uploads, * but if passed a bound 'email' property it will render the user's gravatar image * - * Example: {{gh-profile-image email=controllerEmailProperty setImage="controllerActionName"}} + * Example: {{gh-profile-image email=controllerEmailProperty setImage="controllerActionName" debounce=500}} * - * @param {int} size The size of the image to render - * @param {String} email Reference to a bound email object if gravatar image behavior is desired. - * @param {String} setImage The string name of the action on the controller to be called when an image is added. - * @property {Boolean} hasUploadedImage Whether or not the user has uploaded an image (whether or not to show the default image/gravatar image) - * @property {String} defaultImage String containing the background-image css property of the default user profile image - * @property {String} imageBackground String containing the background-image css property with the gravatar url + * @param {int} size The size of the image to render + * @param {String} email Reference to a bound email object if gravatar image behavior is desired. + * @param {String|action} setImage The string name of the action on the controller to be called when an image is added. + * @param {int} debounce Period to wait after changes to email before attempting to load gravatar + * @property {Boolean} hasUploadedImage Whether or not the user has uploaded an image (whether or not to show the default image/gravatar image) + * @property {String} defaultImage String containing the background-image css property of the default user profile image + * @property {String} imageBackground String containing the background-image css property with the gravatar url */ export default Ember.Component.extend({ email: '', size: 90, + debounce: 300, + + validEmail: '', hasUploadedImage: false, fileStorage: true, ghostPaths: Ember.inject.service('ghost-paths'), - hasEmail: Ember.computed.notEmpty('email'), + displayGravatar: Ember.computed.notEmpty('validEmail'), defaultImage: Ember.computed('ghostPaths', function () { - var url = this.get('ghostPaths.url').asset('/shared/img/user-image.png'); - return `background-image: url(${url})`.htmlSafe(); + const url = this.get('ghostPaths.url').asset('/shared/img/user-image.png'); + return Ember.String.htmlSafe(`background-image: url(${url})`); }), - imageBackground: Ember.computed('email', 'size', function () { - var email = this.get('email'), - size = this.get('size'), - url; + trySetValidEmail: function () { + if (!this.get('isDestroyed')) { + const email = this.get('email'); + this.set('validEmail', validator.isEmail(email) ? email : ''); + } + }, + + didReceiveAttrs: function (attrs) { + const timeout = parseInt(attrs.newAttrs.throttle || this.get('debounce')); + Ember.run.debounce(this, 'trySetValidEmail', timeout); + }, + + imageBackground: Ember.computed('validEmail', 'size', function () { + const email = this.get('validEmail'), + size = this.get('size'); + if (email) { - url = 'http://www.gravatar.com/avatar/' + md5(email) + '?s=' + size + '&d=blank'; - return `background-image: url(${url})`.htmlSafe(); + let url = `http://www.gravatar.com/avatar/${md5(email)}?s=${size}&d=blank`; + return Ember.String.htmlSafe(`background-image: url(${url})`); } }), @@ -42,6 +58,9 @@ export default Ember.Component.extend({ var size = this.get('size'), uploadElement = this.$('.js-file-input'); + // Fire this immediately in case we're initialized with a valid email + this.trySetValidEmail(); + // while theoretically the 'add' and 'processalways' functions could be // added as properties of the hash passed to fileupload(), for some reason // they needed to be placed in an on() call for the add method to work correctly @@ -59,11 +78,13 @@ export default Ember.Component.extend({ }, willDestroyElement: function () { - this.$('.js-file-input').fileupload('destroy'); + if (this.$('.js-file-input').data()['blueimp-fileupload']) { + this.$('.js-file-input').fileupload('destroy'); + } }, queueFile: function (e, data) { - var fileName = data.files[0].name; + const fileName = data.files[0].name; if ((/\.(gif|jpe?g|png|svg?z)$/i).test(fileName)) { this.sendAction('setImage', data); @@ -71,7 +92,7 @@ export default Ember.Component.extend({ }, triggerPreview: function (e, data) { - var file = data.files[data.index]; + const file = data.files[data.index]; if (file.preview) { this.set('hasUploadedImage', true); // necessary jQuery code because file.preview is a raw DOM object diff --git a/core/client/app/controllers/setup/two.js b/core/client/app/controllers/setup/two.js index 70d3834bea..b4a75d6799 100644 --- a/core/client/app/controllers/setup/two.js +++ b/core/client/app/controllers/setup/two.js @@ -7,7 +7,6 @@ export default Ember.Controller.extend(ValidationEngine, { blogTitle: null, name: null, email: '', - validEmail: '', password: null, image: null, blogCreated: false, @@ -53,9 +52,6 @@ export default Ember.Controller.extend(ValidationEngine, { preValidate: function (model) { // Only triggers validation if a value has been entered, preventing empty errors on focusOut if (this.get(model)) { - if (model === 'email') { - this.send('handleEmail'); - } this.validate({property: model}); } }, @@ -119,13 +115,6 @@ export default Ember.Controller.extend(ValidationEngine, { }, setImage: function (image) { this.set('image', image); - }, - handleEmail: function () { - var self = this; - - this.validate({property: 'email'}).then(function () { - self.set('validEmail', self.get('email')); - }); } } }); diff --git a/core/client/app/controllers/signup.js b/core/client/app/controllers/signup.js index f951123977..5f4ad6a929 100644 --- a/core/client/app/controllers/signup.js +++ b/core/client/app/controllers/signup.js @@ -9,7 +9,6 @@ export default Ember.Controller.extend(ValidationEngine, { submitting: false, flowErrors: '', image: null, - validEmail: '', ghostPaths: Ember.inject.service('ghost-paths'), config: Ember.inject.service(), @@ -88,13 +87,6 @@ export default Ember.Controller.extend(ValidationEngine, { }, setImage: function (image) { this.set('image', image); - }, - handleEmail: function () { - var self = this; - - this.validate({property: 'email'}).then(function () { - self.set('validEmail', self.get('email')); - }); } } }); diff --git a/core/client/app/templates/components/gh-profile-image.hbs b/core/client/app/templates/components/gh-profile-image.hbs index bf8c1889ba..876767bab6 100644 --- a/core/client/app/templates/components/gh-profile-image.hbs +++ b/core/client/app/templates/components/gh-profile-image.hbs @@ -2,7 +2,7 @@ {{#unless hasUploadedImage}}
- {{#if hasEmail}} + {{#if displayGravatar}}
User image
diff --git a/core/client/app/templates/setup/two.hbs b/core/client/app/templates/setup/two.hbs index f79c9dfa6c..8c53c6b02f 100644 --- a/core/client/app/templates/setup/two.hbs +++ b/core/client/app/templates/setup/two.hbs @@ -7,7 +7,7 @@ - {{gh-profile-image fileStorage=config.fileStorage email=validEmail setImage="setImage"}} + {{gh-profile-image fileStorage=config.fileStorage email=email setImage="setImage"}} {{#gh-form-group errors=errors hasValidated=hasValidated property="email"}} diff --git a/core/client/app/templates/signup.hbs b/core/client/app/templates/signup.hbs index 15351c0cc1..c30e91bfc3 100644 --- a/core/client/app/templates/signup.hbs +++ b/core/client/app/templates/signup.hbs @@ -11,11 +11,11 @@ - {{gh-profile-image fileStorage=config.fileStorage email=validEmail setImage="setImage"}} + {{gh-profile-image fileStorage=config.fileStorage email=model.email setImage="setImage"}} {{#gh-form-group errors=model.errors hasValidated=hasValidated property="email"}} - {{gh-input type="email" name="email" placeholder="Eg. john@example.com" enter=(action "signup") disabled="disabled" autocorrect="off" value=model.email focusOut=(action "handleEmail")}} + {{gh-input type="email" name="email" placeholder="Eg. john@example.com" enter=(action "signup") disabled="disabled" autocorrect="off" value=model.email focusOut=(action "validate" "email")}} {{gh-error-message errors=model.errors property="email"}} {{/gh-form-group}} diff --git a/core/client/bower.json b/core/client/bower.json index d3463f60ff..1e9bd5c940 100644 --- a/core/client/bower.json +++ b/core/client/bower.json @@ -8,7 +8,7 @@ "ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3", "ember-cli-test-loader": "0.1.3", "ember-data": "1.13.13", - "ember-mocha": "0.8.4", + "ember-mocha": "0.8.6", "ember-load-initializers": "ember-cli/ember-load-initializers#0.1.5", "ember-resolver": "0.1.18", "fastclick": "1.0.6", diff --git a/core/client/tests/integration/components/gh-profile-image-test.js b/core/client/tests/integration/components/gh-profile-image-test.js new file mode 100644 index 0000000000..fa335961a4 --- /dev/null +++ b/core/client/tests/integration/components/gh-profile-image-test.js @@ -0,0 +1,91 @@ +/* jshint expr:true */ +/* global md5 */ +import { expect } from 'chai'; +import { + describeComponent, + it +} from 'ember-mocha'; +import hbs from 'htmlbars-inline-precompile'; +import Ember from 'ember'; + +const {run} = Ember, + pathsStub = Ember.Service.extend({ + url: { + api: function () { + return ''; + }, + asset: function (src) { + return src; + } + } + }); + +describeComponent( + 'gh-profile-image', + 'Integration: Component: gh-profile-image', + { + integration: true + }, + function () { + beforeEach(function () { + this.register('service:ghost-paths', pathsStub); + this.inject.service('ghost-paths', {as: 'ghost-paths'}); + }); + + it('renders', function () { + this.set('email', ''); + + this.render(hbs` + {{gh-profile-image email=email}} + `); + + expect(this.$()).to.have.length(1); + }); + + it('immediately renders the gravatar if valid email supplied', function () { + let email = 'test@example.com', + expectedUrl = `http://www.gravatar.com/avatar/${md5(email)}?s=100&d=blank`; + + this.set('email', email); + + this.render(hbs` + {{gh-profile-image email=email size=100 debounce=300}} + `); + + expect(this.$('.gravatar-img').attr('style'), 'gravatar image style') + .to.equal(`background-image: url(${expectedUrl})`); + }); + + it('throttles gravatar loading as email is changed', function (done) { + let email = 'test@example.com', + expectedUrl = `http://www.gravatar.com/avatar/${md5(email)}?s=100&d=blank`; + + this.set('email', 'test'); + + this.render(hbs` + {{gh-profile-image email=email size=100 debounce=300}} + `); + + expect(this.$('.gravatar-img').length, '.gravatar-img not shown for invalid email') + .to.equal(0); + + run(() => { + this.set('email', email); + }); + + expect(this.$('.gravatar-img').length, '.gravatar-img not immediately changed on email change') + .to.equal(0); + + Ember.run.later(this, function () { + expect(this.$('.gravatar-img').length, '.gravatar-img still not shown before throttle timeout') + .to.equal(0); + }, 250); + + Ember.run.later(this, function () { + expect(this.$('.gravatar-img').attr('style'), '.gravatar-img style after timeout') + .to.equal(`background-image: url(${expectedUrl})`); + done(); + }, 400); + }); + } +); diff --git a/core/client/tests/unit/components/gh-profile-image-test.js b/core/client/tests/unit/components/gh-profile-image-test.js deleted file mode 100644 index 9665aab5b8..0000000000 --- a/core/client/tests/unit/components/gh-profile-image-test.js +++ /dev/null @@ -1,48 +0,0 @@ -/* jshint expr:true */ -/* global md5 */ -import { expect } from 'chai'; -import { - describeComponent, - it -} from 'ember-mocha'; - -describeComponent( - 'gh-profile-image', - 'Unit: Component: gh-profile-image', - { - unit: true, - needs: ['service:ghost-paths'] - }, - function () { - it('renders', function () { - // creates the component instance - var component = this.subject(); - expect(component._state).to.equal('preRender'); - - // renders the component on the page - this.render(); - expect(component._state).to.equal('inDOM'); - }); - it('renders the gravatar image background if email is supplied', function () { - var component = this.subject(), - testEmail = 'test@example.com', - style, size; - - component.set('email', testEmail); - this.render(); - - size = component.get('size'); - - style = 'url(http://www.gravatar.com/avatar/' + md5(testEmail) + '?s=' + size + '&d=blank)'; - - expect(component.$('#account-image').css('background-image')).to.equal(style); - }); - it('doesn\'t render the gravatar image background if email isn\'t supplied', function () { - var component = this.subject(); - - this.render(); - - expect(component.$('#account-image').length).to.equal(0); - }); - } -);