fix(app): load saved account avatars from their own instance (#3267)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-08 03:03:47 +02:00
1 parent de358c0def
commit e218ba21de
3 files changed
+2 -62

No files matched your search

@@ -1,40 +1,11 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import RuntimeConfig, {runtimeConfigSnapshotsAreSameInstance} from '@app/features/app/state/RuntimeConfig';
import type {Account} from '@app/features/platform/state/AuthSession';
import * as AvatarUtils from '@app/features/user/utils/AvatarUtils';
import {getDefaultAvatarAssetURL} from '@app/features/user/utils/DefaultAvatars';
import type {MediaProxyImageSize} from '@fluxer/constants/src/MediaProxyImageSizes';
import {useMemo} from 'react';
const LOCAL_ACCOUNT_AVATAR_DATA_URL_PATTERN = /^data:image\/(?:avif|gif|jpeg|png|webp);base64,[a-zA-Z0-9+/]+=*$/u;
function cachedAccountAvatarURL(account: Account, avatar: string, size?: MediaProxyImageSize): string | null {
const userData = account.userData;
if (!userData || userData.localAvatarHash !== avatar) {
return null;
}
const localAvatarURL = userData.localAvatarURL;
if (localAvatarURL == null || !LOCAL_ACCOUNT_AVATAR_DATA_URL_PATTERN.test(localAvatarURL)) {
return null;
}
if (size != null && (typeof userData.localAvatarSize !== 'number' || userData.localAvatarSize < size)) {
return null;
}
return localAvatarURL;
}
function accountInstanceMediaEndpoint(account: Account): string | null {
const instance = account.instance;
if (instance == null || typeof instance.mediaEndpoint !== 'string' || instance.mediaEndpoint.length === 0) {
return null;
}
if (!runtimeConfigSnapshotsAreSameInstance(instance, RuntimeConfig.getSnapshotOrNull())) {
return null;
}
return instance.mediaEndpoint;
}
function defaultAccountAvatarURL(account: Account, size?: MediaProxyImageSize): string {
try {
return AvatarUtils.getUserAvatarURL({id: account.userId, avatar: null}, false, size);
@@ -45,15 +16,8 @@ function defaultAccountAvatarURL(account: Account, size?: MediaProxyImageSize):
function resolveAccountAvatarURL(account: Account, size?: MediaProxyImageSize): string {
const avatar = account.userData?.avatar ?? null;
if (avatar == null || avatar.length === 0) {
return defaultAccountAvatarURL(account, size);
}
const cached = cachedAccountAvatarURL(account, avatar, size);
if (cached != null) {
return cached;
}
const mediaEndpoint = accountInstanceMediaEndpoint(account);
if (mediaEndpoint == null) {
const mediaEndpoint = account.instance?.mediaEndpoint ?? null;
if (avatar == null || avatar.length === 0 || mediaEndpoint == null || mediaEndpoint.length === 0) {
return defaultAccountAvatarURL(account, size);
}
try {
@@ -20,9 +20,6 @@ export interface UserData {
globalName?: string | null;
email?: string | null;
avatar?: string | null;
localAvatarHash?: string | null;
localAvatarURL?: string | null;
localAvatarSize?: number | null;
}
export interface AccountPresenceIntent {
@@ -90,14 +90,6 @@ function readUserData(value: unknown): UserData | undefined {
if (typeof source.username !== 'string' || typeof source.discriminator !== 'string') {
throw new StoredAccountRuntimeRecoveryError('Stored account user data must contain a username and discriminator');
}
const localAvatarSize = source.localAvatarSize;
if (
localAvatarSize !== undefined &&
localAvatarSize !== null &&
(typeof localAvatarSize !== 'number' || !Number.isFinite(localAvatarSize))
) {
throw new StoredAccountRuntimeRecoveryError('Stored account local avatar size must be finite, null, or absent');
}
const userData: UserData = {
username: source.username,
discriminator: source.discriminator,
@@ -111,19 +103,6 @@ function readUserData(value: unknown): UserData | undefined {
if (Object.hasOwn(source, 'avatar')) {
userData.avatar = readOptionalNullableString(source, 'avatar', 'Stored account avatar');
}
if (Object.hasOwn(source, 'localAvatarHash')) {
userData.localAvatarHash = readOptionalNullableString(
source,
'localAvatarHash',
'Stored account local avatar hash',
);
}
if (Object.hasOwn(source, 'localAvatarURL')) {
userData.localAvatarURL = readOptionalNullableString(source, 'localAvatarURL', 'Stored account local avatar URL');
}
if (Object.hasOwn(source, 'localAvatarSize')) {
userData.localAvatarSize = localAvatarSize;
}
return userData;
}