diff --git a/core/client/components/gh-notification.js b/core/client/components/gh-notification.js index 26e442f774..f1ad863b88 100644 --- a/core/client/components/gh-notification.js +++ b/core/client/components/gh-notification.js @@ -13,9 +13,9 @@ var NotificationComponent = Ember.Component.extend({ actions: { closeNotification: function () { var self = this; - self.notifications.removeObject(self.get('message')); + self.notifications.closeNotification(self.get('message')); } } }); -export default NotificationComponent; \ No newline at end of file +export default NotificationComponent; diff --git a/core/client/controllers/posts/post.js b/core/client/controllers/posts/post.js index 75327d8fe2..7817da5af6 100644 --- a/core/client/controllers/posts/post.js +++ b/core/client/controllers/posts/post.js @@ -7,9 +7,8 @@ var PostController = Ember.ObjectController.extend({ var featured = this.toggleProperty('featured'), self = this; - // @TODO This should call closePassive() to only close passive notifications - self.notifications.closeAll(); - + self.notifications.closePassive(); + this.get('model').save().then(function () { self.notifications.showSuccess('Post successfully marked as ' + (featured ? 'featured' : 'not featured') + '.'); }).catch(function (errors) { @@ -20,4 +19,4 @@ var PostController = Ember.ObjectController.extend({ } }); -export default PostController; \ No newline at end of file +export default PostController; diff --git a/core/client/controllers/settings/general.js b/core/client/controllers/settings/general.js index 56c33f89fb..cdef5188e0 100644 --- a/core/client/controllers/settings/general.js +++ b/core/client/controllers/settings/general.js @@ -30,8 +30,7 @@ var SettingsGeneralController = Ember.ObjectController.extend({ save: function () { var self = this; - // @TODO This should call closePassive() to only close passive notifications - self.notifications.closeAll(); + self.notifications.closePassive(); return this.get('model').save().then(function (model) { self.notifications.showSuccess('Settings successfully saved.'); diff --git a/core/client/controllers/settings/user.js b/core/client/controllers/settings/user.js index 54c4a3a495..affb1326ad 100644 --- a/core/client/controllers/settings/user.js +++ b/core/client/controllers/settings/user.js @@ -22,9 +22,8 @@ var SettingsUserController = Ember.Controller.extend({ actions: { save: function () { var self = this; - - // @TODO This should call closePassive() to only close passive notifications - self.notifications.closeAll(); + + self.notifications.closePassive(); alert('@TODO: Saving user...'); @@ -64,4 +63,4 @@ var SettingsUserController = Ember.Controller.extend({ }); -export default SettingsUserController; \ No newline at end of file +export default SettingsUserController; diff --git a/core/client/controllers/setup.js b/core/client/controllers/setup.js index 94a3d5d838..b60a48f2f4 100644 --- a/core/client/controllers/setup.js +++ b/core/client/controllers/setup.js @@ -15,8 +15,7 @@ var SetupController = Ember.ObjectController.extend(ValidationEngine, { setup: function () { var self = this; - // @TODO This should call closePassive() to only close passive notifications - self.notifications.closeAll(); + self.notifications.closePassive(); this.toggleProperty('submitting'); this.validate({ format: false }).then(function () { diff --git a/core/client/controllers/signup.js b/core/client/controllers/signup.js index a84c275443..a619b968a7 100644 --- a/core/client/controllers/signup.js +++ b/core/client/controllers/signup.js @@ -14,8 +14,7 @@ var SignupController = Ember.ObjectController.extend(ValidationEngine, { signup: function () { var self = this; - // @TODO This should call closePassive() to only close passive notifications - self.notifications.closeAll(); + self.notifications.closePassive(); this.toggleProperty('submitting'); this.validate({ format: false }).then(function () { diff --git a/core/client/mixins/editor-base-controller.js b/core/client/mixins/editor-base-controller.js index dd4ee9bb85..3ae5a987e4 100644 --- a/core/client/mixins/editor-base-controller.js +++ b/core/client/mixins/editor-base-controller.js @@ -166,14 +166,14 @@ var EditorControllerMixin = Ember.Mixin.create(MarkerManager, { showSaveNotification: function (prevStatus, status, delay) { var message = this.messageMap.success.post[prevStatus][status]; - this.notifications.closeAll(); + this.notifications.closePassive(); this.notifications.showSuccess(message, delay); }, showErrorNotification: function (prevStatus, status, errors, delay) { var message = this.messageMap.errors.post[prevStatus][status]; - this.notifications.closeAll(); + this.notifications.closePassive(); message += '
' + errors[0].message; @@ -187,8 +187,7 @@ var EditorControllerMixin = Ember.Mixin.create(MarkerManager, { isNew = this.get('isNew'), self = this; - // @TODO This should call closePassive() to only close passive notifications - self.notifications.closeAll(); + self.notifications.closePassive(); // ensure an incomplete tag is finalised before save this.get('controllers.post-tags-input').send('addNewTag'); diff --git a/core/client/models/notification.js b/core/client/models/notification.js new file mode 100644 index 0000000000..d748011d0d --- /dev/null +++ b/core/client/models/notification.js @@ -0,0 +1,13 @@ +var Notification = DS.Model.extend({ + dismissible: DS.attr('boolean'), + location: DS.attr('string'), + status: DS.attr('string'), + type: DS.attr('string'), + message: DS.attr('string'), + + typeClass: function () { + return 'notification-' + this.get('type'); + }.property('type') +}); + +export default Notification; diff --git a/core/client/router.js b/core/client/router.js index ca2225dce0..20beada645 100644 --- a/core/client/router.js +++ b/core/client/router.js @@ -9,8 +9,7 @@ Router.reopen({ rootURL: ghostPaths().subdir + '/ghost/', // admin interface lives under sub-directory /ghost clearNotifications: function () { - // @TODO This should call closePassive() to only close passive notifications - this.notifications.closeAll(); + this.notifications.closePassive(); this.notifications.displayDelayed(); }.on('didTransition') }); diff --git a/core/client/routes/application.js b/core/client/routes/application.js index 5a0de37bd5..e622ecea6a 100644 --- a/core/client/routes/application.js +++ b/core/client/routes/application.js @@ -33,6 +33,12 @@ var ApplicationRoute = Ember.Route.extend(Ember.SimpleAuth.ApplicationRouteMixin }); }.on('init'), + setupController: function () { + Ember.run.next(this, function () { + this.send('loadServerNotifications'); + }); + }, + actions: { closePopups: function () { this.get('popover').closePopovers(); @@ -51,6 +57,8 @@ var ApplicationRoute = Ember.Route.extend(Ember.SimpleAuth.ApplicationRouteMixin this.set('user', user); this.set('controller.user', user); + + this.send('loadServerNotifications', true); }, signedOut: function () { @@ -90,6 +98,17 @@ var ApplicationRoute = Ember.Route.extend(Ember.SimpleAuth.ApplicationRouteMixin }); }, + loadServerNotifications: function (isDelayed) { + var self = this; + if (this.session.isAuthenticated) { + this.store.findAll('notification').then(function (serverNotifications) { + serverNotifications.forEach(function (notification) { + self.notifications.handleNotification(notification, isDelayed); + }); + }); + } + }, + handleErrors: function (errors) { var self = this; this.notifications.clear(); diff --git a/core/client/utils/notifications.js b/core/client/utils/notifications.js index f2e3ad30d3..1495c22a39 100644 --- a/core/client/utils/notifications.js +++ b/core/client/utils/notifications.js @@ -1,7 +1,10 @@ +import Notification from 'ghost/models/notification'; + var Notifications = Ember.ArrayProxy.extend({ delayedNotifications: [], content: Ember.A(), timeout: 3000, + pushObject: function (object) { object.typeClass = 'notification-' + object.type; // This should be somewhere else. @@ -11,6 +14,10 @@ var Notifications = Ember.ArrayProxy.extend({ this._super(object); }, handleNotification: function (message, delayed) { + if (!message.status) { + message.status = 'passive'; + } + if (!delayed) { this.pushObject(message); } else { @@ -63,6 +70,24 @@ var Notifications = Ember.ArrayProxy.extend({ }); self.delayedNotifications = []; }, + closeNotification: function (notification) { + var self = this; + + if (notification instanceof Notification) { + notification.deleteRecord(); + notification.save().then(function () { + self.removeObject(notification); + }); + } else { + this.removeObject(notification); + } + }, + closePassive: function () { + this.set('content', this.rejectBy('status', 'passive')); + }, + closePersistent: function () { + this.set('content', this.rejectBy('status', 'persistent')); + }, closeAll: function () { this.clear(); } diff --git a/core/server/api/notifications.js b/core/server/api/notifications.js index b8fbdebefa..06468b728c 100644 --- a/core/server/api/notifications.js +++ b/core/server/api/notifications.js @@ -39,7 +39,7 @@ notifications = { return element.id === parseInt(options.id, 10); }); - if (notification && !notification.dismissable) { + if (notification && !notification.dismissible) { return when.reject( new errors.NoPermissionError('You do not have permission to dismiss this notification.') ); @@ -78,14 +78,14 @@ notifications = { * type: 'error', // this can be 'error', 'success', 'warn' and 'info' * message: 'This is an error', // A string. Should fit in one line. * location: 'bottom', // A string where this notification should appear. can be 'bottom' or 'top' - * dismissable: true // A Boolean. Whether the notification is dismissable or not. + * dismissible: true // A Boolean. Whether the notification is dismissible or not. * }] }; * ``` */ add: function add(object) { var defaults = { - dismissable: true, + dismissible: true, location: 'bottom', status: 'persistent' }, @@ -110,4 +110,4 @@ notifications = { } }; -module.exports = notifications; \ No newline at end of file +module.exports = notifications; diff --git a/core/test/integration/api/api_notifications_spec.js b/core/test/integration/api/api_notifications_spec.js index 9a4b150597..188ac6bfba 100644 --- a/core/test/integration/api/api_notifications_spec.js +++ b/core/test/integration/api/api_notifications_spec.js @@ -59,7 +59,7 @@ describe('Notifications API', function () { should.exist(result.notifications); notification = result.notifications[0]; - notification.dismissable.should.be.true; + notification.dismissible.should.be.true; should.exist(notification.location); notification.location.should.equal('bottom'); @@ -85,7 +85,7 @@ describe('Notifications API', function () { notification.id.should.not.equal(99); should.exist(notification.status); notification.status.should.equal('persistent') - + done(); }).catch(done); }); @@ -108,4 +108,4 @@ describe('Notifications API', function () { }).catch(done); }); }); -}); \ No newline at end of file +}); diff --git a/core/test/utils/api.js b/core/test/utils/api.js index 9aa2b180a4..9dbd44f9ea 100644 --- a/core/test/utils/api.js +++ b/core/test/utils/api.js @@ -18,7 +18,7 @@ var url = require('url'), user: ['id', 'uuid', 'name', 'slug', 'email', 'image', 'cover', 'bio', 'website', 'location', 'accessibility', 'status', 'language', 'meta_title', 'meta_description', 'last_login', 'created_at', 'created_by', 'updated_at', 'updated_by'], - notification: ['type', 'message', 'status', 'id', 'dismissable', 'location'], + notification: ['type', 'message', 'status', 'id', 'dismissible', 'location'], slugs: ['slugs'], slug: ['slug'], accesstoken: ['access_token', 'refresh_token', 'expires_in', 'token_type']