Skip to content

Commit

Permalink
Avoid duplicate alerts, clear alerts on successful retry or sign-in
Browse files Browse the repository at this point in the history
closes TryGhost#5903, refs TryGhost#5409
- switch alert/notification component tests from unit to integration where appropriate
- rename `notifications.closeAll` to `notifications.clearAll` to better represent it's behaviour
- add concept of a "key" to alerts/notifications and ability to close only specified keys through notifications service
- close duplicate alerts/notifications before showing a new one
- specify a key for all existing alerts
- close failure alerts on successful retries
- clear all currently displayed alerts on successful sign-in
  • Loading branch information
kevinansfield committed Oct 12, 2015
1 parent ff73f1a commit 1562603
Show file tree
Hide file tree
Showing 36 changed files with 513 additions and 324 deletions.
5 changes: 2 additions & 3 deletions core/client/app/components/gh-alert.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ export default Ember.Component.extend({

notifications: Ember.inject.service(),

typeClass: Ember.computed(function () {
typeClass: Ember.computed('message.type', function () {
var classes = '',
message = this.get('message'),
type = Ember.get(message, 'type'),
type = this.get('message.type'),
typeMapping;

typeMapping = {
Expand Down
5 changes: 2 additions & 3 deletions core/client/app/components/gh-notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ export default Ember.Component.extend({

notifications: Ember.inject.service(),

typeClass: Ember.computed(function () {
typeClass: Ember.computed('message.type', function () {
var classes = '',
message = this.get('message'),
type = Ember.get(message, 'type'),
type = this.get('message.type'),
typeMapping;

typeMapping = {
Expand Down
11 changes: 6 additions & 5 deletions core/client/app/components/gh-user-invited.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ export default Ember.Component.extend({
// If sending the invitation email fails, the API will still return a status of 201
// but the user's status in the response object will be 'invited-pending'.
if (result.users[0].status === 'invited-pending') {
notifications.showAlert('Invitation email was not sent. Please try resending.', {type: 'error'});
notifications.showAlert('Invitation email was not sent. Please try resending.', {type: 'error', key: 'invite.resend.not-sent'});
} else {
user.set('status', result.users[0].status);
notifications.showNotification(notificationText);
notifications.closeAlerts('invite.resend');
}
}).catch(function (error) {
notifications.showAPIError(error);
notifications.showAPIError(error, {key: 'invite.resend'});
}).finally(function () {
self.set('isSending', false);
});
Expand All @@ -50,15 +51,15 @@ export default Ember.Component.extend({
if (user.get('invited')) {
user.destroyRecord().then(function () {
var notificationText = 'Invitation revoked. (' + email + ')';

notifications.showNotification(notificationText);
notifications.closeAlerts('invite.revoke');
}).catch(function (error) {
notifications.showAPIError(error);
notifications.showAPIError(error, {key: 'invite.revoke'});
});
} else {
// if the user is no longer marked as "invited", then show a warning and reload the route
self.sendAction('reload');
notifications.showAlert('This user has already accepted the invitation.', {type: 'error', delayed: true});
notifications.showAlert('This user has already accepted the invitation.', {type: 'error', delayed: true, key: 'invite.revoke.already-accepted'});
}
});
}
Expand Down
4 changes: 2 additions & 2 deletions core/client/app/controllers/modals/delete-all.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ export default Ember.Controller.extend({
ajax(this.get('ghostPaths.url').api('db'), {
type: 'DELETE'
}).then(function () {
self.get('notifications').showAlert('All content deleted from database.', {type: 'success'});
self.get('notifications').showAlert('All content deleted from database.', {type: 'success', key: 'all-content.delete.success'});
self.store.unloadAll('post');
self.store.unloadAll('tag');
}).catch(function (response) {
self.get('notifications').showAPIError(response);
self.get('notifications').showAPIError(response, {key: 'all-content.delete'});
});
},

Expand Down
3 changes: 2 additions & 1 deletion core/client/app/controllers/modals/delete-post.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ export default Ember.Controller.extend({

model.destroyRecord().then(function () {
self.get('dropdown').closeDropdowns();
self.get('notifications').closeAlerts('post.delete');
self.transitionToRoute('posts.index');
}, function () {
self.get('notifications').showAlert('Your post could not be deleted. Please try again.', {type: 'error'});
self.get('notifications').showAlert('Your post could not be deleted. Please try again.', {type: 'error', key: 'post.delete.failed'});
});
},

Expand Down
2 changes: 1 addition & 1 deletion core/client/app/controllers/modals/delete-tag.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default Ember.Controller.extend({
this.send('closeMenus');

tag.destroyRecord().catch(function (error) {
self.get('notifications').showAPIError(error);
self.get('notifications').showAPIError(error, {key: 'tag.delete'});
});
},

Expand Down
3 changes: 2 additions & 1 deletion core/client/app/controllers/modals/delete-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ export default Ember.Controller.extend({
user = this.get('model');

user.destroyRecord().then(function () {
self.get('notifications').closeAlerts('user.delete');
self.store.unloadAll('post');
self.transitionToRoute('team');
}, function () {
self.get('notifications').showAlert('The user could not be deleted. Please try again.', {type: 'error'});
self.get('notifications').showAlert('The user could not be deleted. Please try again.', {type: 'error', key: 'user.delete.failed'});
});
},

Expand Down
11 changes: 6 additions & 5 deletions core/client/app/controllers/modals/invite-new-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ export default Ember.Controller.extend(ValidationEngine, {

if (invitedUser) {
if (invitedUser.get('status') === 'invited' || invitedUser.get('status') === 'invited-pending') {
self.get('notifications').showAlert('A user with that email address was already invited.', {type: 'warn'});
self.get('notifications').showAlert('A user with that email address was already invited.', {type: 'warn', key: 'invite.send.already-invited'});
} else {
self.get('notifications').showAlert('A user with that email address already exists.', {type: 'warn'});
self.get('notifications').showAlert('A user with that email address already exists.', {type: 'warn', key: 'invite.send.user-exists'});
}
} else {
newUser = self.store.createRecord('user', {
Expand All @@ -75,8 +75,9 @@ export default Ember.Controller.extend(ValidationEngine, {
// If sending the invitation email fails, the API will still return a status of 201
// but the user's status in the response object will be 'invited-pending'.
if (newUser.get('status') === 'invited-pending') {
self.get('notifications').showAlert('Invitation email was not sent. Please try resending.', {type: 'error'});
self.get('notifications').showAlert('Invitation email was not sent. Please try resending.', {type: 'error', key: 'invite.send.failed'});
} else {
self.get('notifications').closeAlerts('invite.send');
self.get('notifications').showNotification(notificationText);
}
}).catch(function (errors) {
Expand All @@ -86,9 +87,9 @@ export default Ember.Controller.extend(ValidationEngine, {
// want to use inline-validations here and only show an
// alert if we have an actual error
if (errors) {
self.get('notifications').showErrors(errors);
self.get('notifications').showErrors(errors, {key: 'invite.send'});
} else if (validationErrors) {
self.get('notifications').showAlert(validationErrors.toString(), {type: 'error'});
self.get('notifications').showAlert(validationErrors.toString(), {type: 'error', key: 'invite.send.validation-error'});
}
}).finally(function () {
self.get('errors').clear();
Expand Down
4 changes: 2 additions & 2 deletions core/client/app/controllers/modals/transfer-owner.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ export default Ember.Controller.extend({
});
}

self.get('notifications').showAlert('Ownership successfully transferred to ' + user.get('name'), {type: 'success'});
self.get('notifications').showAlert('Ownership successfully transferred to ' + user.get('name'), {type: 'success', key: 'owner.transfer.success'});
}).catch(function (error) {
self.get('notifications').showAPIError(error);
self.get('notifications').showAPIError(error, {key: 'owner.transfer'});
});
},

Expand Down
2 changes: 1 addition & 1 deletion core/client/app/controllers/modals/upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default Ember.Controller.extend({
this.get('model').save().then(function (model) {
return model;
}).catch(function (err) {
notifications.showAPIError(err);
notifications.showAPIError(err, {key: 'image.upload'});
});
},

Expand Down
4 changes: 2 additions & 2 deletions core/client/app/controllers/reset.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ export default Ember.Controller.extend(ValidationEngine, {
}
}).then(function (resp) {
self.toggleProperty('submitting');
self.get('notifications').showAlert(resp.passwordreset[0].message, {type: 'warn', delayed: true});
self.get('notifications').showAlert(resp.passwordreset[0].message, {type: 'warn', delayed: true, key: 'password.reset'});
self.get('session').authenticate('ghost-authenticator:oauth2-password-grant', {
identification: self.get('email'),
password: credentials.newPassword
});
}).catch(function (response) {
self.get('notifications').showAPIError(response);
self.get('notifications').showAPIError(response, {key: 'password.reset'});
self.toggleProperty('submitting');
});
}).catch(function () {
Expand Down
2 changes: 1 addition & 1 deletion core/client/app/controllers/settings/code-injection.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default Ember.Controller.extend(SettingsSaveMixin, {
var notifications = this.get('notifications');

return this.get('model').save().catch(function (error) {
notifications.showAPIError(error);
notifications.showAPIError(error, {key: 'code-injection.save'});
});
}
});
2 changes: 1 addition & 1 deletion core/client/app/controllers/settings/general.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export default Ember.Controller.extend(SettingsSaveMixin, {
return model;
}).catch(function (error) {
if (error) {
notifications.showAPIError(error);
notifications.showAPIError(error, {key: 'settings.save'});
}
});
},
Expand Down
9 changes: 5 additions & 4 deletions core/client/app/controllers/settings/labs.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,13 @@ export default Ember.Controller.extend({
self.set('session.user', self.store.findRecord('user', currentUserId));
// TODO: keep as notification, add link to view content
notifications.showNotification('Import successful.');
notifications.closeAlerts('import.upload');
}).catch(function (response) {
if (response && response.jqXHR && response.jqXHR.responseJSON && response.jqXHR.responseJSON.errors) {
self.set('importErrors', response.jqXHR.responseJSON.errors);
}

notifications.showAlert('Import Failed', {type: 'error'});
notifications.showAlert('Import Failed', {type: 'error', key: 'import.upload.failed'});
}).finally(function () {
self.set('uploadButtonText', 'Import');
});
Expand All @@ -86,13 +87,13 @@ export default Ember.Controller.extend({
ajax(this.get('ghostPaths.url').api('mail', 'test'), {
type: 'POST'
}).then(function () {
notifications.showAlert('Check your email for the test message.', {type: 'info'});
notifications.showAlert('Check your email for the test message.', {type: 'info', key: 'test-email.send.success'});
self.toggleProperty('submitting');
}).catch(function (error) {
if (typeof error.jqXHR !== 'undefined') {
notifications.showAPIError(error);
notifications.showAPIError(error, {key: 'test-email.send'});
} else {
notifications.showErrors(error);
notifications.showErrors(error, {key: 'test-email.send'});
}
self.toggleProperty('submitting');
});
Expand Down
2 changes: 1 addition & 1 deletion core/client/app/controllers/settings/tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export default Ember.Controller.extend(SettingsMenuMixin, {

activeTag.save().catch(function (error) {
if (error) {
self.notifications.showAPIError(error);
self.notifications.showAPIError(error, {key: 'tag.save'});
}
});
},
Expand Down
4 changes: 2 additions & 2 deletions core/client/app/controllers/setup/three.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,13 @@ export default Ember.Controller.extend({
invitationsString = erroredEmails.length > 1 ? ' invitations: ' : ' invitation: ';
message = 'Failed to send ' + erroredEmails.length + invitationsString;
message += erroredEmails.join(', ');
notifications.showAlert(message, {type: 'error', delayed: successCount > 0});
notifications.showAlert(message, {type: 'error', delayed: successCount > 0, key: 'signup.send-invitations.failed'});
}

if (successCount > 0) {
// pluralize
invitationsString = successCount > 1 ? 'invitations' : 'invitation';
notifications.showAlert(successCount + ' ' + invitationsString + ' sent!', {type: 'success', delayed: true});
notifications.showAlert(successCount + ' ' + invitationsString + ' sent!', {type: 'success', delayed: true, key: 'signup.send-invitations.success'});
}
self.send('loadServerNotifications');
self.toggleProperty('submitting');
Expand Down
4 changes: 2 additions & 2 deletions core/client/app/controllers/setup/two.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export default Ember.Controller.extend(ValidationEngine, {
self.transitionToRoute('setup.three');
}).catch(function (resp) {
self.toggleProperty('submitting');
notifications.showAPIError(resp);
notifications.showAPIError(resp, {key: 'setup.blog-details'});
});
} else {
self.toggleProperty('submitting');
Expand All @@ -111,7 +111,7 @@ export default Ember.Controller.extend(ValidationEngine, {
if (resp && resp.jqXHR && resp.jqXHR.responseJSON && resp.jqXHR.responseJSON.errors) {
self.set('flowErrors', resp.jqXHR.responseJSON.errors[0].message);
} else {
notifications.showAPIError(resp);
notifications.showAPIError(resp, {key: 'setup.blog-details'});
}
});
}).catch(function () {
Expand Down
6 changes: 3 additions & 3 deletions core/client/app/controllers/signin.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export default Ember.Controller.extend(ValidationEngine, {
self.send('authenticate');
}).catch(function (error) {
if (error) {
self.get('notifications').showAPIError(error);
self.get('notifications').showAPIError(error, {key: 'signin.authenticate'});
} else {
self.set('flowErrors', 'Please fill out the form to sign in.');
}
Expand Down Expand Up @@ -86,7 +86,7 @@ export default Ember.Controller.extend(ValidationEngine, {
}
}).then(function () {
self.toggleProperty('submitting');
notifications.showAlert('Please check your email for instructions.', {type: 'info'});
notifications.showAlert('Please check your email for instructions.', {type: 'info', key: 'forgot-password.send.success'});
}).catch(function (resp) {
self.toggleProperty('submitting');
if (resp && resp.jqXHR && resp.jqXHR.responseJSON && resp.jqXHR.responseJSON.errors) {
Expand All @@ -98,7 +98,7 @@ export default Ember.Controller.extend(ValidationEngine, {
self.get('model.errors').add('identification', '');
}
} else {
notifications.showAPIError(resp, {defaultErrorText: 'There was a problem with the reset, please try again.'});
notifications.showAPIError(resp, {defaultErrorText: 'There was a problem with the reset, please try again.', key: 'forgot-password.send'});
}
});
}).catch(function () {
Expand Down
4 changes: 2 additions & 2 deletions core/client/app/controllers/signup.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,14 @@ export default Ember.Controller.extend(ValidationEngine, {
self.sendImage();
}
}).catch(function (resp) {
notifications.showAPIError(resp);
notifications.showAPIError(resp, {key: 'signup.complete'});
});
}).catch(function (resp) {
self.toggleProperty('submitting');
if (resp && resp.jqXHR && resp.jqXHR.responseJSON && resp.jqXHR.responseJSON.errors) {
self.set('flowErrors', resp.jqXHR.responseJSON.errors[0].message);
} else {
notifications.showAPIError(resp);
notifications.showAPIError(resp, {key: 'signup.complete'});
}
});
}).catch(function () {
Expand Down
9 changes: 5 additions & 4 deletions core/client/app/controllers/team/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,12 @@ export default Ember.Controller.extend(ValidationEngine, {
}

self.toggleProperty('submitting');
self.get('notifications').closeAlerts('user.update');

return model;
}).catch(function (errors) {
if (errors) {
self.get('notifications').showErrors(errors);
self.get('notifications').showErrors(errors, {key: 'user.update'});
}

self.toggleProperty('submitting');
Expand All @@ -151,15 +152,15 @@ export default Ember.Controller.extend(ValidationEngine, {
ne2Password: ''
});

self.get('notifications').showAlert('Password updated.', {type: 'success'});
self.get('notifications').showAlert('Password updated.', {type: 'success', key: 'user.change-password.success'});

return model;
}).catch(function (errors) {
self.get('notifications').showAPIError(errors);
self.get('notifications').showAPIError(errors, {key: 'user.change-password'});
});
} else {
// TODO: switch to in-line validation
self.get('notifications').showErrors(user.get('passwordValidationErrors'));
self.get('notifications').showErrors(user.get('passwordValidationErrors'), {key: 'user.change-password'});
}
},

Expand Down
2 changes: 1 addition & 1 deletion core/client/app/mixins/editor-base-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ export default Ember.Mixin.create({

message += '<br />' + error;

notifications.showAlert(message.htmlSafe(), {type: 'error', delayed: delay});
notifications.showAlert(message.htmlSafe(), {type: 'error', delayed: delay, key: 'post.save'});
},

actions: {
Expand Down
2 changes: 1 addition & 1 deletion core/client/app/mixins/pagination-route.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default Ember.Mixin.create({
message += '.';
}

this.get('notifications').showAlert(message, {type: 'error'});
this.get('notifications').showAlert(message, {type: 'error', key: 'pagination.load.failed'});
},

loadFirstPage: function () {
Expand Down
Loading

0 comments on commit 1562603

Please sign in to comment.