mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
Refactored notifications module into a class
refs #12537 - Classes with DI friendly constructors are the pattern that are followed across the codebase
This commit is contained in:
parent
d2f0f0d7bc
commit
935ffdd0f6
4 changed files with 154 additions and 146 deletions
|
@ -1,4 +1,4 @@
|
|||
const notifications = require('../../services/notifications/notifications');
|
||||
const {notifications} = require('../../services/notifications');
|
||||
const api = require('./index');
|
||||
const internalContext = {context: {internal: true}};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const notifications = require('../../services/notifications/notifications');
|
||||
const {notifications} = require('../../services/notifications');
|
||||
const api = require('./index');
|
||||
const internalContext = {context: {internal: true}};
|
||||
|
||||
|
|
6
core/server/services/notifications/index.js
Normal file
6
core/server/services/notifications/index.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
const settingsCache = require('../settings/cache');
|
||||
const {i18n} = require('../../lib/common');
|
||||
const ghostVersion = require('../../lib/ghost-version');
|
||||
const Notifications = require('./notifications');
|
||||
|
||||
module.exports.notifications = new Notifications({settingsCache, i18n, ghostVersion});
|
|
@ -2,176 +2,178 @@ const moment = require('moment-timezone');
|
|||
const semver = require('semver');
|
||||
const Promise = require('bluebird');
|
||||
const _ = require('lodash');
|
||||
const settingsCache = require('../settings/cache');
|
||||
const ghostVersion = require('../../lib/ghost-version');
|
||||
const {i18n} = require('../../lib/common');
|
||||
const errors = require('@tryghost/errors');
|
||||
const ObjectId = require('bson-objectid');
|
||||
|
||||
const fetchAllNotifications = () => {
|
||||
let allNotifications = settingsCache.get('notifications');
|
||||
|
||||
allNotifications.forEach((notification) => {
|
||||
notification.addedAt = moment(notification.addedAt).toDate();
|
||||
});
|
||||
|
||||
return allNotifications;
|
||||
};
|
||||
|
||||
const wasSeen = (notification, user) => {
|
||||
if (notification.seenBy === undefined) {
|
||||
return notification.seen;
|
||||
} else {
|
||||
return notification.seenBy.includes(user.id);
|
||||
class Notifications {
|
||||
constructor({settingsCache, i18n, ghostVersion}) {
|
||||
this.settingsCache = settingsCache;
|
||||
this.i18n = i18n;
|
||||
this.ghostVersion = ghostVersion;
|
||||
}
|
||||
};
|
||||
|
||||
const browse = ({user}) => {
|
||||
let allNotifications = fetchAllNotifications();
|
||||
allNotifications = _.orderBy(allNotifications, 'addedAt', 'desc');
|
||||
fetchAllNotifications() {
|
||||
let allNotifications = this.settingsCache.get('notifications');
|
||||
|
||||
allNotifications = allNotifications.filter((notification) => {
|
||||
// NOTE: Filtering by version below is just a patch for bigger problem - notifications are not removed
|
||||
// after Ghost update. Logic below should be removed when Ghost upgrade detection
|
||||
// is done (https://github.com/TryGhost/Ghost/issues/10236) and notifications are
|
||||
// be removed permanently on upgrade event.
|
||||
const ghost20RegEx = /Ghost 2.0 is now available/gi;
|
||||
allNotifications.forEach((notification) => {
|
||||
notification.addedAt = moment(notification.addedAt).toDate();
|
||||
});
|
||||
|
||||
// CASE: do not return old release notification
|
||||
if (notification.message && (!notification.custom || notification.message.match(ghost20RegEx))) {
|
||||
let notificationVersion = notification.message.match(/(\d+\.)(\d+\.)(\d+)/);
|
||||
return allNotifications;
|
||||
}
|
||||
|
||||
if (notification.message.match(ghost20RegEx)) {
|
||||
notificationVersion = '2.0.0';
|
||||
} else if (notificationVersion){
|
||||
notificationVersion = notificationVersion[0];
|
||||
wasSeen(notification, user) {
|
||||
if (notification.seenBy === undefined) {
|
||||
return notification.seen;
|
||||
} else {
|
||||
return notification.seenBy.includes(user.id);
|
||||
}
|
||||
}
|
||||
|
||||
browse({user}) {
|
||||
let allNotifications = this.fetchAllNotifications();
|
||||
allNotifications = _.orderBy(allNotifications, 'addedAt', 'desc');
|
||||
|
||||
allNotifications = allNotifications.filter((notification) => {
|
||||
// NOTE: Filtering by version below is just a patch for bigger problem - notifications are not removed
|
||||
// after Ghost update. Logic below should be removed when Ghost upgrade detection
|
||||
// is done (https://github.com/TryGhost/Ghost/issues/10236) and notifications are
|
||||
// be removed permanently on upgrade event.
|
||||
const ghost20RegEx = /Ghost 2.0 is now available/gi;
|
||||
|
||||
// CASE: do not return old release notification
|
||||
if (notification.message && (!notification.custom || notification.message.match(ghost20RegEx))) {
|
||||
let notificationVersion = notification.message.match(/(\d+\.)(\d+\.)(\d+)/);
|
||||
|
||||
if (notification.message.match(ghost20RegEx)) {
|
||||
notificationVersion = '2.0.0';
|
||||
} else if (notificationVersion){
|
||||
notificationVersion = notificationVersion[0];
|
||||
}
|
||||
|
||||
const blogVersion = this.ghostVersion.full.match(/^(\d+\.)(\d+\.)(\d+)/);
|
||||
|
||||
if (notificationVersion && blogVersion && semver.gt(notificationVersion, blogVersion[0])) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const blogVersion = ghostVersion.full.match(/^(\d+\.)(\d+\.)(\d+)/);
|
||||
return !this.wasSeen(notification, user);
|
||||
});
|
||||
|
||||
if (notificationVersion && blogVersion && semver.gt(notificationVersion, blogVersion[0])) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
return allNotifications;
|
||||
}
|
||||
|
||||
add({notifications}) {
|
||||
const defaults = {
|
||||
dismissible: true,
|
||||
location: 'bottom',
|
||||
status: 'alert',
|
||||
id: ObjectId.generate()
|
||||
};
|
||||
|
||||
const overrides = {
|
||||
seen: false,
|
||||
addedAt: moment().toDate()
|
||||
};
|
||||
|
||||
let notificationsToCheck = notifications;
|
||||
let notificationsToAdd = [];
|
||||
|
||||
const allNotifications = this.fetchAllNotifications();
|
||||
|
||||
notificationsToCheck.forEach((notification) => {
|
||||
const isDuplicate = allNotifications.find((n) => {
|
||||
return n.id === notification.id;
|
||||
});
|
||||
|
||||
if (!isDuplicate) {
|
||||
notificationsToAdd.push(Object.assign({}, defaults, notification, overrides));
|
||||
}
|
||||
});
|
||||
|
||||
const hasReleaseNotification = notificationsToCheck.find((notification) => {
|
||||
return !notification.custom;
|
||||
});
|
||||
|
||||
// CASE: remove any existing release notifications if a new release notification comes in
|
||||
if (hasReleaseNotification) {
|
||||
_.remove(allNotifications, (el) => {
|
||||
return !el.custom;
|
||||
});
|
||||
}
|
||||
|
||||
return !wasSeen(notification, user);
|
||||
});
|
||||
|
||||
return allNotifications;
|
||||
};
|
||||
|
||||
const add = ({notifications}) => {
|
||||
const defaults = {
|
||||
dismissible: true,
|
||||
location: 'bottom',
|
||||
status: 'alert',
|
||||
id: ObjectId.generate()
|
||||
};
|
||||
|
||||
const overrides = {
|
||||
seen: false,
|
||||
addedAt: moment().toDate()
|
||||
};
|
||||
|
||||
let notificationsToCheck = notifications;
|
||||
let notificationsToAdd = [];
|
||||
|
||||
const allNotifications = fetchAllNotifications();
|
||||
|
||||
notificationsToCheck.forEach((notification) => {
|
||||
const isDuplicate = allNotifications.find((n) => {
|
||||
return n.id === notification.id;
|
||||
});
|
||||
|
||||
if (!isDuplicate) {
|
||||
notificationsToAdd.push(Object.assign({}, defaults, notification, overrides));
|
||||
// CASE: nothing to add, skip
|
||||
if (!notificationsToAdd.length) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
const hasReleaseNotification = notificationsToCheck.find((notification) => {
|
||||
return !notification.custom;
|
||||
});
|
||||
|
||||
// CASE: remove any existing release notifications if a new release notification comes in
|
||||
if (hasReleaseNotification) {
|
||||
_.remove(allNotifications, (el) => {
|
||||
return !el.custom;
|
||||
const releaseNotificationsToAdd = notificationsToAdd.filter((notification) => {
|
||||
return !notification.custom;
|
||||
});
|
||||
|
||||
// CASE: reorder notifications before save
|
||||
if (releaseNotificationsToAdd.length > 1) {
|
||||
notificationsToAdd = notificationsToAdd.filter((notification) => {
|
||||
return notification.custom;
|
||||
});
|
||||
notificationsToAdd.push(_.orderBy(releaseNotificationsToAdd, 'created_at', 'desc')[0]);
|
||||
}
|
||||
|
||||
return {allNotifications, notificationsToAdd};
|
||||
}
|
||||
|
||||
// CASE: nothing to add, skip
|
||||
if (!notificationsToAdd.length) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
destroy({notificationId, user}) {
|
||||
const allNotifications = this.fetchAllNotifications();
|
||||
|
||||
const releaseNotificationsToAdd = notificationsToAdd.filter((notification) => {
|
||||
return !notification.custom;
|
||||
});
|
||||
|
||||
// CASE: reorder notifications before save
|
||||
if (releaseNotificationsToAdd.length > 1) {
|
||||
notificationsToAdd = notificationsToAdd.filter((notification) => {
|
||||
return notification.custom;
|
||||
const notificationToMarkAsSeen = allNotifications.find((notification) => {
|
||||
return notification.id === notificationId;
|
||||
});
|
||||
notificationsToAdd.push(_.orderBy(releaseNotificationsToAdd, 'created_at', 'desc')[0]);
|
||||
}
|
||||
|
||||
return {allNotifications, notificationsToAdd};
|
||||
};
|
||||
const notificationToMarkAsSeenIndex = allNotifications.findIndex((notification) => {
|
||||
return notification.id === notificationId;
|
||||
});
|
||||
|
||||
const destroy = ({notificationId, user}) => {
|
||||
const allNotifications = fetchAllNotifications();
|
||||
if (notificationToMarkAsSeenIndex > -1 && !notificationToMarkAsSeen.dismissible) {
|
||||
return Promise.reject(new errors.NoPermissionError({
|
||||
message: this.i18n.t('errors.api.notifications.noPermissionToDismissNotif')
|
||||
}));
|
||||
}
|
||||
|
||||
const notificationToMarkAsSeen = allNotifications.find((notification) => {
|
||||
return notification.id === notificationId;
|
||||
});
|
||||
if (notificationToMarkAsSeenIndex < 0) {
|
||||
return Promise.reject(new errors.NotFoundError({
|
||||
message: this.i18n.t('errors.api.notifications.notificationDoesNotExist')
|
||||
}));
|
||||
}
|
||||
|
||||
const notificationToMarkAsSeenIndex = allNotifications.findIndex((notification) => {
|
||||
return notification.id === notificationId;
|
||||
});
|
||||
if (this.wasSeen(notificationToMarkAsSeen, user)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (notificationToMarkAsSeenIndex > -1 && !notificationToMarkAsSeen.dismissible) {
|
||||
return Promise.reject(new errors.NoPermissionError({
|
||||
message: i18n.t('errors.api.notifications.noPermissionToDismissNotif')
|
||||
}));
|
||||
}
|
||||
|
||||
if (notificationToMarkAsSeenIndex < 0) {
|
||||
return Promise.reject(new errors.NotFoundError({
|
||||
message: i18n.t('errors.api.notifications.notificationDoesNotExist')
|
||||
}));
|
||||
}
|
||||
|
||||
if (wasSeen(notificationToMarkAsSeen, user)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// @NOTE: We don't remove the notifications, because otherwise we will receive them again from the service.
|
||||
allNotifications[notificationToMarkAsSeenIndex].seen = true;
|
||||
|
||||
if (!allNotifications[notificationToMarkAsSeenIndex].seenBy) {
|
||||
allNotifications[notificationToMarkAsSeenIndex].seenBy = [];
|
||||
}
|
||||
|
||||
allNotifications[notificationToMarkAsSeenIndex].seenBy.push(user.id);
|
||||
|
||||
return allNotifications;
|
||||
};
|
||||
|
||||
const destroyAll = () => {
|
||||
const allNotifications = fetchAllNotifications();
|
||||
|
||||
allNotifications.forEach((notification) => {
|
||||
// @NOTE: We don't remove the notifications, because otherwise we will receive them again from the service.
|
||||
notification.seen = true;
|
||||
});
|
||||
allNotifications[notificationToMarkAsSeenIndex].seen = true;
|
||||
|
||||
return allNotifications;
|
||||
};
|
||||
if (!allNotifications[notificationToMarkAsSeenIndex].seenBy) {
|
||||
allNotifications[notificationToMarkAsSeenIndex].seenBy = [];
|
||||
}
|
||||
|
||||
module.exports.browse = browse;
|
||||
module.exports.add = add;
|
||||
module.exports.destroy = destroy;
|
||||
module.exports.destroyAll = destroyAll;
|
||||
allNotifications[notificationToMarkAsSeenIndex].seenBy.push(user.id);
|
||||
|
||||
return allNotifications;
|
||||
}
|
||||
|
||||
destroyAll() {
|
||||
const allNotifications = this.fetchAllNotifications();
|
||||
|
||||
allNotifications.forEach((notification) => {
|
||||
// @NOTE: We don't remove the notifications, because otherwise we will receive them again from the service.
|
||||
notification.seen = true;
|
||||
});
|
||||
|
||||
return allNotifications;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Notifications;
|
||||
|
|
Loading…
Add table
Reference in a new issue