Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
aad915d
fix: read receipts not turning blue when all active users have read
abhinavkrin Feb 24, 2026
f24d4ea
added changeset
abhinavkrin Feb 24, 2026
f6f0360
unarchive subscription of only active users
abhinavkrin Mar 2, 2026
6b40a46
minor change
abhinavkrin Mar 2, 2026
2c503f1
Merge branch 'develop' into fix/read-receipts-ignore-deactivated-users
abhinavkrin Mar 6, 2026
6d56a2b
Merge branch 'develop' into fix/read-receipts-ignore-deactivated-users
abhinavkrin Mar 7, 2026
f530108
Merge branch 'develop' into fix/read-receipts-ignore-deactivated-users
abhinavkrin Mar 9, 2026
d081e06
test fix
abhinavkrin Mar 9, 2026
3cf6407
Merge branch 'develop' into fix/read-receipts-ignore-deactivated-users
abhinavkrin Mar 10, 2026
5d4e979
requested changes
abhinavkrin Mar 10, 2026
d1ce974
requested changes
abhinavkrin Mar 11, 2026
39dfaff
minor improvement
abhinavkrin Mar 11, 2026
a8ae48d
requested changes
abhinavkrin Mar 11, 2026
2a3559f
requested changes
abhinavkrin Mar 13, 2026
0c6af04
requested changes
abhinavkrin Mar 13, 2026
7deda6b
refactor: apply PR review feedback
abhinavkrin Mar 18, 2026
f0aeb1f
style: run prettier to fix line length in tests
abhinavkrin Mar 18, 2026
8422dd9
Apply suggestion from @abhinavkrin
abhinavkrin Mar 19, 2026
05c8922
improved tests
abhinavkrin Apr 2, 2026
b8b2fc7
switched from aggregation to normal queries
abhinavkrin Apr 15, 2026
3f7a15c
update tests
abhinavkrin Apr 15, 2026
10ff7eb
minor changes
abhinavkrin Apr 15, 2026
d39e70a
fix: preserve dm archive behavior for other users on deactivation
abhinavkrin Apr 16, 2026
d8cd6f0
remove serActiveStatus.spec.ts
abhinavkrin Apr 16, 2026
5f83bc4
move files around
sampaiodiego Apr 16, 2026
cefe518
Merge branch 'develop' into fix/read-receipts-ignore-deactivated-users
abhinavkrin Apr 17, 2026
8dce836
lint
abhinavkrin Apr 17, 2026
bb860a7
Merge branch 'develop' into fix/read-receipts-ignore-deactivated-users
abhinavkrin Apr 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: preserve dm archive behavior for other users on deactivation
Signed-off-by: Abhinav Kumar <abhinav@avitechlab.com>
  • Loading branch information
abhinavkrin committed Apr 16, 2026
commit d39e70a90a7cd577522a29aa249d5706065f3871
16 changes: 14 additions & 2 deletions apps/meteor/app/lib/server/functions/setUserActiveStatus.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { IUser, IUserEmail } from '@rocket.chat/core-typings';
import { isUserFederated, isDirectMessageRoom } from '@rocket.chat/core-typings';
import { Rooms, Users } from '@rocket.chat/models';
import { Rooms, Users, Subscriptions } from '@rocket.chat/models';
import { Accounts } from 'meteor/accounts-base';
import { check } from 'meteor/check';
import { Meteor } from 'meteor/meteor';
Expand All @@ -12,7 +12,12 @@ import { relinquishRoomOwnerships } from './relinquishRoomOwnerships';
import { callbacks } from '../../../../server/lib/callbacks';
import * as Mailer from '../../../mailer/server/api';
import { settings } from '../../../settings/server';
import { notifyOnRoomChangedById, notifyOnRoomChangedByUserDM, notifyOnUserChange } from '../lib/notifyListener';
import {
notifyOnRoomChangedById,
notifyOnRoomChangedByUserDM,
notifyOnSubscriptionChangedByNameAndRoomType,
notifyOnUserChange,
} from '../lib/notifyListener';

async function reactivateDirectConversations(userId: string) {
// since both users can be deactivated at the same time, we should just reactivate rooms if both users are active
Expand Down Expand Up @@ -107,6 +112,13 @@ export async function setUserActiveStatus(
await callbacks.run('afterDeactivateUser', user);
}

if (user.username) {
const { modifiedCount } = await Subscriptions.setArchivedForDMsWithUsername(user.username, !active);
if (modifiedCount) {
void notifyOnSubscriptionChangedByNameAndRoomType({ t: 'd', name: user.username });
}
}

if (active === false) {
await Users.unsetLoginTokens(userId);
await Rooms.setDmReadOnlyByUserId(userId, undefined, true, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ describe('setUserActiveStatus', () => {
setDmReadOnlyByUserId: sandbox.stub(),
getDirectConversationsByUserId: sandbox.stub(),
},
Subscriptions: {
setArchivedForDMsWithUsername: sandbox.stub(),
},
check: sandbox.stub(),
callbacks: {
run: sandbox.stub(),
Expand All @@ -32,6 +35,7 @@ describe('setUserActiveStatus', () => {
notifyOnUserChange: sandbox.stub(),
notifyOnRoomChangedByUserDM: sandbox.stub(),
notifyOnRoomChangedById: sandbox.stub(),
notifyOnSubscriptionChangedByNameAndRoomType: sandbox.stub(),
getSubscribedRoomsForUserWithDetails: sandbox.stub(),
shouldRemoveOrChangeOwner: sandbox.stub(),
getUserSingleOwnedRooms: sandbox.stub(),
Expand All @@ -56,7 +60,7 @@ describe('setUserActiveStatus', () => {
'meteor/meteor': { Meteor: { Error } },
'meteor/accounts-base': { Accounts: stubs.Accounts },
'@rocket.chat/core-typings': { isUserFederated: stubs.isUserFederated, isDirectMessageRoom: sinon.stub() },
'@rocket.chat/models': { Users: stubs.Users, Rooms: stubs.Rooms },
'@rocket.chat/models': { Users: stubs.Users, Rooms: stubs.Rooms, Subscriptions: stubs.Subscriptions },
'./closeOmnichannelConversations': { closeOmnichannelConversations: stubs.closeOmnichannelConversations },
'./getRoomsWithSingleOwner': {
shouldRemoveOrChangeOwner: stubs.shouldRemoveOrChangeOwner,
Expand All @@ -71,6 +75,7 @@ describe('setUserActiveStatus', () => {
notifyOnRoomChangedById: stubs.notifyOnRoomChangedById,
notifyOnRoomChangedByUserDM: stubs.notifyOnRoomChangedByUserDM,
notifyOnUserChange: stubs.notifyOnUserChange,
notifyOnSubscriptionChangedByNameAndRoomType: stubs.notifyOnSubscriptionChangedByNameAndRoomType,
},
});

Expand All @@ -91,9 +96,11 @@ describe('setUserActiveStatus', () => {
stubs.settings.get.returns(false);
stubs.Rooms.setDmReadOnlyByUserId.resolves({ modifiedCount: 0 });
stubs.Rooms.getDirectConversationsByUserId.returns({ toArray: sinon.stub().resolves([]) });
stubs.Subscriptions.setArchivedForDMsWithUsername.resolves({ modifiedCount: 0 });
stubs.notifyOnRoomChangedById.returns(undefined);
stubs.notifyOnUserChange.returns(undefined);
stubs.notifyOnRoomChangedByUserDM.returns(undefined);
stubs.notifyOnSubscriptionChangedByNameAndRoomType.returns(undefined);
});

afterEach(() => {
Expand All @@ -102,12 +109,16 @@ describe('setUserActiveStatus', () => {

describe('Successful status changes', () => {
it('should deactivate a user successfully', async () => {
stubs.Subscriptions.setArchivedForDMsWithUsername.resolves({ modifiedCount: 1 });

const result = await setUserActiveStatus(userId, false);

expect(result).to.be.true;
expect(stubs.Users.setUserActive.calledWith(userId, false)).to.be.true;
expect(stubs.Users.unsetLoginTokens.calledWith(userId)).to.be.true;
expect(stubs.Rooms.setDmReadOnlyByUserId.calledWith(userId, undefined, true, false)).to.be.true;
expect(stubs.Subscriptions.setArchivedForDMsWithUsername.calledWith(username, true)).to.be.true;
expect(stubs.notifyOnSubscriptionChangedByNameAndRoomType.calledWith({ t: 'd', name: username })).to.be.true;
expect(stubs.callbacks.run.calledWith('afterDeactivateUser', sinon.match({ _id: userId }))).to.be.true;
expect(
stubs.notifyOnUserChange.calledWith(
Expand All @@ -125,6 +136,7 @@ describe('setUserActiveStatus', () => {
expect(result).to.be.true;
expect(stubs.callbacks.run.calledWith('beforeActivateUser', sinon.match({ _id: userId }))).to.be.true;
expect(stubs.Users.setUserActive.calledWith(userId, true)).to.be.true;
expect(stubs.Subscriptions.setArchivedForDMsWithUsername.calledWith(username, false)).to.be.true;
expect(stubs.callbacks.run.calledWith('afterActivateUser', sinon.match({ _id: userId }))).to.be.true;
expect(stubs.Users.unsetReason.calledWith(userId)).to.be.true;
expect(stubs.notifyOnUserChange.calledWith(sinon.match({ clientAction: 'updated', id: userId, diff: { active: true } }))).to.be.true;
Expand Down
1 change: 1 addition & 0 deletions packages/model-typings/src/models/ISubscriptionsModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ export interface ISubscriptionsModel extends IBaseModel<ISubscription> {
removeRoleById(_id: string, role: string): Promise<UpdateResult>;
updateDirectFNameByName(name: string, fname: string): Promise<UpdateResult | Document>;
setArchivedByUserId(userId: string, archived: boolean): Promise<UpdateResult | Document>;
setArchivedForDMsWithUsername(username: string, archived: boolean): Promise<UpdateResult | Document>;
updateUserHighlights(userId: string, userHighlights: any): Promise<UpdateResult | Document>;
updateNotificationUserPreferences(
userId: string,
Expand Down
15 changes: 15 additions & 0 deletions packages/models/src/models/Subscriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1742,6 +1742,21 @@ export class SubscriptionsRaw extends BaseRaw<ISubscription> implements ISubscri
return this.updateOne(query, update);
}

setArchivedForDMsWithUsername(username: string, archived: boolean): Promise<UpdateResult | Document> {
const query: Filter<ISubscription> = {
name: username,
t: 'd',
};

const update: UpdateFilter<ISubscription> = {
$set: {
archived,
},
};

return this.updateMany(query, update);
}

setArchivedByUserId(userId: string, archived: boolean): Promise<UpdateResult | Document> {
const query: Filter<ISubscription> = {
'u._id': userId,
Expand Down
Loading