feat: Add profile images

This commit is contained in:
2026-04-17 03:05:47 +02:00
parent 35b616fb77
commit 17738ec484
49 changed files with 2622 additions and 89 deletions

View File

@@ -11,6 +11,9 @@ export async function handleSaveUser(command: SaveUserCommand, dataSource: DataS
username: user.username ?? null,
displayName: user.displayName ?? null,
avatarUrl: user.avatarUrl ?? null,
avatarHash: user.avatarHash ?? null,
avatarMime: user.avatarMime ?? null,
avatarUpdatedAt: user.avatarUpdatedAt ?? null,
status: user.status ?? null,
role: user.role ?? null,
joinedAt: user.joinedAt ?? null,

View File

@@ -47,6 +47,9 @@ export function rowToUser(row: UserEntity) {
username: row.username ?? '',
displayName: row.displayName ?? '',
avatarUrl: row.avatarUrl ?? undefined,
avatarHash: row.avatarHash ?? undefined,
avatarMime: row.avatarMime ?? undefined,
avatarUpdatedAt: row.avatarUpdatedAt ?? undefined,
status: row.status ?? 'offline',
role: row.role ?? 'member',
joinedAt: row.joinedAt ?? 0,

View File

@@ -67,6 +67,9 @@ export interface RoomMemberRecord {
username: string;
displayName: string;
avatarUrl?: string;
avatarHash?: string;
avatarMime?: string;
avatarUpdatedAt?: number;
role: RoomMemberRole;
roleIds?: string[];
joinedAt: number;
@@ -336,6 +339,9 @@ function normalizeRoomMember(rawMember: Record<string, unknown>, now: number): R
const username = trimmedString(rawMember, 'username');
const displayName = trimmedString(rawMember, 'displayName');
const avatarUrl = trimmedString(rawMember, 'avatarUrl');
const avatarHash = trimmedString(rawMember, 'avatarHash');
const avatarMime = trimmedString(rawMember, 'avatarMime');
const avatarUpdatedAt = isFiniteNumber(rawMember['avatarUpdatedAt']) ? rawMember['avatarUpdatedAt'] : undefined;
return {
id: normalizedId || normalizedKey,
@@ -343,6 +349,9 @@ function normalizeRoomMember(rawMember: Record<string, unknown>, now: number): R
username: username || fallbackUsername({ id: normalizedId || normalizedKey, oderId: normalizedOderId || undefined, displayName }),
displayName: displayName || fallbackDisplayName({ id: normalizedId || normalizedKey, oderId: normalizedOderId || undefined, username }),
avatarUrl: avatarUrl || undefined,
avatarHash: avatarHash || undefined,
avatarMime: avatarMime || undefined,
avatarUpdatedAt,
role: normalizeRoomMemberRole(rawMember['role']),
roleIds: uniqueStrings(Array.isArray(rawMember['roleIds']) ? rawMember['roleIds'] as string[] : undefined),
joinedAt,
@@ -356,6 +365,11 @@ function mergeRoomMembers(existingMember: RoomMemberRecord | undefined, incoming
}
const preferIncoming = incomingMember.lastSeenAt >= existingMember.lastSeenAt;
const existingAvatarUpdatedAt = existingMember.avatarUpdatedAt ?? 0;
const incomingAvatarUpdatedAt = incomingMember.avatarUpdatedAt ?? 0;
const preferIncomingAvatar = incomingAvatarUpdatedAt === existingAvatarUpdatedAt
? preferIncoming
: incomingAvatarUpdatedAt > existingAvatarUpdatedAt;
return {
id: existingMember.id || incomingMember.id,
@@ -366,9 +380,16 @@ function mergeRoomMembers(existingMember: RoomMemberRecord | undefined, incoming
displayName: preferIncoming
? (incomingMember.displayName || existingMember.displayName)
: (existingMember.displayName || incomingMember.displayName),
avatarUrl: preferIncoming
avatarUrl: preferIncomingAvatar
? (incomingMember.avatarUrl || existingMember.avatarUrl)
: (existingMember.avatarUrl || incomingMember.avatarUrl),
avatarHash: preferIncomingAvatar
? (incomingMember.avatarHash || existingMember.avatarHash)
: (existingMember.avatarHash || incomingMember.avatarHash),
avatarMime: preferIncomingAvatar
? (incomingMember.avatarMime || existingMember.avatarMime)
: (existingMember.avatarMime || incomingMember.avatarMime),
avatarUpdatedAt: Math.max(existingAvatarUpdatedAt, incomingAvatarUpdatedAt) || undefined,
role: mergeRoomMemberRole(existingMember.role, incomingMember.role, preferIncoming),
roleIds: preferIncoming
? (incomingMember.roleIds || existingMember.roleIds)
@@ -760,6 +781,9 @@ export async function replaceRoomRelations(
username: member.username,
displayName: member.displayName,
avatarUrl: member.avatarUrl ?? null,
avatarHash: member.avatarHash ?? null,
avatarMime: member.avatarMime ?? null,
avatarUpdatedAt: member.avatarUpdatedAt ?? null,
role: member.role,
joinedAt: member.joinedAt,
lastSeenAt: member.lastSeenAt
@@ -907,6 +931,9 @@ export async function loadRoomRelationsMap(
username: row.username,
displayName: row.displayName,
avatarUrl: row.avatarUrl ?? undefined,
avatarHash: row.avatarHash ?? undefined,
avatarMime: row.avatarMime ?? undefined,
avatarUpdatedAt: row.avatarUpdatedAt ?? undefined,
role: row.role,
joinedAt: row.joinedAt,
lastSeenAt: row.lastSeenAt

View File

@@ -106,6 +106,9 @@ export interface UserPayload {
username?: string;
displayName?: string;
avatarUrl?: string;
avatarHash?: string;
avatarMime?: string;
avatarUpdatedAt?: number;
status?: string;
role?: string;
joinedAt?: number;