diff --git a/apps/meteor/app/lib/server/functions/deleteUser.ts b/apps/meteor/app/lib/server/functions/deleteUser.ts index 21ae3fecf81c..d63593af1486 100644 --- a/apps/meteor/app/lib/server/functions/deleteUser.ts +++ b/apps/meteor/app/lib/server/functions/deleteUser.ts @@ -41,9 +41,11 @@ export async function deleteUser(userId: string, confirmRelinquish = false, dele // Users without username can't do anything, so there is nothing to remove if (user.username != null) { + let userToReplaceWhenUnlinking: IUser | null = null; + const nameAlias = i18n.t('Removed_User'); await relinquishRoomOwnerships(userId, subscribedRooms); - const messageErasureType = settings.get('Message_ErasureType'); + const messageErasureType = settings.get<'Delete' | 'Unlink' | 'Keep'>('Message_ErasureType'); switch (messageErasureType) { case 'Delete': const store = FileUpload.getStore('Uploads'); @@ -68,12 +70,11 @@ export async function deleteUser(userId: string, confirmRelinquish = false, dele break; case 'Unlink': - const rocketCat = await Users.findOneById('rocket.cat'); - const nameAlias = i18n.t('Removed_User'); - if (!rocketCat?._id || !rocketCat?.username) { + userToReplaceWhenUnlinking = await Users.findOneById('rocket.cat'); + if (!userToReplaceWhenUnlinking?._id || !userToReplaceWhenUnlinking?.username) { break; } - await Messages.unlinkUserId(userId, rocketCat?._id, rocketCat?.username, nameAlias); + await Messages.unlinkUserId(userId, userToReplaceWhenUnlinking?._id, userToReplaceWhenUnlinking?.username, nameAlias); break; } @@ -104,8 +105,16 @@ export async function deleteUser(userId: string, confirmRelinquish = false, dele await Integrations.disableByUserId(userId); // Disables all the integrations which rely on the user being deleted. // Don't broadcast user.deleted for Erasure Type of 'Keep' so that messages don't disappear from logged in sessions - if (messageErasureType !== 'Keep') { - void api.broadcast('user.deleted', user); + if (messageErasureType === 'Delete') { + void api.broadcast('user.deleted', user, { + messageErasureType, + }); + } + if (messageErasureType === 'Unlink' && userToReplaceWhenUnlinking) { + void api.broadcast('user.deleted', user, { + messageErasureType, + replaceByUser: { _id: userToReplaceWhenUnlinking._id, username: userToReplaceWhenUnlinking?.username, alias: nameAlias }, + }); } } diff --git a/apps/meteor/client/startup/UserDeleted.ts b/apps/meteor/client/startup/UserDeleted.ts index 0e7b75bf4efa..bbaeb6bc0229 100644 --- a/apps/meteor/client/startup/UserDeleted.ts +++ b/apps/meteor/client/startup/UserDeleted.ts @@ -4,7 +4,23 @@ import { ChatMessage } from '../../app/models/client'; import { Notifications } from '../../app/notifications/client'; Meteor.startup(() => { - Notifications.onLogged('Users:Deleted', ({ userId }) => { + Notifications.onLogged('Users:Deleted', ({ userId, messageErasureType, replaceByUser }) => { + if (messageErasureType === 'Unlink' && replaceByUser) { + return ChatMessage.update( + { + 'u._id': userId, + }, + { + $set: { + 'alias': replaceByUser.alias, + 'u._id': replaceByUser._id, + 'u.username': replaceByUser.username, + 'u.name': undefined, + }, + }, + { multi: true }, + ); + } ChatMessage.remove({ 'u._id': userId, }); diff --git a/apps/meteor/server/modules/listeners/listeners.module.ts b/apps/meteor/server/modules/listeners/listeners.module.ts index f21081e43d0a..ca370ad64c5e 100644 --- a/apps/meteor/server/modules/listeners/listeners.module.ts +++ b/apps/meteor/server/modules/listeners/listeners.module.ts @@ -96,9 +96,10 @@ export class ListenersModule { }); }); - service.onEvent('user.deleted', ({ _id: userId }) => { + service.onEvent('user.deleted', ({ _id: userId }, data) => { notifications.notifyLoggedInThisInstance('Users:Deleted', { userId, + ...data, }); }); diff --git a/ee/packages/ddp-client/src/types/streams.ts b/ee/packages/ddp-client/src/types/streams.ts index a32dec470564..65a488f8b881 100644 --- a/ee/packages/ddp-client/src/types/streams.ts +++ b/ee/packages/ddp-client/src/types/streams.ts @@ -224,9 +224,16 @@ export interface StreamerEvents { { key: 'Users:Deleted'; args: [ - { - userId: IUser['_id']; - }, + | { + userId: IUser['_id']; + messageErasureType: 'Delete'; + replaceByUser?: never; + } + | { + userId: IUser['_id']; + messageErasureType: 'Unlink'; + replaceByUser?: { _id: IUser['_id']; username: IUser['username']; alias: string }; + }, ]; }, { diff --git a/packages/core-services/src/events/Events.ts b/packages/core-services/src/events/Events.ts index 1cba636cd7cf..b249882d10a6 100644 --- a/packages/core-services/src/events/Events.ts +++ b/packages/core-services/src/events/Events.ts @@ -95,7 +95,17 @@ export type EventSignatures = { 'stream'([streamer, eventName, payload]: [string, string, any[]]): void; 'subscription'(data: { action: string; subscription: Partial }): void; 'user.avatarUpdate'(user: Partial): void; - 'user.deleted'(user: Pick): void; + 'user.deleted'( + user: Pick, + data: + | { + messageErasureType: 'Delete'; + } + | { + messageErasureType: 'Unlink'; + replaceByUser: { _id: IUser['_id']; username: IUser['username']; alias: string }; + }, + ): void; 'user.deleteCustomStatus'(userStatus: IUserStatus): void; 'user.nameChanged'(user: Pick): void; 'user.realNameChanged'(user: Partial): void;