mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Notifications on front end
Should close #37. There are persistent and passive notifications. Persistent ones: * are stored on `ghost.notifications`. * have an api made to add / remove them with client side ajax logic (probably not the most elegant, but works) * uses a modified `flashes.hbs` template * will only disappear if user closes the bar * stack Passive * added with backbone view / collection combo * stack * disappears on navigation and when user closes it
This commit is contained in:
parent
d337e16afe
commit
b77a8fd0d9
9 changed files with 184 additions and 41 deletions
|
@ -38,10 +38,50 @@
|
||||||
$(window).one('centered', fadeInAndFocus);
|
$(window).one('centered', fadeInAndFocus);
|
||||||
|
|
||||||
// Allow notifications to be dismissed
|
// Allow notifications to be dismissed
|
||||||
$(document).on('click', '.js-notification .close', function () {
|
$(document).on('click', '.js-notification.notification-passive .close', function () {
|
||||||
$(this).parent().fadeOut(200, function () { $(this).remove(); });
|
$(this).parent().fadeOut(200, function () { $(this).remove(); });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.js-notification.notification-persistent .close', function () {
|
||||||
|
var self = this;
|
||||||
|
$.ajax({
|
||||||
|
type: "DELETE",
|
||||||
|
url: '/api/v0.1/notifications/' + $(this).data('id')
|
||||||
|
}).done(function (result) {
|
||||||
|
if ($(self).parent().parent().hasClass('js-bb-notification')) {
|
||||||
|
$(self).parent().parent().fadeOut(200, function () { $(self).remove(); });
|
||||||
|
} else {
|
||||||
|
$(self).parent().fadeOut(200, function () { $(self).remove(); });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example of how to add a persistent notification.
|
||||||
|
*/
|
||||||
|
// $(document).on('click', '.add-persistent-notification', function (event) {
|
||||||
|
// event.preventDefault();
|
||||||
|
// var msg = {
|
||||||
|
// type: 'error',
|
||||||
|
// message: 'This is an error',
|
||||||
|
// status: 'persistent',
|
||||||
|
// id: 'per-' + $('.notification-persistent').length + 1
|
||||||
|
// };
|
||||||
|
|
||||||
|
// $.ajax({
|
||||||
|
// type: "POST",
|
||||||
|
// url: '/api/v0.1/notifications/',
|
||||||
|
// data: msg
|
||||||
|
// }).done(function (result) {
|
||||||
|
// var fcv;
|
||||||
|
// fcv = new Ghost.Views.FlashCollectionView({
|
||||||
|
// model: [msg]
|
||||||
|
// });
|
||||||
|
// console.log(fcv);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
||||||
// ## Set interactions for all menus
|
// ## Set interactions for all menus
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// # Article Editor
|
// # Article Editor
|
||||||
|
|
||||||
/*global window, document, $, _, Backbone, Ghost, Showdown, CodeMirror, shortcut, Countable */
|
/*global window, document, $, _, Backbone, Ghost, Showdown, CodeMirror, shortcut, Countable, JST */
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
@ -93,26 +93,55 @@
|
||||||
this.savePost({
|
this.savePost({
|
||||||
status: keys[newIndex]
|
status: keys[newIndex]
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
window.alert('Your post: ' + model.get('title') + ' has been ' + keys[newIndex]);
|
this.addSubview(new Ghost.Views.FlashCollectionView({
|
||||||
|
model: [{
|
||||||
|
type: 'success',
|
||||||
|
message: 'Your post: ' + model.get('title') + ' has been ' + keys[newIndex],
|
||||||
|
status: 'passive'
|
||||||
|
}]
|
||||||
|
}));
|
||||||
|
// window.alert('Your post: ' + model.get('title') + ' has been ' + keys[newIndex]);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
handleStatus: function (e) {
|
handleStatus: function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var status = $(e.currentTarget).attr('data-set-status'),
|
var status = $(e.currentTarget).attr('data-set-status'),
|
||||||
model = this.model;
|
model = this.model,
|
||||||
|
self = this;
|
||||||
|
|
||||||
if (status === 'publish-on') {
|
if (status === 'publish-on') {
|
||||||
return window.alert('Scheduled publishing not supported yet.');
|
this.addSubview(new Ghost.Views.FlashCollectionView({
|
||||||
|
model: [{
|
||||||
|
type: 'alert',
|
||||||
|
message: 'Scheduled publishing not supported yet.',
|
||||||
|
status: 'passive'
|
||||||
|
}]
|
||||||
|
}));
|
||||||
|
// return window.alert('Scheduled publishing not supported yet.');
|
||||||
}
|
}
|
||||||
if (status === 'queue') {
|
if (status === 'queue') {
|
||||||
return window.alert('Scheduled publishing not supported yet.');
|
this.addSubview(new Ghost.Views.FlashCollectionView({
|
||||||
|
model: [{
|
||||||
|
type: 'alert',
|
||||||
|
message: 'Scheduled publishing not supported yet.',
|
||||||
|
status: 'passive'
|
||||||
|
}]
|
||||||
|
}));
|
||||||
|
// return window.alert('Scheduled publishing not supported yet.');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.savePost({
|
this.savePost({
|
||||||
status: status
|
status: status
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
window.alert('Your post: ' + model.get('title') + ' has been ' + status);
|
self.addSubview(new Ghost.Views.FlashCollectionView({
|
||||||
|
model: [{
|
||||||
|
type: 'success',
|
||||||
|
message: 'Your post: ' + model.get('title') + ' has been ' + status,
|
||||||
|
status: 'passive'
|
||||||
|
}]
|
||||||
|
}));
|
||||||
|
// window.alert('Your post: ' + model.get('title') + ' has been ' + status);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -120,11 +149,26 @@
|
||||||
if (e) {
|
if (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
var model = this.model;
|
var model = this.model,
|
||||||
|
self = this;
|
||||||
this.savePost().then(function () {
|
this.savePost().then(function () {
|
||||||
window.alert('Your post was saved as ' + model.get('status'));
|
self.addSubview(new Ghost.Views.FlashCollectionView({
|
||||||
|
model: [{
|
||||||
|
type: 'success',
|
||||||
|
message: 'Your post was saved as ' + model.get('status'),
|
||||||
|
status: 'passive'
|
||||||
|
}]
|
||||||
|
}));
|
||||||
|
// window.alert('Your post was saved as ' + model.get('status'));
|
||||||
}, function () {
|
}, function () {
|
||||||
window.alert(model.validationError);
|
self.addSubview(new Ghost.Views.FlashCollectionView({
|
||||||
|
model: [{
|
||||||
|
type: 'error',
|
||||||
|
message: model.validationError,
|
||||||
|
status: 'passive'
|
||||||
|
}]
|
||||||
|
}));
|
||||||
|
// window.alert(model.validationError);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -254,4 +298,52 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the view to generate the markup for the individual
|
||||||
|
* notification. Will be included into #flashbar.
|
||||||
|
*
|
||||||
|
* States can be
|
||||||
|
* - persistent
|
||||||
|
* - passive
|
||||||
|
*
|
||||||
|
* Types can be
|
||||||
|
* - error
|
||||||
|
* - success
|
||||||
|
* - alert
|
||||||
|
* - (empty)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Ghost.Views.FlashView = Ghost.View.extend({
|
||||||
|
templateName: 'notification',
|
||||||
|
className: 'js-bb-notification',
|
||||||
|
template: function (data) {
|
||||||
|
return JST[this.templateName](data);
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
var html = this.template(this.model);
|
||||||
|
this.$el.html(html);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This handles Notification groups
|
||||||
|
*/
|
||||||
|
Ghost.Views.FlashCollectionView = Ghost.View.extend({
|
||||||
|
el: '#flashbar',
|
||||||
|
initialize: function() {
|
||||||
|
this.render();
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
_.each(this.model, function (item) {
|
||||||
|
this.renderItem(item);
|
||||||
|
}, this);
|
||||||
|
},
|
||||||
|
renderItem: function (item) {
|
||||||
|
var itemView = new Ghost.Views.FlashView({ model: item });
|
||||||
|
this.$el.prepend(itemView.render().el);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}());
|
}());
|
|
@ -13,6 +13,7 @@ var Ghost = require('../ghost'),
|
||||||
dataProvider = ghost.dataProvider,
|
dataProvider = ghost.dataProvider,
|
||||||
posts,
|
posts,
|
||||||
users,
|
users,
|
||||||
|
notifications,
|
||||||
settings,
|
settings,
|
||||||
requestHandler,
|
requestHandler,
|
||||||
cachedSettingsRequestHandler,
|
cachedSettingsRequestHandler,
|
||||||
|
@ -58,6 +59,20 @@ users = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// # Notifications
|
||||||
|
|
||||||
|
notifications = {
|
||||||
|
destroy: function destroy(i) {
|
||||||
|
ghost.notifications = _.reject(ghost.notifications, function (element) {
|
||||||
|
return element.id === i.id;
|
||||||
|
});
|
||||||
|
return when(ghost.notifications);
|
||||||
|
},
|
||||||
|
add: function add(notification) {
|
||||||
|
return when(ghost.notifications.push(notification));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// # Settings
|
// # Settings
|
||||||
|
|
||||||
// Turn a settings collection into a single object/hashmap
|
// Turn a settings collection into a single object/hashmap
|
||||||
|
@ -147,6 +162,7 @@ cachedSettingsRequestHandler = function (apiMethod) {
|
||||||
|
|
||||||
module.exports.posts = posts;
|
module.exports.posts = posts;
|
||||||
module.exports.users = users;
|
module.exports.users = users;
|
||||||
|
module.exports.notifications = notifications;
|
||||||
module.exports.settings = settings;
|
module.exports.settings = settings;
|
||||||
module.exports.requestHandler = requestHandler;
|
module.exports.requestHandler = requestHandler;
|
||||||
module.exports.cachedSettingsRequestHandler = cachedSettingsRequestHandler;
|
module.exports.cachedSettingsRequestHandler = cachedSettingsRequestHandler;
|
|
@ -110,6 +110,7 @@ adminControllers = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'editor': function (req, res) {
|
'editor': function (req, res) {
|
||||||
|
console.log(res.locals);
|
||||||
if (req.params.id !== undefined) {
|
if (req.params.id !== undefined) {
|
||||||
api.posts.read({id: parseInt(req.params.id, 10)})
|
api.posts.read({id: parseInt(req.params.id, 10)})
|
||||||
.then(function (post) {
|
.then(function (post) {
|
||||||
|
|
4
core/server/helpers/tpl/notification.hbs
Normal file
4
core/server/helpers/tpl/notification.hbs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<section class="notification{{#if type}}-{{type}}{{/if}} notification-{{status}} js-notification">
|
||||||
|
{{message}}
|
||||||
|
<a class="close" href="#"><span class="hidden">Close</span></a>
|
||||||
|
</section>
|
|
@ -27,7 +27,9 @@
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
<main role="main" id="main">
|
<main role="main" id="main">
|
||||||
|
<aside id="flashbar">
|
||||||
{{> flashes}}
|
{{> flashes}}
|
||||||
|
</aside>
|
||||||
|
|
||||||
{{{body}}}
|
{{{body}}}
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -1,26 +1,8 @@
|
||||||
{{#if messages}}
|
{{#if messages}}
|
||||||
{{#each messages.error}}
|
{{#each messages}}
|
||||||
<section class="notification-error js-notification">
|
<section class="notification{{#if type}}-{{type}}{{/if}} notification-{{status}} js-notification">
|
||||||
{{.}}
|
{{message}}
|
||||||
<a class="close" href="#"><span class="hidden">Close</span></a>
|
<a class="close" href="#" data-id="{{id}}"><span class="hidden">Close</span></a>
|
||||||
</section>
|
|
||||||
{{/each}}
|
|
||||||
{{#each messages.success}}
|
|
||||||
<section class="notification-success js-notification">
|
|
||||||
{{.}}
|
|
||||||
<a class="close" href="#"><span class="hidden">Close</span></a>
|
|
||||||
</section>
|
|
||||||
{{/each}}
|
|
||||||
{{#each messages.warn}}
|
|
||||||
<section class="notification-alert js-notification">
|
|
||||||
{{.}}
|
|
||||||
<a class="close" href="#"><span class="hidden">Close</span></a>
|
|
||||||
</section>
|
|
||||||
{{/each}}
|
|
||||||
{{#each messages.info}}
|
|
||||||
<section class="notification js-notification">
|
|
||||||
{{.}}
|
|
||||||
<a class="close" href="#"><span class="hidden">Close</span></a>
|
|
||||||
</section>
|
</section>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/if}}
|
{{/if}}
|
10
index.js
10
index.js
|
@ -96,7 +96,7 @@ ghostLocals = function (req, res, next) {
|
||||||
} else {
|
} else {
|
||||||
_.extend(res.locals, {
|
_.extend(res.locals, {
|
||||||
// pass the admin flash messages, settings and paths
|
// pass the admin flash messages, settings and paths
|
||||||
messages: req.flash(),
|
messages: ghost.notifications,
|
||||||
settings: ghost.settings(),
|
settings: ghost.settings(),
|
||||||
availableThemes: ghost.paths().availableThemes,
|
availableThemes: ghost.paths().availableThemes,
|
||||||
availablePlugins: ghost.paths().availablePlugins
|
availablePlugins: ghost.paths().availablePlugins
|
||||||
|
@ -173,6 +173,12 @@ when.all([ghost.init(), filters.loadCoreFilters(ghost), helpers.loadCoreHelpers(
|
||||||
});
|
});
|
||||||
ghost.app().get('/ghost/', auth, admin.index);
|
ghost.app().get('/ghost/', auth, admin.index);
|
||||||
|
|
||||||
|
|
||||||
|
// Notifications routes
|
||||||
|
ghost.app().del('/api/v0.1/notifications/:id', authAPI, disableCachedResult, api.requestHandler(api.notifications.destroy));
|
||||||
|
ghost.app().post('/api/v0.1/notifications/', authAPI, disableCachedResult, api.requestHandler(api.notifications.add));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Frontend routes..
|
* Frontend routes..
|
||||||
* @todo dynamic routing, homepage generator, filters ETC ETC
|
* @todo dynamic routing, homepage generator, filters ETC ETC
|
||||||
|
@ -183,6 +189,7 @@ when.all([ghost.init(), filters.loadCoreFilters(ghost), helpers.loadCoreHelpers(
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ghost.app().listen(
|
ghost.app().listen(
|
||||||
ghost.config().env[process.env.NODE_ENV || 'development'].url.port,
|
ghost.config().env[process.env.NODE_ENV || 'development'].url.port,
|
||||||
ghost.config().env[process.env.NODE_ENV || 'development'].url.host,
|
ghost.config().env[process.env.NODE_ENV || 'development'].url.host,
|
||||||
|
@ -195,5 +202,4 @@ when.all([ghost.init(), filters.loadCoreFilters(ghost), helpers.loadCoreHelpers(
|
||||||
loading.resolve();
|
loading.resolve();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
}, errors.logAndThrowError);
|
}, errors.logAndThrowError);
|
||||||
|
|
Loading…
Add table
Reference in a new issue