fix: Bug - Users appear as both online and offline
Align chat message sender ids with per-server presence identities so profile cards opened from message authors resolve the same live user state as the members panel. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import type { User } from '../../shared-kernel';
|
||||
import {
|
||||
buildUserIdentityLookup,
|
||||
findUserEntityByIdentity,
|
||||
resolveUserByIdentity
|
||||
} from './user-identity-lookup.rules';
|
||||
|
||||
function createUser(overrides: Partial<User> = {}): User {
|
||||
return {
|
||||
id: 'server-user-1',
|
||||
oderId: 'server-user-1',
|
||||
username: 'alice',
|
||||
displayName: 'Alice',
|
||||
status: 'online',
|
||||
role: 'member',
|
||||
joinedAt: 1,
|
||||
presenceServerIds: ['room-1'],
|
||||
isOnline: true,
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
describe('user-identity-lookup.rules', () => {
|
||||
it('indexes id, oderId, and peerId aliases in buildUserIdentityLookup', () => {
|
||||
const user = createUser({
|
||||
id: 'server-user-1',
|
||||
oderId: 'oder-1',
|
||||
peerId: 'peer-1'
|
||||
});
|
||||
const lookup = buildUserIdentityLookup([user]);
|
||||
|
||||
expect(resolveUserByIdentity(lookup, 'server-user-1')).toBe(user);
|
||||
expect(resolveUserByIdentity(lookup, 'oder-1')).toBe(user);
|
||||
expect(resolveUserByIdentity(lookup, 'peer-1')).toBe(user);
|
||||
});
|
||||
|
||||
it('findUserEntityByIdentity resolves users keyed by a different entity id', () => {
|
||||
const user = createUser({
|
||||
id: 'server-user-1',
|
||||
oderId: 'oder-1'
|
||||
});
|
||||
const entities = {
|
||||
'server-user-1': user
|
||||
};
|
||||
|
||||
expect(findUserEntityByIdentity(entities, 'oder-1')).toBe(user);
|
||||
expect(findUserEntityByIdentity(entities, 'home-user-1')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
68
toju-app/src/app/store/users/user-identity-lookup.rules.ts
Normal file
68
toju-app/src/app/store/users/user-identity-lookup.rules.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import type { User } from '../../shared-kernel';
|
||||
|
||||
type UserIdentityFields = Pick<User, 'id' | 'oderId' | 'peerId'>;
|
||||
|
||||
function collectUserIdentityKeys(user: UserIdentityFields): string[] {
|
||||
const keys: string[] = [];
|
||||
|
||||
if (user.id?.trim()) {
|
||||
keys.push(user.id.trim());
|
||||
}
|
||||
|
||||
if (user.oderId?.trim() && user.oderId !== user.id) {
|
||||
keys.push(user.oderId.trim());
|
||||
}
|
||||
|
||||
if (user.peerId?.trim() && user.peerId !== user.id && user.peerId !== user.oderId) {
|
||||
keys.push(user.peerId.trim());
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
/** Build a lookup map keyed by every known identity alias for each user. */
|
||||
export function buildUserIdentityLookup(users: readonly User[]): ReadonlyMap<string, User> {
|
||||
const lookup = new Map<string, User>();
|
||||
|
||||
for (const user of users) {
|
||||
for (const key of collectUserIdentityKeys(user)) {
|
||||
lookup.set(key, user);
|
||||
}
|
||||
}
|
||||
|
||||
return lookup;
|
||||
}
|
||||
|
||||
/** Resolve a user from a pre-built identity lookup map. */
|
||||
export function resolveUserByIdentity(
|
||||
lookup: ReadonlyMap<string, User>,
|
||||
identity: string | undefined
|
||||
): User | undefined {
|
||||
if (!identity?.trim()) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return lookup.get(identity.trim());
|
||||
}
|
||||
|
||||
/** Resolve a user entity when NgRx entity keys may not match the queried identity. */
|
||||
export function findUserEntityByIdentity(
|
||||
entities: Record<string, User | undefined>,
|
||||
identity: string | undefined
|
||||
): User | undefined {
|
||||
if (!identity?.trim()) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const trimmed = identity.trim();
|
||||
const direct = entities[trimmed];
|
||||
|
||||
if (direct) {
|
||||
return direct;
|
||||
}
|
||||
|
||||
return Object.values(entities).find((user): user is User =>
|
||||
!!user
|
||||
&& (user.id === trimmed || user.oderId === trimmed || user.peerId === trimmed)
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user