fix: Bug - Same user logged in on multiple clients acts like 2 different users
Collapse home and signal-server actor aliases into one canonical room member so multi-device sessions no longer duplicate the local user in the members panel. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -26,6 +26,7 @@ import { selectCurrentRoom, selectSavedRooms } from './rooms.selectors';
|
||||
import { normalizeRoomAccessControl, resolveLegacyRole } from '../../domains/access-control';
|
||||
import {
|
||||
areRoomMembersEqual,
|
||||
collapseSelfRoomMembers,
|
||||
findRoomMember,
|
||||
mergeRoomMembers,
|
||||
pruneRoomMembers,
|
||||
@@ -36,6 +37,7 @@ import {
|
||||
updateRoomMemberRole,
|
||||
upsertRoomMember
|
||||
} from './room-members.helpers';
|
||||
import { resolveRoomMemberActorIdentity } from './room-member-identity.rules';
|
||||
import { SignalServerAuthService } from '../../domains/authentication/application/services/signal-server-auth.service';
|
||||
import { isSelfPresenceUserId } from '../../domains/authentication/domain/logic/self-presence-identity.rules';
|
||||
|
||||
@@ -63,7 +65,7 @@ export class RoomMembersSyncEffects {
|
||||
room.members ?? [],
|
||||
this.buildCurrentUserMember(room, currentUser, true)
|
||||
);
|
||||
const actions = this.createRoomMemberUpdateActions(room, members);
|
||||
const actions = this.createRoomMemberUpdateActions(room, members, currentUser, true);
|
||||
|
||||
return actions.length > 0 ? actions : EMPTY;
|
||||
})
|
||||
@@ -93,7 +95,7 @@ export class RoomMembersSyncEffects {
|
||||
currentRoom.members ?? [],
|
||||
this.buildCurrentUserMember(currentRoom, currentUser, true)
|
||||
);
|
||||
const actions = this.createRoomMemberUpdateActions(currentRoom, members);
|
||||
const actions = this.createRoomMemberUpdateActions(currentRoom, members, currentUser, true);
|
||||
|
||||
return actions.length > 0 ? actions : EMPTY;
|
||||
})
|
||||
@@ -104,13 +106,20 @@ export class RoomMembersSyncEffects {
|
||||
syncRoleChangesIntoCurrentRoom$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(UsersActions.updateUserRole),
|
||||
withLatestFrom(this.store.select(selectCurrentRoom)),
|
||||
mergeMap(([{ userId, role }, currentRoom]) => {
|
||||
withLatestFrom(
|
||||
this.store.select(selectCurrentRoom),
|
||||
this.store.select(selectCurrentUser)
|
||||
),
|
||||
mergeMap(([
|
||||
{ userId, role },
|
||||
currentRoom,
|
||||
currentUser
|
||||
]) => {
|
||||
if (!currentRoom)
|
||||
return EMPTY;
|
||||
|
||||
const members = updateRoomMemberRole(currentRoom.members ?? [], userId, role);
|
||||
const actions = this.createRoomMemberUpdateActions(currentRoom, members);
|
||||
const actions = this.createRoomMemberUpdateActions(currentRoom, members, currentUser, true);
|
||||
|
||||
return actions.length > 0 ? actions : EMPTY;
|
||||
})
|
||||
@@ -194,7 +203,12 @@ export class RoomMembersSyncEffects {
|
||||
members = upsertRoomMember(members, this.buildPresenceMember(room, user));
|
||||
}
|
||||
|
||||
const actions = this.createRoomMemberUpdateActions(room, members);
|
||||
const actions = this.createRoomMemberUpdateActions(
|
||||
room,
|
||||
members,
|
||||
currentUser ?? null,
|
||||
currentRoom?.id === room.id
|
||||
);
|
||||
|
||||
return actions.length > 0 ? actions : EMPTY;
|
||||
}
|
||||
@@ -211,7 +225,12 @@ export class RoomMembersSyncEffects {
|
||||
room.members ?? [],
|
||||
this.buildPresenceMember(room, joinedUser)
|
||||
);
|
||||
const actions = this.createRoomMemberUpdateActions(room, members);
|
||||
const actions = this.createRoomMemberUpdateActions(
|
||||
room,
|
||||
members,
|
||||
currentUser ?? null,
|
||||
currentRoom?.id === room.id
|
||||
);
|
||||
|
||||
return actions.length > 0 ? actions : EMPTY;
|
||||
}
|
||||
@@ -221,7 +240,12 @@ export class RoomMembersSyncEffects {
|
||||
return EMPTY;
|
||||
|
||||
const members = touchRoomMemberLastSeen(room.members ?? [], signalingMessage.oderId, Date.now());
|
||||
const actions = this.createRoomMemberUpdateActions(room, members);
|
||||
const actions = this.createRoomMemberUpdateActions(
|
||||
room,
|
||||
members,
|
||||
currentUser ?? null,
|
||||
currentRoom?.id === room.id
|
||||
);
|
||||
|
||||
return actions.length > 0 ? actions : EMPTY;
|
||||
}
|
||||
@@ -339,15 +363,30 @@ export class RoomMembersSyncEffects {
|
||||
}
|
||||
|
||||
private buildCurrentUserMember(room: Room, currentUser: User, isCurrentRoom: boolean): RoomMember {
|
||||
const existingMember = findRoomMember(room.members ?? [], currentUser.oderId || currentUser.id);
|
||||
const role = room.hostId === currentUser.id
|
||||
const selfIds = this.signalServerAuth.resolveSelfPresenceUserIdsForRoom(currentUser, room.sourceUrl);
|
||||
const identity = resolveRoomMemberActorIdentity(
|
||||
room,
|
||||
currentUser,
|
||||
(serverUrl, fallback) => this.signalServerAuth.resolveActorUserIdForServer(serverUrl, fallback),
|
||||
(user, roomSourceUrl) => this.signalServerAuth.resolveSelfPresenceUserIdsForRoom(user, roomSourceUrl)
|
||||
);
|
||||
const actorUserId = identity.actorUserId;
|
||||
const existingMember = findRoomMember(room.members ?? [], actorUserId)
|
||||
?? (room.members ?? []).find((member) =>
|
||||
isSelfPresenceUserId(member.oderId, selfIds) || isSelfPresenceUserId(member.id, selfIds)
|
||||
);
|
||||
const isHost = room.hostId === currentUser.id
|
||||
|| room.hostId === currentUser.oderId
|
||||
|| room.hostId === actorUserId;
|
||||
const role = isHost
|
||||
? 'host'
|
||||
: (isCurrentRoom ? currentUser.role : existingMember?.role ?? 'member');
|
||||
const seenAt = existingMember?.lastSeenAt ?? currentUser.joinedAt ?? Date.now();
|
||||
|
||||
return {
|
||||
...roomMemberFromUser(currentUser, seenAt, role),
|
||||
id: existingMember?.id ?? currentUser.id,
|
||||
id: identity.existingMemberId ?? actorUserId,
|
||||
oderId: actorUserId,
|
||||
joinedAt: existingMember?.joinedAt ?? currentUser.joinedAt ?? seenAt,
|
||||
avatarUrl: currentUser.avatarUrl ?? existingMember?.avatarUrl,
|
||||
role
|
||||
@@ -375,10 +414,24 @@ export class RoomMembersSyncEffects {
|
||||
};
|
||||
}
|
||||
|
||||
private createRoomMemberUpdateActions(room: Room, members: RoomMember[]): Action[] {
|
||||
return areRoomMembersEqual(room.members ?? [], members)
|
||||
private createRoomMemberUpdateActions(
|
||||
room: Room,
|
||||
members: RoomMember[],
|
||||
currentUser: User | null = null,
|
||||
isCurrentRoom = false
|
||||
): Action[] {
|
||||
const normalizedMembers = currentUser
|
||||
? collapseSelfRoomMembers(
|
||||
members,
|
||||
this.signalServerAuth.resolveSelfPresenceUserIdsForRoom(currentUser, room.sourceUrl),
|
||||
this.buildCurrentUserMember(room, currentUser, isCurrentRoom),
|
||||
Date.now()
|
||||
)
|
||||
: pruneRoomMembers(members);
|
||||
|
||||
return areRoomMembersEqual(room.members ?? [], normalizedMembers)
|
||||
? []
|
||||
: [RoomsActions.updateRoom({ roomId: room.id, changes: { members } })];
|
||||
: [RoomsActions.updateRoom({ roomId: room.id, changes: { members: normalizedMembers } })];
|
||||
}
|
||||
|
||||
private resolveRoleSyncRoom(
|
||||
@@ -491,7 +544,7 @@ export class RoomMembersSyncEffects {
|
||||
members
|
||||
});
|
||||
|
||||
return this.createRoomMemberUpdateActions(room, members);
|
||||
return this.createRoomMemberUpdateActions(room, members, currentUser, isCurrentRoom);
|
||||
}
|
||||
|
||||
private handleMemberRoster(
|
||||
@@ -514,7 +567,12 @@ export class RoomMembersSyncEffects {
|
||||
);
|
||||
}
|
||||
|
||||
const actions = this.createRoomMemberUpdateActions(room, members);
|
||||
const actions = this.createRoomMemberUpdateActions(
|
||||
room,
|
||||
members,
|
||||
currentUser,
|
||||
currentRoom?.id === room.id
|
||||
);
|
||||
const currentUserId = currentUser?.oderId || currentUser?.id;
|
||||
|
||||
for (const member of members) {
|
||||
@@ -551,7 +609,9 @@ export class RoomMembersSyncEffects {
|
||||
|
||||
const actions = this.createRoomMemberUpdateActions(
|
||||
room,
|
||||
removeRoomMember(room.members ?? [], event.targetUserId, event.oderId)
|
||||
removeRoomMember(room.members ?? [], event.targetUserId, event.oderId),
|
||||
null,
|
||||
currentRoom?.id === room.id
|
||||
);
|
||||
const departedUserId = event.oderId ?? event.targetUserId;
|
||||
|
||||
@@ -652,7 +712,9 @@ export class RoomMembersSyncEffects {
|
||||
|
||||
const actions = this.createRoomMemberUpdateActions(
|
||||
room,
|
||||
updateRoomMemberRole(room.members ?? [], event.targetUserId, event.role)
|
||||
updateRoomMemberRole(room.members ?? [], event.targetUserId, event.role),
|
||||
null,
|
||||
currentRoom?.id === room.id
|
||||
);
|
||||
|
||||
if (currentRoom?.id === room.id) {
|
||||
|
||||
Reference in New Issue
Block a user