mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +01:00
refactor notifications cleanup
This commit is contained in:
@@ -181,7 +181,7 @@ describe('User Model', () => {
|
||||
});
|
||||
});
|
||||
|
||||
context('notifications', () => {
|
||||
context.only('notifications', () => {
|
||||
it('can add notifications without data', () => {
|
||||
const user = new User();
|
||||
|
||||
@@ -215,6 +215,26 @@ describe('User Model', () => {
|
||||
expect(userToJSON.notifications[0].id).to.equal('123');
|
||||
});
|
||||
|
||||
it('removes invalid notifications when saving', async () => {
|
||||
const user = new User();
|
||||
|
||||
user.notifications = [
|
||||
null, // invalid, not an object
|
||||
{ seen: true }, // invalid, no type or id
|
||||
{ id: 123 }, // invalid, no type
|
||||
// invalid, no id, not included here because the id would be added automatically
|
||||
// {type: 'ABC'},
|
||||
{ type: 'ABC', id: '123' }, // valid
|
||||
];
|
||||
|
||||
await user.save();
|
||||
expect(user.notifications.length).to.equal(1);
|
||||
|
||||
expect(user.notifications[0]).to.have.all.keys(['data', 'id', 'type', 'seen']);
|
||||
expect(user.notifications[0].type).to.equal('ABC');
|
||||
expect(user.notifications[0].id).to.equal('123');
|
||||
});
|
||||
|
||||
it('can add notifications with data and already marked as seen', () => {
|
||||
const user = new User();
|
||||
|
||||
|
||||
@@ -25,11 +25,6 @@ schema.plugin(baseModel, {
|
||||
|
||||
delete plainObj.filters;
|
||||
|
||||
if (originalDoc.notifications) {
|
||||
plainObj.notifications = UserNotification
|
||||
.convertNotificationsToSafeJson(originalDoc.notifications);
|
||||
}
|
||||
|
||||
return plainObj;
|
||||
},
|
||||
});
|
||||
@@ -182,6 +177,22 @@ function _setProfileName (user) {
|
||||
return localUsername || anonymous;
|
||||
}
|
||||
|
||||
schema.post('init', function postInitUser () {
|
||||
// Cleanup any corrupt data that could have ended up inside the user schema.
|
||||
// In particular:
|
||||
// - tags https://github.com/HabitRPG/habitica/issues/10688
|
||||
// - notifications https://github.com/HabitRPG/habitica/issues/9923
|
||||
// - push devices https://github.com/HabitRPG/habitica/issues/11805
|
||||
// and https://github.com/HabitRPG/habitica/issues/11868
|
||||
|
||||
// Make sure notifications are loaded
|
||||
if (this.isDirectSelected('notifications')) {
|
||||
this.notifications = UserNotification.cleanupCorruptData(this.notifications);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
schema.pre('validate', function preValidateUser (next) {
|
||||
// Populate new user with profile name, not running in pre('save') because the field
|
||||
// is required and validation fails if it doesn't exists like for new users
|
||||
@@ -258,11 +269,8 @@ schema.pre('save', true, function preSaveUser (next, done) {
|
||||
const unallocatedPointsNotifications = [];
|
||||
|
||||
this.notifications = this.notifications.filter(notification => {
|
||||
// Remove corrupt notifications
|
||||
if (!notification || !notification.type) return false;
|
||||
|
||||
// Remove all unsallocated stats points
|
||||
if (notification && notification.type === 'UNALLOCATED_STATS_POINTS') {
|
||||
// Remove all unallocated stats points
|
||||
if (notification.type === 'UNALLOCATED_STATS_POINTS') {
|
||||
unallocatedPointsNotifications.push(notification);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -88,30 +88,30 @@ export const schema = new Schema({
|
||||
});
|
||||
|
||||
/**
|
||||
* Convert notifications to JSON making sure to return only valid data.
|
||||
* Fix for https://github.com/HabitRPG/habitica/issues/9923#issuecomment-362869881
|
||||
* @TODO Remove once https://github.com/HabitRPG/habitica/issues/9923
|
||||
* is fixed
|
||||
* Remove invalid data from an array of notifications.
|
||||
* Fix for https://github.com/HabitRPG/habitica/issues/9923
|
||||
* Called by user's post init hook (models/user/hooks.js)
|
||||
*/
|
||||
schema.statics.convertNotificationsToSafeJson = function convNotifsToSafeJson (notifications) {
|
||||
schema.statics.cleanupCorruptData = function cleanupCorruptNotificationsData (notifications) {
|
||||
if (!notifications) return notifications;
|
||||
|
||||
let filteredNotifications = notifications.filter(n => {
|
||||
// Exclude notifications with a nullish value
|
||||
if (!n) return false;
|
||||
// Exclude notifications without an id or a type
|
||||
if (!n.id || !n.type) return false;
|
||||
let filteredNotifications = notifications.filter(notification => {
|
||||
// Exclude notifications with a nullish value, no id or no type
|
||||
if (!notification || !notification.id || !notification.type) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
// Remove duplicate NEW_CHAT_MESSAGES notifications
|
||||
// can be caused by a race condition when adding a new notification of this type
|
||||
// in group.sendChat if two messages are posted at the same time
|
||||
filteredNotifications = _.uniqWith(filteredNotifications, (val, otherVal) => {
|
||||
if (val.type === otherVal.type && val.type === 'NEW_CHAT_MESSAGE') {
|
||||
if (val.type === 'NEW_CHAT_MESSAGE' && val.type === otherVal.type) {
|
||||
return val.data.group.id === otherVal.data.group.id;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
return filteredNotifications.map(n => n.toJSON());
|
||||
return filteredNotifications;
|
||||
};
|
||||
|
||||
schema.plugin(baseModel, {
|
||||
|
||||
Reference in New Issue
Block a user