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

@@ -41,6 +41,7 @@ export interface ChatEventBase {
lastUpdated?: number;
file?: ChatAttachmentAnnouncement;
fileId?: string;
username?: string;
hostId?: string;
hostOderId?: string;
previousHostId?: string;
@@ -62,6 +63,10 @@ export interface ChatEventBase {
isCameraEnabled?: boolean;
icon?: string;
iconUpdatedAt?: number;
avatarUrl?: string;
avatarHash?: string;
avatarMime?: string;
avatarUpdatedAt?: number;
role?: UserRole;
room?: Partial<Room>;
channels?: Channel[];
@@ -263,6 +268,43 @@ export interface ServerIconUpdateEvent extends ChatEventBase {
iconUpdatedAt: number;
}
export interface UserAvatarSummaryEvent extends ChatEventBase {
type: 'user-avatar-summary';
oderId: string;
username?: string;
displayName?: string;
avatarHash?: string;
avatarMime?: string;
avatarUpdatedAt: number;
}
export interface UserAvatarRequestEvent extends ChatEventBase {
type: 'user-avatar-request';
oderId: string;
}
export interface UserAvatarFullEvent extends ChatEventBase {
type: 'user-avatar-full';
oderId: string;
username?: string;
displayName?: string;
avatarHash?: string;
avatarMime: string;
avatarUpdatedAt: number;
total: number;
}
export interface UserAvatarChunkEvent extends ChatEventBase {
type: 'user-avatar-chunk';
oderId: string;
avatarHash?: string;
avatarMime?: string;
avatarUpdatedAt?: number;
index: number;
total: number;
data: string;
}
export interface ServerStateRequestEvent extends ChatEventBase {
type: 'server-state-request';
roomId: string;
@@ -343,6 +385,10 @@ export type ChatEvent =
| StateRequestEvent
| ScreenShareRequestEvent
| ScreenShareStopEvent
| UserAvatarSummaryEvent
| UserAvatarRequestEvent
| UserAvatarFullEvent
| UserAvatarChunkEvent
| ServerIconSummaryEvent
| ServerIconRequestEvent
| ServerIconFullEvent

View File

@@ -8,3 +8,5 @@ export * from './chat-events';
export * from './media-preferences';
export * from './signaling-contracts';
export * from './attachment-contracts';
export * from './p2p-transfer.constants';
export * from './p2p-transfer.utils';

View File

@@ -0,0 +1,2 @@
/** Shared binary chunk size for payloads streamed over RTCDataChannel. */
export const P2P_BASE64_CHUNK_SIZE_BYTES = 64 * 1024;

View File

@@ -0,0 +1,48 @@
import { P2P_BASE64_CHUNK_SIZE_BYTES } from './p2p-transfer.constants';
export function arrayBufferToBase64(buffer: ArrayBuffer): string {
let binary = '';
const bytes = new Uint8Array(buffer);
for (let index = 0; index < bytes.byteLength; index++) {
binary += String.fromCharCode(bytes[index]);
}
return btoa(binary);
}
export function decodeBase64(base64: string): Uint8Array {
const binary = atob(base64);
const bytes = new Uint8Array(binary.length);
for (let index = 0; index < binary.length; index++) {
bytes[index] = binary.charCodeAt(index);
}
return bytes;
}
export async function* iterateBlobChunks(
blob: Blob,
chunkSize = P2P_BASE64_CHUNK_SIZE_BYTES
): AsyncGenerator<{ base64: string; index: number; total: number }, void, undefined> {
const totalChunks = Math.ceil(blob.size / chunkSize);
let offset = 0;
let chunkIndex = 0;
while (offset < blob.size) {
const slice = blob.slice(offset, offset + chunkSize);
const arrayBuffer = await slice.arrayBuffer();
yield {
base64: arrayBufferToBase64(arrayBuffer),
index: chunkIndex,
total: totalChunks
};
offset += chunkSize;
chunkIndex++;
}
}

View File

@@ -14,6 +14,9 @@ export interface User {
username: string;
displayName: string;
avatarUrl?: string;
avatarHash?: string;
avatarMime?: string;
avatarUpdatedAt?: number;
status: UserStatus;
role: UserRole;
joinedAt: number;
@@ -33,6 +36,9 @@ export interface RoomMember {
username: string;
displayName: string;
avatarUrl?: string;
avatarHash?: string;
avatarMime?: string;
avatarUpdatedAt?: number;
role: UserRole;
roleIds?: string[];
joinedAt: number;