feat: Add profile images
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -27,6 +27,15 @@ export class RoomMemberEntity {
|
||||
@Column('text', { nullable: true })
|
||||
avatarUrl!: string | null;
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
avatarHash!: string | null;
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
avatarMime!: string | null;
|
||||
|
||||
@Column('integer', { nullable: true })
|
||||
avatarUpdatedAt!: number | null;
|
||||
|
||||
@Column('text')
|
||||
role!: 'host' | 'admin' | 'moderator' | 'member';
|
||||
|
||||
|
||||
@@ -21,6 +21,15 @@ export class UserEntity {
|
||||
@Column('text', { nullable: true })
|
||||
avatarUrl!: string | null;
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
avatarHash!: string | null;
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
avatarMime!: string | null;
|
||||
|
||||
@Column('integer', { nullable: true })
|
||||
avatarUpdatedAt!: number | null;
|
||||
|
||||
@Column('text', { nullable: true })
|
||||
status!: string | null;
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddProfileAvatarMetadata1000000000006 implements MigrationInterface {
|
||||
name = 'AddProfileAvatarMetadata1000000000006';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "users" ADD COLUMN "avatarHash" TEXT`);
|
||||
await queryRunner.query(`ALTER TABLE "users" ADD COLUMN "avatarMime" TEXT`);
|
||||
await queryRunner.query(`ALTER TABLE "users" ADD COLUMN "avatarUpdatedAt" INTEGER`);
|
||||
await queryRunner.query(`ALTER TABLE "room_members" ADD COLUMN "avatarHash" TEXT`);
|
||||
await queryRunner.query(`ALTER TABLE "room_members" ADD COLUMN "avatarMime" TEXT`);
|
||||
await queryRunner.query(`ALTER TABLE "room_members" ADD COLUMN "avatarUpdatedAt" INTEGER`);
|
||||
}
|
||||
|
||||
public async down(): Promise<void> {
|
||||
// SQLite column removal requires table rebuilds. Keep rollback no-op.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user