feat: Add user metadata changing display name and description with sync
All checks were successful
Queue Release Build / prepare (push) Successful in 28s
Deploy Web Apps / deploy (push) Successful in 5m2s
Queue Release Build / build-windows (push) Successful in 16m44s
Queue Release Build / build-linux (push) Successful in 27m12s
Queue Release Build / finalize (push) Successful in 22s

This commit is contained in:
2026-04-17 22:04:18 +02:00
parent 3ba8a2c9eb
commit bd21568726
41 changed files with 1176 additions and 191 deletions

View File

@@ -36,6 +36,51 @@ function normalizeAvatarUpdatedAt(value: unknown): number | undefined {
: undefined;
}
function normalizeProfileUpdatedAt(value: unknown): number | undefined {
return typeof value === 'number' && Number.isFinite(value) && value > 0
? value
: undefined;
}
function normalizeDescription(value: unknown): string | undefined {
if (typeof value !== 'string') {
return undefined;
}
const normalized = value.trim();
return normalized || undefined;
}
function hasOwnProperty(object: object, key: string): boolean {
return Object.prototype.hasOwnProperty.call(object, key);
}
function mergeProfileFields(
existingMember: Pick<RoomMember, 'displayName' | 'description' | 'profileUpdatedAt'>,
incomingMember: Pick<RoomMember, 'displayName' | 'description' | 'profileUpdatedAt'>,
preferIncomingFallback: boolean
): Pick<RoomMember, 'displayName' | 'description' | 'profileUpdatedAt'> {
const existingUpdatedAt = existingMember.profileUpdatedAt ?? 0;
const incomingUpdatedAt = incomingMember.profileUpdatedAt ?? 0;
const preferIncoming = incomingUpdatedAt === existingUpdatedAt
? preferIncomingFallback
: incomingUpdatedAt > existingUpdatedAt;
const incomingHasDescription = hasOwnProperty(incomingMember, 'description');
const incomingDescription = normalizeDescription(incomingMember.description);
const existingDescription = normalizeDescription(existingMember.description);
return {
displayName: preferIncoming
? (incomingMember.displayName || existingMember.displayName)
: (existingMember.displayName || incomingMember.displayName),
description: preferIncoming
? (incomingHasDescription ? incomingDescription : existingDescription)
: existingDescription,
profileUpdatedAt: Math.max(existingUpdatedAt, incomingUpdatedAt) || undefined
};
}
function mergeAvatarFields(
existingMember: Pick<RoomMember, 'avatarUrl' | 'avatarHash' | 'avatarMime' | 'avatarUpdatedAt'>,
incomingMember: Pick<RoomMember, 'avatarUrl' | 'avatarHash' | 'avatarMime' | 'avatarUpdatedAt'>,
@@ -73,12 +118,12 @@ function normalizeMember(member: RoomMember, now = Date.now()): RoomMember {
typeof member.joinedAt === 'number' && Number.isFinite(member.joinedAt)
? member.joinedAt
: lastSeenAt;
return {
const nextMember: RoomMember = {
id: member.id || key,
oderId: member.oderId || undefined,
username: member.username || fallbackUsername(member),
displayName: fallbackDisplayName(member),
profileUpdatedAt: normalizeProfileUpdatedAt(member.profileUpdatedAt),
avatarUrl: member.avatarUrl || undefined,
avatarHash: member.avatarHash || undefined,
avatarMime: member.avatarMime || undefined,
@@ -88,6 +133,12 @@ function normalizeMember(member: RoomMember, now = Date.now()): RoomMember {
joinedAt,
lastSeenAt
};
if (hasOwnProperty(member, 'description')) {
nextMember.description = normalizeDescription(member.description);
}
return nextMember;
}
function compareMembers(firstMember: RoomMember, secondMember: RoomMember): number {
@@ -128,6 +179,7 @@ function mergeMembers(
const normalizedExisting = normalizeMember(existingMember, now);
const preferIncoming = normalizedIncoming.lastSeenAt >= normalizedExisting.lastSeenAt;
const profileFields = mergeProfileFields(normalizedExisting, normalizedIncoming, preferIncoming);
const avatarFields = mergeAvatarFields(normalizedExisting, normalizedIncoming, preferIncoming);
return {
@@ -136,9 +188,7 @@ function mergeMembers(
username: preferIncoming
? (normalizedIncoming.username || normalizedExisting.username)
: (normalizedExisting.username || normalizedIncoming.username),
displayName: preferIncoming
? (normalizedIncoming.displayName || normalizedExisting.displayName)
: (normalizedExisting.displayName || normalizedIncoming.displayName),
...profileFields,
...avatarFields,
role: mergeRole(normalizedExisting.role, normalizedIncoming.role, preferIncoming),
roleIds: preferIncoming
@@ -177,6 +227,8 @@ export function roomMemberFromUser(
oderId: user.oderId || undefined,
username: user.username || '',
displayName: user.displayName || user.username || 'User',
description: user.description,
profileUpdatedAt: user.profileUpdatedAt,
avatarUrl: user.avatarUrl,
avatarHash: user.avatarHash,
avatarMime: user.avatarMime,