Remove and clean up unused invite notif (#15279)

* fix(notifications): remove and clean up unused invite notif

* fix(lint): remove unused const

* refactor(invites): updateMany in migration, don't load inviter unless needed

* fix(lint): remove extra whitespace

* fix(groups): remove more broken inviter logic

---------

Co-authored-by: Sabe Jones <sabe@habitica.com>
This commit is contained in:
Sabe Jones
2024-08-06 12:43:24 -05:00
committed by GitHub
parent 63918b3c20
commit afd00a8ab6
3 changed files with 63 additions and 82 deletions

View File

@@ -0,0 +1,47 @@
/* eslint-disable no-console */
import { model as User } from '../../../website/server/models/user';
const MIGRATION_NAME = '2024_purge_invite_accepted';
const progressCount = 1000;
let count = 0;
async function updateUsers (userIds) {
count += userIds.length;
if (count % progressCount === 0) console.warn(`${count} ${userIds[0]}`);
return await User.updateMany(
{ _id: { $in: userIds } },
{ $pull: { notifications: { type: 'GROUP_INVITE_ACCEPTED' } } },
).exec();
}
export default async function processUsers () {
let query = {
migration: { $ne: MIGRATION_NAME },
'notifications.type': 'GROUP_INVITE_ACCEPTED',
'auth.timestamps.loggedin': { $gt: new Date('2024-06-25') },
};
while (true) { // eslint-disable-line no-constant-condition
const users = await User // eslint-disable-line no-await-in-loop
.find(query)
.limit(250)
.sort({ _id: 1 })
.select({ _id: 1 })
.exec();
if (users.length === 0) {
console.warn('All appropriate users found and modified.');
console.warn(`\n${count} users processed\n`);
break;
} else {
query._id = {
$gt: users[users.length - 1],
};
}
const userIds = users.map(user => user._id);
await updateUsers(userIds); // eslint-disable-line no-await-in-loop
}
};

View File

@@ -85,22 +85,6 @@ describe('POST /group/:groupId/join', () => {
await expect(user.get('/user')).to.eventually.have.nested.property('items.quests.basilist', 1);
});
it('notifies inviting user that their invitation was accepted', async () => {
await invitedUser.post(`/groups/${guild._id}/join`);
const inviter = await user.get('/user');
const expectedData = {
headerText: t('invitationAcceptedHeader'),
bodyText: t('invitationAcceptedBody', {
username: invitedUser.auth.local.username,
groupName: guild.name,
}),
};
expect(inviter.notifications[1].type).to.eql('GROUP_INVITE_ACCEPTED');
expect(inviter.notifications[1].data).to.eql(expectedData);
});
it('awards Joined Guild achievement', async () => {
await invitedUser.post(`/groups/${guild._id}/join`);
@@ -155,23 +139,6 @@ describe('POST /group/:groupId/join', () => {
await expect(invitedUser.get('/user')).to.eventually.have.nested.property('party._id', party._id);
});
it('notifies inviting user that their invitation was accepted', async () => {
await invitedUser.post(`/groups/${party._id}/join`);
const inviter = await user.get('/user');
const expectedData = {
headerText: t('invitationAcceptedHeader'),
bodyText: t('invitationAcceptedBody', {
username: invitedUser.auth.local.username,
groupName: party.name,
}),
};
expect(inviter.notifications[0].type).to.eql('GROUP_INVITE_ACCEPTED');
expect(inviter.notifications[0].data).to.eql(expectedData);
});
it('clears invitation from user when joining party', async () => {
await invitedUser.post(`/groups/${party._id}/join`);

View File

@@ -610,7 +610,6 @@ api.joinGroup = {
if (hasInvitation) {
isUserInvited = true;
inviter = hasInvitation.inviter;
} else {
isUserInvited = group.privacy !== 'private';
}
@@ -634,42 +633,28 @@ api.joinGroup = {
group.leader = user._id; // If new user is only member -> set as leader
}
let promises = [user.save()];
if (group.type === 'party') {
// For parties we count the number of members from the database to get the correct value.
// See #12275 on why this is necessary and only done for parties.
const currentMembers = await group.getMemberCount();
group.memberCount = currentMembers + 1;
} else {
group.memberCount += 1;
}
let promises = [group.save(), user.save()];
// Load the inviter
if (inviter) inviter = await User.findById(inviter).exec();
// Check the inviter again, could be a deleted account
if (inviter) {
const data = {
headerText: common.i18n.t('invitationAcceptedHeader', inviter.preferences.language),
bodyText: common.i18n.t('invitationAcceptedBody', {
groupName: group.name,
username: user.profile.name,
}, inviter.preferences.language),
};
inviter.addNotification('GROUP_INVITE_ACCEPTED', data);
// Reward Inviter
if (group.type === 'party') {
if (!inviter.items.quests.basilist) {
inviter.items.quests.basilist = 0;
}
inviter.items.quests.basilist += 1;
inviter.markModified('items.quests');
promises.push(inviter.save());
}
}
group.memberCount = currentMembers + 1;
if (group.type === 'party' && inviter) {
// Handle awarding party-related achievements
if (group.memberCount > 1) {
const notification = new UserNotification({ type: 'ACHIEVEMENT_PARTY_UP' });
@@ -677,20 +662,12 @@ api.joinGroup = {
{
$or: [{ 'party._id': group._id }, { _id: user._id }],
'achievements.partyUp': { $ne: true },
_id: { $ne: inviter._id },
},
{
$set: { 'achievements.partyUp': true },
$push: { notifications: notification.toObject() },
},
).exec());
if (inviter) {
if (inviter.achievements.partyUp !== true) {
inviter.achievements.partyUp = true;
inviter.addNotification('ACHIEVEMENT_PARTY_UP');
}
}
}
if (group.memberCount > 3) {
@@ -700,22 +677,18 @@ api.joinGroup = {
{
$or: [{ 'party._id': group._id }, { _id: user._id }],
'achievements.partyOn': { $ne: true },
_id: { $ne: inviter._id },
},
{
$set: { 'achievements.partyOn': true },
$push: { notifications: notification.toObject() },
},
).exec());
}
} else {
group.memberCount += 1;
}
if (inviter) {
if (inviter.achievements.partyOn !== true) {
inviter.achievements.partyOn = true;
inviter.addNotification('ACHIEVEMENT_PARTY_ON');
}
}
}
}
promises.push(group.save());
const analyticsObject = {
uuid: user._id,
@@ -727,15 +700,9 @@ api.joinGroup = {
privacy: group.privacy,
headers: req.headers,
invited: isUserInvited,
seekingParty: group.type === 'party' ? seekingParty : null,
};
if (group.type === 'party') {
analyticsObject.seekingParty = seekingParty;
}
if (group.privacy === 'public') {
analyticsObject.groupName = group.name;
}
if (inviter) promises.push(inviter.save());
promises = await Promise.all(promises);
if (group.hasNotCancelled()) {
@@ -743,7 +710,7 @@ api.joinGroup = {
await group.updateGroupPlan();
}
const response = await Group.toJSONCleanChat(promises[0], user);
const response = await Group.toJSONCleanChat(group, user);
const leader = await User.findById(response.leader).select(nameFields).exec();
if (leader) {
response.leader = leader.toJSON({ minimize: true });