mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-16 06:07:21 +01:00
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:
47
migrations/archive/2024/2024_purge_invite_accepted.js
Normal file
47
migrations/archive/2024/2024_purge_invite_accepted.js
Normal 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
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -85,22 +85,6 @@ describe('POST /group/:groupId/join', () => {
|
|||||||
await expect(user.get('/user')).to.eventually.have.nested.property('items.quests.basilist', 1);
|
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 () => {
|
it('awards Joined Guild achievement', async () => {
|
||||||
await invitedUser.post(`/groups/${guild._id}/join`);
|
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);
|
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 () => {
|
it('clears invitation from user when joining party', async () => {
|
||||||
await invitedUser.post(`/groups/${party._id}/join`);
|
await invitedUser.post(`/groups/${party._id}/join`);
|
||||||
|
|
||||||
|
|||||||
@@ -610,7 +610,6 @@ api.joinGroup = {
|
|||||||
|
|
||||||
if (hasInvitation) {
|
if (hasInvitation) {
|
||||||
isUserInvited = true;
|
isUserInvited = true;
|
||||||
inviter = hasInvitation.inviter;
|
|
||||||
} else {
|
} else {
|
||||||
isUserInvited = group.privacy !== 'private';
|
isUserInvited = group.privacy !== 'private';
|
||||||
}
|
}
|
||||||
@@ -634,42 +633,28 @@ api.joinGroup = {
|
|||||||
group.leader = user._id; // If new user is only member -> set as leader
|
group.leader = user._id; // If new user is only member -> set as leader
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let promises = [user.save()];
|
||||||
|
|
||||||
if (group.type === 'party') {
|
if (group.type === 'party') {
|
||||||
// For parties we count the number of members from the database to get the correct value.
|
// 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.
|
// See #12275 on why this is necessary and only done for parties.
|
||||||
const currentMembers = await group.getMemberCount();
|
const currentMembers = await group.getMemberCount();
|
||||||
group.memberCount = currentMembers + 1;
|
// Load the inviter
|
||||||
} else {
|
if (inviter) inviter = await User.findById(inviter).exec();
|
||||||
group.memberCount += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let promises = [group.save(), user.save()];
|
// Check the inviter again, could be a deleted account
|
||||||
|
if (inviter) {
|
||||||
// Load the inviter
|
// Reward 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) {
|
if (!inviter.items.quests.basilist) {
|
||||||
inviter.items.quests.basilist = 0;
|
inviter.items.quests.basilist = 0;
|
||||||
}
|
}
|
||||||
inviter.items.quests.basilist += 1;
|
inviter.items.quests.basilist += 1;
|
||||||
inviter.markModified('items.quests');
|
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) {
|
if (group.memberCount > 1) {
|
||||||
const notification = new UserNotification({ type: 'ACHIEVEMENT_PARTY_UP' });
|
const notification = new UserNotification({ type: 'ACHIEVEMENT_PARTY_UP' });
|
||||||
|
|
||||||
@@ -677,20 +662,12 @@ api.joinGroup = {
|
|||||||
{
|
{
|
||||||
$or: [{ 'party._id': group._id }, { _id: user._id }],
|
$or: [{ 'party._id': group._id }, { _id: user._id }],
|
||||||
'achievements.partyUp': { $ne: true },
|
'achievements.partyUp': { $ne: true },
|
||||||
_id: { $ne: inviter._id },
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$set: { 'achievements.partyUp': true },
|
$set: { 'achievements.partyUp': true },
|
||||||
$push: { notifications: notification.toObject() },
|
$push: { notifications: notification.toObject() },
|
||||||
},
|
},
|
||||||
).exec());
|
).exec());
|
||||||
|
|
||||||
if (inviter) {
|
|
||||||
if (inviter.achievements.partyUp !== true) {
|
|
||||||
inviter.achievements.partyUp = true;
|
|
||||||
inviter.addNotification('ACHIEVEMENT_PARTY_UP');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group.memberCount > 3) {
|
if (group.memberCount > 3) {
|
||||||
@@ -700,23 +677,19 @@ api.joinGroup = {
|
|||||||
{
|
{
|
||||||
$or: [{ 'party._id': group._id }, { _id: user._id }],
|
$or: [{ 'party._id': group._id }, { _id: user._id }],
|
||||||
'achievements.partyOn': { $ne: true },
|
'achievements.partyOn': { $ne: true },
|
||||||
_id: { $ne: inviter._id },
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$set: { 'achievements.partyOn': true },
|
$set: { 'achievements.partyOn': true },
|
||||||
$push: { notifications: notification.toObject() },
|
$push: { notifications: notification.toObject() },
|
||||||
},
|
},
|
||||||
).exec());
|
).exec());
|
||||||
|
|
||||||
if (inviter) {
|
|
||||||
if (inviter.achievements.partyOn !== true) {
|
|
||||||
inviter.achievements.partyOn = true;
|
|
||||||
inviter.addNotification('ACHIEVEMENT_PARTY_ON');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
group.memberCount += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
promises.push(group.save());
|
||||||
|
|
||||||
const analyticsObject = {
|
const analyticsObject = {
|
||||||
uuid: user._id,
|
uuid: user._id,
|
||||||
hitType: 'event',
|
hitType: 'event',
|
||||||
@@ -727,15 +700,9 @@ api.joinGroup = {
|
|||||||
privacy: group.privacy,
|
privacy: group.privacy,
|
||||||
headers: req.headers,
|
headers: req.headers,
|
||||||
invited: isUserInvited,
|
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);
|
promises = await Promise.all(promises);
|
||||||
|
|
||||||
if (group.hasNotCancelled()) {
|
if (group.hasNotCancelled()) {
|
||||||
@@ -743,7 +710,7 @@ api.joinGroup = {
|
|||||||
await group.updateGroupPlan();
|
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();
|
const leader = await User.findById(response.leader).select(nameFields).exec();
|
||||||
if (leader) {
|
if (leader) {
|
||||||
response.leader = leader.toJSON({ minimize: true });
|
response.leader = leader.toJSON({ minimize: true });
|
||||||
|
|||||||
Reference in New Issue
Block a user