mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 05:25:40 +09:00
fix(app): load saved account avatars from their own instance (#3267)
This commit is contained in:
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
|
// 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 type {Account} from '@app/features/platform/state/AuthSession';
|
||||||
import * as AvatarUtils from '@app/features/user/utils/AvatarUtils';
|
import * as AvatarUtils from '@app/features/user/utils/AvatarUtils';
|
||||||
import {getDefaultAvatarAssetURL} from '@app/features/user/utils/DefaultAvatars';
|
import {getDefaultAvatarAssetURL} from '@app/features/user/utils/DefaultAvatars';
|
||||||
import type {MediaProxyImageSize} from '@fluxer/constants/src/MediaProxyImageSizes';
|
import type {MediaProxyImageSize} from '@fluxer/constants/src/MediaProxyImageSizes';
|
||||||
import {useMemo} from 'react';
|
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 {
|
function defaultAccountAvatarURL(account: Account, size?: MediaProxyImageSize): string {
|
||||||
try {
|
try {
|
||||||
return AvatarUtils.getUserAvatarURL({id: account.userId, avatar: null}, false, size);
|
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 {
|
function resolveAccountAvatarURL(account: Account, size?: MediaProxyImageSize): string {
|
||||||
const avatar = account.userData?.avatar ?? null;
|
const avatar = account.userData?.avatar ?? null;
|
||||||
if (avatar == null || avatar.length === 0) {
|
const mediaEndpoint = account.instance?.mediaEndpoint ?? null;
|
||||||
return defaultAccountAvatarURL(account, size);
|
if (avatar == null || avatar.length === 0 || mediaEndpoint == null || mediaEndpoint.length === 0) {
|
||||||
}
|
|
||||||
const cached = cachedAccountAvatarURL(account, avatar, size);
|
|
||||||
if (cached != null) {
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
const mediaEndpoint = accountInstanceMediaEndpoint(account);
|
|
||||||
if (mediaEndpoint == null) {
|
|
||||||
return defaultAccountAvatarURL(account, size);
|
return defaultAccountAvatarURL(account, size);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -20,9 +20,6 @@ export interface UserData {
|
|||||||
globalName?: string | null;
|
globalName?: string | null;
|
||||||
email?: string | null;
|
email?: string | null;
|
||||||
avatar?: string | null;
|
avatar?: string | null;
|
||||||
localAvatarHash?: string | null;
|
|
||||||
localAvatarURL?: string | null;
|
|
||||||
localAvatarSize?: number | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AccountPresenceIntent {
|
export interface AccountPresenceIntent {
|
||||||
|
|||||||
@@ -90,14 +90,6 @@ function readUserData(value: unknown): UserData | undefined {
|
|||||||
if (typeof source.username !== 'string' || typeof source.discriminator !== 'string') {
|
if (typeof source.username !== 'string' || typeof source.discriminator !== 'string') {
|
||||||
throw new StoredAccountRuntimeRecoveryError('Stored account user data must contain a username and discriminator');
|
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 = {
|
const userData: UserData = {
|
||||||
username: source.username,
|
username: source.username,
|
||||||
discriminator: source.discriminator,
|
discriminator: source.discriminator,
|
||||||
@@ -111,19 +103,6 @@ function readUserData(value: unknown): UserData | undefined {
|
|||||||
if (Object.hasOwn(source, 'avatar')) {
|
if (Object.hasOwn(source, 'avatar')) {
|
||||||
userData.avatar = readOptionalNullableString(source, 'avatar', 'Stored account 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;
|
return userData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user