feat(web): animated GIF avatars that refresh across chat and member list [CI]
This commit is contained in:
1 parent
3b062687cd
commit
a08aa7568f
10 files changed
+163
-19
No files matched your search
@@ -15,6 +15,7 @@ import { serial } from './worker/serial';
|
||||
import { errorMessage } from './errors';
|
||||
import { accountStatus, loginAccount, logoutAccount, registerAccount, syncAccount, listAccountInvitations, acceptInvitation, inviteChannelUser, listChannelInvitations, revokeChannelInvitation } from './worker/account';
|
||||
import { createFolder, createThread, createGroup, renameGroup, deleteGroup, createRole, updateRole, deleteRole, setRoleMembers, groupThread, moveItems, renameFolder, renameThread, setChannelHistory } from './worker/channelThreads';
|
||||
import { refreshLoadedWorkspaces } from './worker/workspaceStructure';
|
||||
import { restoreAccount, updatePersonal, listAccountDevices, renameAccountDevice, revokeAccountDevice, getSavedLogin, clearSavedLogin } from './worker/account';
|
||||
|
||||
interface WorkerRequest { requestId: number; operation: string; payload: unknown }
|
||||
@@ -44,7 +45,7 @@ async function dispatch(operation: string, input: unknown): Promise<unknown> {
|
||||
case 'beginPasskeyLogin': return beginPasskeyLogin(String(payload.username ?? ''));
|
||||
case 'finishPasskeyLogin': return finishPasskeyLogin(String(payload.username ?? ''), id, String(payload.credential ?? ''), payload.prf as ArrayBuffer | undefined, payload.remember !== false);
|
||||
case 'unlockPasskeyLogin': return unlockPasskeyLogin(String(payload.password ?? ''), payload.remember !== false);
|
||||
case 'updatePersonal': await updatePersonal(String(payload.displayName ?? ''), typeof payload.avatar === 'string' ? payload.avatar : undefined); return accountStatus();
|
||||
case 'updatePersonal': await updatePersonal(String(payload.displayName ?? ''), typeof payload.avatar === 'string' ? payload.avatar : undefined); void refreshLoadedWorkspaces(); return accountStatus();
|
||||
case 'accountDevices': return listAccountDevices();
|
||||
case 'renameAccountDevice': await renameAccountDevice(id, String(payload.name ?? '')); return true;
|
||||
case 'revokeAccountDevice': await revokeAccountDevice(id); return accountStatus();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { canDecryptRecord, objectAuthorAccount, objectEventProjection, readDataK
|
||||
import { getResource, cacheResource, type SavedResource } from '../database';
|
||||
import { channelCache } from './resourceMetadata';
|
||||
import { cachedPerson, loadWorkspace } from './workspaceStructure';
|
||||
import { avatarThumbnail } from './senderSnapshot';
|
||||
import { displayAvatar } from './senderSnapshot';
|
||||
import type { FileItem, Item, SenderInfo, Snapshot } from '../types';
|
||||
import { previewKindOf } from '@/lib/fileKind';
|
||||
import { blobs, readers } from './sessions';
|
||||
@@ -37,7 +37,7 @@ async function authorOf(resourceId: string, itemId: string): Promise<SenderInfo
|
||||
const username = objectAuthorAccount(resourceId, itemId);
|
||||
if (!username) return undefined;
|
||||
const person = cachedPerson(resourceId, username);
|
||||
const avatar = person?.avatar ? await avatarThumbnail(person.avatar) : undefined;
|
||||
const avatar = person?.avatar ? await displayAvatar(person.avatar) : undefined;
|
||||
return { username, displayName: person?.displayName || username, ...(avatar ? { avatar } : {}) };
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,11 @@ export async function senderSnapshot(): Promise<SenderInfo | undefined> {
|
||||
return { username: sender.username, displayName: sender.displayName, ...(avatar ? { avatar } : {}) };
|
||||
}
|
||||
|
||||
/** 本机显示用:动图头像原样保留(会动);其他压成小缩略图。发进消息里的快照仍用 avatarThumbnail,不能把几 MB 的动图塞进每条消息 */
|
||||
export function displayAvatar(value: string): Promise<string | undefined> {
|
||||
return value.startsWith('data:image/gif') ? Promise.resolve(value) : avatarThumbnail(value);
|
||||
}
|
||||
|
||||
export function avatarThumbnail(value: string): Promise<string | undefined> {
|
||||
let thumbnail = thumbnails.get(value);
|
||||
if (!thumbnail) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getResource, cacheResource } from '../database';
|
||||
import { getSession } from './sessions';
|
||||
import { apiFetch } from './http';
|
||||
import { publishWorkspace } from './presentation';
|
||||
import { avatarThumbnail } from './senderSnapshot';
|
||||
import { displayAvatar } from './senderSnapshot';
|
||||
|
||||
const current = new Map<string, WorkspaceBody>();
|
||||
export function clearWorkspaceStructures(): void { current.clear(); }
|
||||
@@ -19,7 +19,7 @@ async function publish(id: string, body: WorkspaceBody): Promise<WorkspaceBody>
|
||||
current.set(id, body);
|
||||
const resource = await getResource(id);
|
||||
if (resource) await cacheResource(resource, { workspaceMetadata: body });
|
||||
const people = await Promise.all(body.people.map(async person => ({ ...person, avatar: person.avatar ? await avatarThumbnail(person.avatar) : undefined })));
|
||||
const people = await Promise.all(body.people.map(async person => ({ ...person, avatar: person.avatar ? await displayAvatar(person.avatar) : undefined })));
|
||||
if (current.get(id) !== body) return current.get(id) ?? body;
|
||||
publishWorkspace(id, { t: 'structure', state: body.structure, people });
|
||||
return body;
|
||||
@@ -37,6 +37,10 @@ export async function loadWorkspace(id: string): Promise<WorkspaceBody> {
|
||||
const body = await apiFetch(`/spaces/${id}/structure`, { headers: session.authHeaders('read') }).then(r => r.json() as Promise<WorkspaceBody>);
|
||||
return publish(id, body);
|
||||
}
|
||||
/** 自己改了头像或名字后立刻重新拉取所有已打开空间的成员资料(其他人靠服务端的设置变更通知) */
|
||||
export async function refreshLoadedWorkspaces(): Promise<void> {
|
||||
await Promise.all([...current.keys()].map((id) => loadWorkspace(id).catch(() => undefined)));
|
||||
}
|
||||
export async function changeWorkspace(id: string, path: string, method: 'POST' | 'PATCH' | 'PUT' | 'DELETE', value?: unknown): Promise<WorkspaceBody> {
|
||||
const { session } = await getSession(id);
|
||||
const body = await apiFetch(`/spaces/${id}/${path}`, {
|
||||
|
||||
Reference in new issue
Block a user