feat(web): upload large avatars in two steps so the thumbnail applies immediately
This commit is contained in:
1 parent
a27534b2dd
commit
ea7895d85c
1 file changed
+28
-3
@@ -233,13 +233,21 @@ export async function clearSavedLogin(): Promise<void> {
|
|||||||
}
|
}
|
||||||
await forgetAccount(); await forgetLoginFields();
|
await forgetAccount(); await forgetLoginFields();
|
||||||
}
|
}
|
||||||
|
/** 原图超过这个大小(字符数)就分两步传:先传缩略图,头像立刻生效,原图在后台慢慢传(弱网下 MB 级的动图一次传完要等很久) */
|
||||||
|
const SPLIT_UPLOAD_CHARS = 600_000;
|
||||||
|
|
||||||
export async function updatePersonal(displayName: string, change?: AvatarChange): Promise<void> {
|
export async function updatePersonal(displayName: string, change?: AvatarChange): Promise<void> {
|
||||||
if (!account) throw new Error('请先登录账号');
|
if (!account) throw new Error('请先登录账号');
|
||||||
const session = account;
|
const session = account;
|
||||||
const request: AccountProfileRequest = change === 'keep' ? { displayName, keepAvatar: true }
|
const send = async (request: AccountProfileRequest) => {
|
||||||
: change ? { displayName, keepAvatar: false, avatar: change.avatar, avatarThumb: change.thumb } : { displayName, keepAvatar: false };
|
|
||||||
const { body, headers: encoding } = await jsonBody(request);
|
const { body, headers: encoding } = await jsonBody(request);
|
||||||
const value = await apiFetch('/accounts/profile', { method: 'PATCH', headers: { ...headers(session), ...encoding }, body }).then(r => r.json() as Promise<AccountProfile>);
|
return apiFetch('/accounts/profile', { method: 'PATCH', headers: { ...headers(session), ...encoding }, body }).then(r => r.json() as Promise<AccountProfile>);
|
||||||
|
};
|
||||||
|
const split = change && change !== 'keep' && change.avatar.length > SPLIT_UPLOAD_CHARS;
|
||||||
|
const request: AccountProfileRequest = change === 'keep' ? { displayName, keepAvatar: true }
|
||||||
|
: split ? { displayName, keepAvatar: false, avatar: change.thumb, avatarThumb: change.thumb }
|
||||||
|
: change ? { displayName, keepAvatar: false, avatar: change.avatar, avatarThumb: change.thumb } : { displayName, keepAvatar: false };
|
||||||
|
const value = await send(request);
|
||||||
stillActive(session);
|
stillActive(session);
|
||||||
const previous = session.personal?.avatar;
|
const previous = session.personal?.avatar;
|
||||||
session.personal = personalProfile(value);
|
session.personal = personalProfile(value);
|
||||||
@@ -249,6 +257,23 @@ export async function updatePersonal(displayName: string, change?: AvatarChange)
|
|||||||
primeAvatar(value.username, value.modifiedAt, shown);
|
primeAvatar(value.username, value.modifiedAt, shown);
|
||||||
setAccountIdentity(session, session.profile.keys.publicKey, session.personal);
|
setAccountIdentity(session, session.profile.keys.publicKey, session.personal);
|
||||||
await persistSession(session, { vault: await sealJson(session.profile, session.key), revision: session.revision });
|
await persistSession(session, { vault: await sealJson(session.profile, session.key), revision: session.revision });
|
||||||
|
if (split) void uploadOriginal(session, displayName, change, send);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 后台补传原图;失败就保留缩略图当头像(预览时看到的是缩略图),不打扰用户。重试几次,间隔逐步拉长 */
|
||||||
|
async function uploadOriginal(session: NonNullable<typeof account>, displayName: string, change: { avatar: string; thumb: string }, send: (request: AccountProfileRequest) => Promise<AccountProfile>): Promise<void> {
|
||||||
|
for (const wait of [0, 5_000, 20_000, 60_000]) {
|
||||||
|
if (wait) await new Promise((resolve) => setTimeout(resolve, wait));
|
||||||
|
if (account !== session) return;
|
||||||
|
try {
|
||||||
|
const value = await send({ displayName, keepAvatar: false, avatar: change.avatar, avatarThumb: change.thumb });
|
||||||
|
if (account !== session) return;
|
||||||
|
session.personal = { ...personalProfile(value), avatar: change.thumb, hasAvatar: true };
|
||||||
|
primeAvatar(value.username, value.modifiedAt, change.thumb);
|
||||||
|
setAccountIdentity(session, session.profile.keys.publicKey, session.personal);
|
||||||
|
return;
|
||||||
|
} catch { /* 弱网或暂时失败:稍后重试 */ }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
export async function listAccountDevices(): Promise<AccountDeviceBody[]> { requireAccount(); return apiFetch('/accounts/devices').then((r) => r.json() as Promise<AccountDeviceBody[]>); }
|
export async function listAccountDevices(): Promise<AccountDeviceBody[]> { requireAccount(); return apiFetch('/accounts/devices').then((r) => r.json() as Promise<AccountDeviceBody[]>); }
|
||||||
export async function renameAccountDevice(id: string, name: string): Promise<void> {
|
export async function renameAccountDevice(id: string, name: string): Promise<void> {
|
||||||
|
|||||||
Reference in new issue
Block a user