mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
fix(app): request avatar and banner sizes the media proxy serves (#1842)
This commit is contained in:
1 parent
c3e747aaa4
commit
bd381ccc74
8 files changed
+181
-563
No files matched your search
@@ -131,13 +131,10 @@ export const InviteAcceptModal = observer(function InviteAcceptModal({code}: Inv
|
|||||||
if (!guild.id || !guild.splash) {
|
if (!guild.id || !guild.splash) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return AvatarUtils.getGuildSplashURL(
|
return AvatarUtils.getGuildSplashURL({
|
||||||
{
|
id: guild.id,
|
||||||
id: guild.id,
|
splash: guild.splash,
|
||||||
splash: guild.splash,
|
});
|
||||||
},
|
|
||||||
4096,
|
|
||||||
);
|
|
||||||
}, [invite]);
|
}, [invite]);
|
||||||
const isJoinDisabled = isGuildInviteActionDisabled(guildActionState);
|
const isJoinDisabled = isGuildInviteActionDisabled(guildActionState);
|
||||||
const primaryActionType = getGuildInvitePrimaryAction(guildActionState);
|
const primaryActionType = getGuildInvitePrimaryAction(guildActionState);
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export const InvitePagePreviewModal: React.FC<InvitePagePreviewModalProps> = obs
|
|||||||
const splashUrl = useMemo(() => {
|
const splashUrl = useMemo(() => {
|
||||||
if (previewSplashUrl) return previewSplashUrl;
|
if (previewSplashUrl) return previewSplashUrl;
|
||||||
if (guild?.splash) {
|
if (guild?.splash) {
|
||||||
return AvatarUtils.getGuildSplashURL({id: guild.id, splash: guild.splash}, 4096);
|
return AvatarUtils.getGuildSplashURL({id: guild.id, splash: guild.splash});
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}, [previewSplashUrl, guild?.id, guild?.splash]);
|
}, [previewSplashUrl, guild?.id, guild?.splash]);
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ const InviteLoginPageContainer = observer(() => {
|
|||||||
}
|
}
|
||||||
const guild = guildInvite.guild;
|
const guild = guildInvite.guild;
|
||||||
if (guild?.splash && guild.id) {
|
if (guild?.splash && guild.id) {
|
||||||
const splashUrl = getGuildSplashURL({id: guild.id, splash: guild.splash}, 4096) ?? null;
|
const splashUrl = getGuildSplashURL({id: guild.id, splash: guild.splash}) ?? null;
|
||||||
setSplashUrl(splashUrl);
|
setSplashUrl(splashUrl);
|
||||||
}
|
}
|
||||||
}, [guildInvite?.guild?.splash, guildInvite?.guild?.id, setSplashUrl]);
|
}, [guildInvite?.guild?.splash, guildInvite?.guild?.id, setSplashUrl]);
|
||||||
|
|||||||
@@ -66,13 +66,10 @@ const InviteRegisterPage = observer(function InviteRegisterPage() {
|
|||||||
}
|
}
|
||||||
const guild = guildInvite.guild;
|
const guild = guildInvite.guild;
|
||||||
if (guild?.splash) {
|
if (guild?.splash) {
|
||||||
const splashUrl = AvatarUtils.getGuildSplashURL(
|
const splashUrl = AvatarUtils.getGuildSplashURL({
|
||||||
{
|
id: guild.id,
|
||||||
id: guild.id,
|
splash: guild.splash,
|
||||||
splash: guild.splash,
|
});
|
||||||
},
|
|
||||||
4096,
|
|
||||||
);
|
|
||||||
setSplashUrl(splashUrl);
|
setSplashUrl(splashUrl);
|
||||||
}
|
}
|
||||||
}, [guildInvite?.guild?.splash, guildInvite?.guild?.id, setSplashUrl]);
|
}, [guildInvite?.guild?.splash, guildInvite?.guild?.id, setSplashUrl]);
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ import {hasActiveDirectCallWithUser} from '@app/features/voice/utils/PrivateCall
|
|||||||
import {ME} from '@fluxer/constants/src/AppConstants';
|
import {ME} from '@fluxer/constants/src/AppConstants';
|
||||||
import {Permissions} from '@fluxer/constants/src/ChannelConstants';
|
import {Permissions} from '@fluxer/constants/src/ChannelConstants';
|
||||||
import {
|
import {
|
||||||
MEDIA_PROXY_AVATAR_SIZE_PROFILE,
|
MEDIA_PROXY_AVATAR_SIZE_DEFAULT,
|
||||||
MEDIA_PROXY_PROFILE_BANNER_SIZE_MODAL,
|
MEDIA_PROXY_PROFILE_BANNER_SIZE_MODAL,
|
||||||
} from '@fluxer/constants/src/MediaProxyAssetSizes';
|
} from '@fluxer/constants/src/MediaProxyAssetSizes';
|
||||||
import {PublicUserFlags, RelationshipTypes} from '@fluxer/constants/src/UserConstants';
|
import {PublicUserFlags, RelationshipTypes} from '@fluxer/constants/src/UserConstants';
|
||||||
@@ -282,7 +282,7 @@ const UserProfileMobileSheetContent: React.FC<UserProfileMobileSheetContentProps
|
|||||||
);
|
);
|
||||||
const shouldAutoplayProfileAnimations = useAutoplayExpandedProfileAnimations();
|
const shouldAutoplayProfileAnimations = useAutoplayExpandedProfileAnimations();
|
||||||
const {avatarUrl, hoverAvatarUrl} = useMemo(
|
const {avatarUrl, hoverAvatarUrl} = useMemo(
|
||||||
() => ProfileDisplayUtils.getProfileAvatarUrls(profileContext, undefined, MEDIA_PROXY_AVATAR_SIZE_PROFILE),
|
() => ProfileDisplayUtils.getProfileAvatarUrls(profileContext, undefined, MEDIA_PROXY_AVATAR_SIZE_DEFAULT),
|
||||||
[profileContext],
|
[profileContext],
|
||||||
);
|
);
|
||||||
const {bannerUrl: staticBannerUrl, hoverBannerUrl} = useMemo(
|
const {bannerUrl: staticBannerUrl, hoverBannerUrl} = useMemo(
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import {SettingsTabSection} from '@app/features/app/components/dialogs/shared/Se
|
|||||||
import {ChannelItemCore} from '@app/features/app/components/layout/ChannelItem';
|
import {ChannelItemCore} from '@app/features/app/components/layout/ChannelItem';
|
||||||
import channelItemSurfaceStyles from '@app/features/app/components/layout/ChannelItemSurface.module.css';
|
import channelItemSurfaceStyles from '@app/features/app/components/layout/ChannelItemSurface.module.css';
|
||||||
import {Typing} from '@app/features/channel/components/ChannelTyping';
|
import {Typing} from '@app/features/channel/components/ChannelTyping';
|
||||||
import {cdnUrl} from '@app/features/messaging/utils/MessagingUrlUtils';
|
|
||||||
import {AvatarStack} from '@app/features/ui/avatars/AvatarStack';
|
import {AvatarStack} from '@app/features/ui/avatars/AvatarStack';
|
||||||
import {MockAvatar} from '@app/features/ui/components/MockAvatar';
|
import {MockAvatar} from '@app/features/ui/components/MockAvatar';
|
||||||
import type {RadioOption} from '@app/features/ui/radio_group/RadioGroup';
|
import type {RadioOption} from '@app/features/ui/radio_group/RadioGroup';
|
||||||
import {RadioGroup} from '@app/features/ui/radio_group/RadioGroup';
|
import {RadioGroup} from '@app/features/ui/radio_group/RadioGroup';
|
||||||
import {Tooltip} from '@app/features/ui/tooltip/Tooltip';
|
import {Tooltip} from '@app/features/ui/tooltip/Tooltip';
|
||||||
import styles from '@app/features/user/components/modals/tabs/appearance_tab/InterfaceTab.module.css';
|
import styles from '@app/features/user/components/modals/tabs/appearance_tab/InterfaceTab.module.css';
|
||||||
|
import {getDefaultAvatarURLForIndex} from '@app/features/user/utils/AvatarUtils';
|
||||||
import {msg} from '@lingui/core/macro';
|
import {msg} from '@lingui/core/macro';
|
||||||
import {Trans, useLingui} from '@lingui/react/macro';
|
import {Trans, useLingui} from '@lingui/react/macro';
|
||||||
import {clsx} from 'clsx';
|
import {clsx} from 'clsx';
|
||||||
@@ -92,7 +92,7 @@ const ChannelListPreview = observer(({mode}: {mode: ChannelTypingIndicatorMode})
|
|||||||
key={index}
|
key={index}
|
||||||
size={12}
|
size={12}
|
||||||
userTag={`User ${index}`}
|
userTag={`User ${index}`}
|
||||||
avatarUrl={cdnUrl(`avatars/${index}.png`)}
|
avatarUrl={getDefaultAvatarURLForIndex(index)}
|
||||||
data-flx="user.appearance-tab.interface-tab.channel-list-preview.mock-avatar"
|
data-flx="user.appearance-tab.interface-tab.channel-list-preview.mock-avatar"
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,482 +1,3 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
|
export * from '@app/features/user/utils/AvatarUtils';
|
||||||
import DeveloperOptions from '@app/features/devtools/state/DeveloperOptions';
|
|
||||||
import {buildMediaProxyURL} from '@app/features/messaging/utils/MediaProxyUtils';
|
|
||||||
import {cdnUrl, mediaUrl, setPathQueryParams} from '@app/features/messaging/utils/MessagingUrlUtils';
|
|
||||||
import type {User} from '@app/features/user/models/User';
|
|
||||||
import {
|
|
||||||
getDefaultAvatarIndex,
|
|
||||||
getDefaultAvatarPrimaryColor as getSharedDefaultAvatarPrimaryColor,
|
|
||||||
normalizeEndpoint,
|
|
||||||
parseAvatarHash,
|
|
||||||
} from '@app/features/user/utils/AvatarMediaUtils';
|
|
||||||
import {
|
|
||||||
MEDIA_PROXY_AVATAR_SIZE_DEFAULT,
|
|
||||||
MEDIA_PROXY_ICON_SIZE_DEFAULT,
|
|
||||||
} from '@fluxer/constants/src/MediaProxyAssetSizes';
|
|
||||||
import type {MediaProxyImageSize} from '@fluxer/constants/src/MediaProxyImageSizes';
|
|
||||||
import {SOUNDBOARD_SOUND_PATH_PREFIX} from '@fluxer/constants/src/SoundboardConstants';
|
|
||||||
|
|
||||||
const getDefaultAvatar = (index: number): string => cdnUrl(`avatars/${index}.png`);
|
|
||||||
|
|
||||||
export function getDefaultAvatarPrimaryColor(id: string) {
|
|
||||||
return getSharedDefaultAvatarPrimaryColor(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDefaultAvatarURL(id: string) {
|
|
||||||
return getDefaultAvatar(getDefaultAvatarIndex(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
type AvatarOptions = Pick<User, 'id' | 'avatar'>;
|
|
||||||
type BannerOptions = Pick<User, 'id' | 'banner'>;
|
|
||||||
|
|
||||||
interface IconOptions {
|
|
||||||
id: string;
|
|
||||||
icon: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
type MediaURLParams = {
|
|
||||||
path: string;
|
|
||||||
id: string;
|
|
||||||
hash: string;
|
|
||||||
size?: MediaProxyImageSize;
|
|
||||||
format: string;
|
|
||||||
animated?: boolean;
|
|
||||||
endpoint?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const parseMediaHashForRequest = (value: string, animated = false) => {
|
|
||||||
const {animated: isAnimated} = parseAvatarHash(value);
|
|
||||||
return {
|
|
||||||
hash: value,
|
|
||||||
animated: isAnimated ? animated : undefined,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
const getMediaURL = ({path, id, hash, size, format, animated, endpoint}: MediaURLParams) => {
|
|
||||||
if (DeveloperOptions.forceRenderPlaceholders) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const baseEndpoint = endpoint === undefined ? RuntimeConfig.mediaEndpoint : endpoint;
|
|
||||||
if (!baseEndpoint) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const basePath = `${path}/${id}/${hash}.${format}`;
|
|
||||||
const url = size ? setPathQueryParams(basePath, {size}) : basePath;
|
|
||||||
const proxyOptions = animated === undefined ? undefined : {animated};
|
|
||||||
return buildMediaProxyURL(`${normalizeEndpoint(baseEndpoint)}/${url}`, proxyOptions);
|
|
||||||
};
|
|
||||||
|
|
||||||
type GuildMemberMediaURLParams = {
|
|
||||||
path: string;
|
|
||||||
guildId: string;
|
|
||||||
userId: string;
|
|
||||||
hash: string;
|
|
||||||
size?: MediaProxyImageSize;
|
|
||||||
format: string;
|
|
||||||
animated?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getGuildMemberMediaURL = ({path, guildId, userId, hash, size, format, animated}: GuildMemberMediaURLParams) => {
|
|
||||||
if (DeveloperOptions.forceRenderPlaceholders) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const baseEndpoint = RuntimeConfig.mediaEndpoint;
|
|
||||||
if (!baseEndpoint) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const basePath = `guilds/${guildId}/users/${userId}/${path}/${hash}.${format}`;
|
|
||||||
const url = size ? setPathQueryParams(basePath, {size}) : basePath;
|
|
||||||
const proxyOptions = animated === undefined ? undefined : {animated};
|
|
||||||
return buildMediaProxyURL(`${normalizeEndpoint(baseEndpoint)}/${url}`, proxyOptions);
|
|
||||||
};
|
|
||||||
const buildWebpMediaUrl = (params: Omit<MediaURLParams, 'format'>) => getMediaURL({...params, format: 'webp'});
|
|
||||||
const buildPngMediaUrl = (params: Omit<MediaURLParams, 'format'>) => getMediaURL({...params, format: 'png'});
|
|
||||||
const buildGuildMemberWebpUrl = (params: Omit<GuildMemberMediaURLParams, 'format'>) =>
|
|
||||||
getGuildMemberMediaURL({...params, format: 'webp'});
|
|
||||||
|
|
||||||
export function getUserAvatarURL(
|
|
||||||
{id, avatar}: AvatarOptions,
|
|
||||||
animated = false,
|
|
||||||
size: MediaProxyImageSize = MEDIA_PROXY_AVATAR_SIZE_DEFAULT,
|
|
||||||
) {
|
|
||||||
if (!avatar) {
|
|
||||||
return getDefaultAvatar(getDefaultAvatarIndex(id));
|
|
||||||
}
|
|
||||||
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(avatar, animated);
|
|
||||||
return buildWebpMediaUrl({
|
|
||||||
path: 'avatars',
|
|
||||||
id,
|
|
||||||
hash,
|
|
||||||
size,
|
|
||||||
animated: shouldAnimate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getUserNotificationAvatarURL(
|
|
||||||
{id, avatar}: AvatarOptions,
|
|
||||||
size: MediaProxyImageSize = MEDIA_PROXY_AVATAR_SIZE_DEFAULT,
|
|
||||||
) {
|
|
||||||
if (!avatar) {
|
|
||||||
return getDefaultAvatar(getDefaultAvatarIndex(id));
|
|
||||||
}
|
|
||||||
const {hash, animated} = parseMediaHashForRequest(avatar, false);
|
|
||||||
return buildPngMediaUrl({
|
|
||||||
path: 'avatars',
|
|
||||||
id,
|
|
||||||
hash,
|
|
||||||
size,
|
|
||||||
animated,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getUserBannerURL({id, banner}: BannerOptions, animated = false, size: MediaProxyImageSize = 1024) {
|
|
||||||
if (!banner) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(banner, animated);
|
|
||||||
return buildWebpMediaUrl({
|
|
||||||
path: 'banners',
|
|
||||||
id,
|
|
||||||
hash,
|
|
||||||
size,
|
|
||||||
animated: shouldAnimate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const SOUNDBOARD_SOUND_URL_CACHE = new Map<string, string>();
|
|
||||||
const SOUNDBOARD_SOUND_URL_CACHE_LIMIT = 4096;
|
|
||||||
|
|
||||||
export function getSoundboardSoundURL(soundId: string): string {
|
|
||||||
if (DeveloperOptions.forceRenderPlaceholders) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const key = `${RuntimeConfig.mediaEndpoint}:${soundId}`;
|
|
||||||
const cached = SOUNDBOARD_SOUND_URL_CACHE.get(key);
|
|
||||||
if (cached !== undefined) return cached;
|
|
||||||
const result = mediaUrl(`${SOUNDBOARD_SOUND_PATH_PREFIX}/${soundId}`);
|
|
||||||
if (SOUNDBOARD_SOUND_URL_CACHE.size >= SOUNDBOARD_SOUND_URL_CACHE_LIMIT) SOUNDBOARD_SOUND_URL_CACHE.clear();
|
|
||||||
SOUNDBOARD_SOUND_URL_CACHE.set(key, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getGuildIconURL({id, icon}: IconOptions, animated = false) {
|
|
||||||
if (!icon) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(icon, animated);
|
|
||||||
return buildWebpMediaUrl({
|
|
||||||
path: 'icons',
|
|
||||||
id,
|
|
||||||
hash,
|
|
||||||
size: MEDIA_PROXY_ICON_SIZE_DEFAULT,
|
|
||||||
animated: shouldAnimate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getGuildIconURLWithProxy({id, icon}: IconOptions, endpoint: string, animated = false) {
|
|
||||||
if (!endpoint || !icon) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(icon, animated);
|
|
||||||
return buildWebpMediaUrl({
|
|
||||||
path: 'icons',
|
|
||||||
id,
|
|
||||||
hash,
|
|
||||||
size: MEDIA_PROXY_ICON_SIZE_DEFAULT,
|
|
||||||
animated: shouldAnimate,
|
|
||||||
endpoint,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getGuildSplashURL(
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
splash,
|
|
||||||
}: {
|
|
||||||
id: string;
|
|
||||||
splash: string | null;
|
|
||||||
},
|
|
||||||
size: MediaProxyImageSize = 1024,
|
|
||||||
) {
|
|
||||||
if (!splash) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return buildWebpMediaUrl({
|
|
||||||
path: 'splashes',
|
|
||||||
id,
|
|
||||||
hash: splash,
|
|
||||||
size,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getGuildDiscoverySplashURL({id, splash}: {id: string; splash: string | null}) {
|
|
||||||
if (!splash) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return buildWebpMediaUrl({
|
|
||||||
path: 'discovery-splashes',
|
|
||||||
id,
|
|
||||||
hash: splash,
|
|
||||||
size: 1024,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getGuildBannerURL(
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
banner,
|
|
||||||
}: {
|
|
||||||
id: string;
|
|
||||||
banner: string | null;
|
|
||||||
},
|
|
||||||
animated = false,
|
|
||||||
) {
|
|
||||||
if (!banner) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(banner, animated);
|
|
||||||
return buildWebpMediaUrl({
|
|
||||||
path: 'banners',
|
|
||||||
id,
|
|
||||||
hash,
|
|
||||||
size: 1024,
|
|
||||||
animated: shouldAnimate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getGuildMemberAvatarURL({
|
|
||||||
guildId,
|
|
||||||
userId,
|
|
||||||
avatar,
|
|
||||||
memberAvatar,
|
|
||||||
animated = false,
|
|
||||||
size = MEDIA_PROXY_AVATAR_SIZE_DEFAULT,
|
|
||||||
}: {
|
|
||||||
guildId: string;
|
|
||||||
userId: string;
|
|
||||||
avatar: string | null;
|
|
||||||
memberAvatar?: string | null;
|
|
||||||
animated?: boolean;
|
|
||||||
size?: MediaProxyImageSize;
|
|
||||||
}) {
|
|
||||||
if (memberAvatar) {
|
|
||||||
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(memberAvatar, animated);
|
|
||||||
return buildGuildMemberWebpUrl({
|
|
||||||
path: 'avatars',
|
|
||||||
guildId,
|
|
||||||
userId,
|
|
||||||
hash,
|
|
||||||
size,
|
|
||||||
animated: shouldAnimate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (avatar) {
|
|
||||||
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(avatar, animated);
|
|
||||||
return buildWebpMediaUrl({
|
|
||||||
path: 'avatars',
|
|
||||||
id: userId,
|
|
||||||
hash,
|
|
||||||
size,
|
|
||||||
animated: shouldAnimate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return getDefaultAvatar(getDefaultAvatarIndex(userId));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getGuildMemberDisplayAvatarURL({
|
|
||||||
guildId,
|
|
||||||
user,
|
|
||||||
memberAvatar,
|
|
||||||
avatarUnset = false,
|
|
||||||
animated = false,
|
|
||||||
size = MEDIA_PROXY_AVATAR_SIZE_DEFAULT,
|
|
||||||
}: {
|
|
||||||
guildId: string;
|
|
||||||
user: AvatarOptions;
|
|
||||||
memberAvatar?: string | null;
|
|
||||||
avatarUnset?: boolean;
|
|
||||||
animated?: boolean;
|
|
||||||
size?: MediaProxyImageSize;
|
|
||||||
}) {
|
|
||||||
if (avatarUnset) {
|
|
||||||
return getUserAvatarURL({id: user.id, avatar: null}, animated, size);
|
|
||||||
}
|
|
||||||
return getGuildMemberAvatarURL({
|
|
||||||
guildId,
|
|
||||||
userId: user.id,
|
|
||||||
avatar: user.avatar,
|
|
||||||
memberAvatar,
|
|
||||||
animated,
|
|
||||||
size,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getGuildMemberBannerURL({
|
|
||||||
guildId,
|
|
||||||
userId,
|
|
||||||
banner,
|
|
||||||
memberBanner,
|
|
||||||
animated = false,
|
|
||||||
size = 1024,
|
|
||||||
}: {
|
|
||||||
guildId: string;
|
|
||||||
userId: string;
|
|
||||||
banner: string | null;
|
|
||||||
memberBanner?: string | null;
|
|
||||||
animated?: boolean;
|
|
||||||
size?: MediaProxyImageSize;
|
|
||||||
}) {
|
|
||||||
if (memberBanner) {
|
|
||||||
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(memberBanner, animated);
|
|
||||||
return buildGuildMemberWebpUrl({
|
|
||||||
path: 'banners',
|
|
||||||
guildId,
|
|
||||||
userId,
|
|
||||||
hash,
|
|
||||||
size,
|
|
||||||
animated: shouldAnimate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (banner) {
|
|
||||||
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(banner, animated);
|
|
||||||
return buildWebpMediaUrl({
|
|
||||||
path: 'banners',
|
|
||||||
id: userId,
|
|
||||||
hash,
|
|
||||||
size,
|
|
||||||
animated: shouldAnimate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getUserAvatarURLWithProxy(
|
|
||||||
options: AvatarOptions,
|
|
||||||
endpoint: string,
|
|
||||||
animated = false,
|
|
||||||
size: MediaProxyImageSize = MEDIA_PROXY_AVATAR_SIZE_DEFAULT,
|
|
||||||
) {
|
|
||||||
const {id, avatar} = options;
|
|
||||||
if (!endpoint || !avatar) {
|
|
||||||
return getDefaultAvatar(getDefaultAvatarIndex(id));
|
|
||||||
}
|
|
||||||
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(avatar, animated);
|
|
||||||
return buildWebpMediaUrl({
|
|
||||||
path: 'avatars',
|
|
||||||
id,
|
|
||||||
hash,
|
|
||||||
size,
|
|
||||||
animated: shouldAnimate,
|
|
||||||
endpoint,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getGuildEmbedSplashURL(
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
embedSplash,
|
|
||||||
}: {
|
|
||||||
id: string;
|
|
||||||
embedSplash: string | null;
|
|
||||||
},
|
|
||||||
size: MediaProxyImageSize = 1024,
|
|
||||||
) {
|
|
||||||
if (!embedSplash) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return buildWebpMediaUrl({
|
|
||||||
path: 'embed-splashes',
|
|
||||||
id,
|
|
||||||
hash: embedSplash,
|
|
||||||
size,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getChannelIconURL(
|
|
||||||
{id, icon}: IconOptions,
|
|
||||||
size: MediaProxyImageSize = MEDIA_PROXY_ICON_SIZE_DEFAULT,
|
|
||||||
animated = false,
|
|
||||||
) {
|
|
||||||
if (!icon) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(icon, animated);
|
|
||||||
return buildWebpMediaUrl({
|
|
||||||
path: 'icons',
|
|
||||||
id,
|
|
||||||
hash,
|
|
||||||
size,
|
|
||||||
animated: shouldAnimate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getChannelIconURLWithProxy(
|
|
||||||
{id, icon}: IconOptions,
|
|
||||||
endpoint: string,
|
|
||||||
size: MediaProxyImageSize = MEDIA_PROXY_ICON_SIZE_DEFAULT,
|
|
||||||
animated = false,
|
|
||||||
) {
|
|
||||||
if (!endpoint || !icon) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(icon, animated);
|
|
||||||
return buildWebpMediaUrl({
|
|
||||||
path: 'icons',
|
|
||||||
id,
|
|
||||||
hash,
|
|
||||||
size,
|
|
||||||
animated: shouldAnimate,
|
|
||||||
endpoint,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getWebhookAvatarURL({id, avatar}: AvatarOptions, animated = false) {
|
|
||||||
if (!avatar) {
|
|
||||||
return getDefaultAvatar(getDefaultAvatarIndex(id));
|
|
||||||
}
|
|
||||||
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(avatar, animated);
|
|
||||||
return buildWebpMediaUrl({
|
|
||||||
path: 'avatars',
|
|
||||||
id,
|
|
||||||
hash,
|
|
||||||
size: MEDIA_PROXY_AVATAR_SIZE_DEFAULT,
|
|
||||||
animated: shouldAnimate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const EMOJI_URL_CACHE = new Map<string, string>();
|
|
||||||
const EMOJI_URL_CACHE_LIMIT = 4096;
|
|
||||||
|
|
||||||
export function getEmojiURL({id, animated}: {id: string; animated?: boolean}) {
|
|
||||||
if (DeveloperOptions.forceRenderPlaceholders) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const animatedFlag = Boolean(animated);
|
|
||||||
const key = `${RuntimeConfig.mediaEndpoint}:${animatedFlag ? 'a' : 's'}:${id}`;
|
|
||||||
const cached = EMOJI_URL_CACHE.get(key);
|
|
||||||
if (cached !== undefined) return cached;
|
|
||||||
const result = mediaUrl(setPathQueryParams(`emojis/${id}.webp`, {v: 5}), {animated: animatedFlag});
|
|
||||||
if (EMOJI_URL_CACHE.size >= EMOJI_URL_CACHE_LIMIT) EMOJI_URL_CACHE.clear();
|
|
||||||
EMOJI_URL_CACHE.set(key, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
type StickerSize = 160 | 320;
|
|
||||||
|
|
||||||
export function getStickerURL({id, animated, size = 320}: {id: string; animated?: boolean; size?: StickerSize}) {
|
|
||||||
if (DeveloperOptions.forceRenderPlaceholders) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const safeSize: StickerSize = size === 320 ? 320 : 160;
|
|
||||||
return mediaUrl(setPathQueryParams(`stickers/${id}.webp`, {size: safeSize}), {animated: Boolean(animated)});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fileToBase64(file: File) {
|
|
||||||
return new Promise<string>((resolve, reject) => {
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = () => resolve(reader.result as string);
|
|
||||||
reader.onerror = reject;
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,15 @@
|
|||||||
|
|
||||||
import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
|
import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
|
||||||
import DeveloperOptions from '@app/features/devtools/state/DeveloperOptions';
|
import DeveloperOptions from '@app/features/devtools/state/DeveloperOptions';
|
||||||
import {buildMediaProxyURL} from '@app/features/messaging/utils/MediaProxyUtils';
|
import {
|
||||||
|
buildMediaProxyURL,
|
||||||
|
LARGEST_MEDIA_PROXY_IMAGE_SIZE,
|
||||||
|
MEDIA_PROXY_IMAGE_SIZE_LADDER,
|
||||||
|
snapMediaProxyImageSize,
|
||||||
|
} from '@app/features/messaging/utils/MediaProxyUtils';
|
||||||
|
|
||||||
|
export {MEDIA_PROXY_IMAGE_SIZE_LADDER, snapMediaProxyImageSize};
|
||||||
|
|
||||||
import {cdnUrl, mediaUrl, setPathQueryParams} from '@app/features/messaging/utils/MessagingUrlUtils';
|
import {cdnUrl, mediaUrl, setPathQueryParams} from '@app/features/messaging/utils/MessagingUrlUtils';
|
||||||
import type {User} from '@app/features/user/models/User';
|
import type {User} from '@app/features/user/models/User';
|
||||||
import {
|
import {
|
||||||
@@ -16,13 +24,68 @@ import {
|
|||||||
MEDIA_PROXY_ICON_SIZE_DEFAULT,
|
MEDIA_PROXY_ICON_SIZE_DEFAULT,
|
||||||
} from '@fluxer/constants/src/MediaProxyAssetSizes';
|
} from '@fluxer/constants/src/MediaProxyAssetSizes';
|
||||||
import type {MediaProxyImageSize} from '@fluxer/constants/src/MediaProxyImageSizes';
|
import type {MediaProxyImageSize} from '@fluxer/constants/src/MediaProxyImageSizes';
|
||||||
|
import {SOUNDBOARD_SOUND_PATH_PREFIX} from '@fluxer/constants/src/SoundboardConstants';
|
||||||
|
|
||||||
const getDefaultAvatar = (index: number): string => cdnUrl(`avatars/${index}.png`);
|
const GUILD_BANNER_CSS_WIDTH = 360;
|
||||||
|
const GUILD_EMBED_SPLASH_CSS_WIDTH = 360;
|
||||||
|
const GUILD_SPLASH_FALLBACK_CSS_WIDTH = 1024;
|
||||||
|
|
||||||
|
const MEDIA_PROXY_WIDE_ASSET_SERVER_MIN_DIMENSION = 480;
|
||||||
|
const MEDIA_PROXY_WIDE_ASSET_SERVER_MAX_DIMENSION = 2400;
|
||||||
|
const MEDIA_PROXY_ICON_SERVER_MIN_DIMENSION = 128;
|
||||||
|
const MEDIA_PROXY_ICON_SERVER_MAX_DIMENSION = 1024;
|
||||||
|
|
||||||
|
function snapImageSizeWithinServerRange(
|
||||||
|
cssPixels: number,
|
||||||
|
minDimension: number,
|
||||||
|
maxDimension: number,
|
||||||
|
): MediaProxyImageSize {
|
||||||
|
const floor = MEDIA_PROXY_IMAGE_SIZE_LADDER.find((rung) => rung >= minDimension) ?? LARGEST_MEDIA_PROXY_IMAGE_SIZE;
|
||||||
|
const ceiling = MEDIA_PROXY_IMAGE_SIZE_LADDER.reduce(
|
||||||
|
(largest, rung) => (rung <= maxDimension ? rung : largest),
|
||||||
|
MEDIA_PROXY_IMAGE_SIZE_LADDER[0],
|
||||||
|
);
|
||||||
|
const snapped = snapMediaProxyImageSize(cssPixels);
|
||||||
|
if (snapped < floor) return floor;
|
||||||
|
if (snapped > ceiling) return ceiling;
|
||||||
|
return snapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
function snapWideAssetImageSize(cssPixels: number): MediaProxyImageSize {
|
||||||
|
return snapImageSizeWithinServerRange(
|
||||||
|
cssPixels,
|
||||||
|
MEDIA_PROXY_WIDE_ASSET_SERVER_MIN_DIMENSION,
|
||||||
|
MEDIA_PROXY_WIDE_ASSET_SERVER_MAX_DIMENSION,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function snapIconImageSize(cssPixels: number): MediaProxyImageSize {
|
||||||
|
return snapImageSizeWithinServerRange(
|
||||||
|
cssPixels,
|
||||||
|
MEDIA_PROXY_ICON_SERVER_MIN_DIMENSION,
|
||||||
|
MEDIA_PROXY_ICON_SERVER_MAX_DIMENSION,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getViewportSplashSize = (): MediaProxyImageSize => {
|
||||||
|
const screenWidth = typeof window === 'undefined' ? Number.NaN : window.screen.width;
|
||||||
|
const cssWidth = Number.isFinite(screenWidth) && screenWidth > 0 ? screenWidth : GUILD_SPLASH_FALLBACK_CSS_WIDTH;
|
||||||
|
return snapWideAssetImageSize(cssWidth);
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULT_AVATAR_ASSET_VERSION = '1';
|
||||||
|
|
||||||
|
export const getDefaultAvatarURLForIndex = (index: number): string =>
|
||||||
|
cdnUrl(`avatars/${index}.png?v=${DEFAULT_AVATAR_ASSET_VERSION}`);
|
||||||
|
|
||||||
export function getDefaultAvatarPrimaryColor(id: string) {
|
export function getDefaultAvatarPrimaryColor(id: string) {
|
||||||
return getSharedDefaultAvatarPrimaryColor(id);
|
return getSharedDefaultAvatarPrimaryColor(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getDefaultAvatarURL(id: string) {
|
||||||
|
return getDefaultAvatarURLForIndex(getDefaultAvatarIndex(id));
|
||||||
|
}
|
||||||
|
|
||||||
type AvatarOptions = Pick<User, 'id' | 'avatar'>;
|
type AvatarOptions = Pick<User, 'id' | 'avatar'>;
|
||||||
type BannerOptions = Pick<User, 'id' | 'banner'>;
|
type BannerOptions = Pick<User, 'id' | 'banner'>;
|
||||||
|
|
||||||
@@ -42,17 +105,18 @@ type MediaURLParams = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const parseMediaHashForRequest = (value: string, animated = false) => {
|
const parseMediaHashForRequest = (value: string, animated = false) => {
|
||||||
const {animated: isAnimated} = parseAvatarHash(value);
|
const {animated: isAnimated, hash} = parseAvatarHash(value);
|
||||||
|
const shouldAnimate = isAnimated && animated;
|
||||||
return {
|
return {
|
||||||
hash: value,
|
hash: shouldAnimate ? value : hash,
|
||||||
animated: isAnimated ? animated : undefined,
|
animated: shouldAnimate ? true : undefined,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const getMediaURL = ({path, id, hash, size, format, animated, endpoint}: MediaURLParams) => {
|
const getMediaURL = ({path, id, hash, size, format, animated, endpoint}: MediaURLParams) => {
|
||||||
if (DeveloperOptions.forceRenderPlaceholders) {
|
if (DeveloperOptions.forceRenderPlaceholders) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
const baseEndpoint = endpoint ?? RuntimeConfig.mediaEndpoint;
|
const baseEndpoint = endpoint === undefined ? RuntimeConfig.mediaEndpoint : endpoint;
|
||||||
if (!baseEndpoint) {
|
if (!baseEndpoint) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@@ -89,6 +153,8 @@ const buildWebpMediaUrl = (params: Omit<MediaURLParams, 'format'>) => getMediaUR
|
|||||||
const buildPngMediaUrl = (params: Omit<MediaURLParams, 'format'>) => getMediaURL({...params, format: 'png'});
|
const buildPngMediaUrl = (params: Omit<MediaURLParams, 'format'>) => getMediaURL({...params, format: 'png'});
|
||||||
const buildGuildMemberWebpUrl = (params: Omit<GuildMemberMediaURLParams, 'format'>) =>
|
const buildGuildMemberWebpUrl = (params: Omit<GuildMemberMediaURLParams, 'format'>) =>
|
||||||
getGuildMemberMediaURL({...params, format: 'webp'});
|
getGuildMemberMediaURL({...params, format: 'webp'});
|
||||||
|
const buildGuildMemberPngUrl = (params: Omit<GuildMemberMediaURLParams, 'format'>) =>
|
||||||
|
getGuildMemberMediaURL({...params, format: 'png'});
|
||||||
|
|
||||||
export function getUserAvatarURL(
|
export function getUserAvatarURL(
|
||||||
{id, avatar}: AvatarOptions,
|
{id, avatar}: AvatarOptions,
|
||||||
@@ -96,14 +162,14 @@ export function getUserAvatarURL(
|
|||||||
size: MediaProxyImageSize = MEDIA_PROXY_AVATAR_SIZE_DEFAULT,
|
size: MediaProxyImageSize = MEDIA_PROXY_AVATAR_SIZE_DEFAULT,
|
||||||
) {
|
) {
|
||||||
if (!avatar) {
|
if (!avatar) {
|
||||||
return getDefaultAvatar(getDefaultAvatarIndex(id));
|
return getDefaultAvatarURLForIndex(getDefaultAvatarIndex(id));
|
||||||
}
|
}
|
||||||
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(avatar, animated);
|
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(avatar, animated);
|
||||||
return buildWebpMediaUrl({
|
return buildWebpMediaUrl({
|
||||||
path: 'avatars',
|
path: 'avatars',
|
||||||
id,
|
id,
|
||||||
hash,
|
hash,
|
||||||
size,
|
size: snapIconImageSize(size),
|
||||||
animated: shouldAnimate,
|
animated: shouldAnimate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -113,18 +179,49 @@ export function getUserNotificationAvatarURL(
|
|||||||
size: MediaProxyImageSize = MEDIA_PROXY_AVATAR_SIZE_DEFAULT,
|
size: MediaProxyImageSize = MEDIA_PROXY_AVATAR_SIZE_DEFAULT,
|
||||||
) {
|
) {
|
||||||
if (!avatar) {
|
if (!avatar) {
|
||||||
return getDefaultAvatar(getDefaultAvatarIndex(id));
|
return getDefaultAvatarURLForIndex(getDefaultAvatarIndex(id));
|
||||||
}
|
}
|
||||||
const {hash, animated} = parseMediaHashForRequest(avatar, false);
|
const {hash, animated} = parseMediaHashForRequest(avatar, false);
|
||||||
return buildPngMediaUrl({
|
return buildPngMediaUrl({
|
||||||
path: 'avatars',
|
path: 'avatars',
|
||||||
id,
|
id,
|
||||||
hash,
|
hash,
|
||||||
size,
|
size: snapIconImageSize(size),
|
||||||
animated,
|
animated,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getGuildMemberNotificationAvatarURL({
|
||||||
|
guildId,
|
||||||
|
userId,
|
||||||
|
avatar,
|
||||||
|
memberAvatar,
|
||||||
|
avatarUnset = false,
|
||||||
|
size = MEDIA_PROXY_AVATAR_SIZE_DEFAULT,
|
||||||
|
}: {
|
||||||
|
guildId: string;
|
||||||
|
userId: string;
|
||||||
|
avatar: string | null;
|
||||||
|
memberAvatar?: string | null;
|
||||||
|
avatarUnset?: boolean;
|
||||||
|
size?: MediaProxyImageSize;
|
||||||
|
}) {
|
||||||
|
if (avatarUnset) {
|
||||||
|
return getUserNotificationAvatarURL({id: userId, avatar: null}, size);
|
||||||
|
}
|
||||||
|
if (memberAvatar) {
|
||||||
|
const {hash} = parseMediaHashForRequest(memberAvatar, false);
|
||||||
|
return buildGuildMemberPngUrl({
|
||||||
|
path: 'avatars',
|
||||||
|
guildId,
|
||||||
|
userId,
|
||||||
|
hash,
|
||||||
|
size: snapIconImageSize(size),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return getUserNotificationAvatarURL({id: userId, avatar}, size);
|
||||||
|
}
|
||||||
|
|
||||||
export function getUserBannerURL({id, banner}: BannerOptions, animated = false, size: MediaProxyImageSize = 1024) {
|
export function getUserBannerURL({id, banner}: BannerOptions, animated = false, size: MediaProxyImageSize = 1024) {
|
||||||
if (!banner) {
|
if (!banner) {
|
||||||
return '';
|
return '';
|
||||||
@@ -134,11 +231,27 @@ export function getUserBannerURL({id, banner}: BannerOptions, animated = false,
|
|||||||
path: 'banners',
|
path: 'banners',
|
||||||
id,
|
id,
|
||||||
hash,
|
hash,
|
||||||
size,
|
size: snapWideAssetImageSize(size),
|
||||||
animated: shouldAnimate,
|
animated: shouldAnimate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SOUNDBOARD_SOUND_URL_CACHE = new Map<string, string>();
|
||||||
|
const SOUNDBOARD_SOUND_URL_CACHE_LIMIT = 4096;
|
||||||
|
|
||||||
|
export function getSoundboardSoundURL(soundId: string): string {
|
||||||
|
if (DeveloperOptions.forceRenderPlaceholders) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const key = `${RuntimeConfig.mediaEndpoint}:${soundId}`;
|
||||||
|
const cached = SOUNDBOARD_SOUND_URL_CACHE.get(key);
|
||||||
|
if (cached !== undefined) return cached;
|
||||||
|
const result = mediaUrl(`${SOUNDBOARD_SOUND_PATH_PREFIX}/${soundId}`);
|
||||||
|
if (SOUNDBOARD_SOUND_URL_CACHE.size >= SOUNDBOARD_SOUND_URL_CACHE_LIMIT) SOUNDBOARD_SOUND_URL_CACHE.clear();
|
||||||
|
SOUNDBOARD_SOUND_URL_CACHE.set(key, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
export function getGuildIconURL({id, icon}: IconOptions, animated = false) {
|
export function getGuildIconURL({id, icon}: IconOptions, animated = false) {
|
||||||
if (!icon) {
|
if (!icon) {
|
||||||
return '';
|
return '';
|
||||||
@@ -148,21 +261,12 @@ export function getGuildIconURL({id, icon}: IconOptions, animated = false) {
|
|||||||
path: 'icons',
|
path: 'icons',
|
||||||
id,
|
id,
|
||||||
hash,
|
hash,
|
||||||
size: MEDIA_PROXY_ICON_SIZE_DEFAULT,
|
size: snapIconImageSize(MEDIA_PROXY_ICON_SIZE_DEFAULT),
|
||||||
animated: shouldAnimate,
|
animated: shouldAnimate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getGuildSplashURL(
|
export function getGuildSplashURL({id, splash}: {id: string; splash: string | null}) {
|
||||||
{
|
|
||||||
id,
|
|
||||||
splash,
|
|
||||||
}: {
|
|
||||||
id: string;
|
|
||||||
splash: string | null;
|
|
||||||
},
|
|
||||||
size: MediaProxyImageSize = 1024,
|
|
||||||
) {
|
|
||||||
if (!splash) {
|
if (!splash) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@@ -170,19 +274,7 @@ export function getGuildSplashURL(
|
|||||||
path: 'splashes',
|
path: 'splashes',
|
||||||
id,
|
id,
|
||||||
hash: splash,
|
hash: splash,
|
||||||
size,
|
size: getViewportSplashSize(),
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getGuildDiscoverySplashURL({id, splash}: {id: string; splash: string | null}) {
|
|
||||||
if (!splash) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return buildWebpMediaUrl({
|
|
||||||
path: 'discovery-splashes',
|
|
||||||
id,
|
|
||||||
hash: splash,
|
|
||||||
size: 1024,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +296,7 @@ export function getGuildBannerURL(
|
|||||||
path: 'banners',
|
path: 'banners',
|
||||||
id,
|
id,
|
||||||
hash,
|
hash,
|
||||||
size: 1024,
|
size: snapWideAssetImageSize(GUILD_BANNER_CSS_WIDTH),
|
||||||
animated: shouldAnimate,
|
animated: shouldAnimate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -231,7 +323,7 @@ export function getGuildMemberAvatarURL({
|
|||||||
guildId,
|
guildId,
|
||||||
userId,
|
userId,
|
||||||
hash,
|
hash,
|
||||||
size,
|
size: snapIconImageSize(size),
|
||||||
animated: shouldAnimate,
|
animated: shouldAnimate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -241,11 +333,11 @@ export function getGuildMemberAvatarURL({
|
|||||||
path: 'avatars',
|
path: 'avatars',
|
||||||
id: userId,
|
id: userId,
|
||||||
hash,
|
hash,
|
||||||
size,
|
size: snapIconImageSize(size),
|
||||||
animated: shouldAnimate,
|
animated: shouldAnimate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return getDefaultAvatar(getDefaultAvatarIndex(userId));
|
return getDefaultAvatarURLForIndex(getDefaultAvatarIndex(userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getGuildMemberDisplayAvatarURL({
|
export function getGuildMemberDisplayAvatarURL({
|
||||||
@@ -298,7 +390,7 @@ export function getGuildMemberBannerURL({
|
|||||||
guildId,
|
guildId,
|
||||||
userId,
|
userId,
|
||||||
hash,
|
hash,
|
||||||
size,
|
size: snapWideAssetImageSize(size),
|
||||||
animated: shouldAnimate,
|
animated: shouldAnimate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -308,7 +400,7 @@ export function getGuildMemberBannerURL({
|
|||||||
path: 'banners',
|
path: 'banners',
|
||||||
id: userId,
|
id: userId,
|
||||||
hash,
|
hash,
|
||||||
size,
|
size: snapWideAssetImageSize(size),
|
||||||
animated: shouldAnimate,
|
animated: shouldAnimate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -326,29 +418,20 @@ export function getUserAvatarURLWithProxy(
|
|||||||
}
|
}
|
||||||
const {id, avatar} = options;
|
const {id, avatar} = options;
|
||||||
if (!avatar) {
|
if (!avatar) {
|
||||||
return getDefaultAvatar(getDefaultAvatarIndex(id));
|
return getDefaultAvatarURLForIndex(getDefaultAvatarIndex(id));
|
||||||
}
|
}
|
||||||
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(avatar, animated);
|
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(avatar, animated);
|
||||||
return buildWebpMediaUrl({
|
return buildWebpMediaUrl({
|
||||||
path: 'avatars',
|
path: 'avatars',
|
||||||
id,
|
id,
|
||||||
hash,
|
hash,
|
||||||
size,
|
size: snapIconImageSize(size),
|
||||||
animated: shouldAnimate,
|
animated: shouldAnimate,
|
||||||
endpoint,
|
endpoint,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getGuildEmbedSplashURL(
|
export function getGuildEmbedSplashURL({id, embedSplash}: {id: string; embedSplash: string | null}) {
|
||||||
{
|
|
||||||
id,
|
|
||||||
embedSplash,
|
|
||||||
}: {
|
|
||||||
id: string;
|
|
||||||
embedSplash: string | null;
|
|
||||||
},
|
|
||||||
size: MediaProxyImageSize = 1024,
|
|
||||||
) {
|
|
||||||
if (!embedSplash) {
|
if (!embedSplash) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@@ -356,7 +439,7 @@ export function getGuildEmbedSplashURL(
|
|||||||
path: 'embed-splashes',
|
path: 'embed-splashes',
|
||||||
id,
|
id,
|
||||||
hash: embedSplash,
|
hash: embedSplash,
|
||||||
size,
|
size: snapWideAssetImageSize(GUILD_EMBED_SPLASH_CSS_WIDTH),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,21 +456,21 @@ export function getChannelIconURL(
|
|||||||
path: 'icons',
|
path: 'icons',
|
||||||
id,
|
id,
|
||||||
hash,
|
hash,
|
||||||
size,
|
size: snapIconImageSize(size),
|
||||||
animated: shouldAnimate,
|
animated: shouldAnimate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWebhookAvatarURL({id, avatar}: AvatarOptions, animated = false) {
|
export function getWebhookAvatarURL({id, avatar}: AvatarOptions, animated = false) {
|
||||||
if (!avatar) {
|
if (!avatar) {
|
||||||
return getDefaultAvatar(getDefaultAvatarIndex(id));
|
return getDefaultAvatarURLForIndex(getDefaultAvatarIndex(id));
|
||||||
}
|
}
|
||||||
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(avatar, animated);
|
const {hash, animated: shouldAnimate} = parseMediaHashForRequest(avatar, animated);
|
||||||
return buildWebpMediaUrl({
|
return buildWebpMediaUrl({
|
||||||
path: 'avatars',
|
path: 'avatars',
|
||||||
id,
|
id,
|
||||||
hash,
|
hash,
|
||||||
size: MEDIA_PROXY_AVATAR_SIZE_DEFAULT,
|
size: snapIconImageSize(MEDIA_PROXY_AVATAR_SIZE_DEFAULT),
|
||||||
animated: shouldAnimate,
|
animated: shouldAnimate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -395,15 +478,15 @@ export function getWebhookAvatarURL({id, avatar}: AvatarOptions, animated = fals
|
|||||||
const EMOJI_URL_CACHE = new Map<string, string>();
|
const EMOJI_URL_CACHE = new Map<string, string>();
|
||||||
const EMOJI_URL_CACHE_LIMIT = 4096;
|
const EMOJI_URL_CACHE_LIMIT = 4096;
|
||||||
|
|
||||||
export function getEmojiURL({id, animated}: {id: string; animated?: boolean}) {
|
export function getEmojiURL({id, animated, isAnimatable}: {id: string; animated?: boolean; isAnimatable?: boolean}) {
|
||||||
if (DeveloperOptions.forceRenderPlaceholders) {
|
if (DeveloperOptions.forceRenderPlaceholders) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
const animatedFlag = Boolean(animated);
|
const animatedFlag = isAnimatable === false ? false : animated === true;
|
||||||
const key = animatedFlag ? `a:${id}` : `s:${id}`;
|
const key = `${RuntimeConfig.mediaEndpoint}:${animatedFlag ? 'a' : 's'}:${id}`;
|
||||||
const cached = EMOJI_URL_CACHE.get(key);
|
const cached = EMOJI_URL_CACHE.get(key);
|
||||||
if (cached !== undefined) return cached;
|
if (cached !== undefined) return cached;
|
||||||
const result = mediaUrl(setPathQueryParams(`emojis/${id}.webp`, {v: 5}), {animated: animatedFlag});
|
const result = mediaUrl(`emojis/${id}.webp`, {animated: animatedFlag});
|
||||||
if (EMOJI_URL_CACHE.size >= EMOJI_URL_CACHE_LIMIT) EMOJI_URL_CACHE.clear();
|
if (EMOJI_URL_CACHE.size >= EMOJI_URL_CACHE_LIMIT) EMOJI_URL_CACHE.clear();
|
||||||
EMOJI_URL_CACHE.set(key, result);
|
EMOJI_URL_CACHE.set(key, result);
|
||||||
return result;
|
return result;
|
||||||
@@ -411,12 +494,32 @@ export function getEmojiURL({id, animated}: {id: string; animated?: boolean}) {
|
|||||||
|
|
||||||
type StickerSize = 160 | 320;
|
type StickerSize = 160 | 320;
|
||||||
|
|
||||||
export function getStickerURL({id, animated, size = 320}: {id: string; animated?: boolean; size?: StickerSize}) {
|
const STICKER_URL_CACHE = new Map<string, string>();
|
||||||
|
const STICKER_URL_CACHE_LIMIT = 4096;
|
||||||
|
|
||||||
|
export function getStickerURL({
|
||||||
|
id,
|
||||||
|
animated,
|
||||||
|
isAnimatable,
|
||||||
|
size = 320,
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
animated?: boolean;
|
||||||
|
isAnimatable?: boolean;
|
||||||
|
size?: StickerSize;
|
||||||
|
}) {
|
||||||
if (DeveloperOptions.forceRenderPlaceholders) {
|
if (DeveloperOptions.forceRenderPlaceholders) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
const animatedFlag = isAnimatable === false ? false : animated === true;
|
||||||
const safeSize: StickerSize = size === 320 ? 320 : 160;
|
const safeSize: StickerSize = size === 320 ? 320 : 160;
|
||||||
return mediaUrl(setPathQueryParams(`stickers/${id}.webp`, {size: safeSize}), {animated: Boolean(animated)});
|
const key = `${RuntimeConfig.mediaEndpoint}:${animatedFlag ? 'a' : 's'}:${safeSize}:${id}`;
|
||||||
|
const cached = STICKER_URL_CACHE.get(key);
|
||||||
|
if (cached !== undefined) return cached;
|
||||||
|
const result = mediaUrl(setPathQueryParams(`stickers/${id}.webp`, {size: safeSize}), {animated: animatedFlag});
|
||||||
|
if (STICKER_URL_CACHE.size >= STICKER_URL_CACHE_LIMIT) STICKER_URL_CACHE.clear();
|
||||||
|
STICKER_URL_CACHE.set(key, result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fileToBase64(file: File) {
|
export function fileToBase64(file: File) {
|
||||||
|
|||||||
Reference in new issue
Block a user