mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 04:55:42 +09:00
fix(app): stop own profile preview trapping mobile web (#3463)
This commit is contained in:
1 parent
d7bbe933fa
commit
381699623e
4 files changed
+215
-187
No files matched your search
@@ -766,6 +766,7 @@ export const UserProfileModal: UserProfileModalComponent = observer(
|
||||
return (
|
||||
<Modal.Root
|
||||
size="medium"
|
||||
centered
|
||||
initialFocusRef={autoFocusNote ? noteRef : undefined}
|
||||
className={userProfileModalStyles.modalRoot}
|
||||
data-flx="user.user-profile-modal.modal-root"
|
||||
|
||||
@@ -30,7 +30,10 @@ import * as UserCommands from '@app/features/user/commands/UserCommands';
|
||||
import * as UserProfileCommands from '@app/features/user/commands/UserProfileCommands';
|
||||
import styles from '@app/features/user/components/modals/tabs/MyProfileTab.module.css';
|
||||
import {AccentColorPicker} from '@app/features/user/components/modals/tabs/my_profile_tab/AccentColorPicker';
|
||||
import {AvatarUploader} from '@app/features/user/components/modals/tabs/my_profile_tab/AvatarUploader';
|
||||
import {
|
||||
AvatarUploader,
|
||||
useChangeAvatarAction,
|
||||
} from '@app/features/user/components/modals/tabs/my_profile_tab/AvatarUploader';
|
||||
import {BannerUploader} from '@app/features/user/components/modals/tabs/my_profile_tab/BannerUploader';
|
||||
import {BIO_MARKDOWN_PARSER_FLAGS, BioEditor} from '@app/features/user/components/modals/tabs/my_profile_tab/BioEditor';
|
||||
import {UsernameSection} from '@app/features/user/components/modals/tabs/my_profile_tab/MyProfileTabUsernameSection';
|
||||
@@ -615,6 +618,7 @@ const MyProfileTabComponent = observer(function MyProfileTabComponent({
|
||||
form.setValue('avatar', null);
|
||||
transitionAvatarAsset({type: 'asset.cleared'});
|
||||
}, [form, transitionAvatarAsset]);
|
||||
const {openChangeAvatar} = useChangeAvatarAction({onAvatarChange: handleAvatarChange});
|
||||
const handleBannerChange = useCallback(
|
||||
(base64: string) => {
|
||||
form.setValue('banner', base64);
|
||||
@@ -674,6 +678,7 @@ const MyProfileTabComponent = observer(function MyProfileTabComponent({
|
||||
const guilds = Guilds.getGuilds();
|
||||
const selectedGuild = selectedGuildId ? guilds.find((g) => g.id === selectedGuildId) : null;
|
||||
const isPerGuildProfileCustomizationDisabled = isPerGuildProfile && !hasPerGuildProfiles;
|
||||
const isAvatarCustomizationDisabled = isProfileCustomizationLocked || isPerGuildProfileCustomizationDisabled;
|
||||
const isPronounsDisabled = isProfileCustomizationLocked;
|
||||
const profileCustomizationDescription = isProfileEmailLocked
|
||||
? isPerGuildProfile
|
||||
@@ -840,7 +845,7 @@ const MyProfileTabComponent = observer(function MyProfileTabComponent({
|
||||
hasAvatar={hasAvatar}
|
||||
onAvatarChange={handleAvatarChange}
|
||||
onAvatarClear={handleAvatarClear}
|
||||
disabled={isProfileCustomizationLocked || isPerGuildProfileCustomizationDisabled}
|
||||
disabled={isAvatarCustomizationDisabled}
|
||||
disableModeSelection={isProfileCustomizationLocked}
|
||||
isPerGuildProfile={isPerGuildProfile}
|
||||
errorMessage={form.formState.errors.avatar?.message}
|
||||
@@ -934,6 +939,9 @@ const MyProfileTabComponent = observer(function MyProfileTabComponent({
|
||||
}}
|
||||
ignoreGuildAvatarInPreview={isPerGuildProfile && avatarAsset.mode === 'inherit'}
|
||||
ignoreGuildBannerInPreview={isPerGuildProfile && bannerAsset.mode === 'inherit'}
|
||||
onAvatarClick={
|
||||
mobileLayout.enabled && !isAvatarCustomizationDisabled ? openChangeAvatar : undefined
|
||||
}
|
||||
data-flx="user.my-profile-tab.my-profile-tab-component.profile-preview"
|
||||
/>
|
||||
</div>
|
||||
|
||||
+201
-184
@@ -116,6 +116,203 @@ interface AvatarUploaderProps {
|
||||
onAvatarModeChange?: (mode: AvatarMode) => void;
|
||||
}
|
||||
|
||||
interface ChangeAvatarActionOptions {
|
||||
onAvatarChange: (base64: string) => void;
|
||||
requireAnimatedAvatarEntitlement?: boolean;
|
||||
}
|
||||
|
||||
export function useChangeAvatarAction({
|
||||
onAvatarChange,
|
||||
requireAnimatedAvatarEntitlement = true,
|
||||
}: ChangeAvatarActionOptions) {
|
||||
const {i18n} = useLingui();
|
||||
const PREMIUM_PRODUCT_NAME = getPremiumProductName();
|
||||
const hasAnimatedAvatarEntitlement = isLimitToggleEnabled(
|
||||
{feature_animated_avatar: LimitResolver.resolve({key: 'feature_animated_avatar', fallback: 0})},
|
||||
'feature_animated_avatar',
|
||||
);
|
||||
const canUploadAnimatedAvatar = !requireAnimatedAvatarEntitlement || hasAnimatedAvatarEntitlement;
|
||||
const processAvatarFile = useCallback(
|
||||
async (file: File) => {
|
||||
if (file.size > GlobalLimits.getAvatarMaxSize()) {
|
||||
showGenericErrorModal({
|
||||
title: i18n._(COULDN_T_UPLOAD_AVATAR_DESCRIPTOR),
|
||||
message: i18n._(AVATAR_FILE_IS_TOO_LARGE_PLEASE_CHOOSE_A_DESCRIPTOR, {
|
||||
imageMaxSizeLabel: formatFileSize(i18n.locale, GlobalLimits.getAvatarMaxSize()),
|
||||
}),
|
||||
dataFlx: 'user.user-error-modal-utils.show-user-error-modal.generic-error-modal',
|
||||
});
|
||||
return;
|
||||
}
|
||||
const svg = isSvgFile(file);
|
||||
if (!svg && !(await canCropFile(file))) {
|
||||
showGenericErrorModal({
|
||||
title: i18n._(COULDN_T_UPLOAD_AVATAR_DESCRIPTOR),
|
||||
message: getAssetFormatErrorMessage(i18n, 'avatar', 'unsupported_mime'),
|
||||
dataFlx: 'user.user-error-modal-utils.show-user-error-modal.generic-error-modal',
|
||||
});
|
||||
return;
|
||||
}
|
||||
const {format, animated} = svg ? {format: 'unknown', animated: false} : await inspectImageFile(file);
|
||||
if (animated && !canUploadAnimatedAvatar) {
|
||||
if (shouldShowPremiumFeatures()) {
|
||||
ModalCommands.push(
|
||||
modal(() => (
|
||||
<ConfirmModal
|
||||
title={i18n._(ANIMATED_AVATARS_REQUIRE_DESCRIPTOR, {premiumProductName: getPremiumProductName()})}
|
||||
description={
|
||||
<>
|
||||
<p data-flx="user.my-profile-tab.avatar-uploader.handle-avatar-upload.p">
|
||||
<Trans>
|
||||
Animated avatars ({ANIMATED_AVATAR_FORMATS}) are exclusively available to {PREMIUM_PRODUCT_NAME}{' '}
|
||||
subscribers.
|
||||
</Trans>
|
||||
</p>
|
||||
<p
|
||||
className={styles.spacedParagraph}
|
||||
data-flx="user.my-profile-tab.avatar-uploader.handle-avatar-upload.spaced-paragraph"
|
||||
>
|
||||
{RuntimeConfig.usesUniqueUsernames ? (
|
||||
<Trans>
|
||||
With {PREMIUM_PRODUCT_NAME}, you can use animated avatars ({ANIMATED_AVATAR_FORMATS}) and
|
||||
profile banners, and unlock many other {PREMIUM_PRODUCT_NAME} perks.
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans>
|
||||
With {PREMIUM_PRODUCT_NAME}, you can use animated avatars ({ANIMATED_AVATAR_FORMATS}) and
|
||||
profile banners, customize your tag, and unlock many other {PREMIUM_PRODUCT_NAME} perks.
|
||||
</Trans>
|
||||
)}
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
primaryText={i18n._(GET_PREMIUM_DESCRIPTOR, {premiumProductName: getPremiumProductName()})}
|
||||
primaryVariant="primary"
|
||||
secondaryText={i18n._(CANCEL_DESCRIPTOR)}
|
||||
onPrimary={() => {
|
||||
window.setTimeout(() => {
|
||||
PremiumModalCommands.open();
|
||||
}, 0);
|
||||
}}
|
||||
data-flx="user.my-profile-tab.avatar-uploader.handle-avatar-upload.confirm-modal"
|
||||
/>
|
||||
)),
|
||||
);
|
||||
} else {
|
||||
ModalCommands.push(
|
||||
modal(() => (
|
||||
<GenericErrorModal
|
||||
title={i18n._(ANIMATED_AVATARS_NOT_AVAILABLE_DESCRIPTOR)}
|
||||
message={
|
||||
<Trans>
|
||||
Animated avatars ({ANIMATED_AVATAR_FORMATS}) are not available on this instance. Upload a static
|
||||
image instead.
|
||||
</Trans>
|
||||
}
|
||||
data-flx="user.my-profile-tab.avatar-uploader.handle-avatar-upload.confirm-modal--2"
|
||||
/>
|
||||
)),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const isAnimatedAvif = animated && format === 'avif';
|
||||
if (isAnimatedAvif && !(await canDecodeAnimatedAvif())) {
|
||||
showAnimatedAvifUnsupportedModal({i18n});
|
||||
return;
|
||||
}
|
||||
const base64 = svg ? await readImageFileAsUploadDataUrl(file) : await readBlobAsDataUrl(file);
|
||||
ModalCommands.push(
|
||||
modal(() => (
|
||||
<AssetCropModal
|
||||
assetType={AssetType.AVATAR}
|
||||
imageUrl={base64}
|
||||
onCropComplete={(croppedBlob) => {
|
||||
void readBlobAsDataUrl(croppedBlob).then(
|
||||
(croppedBase64) => {
|
||||
onAvatarChange(croppedBase64);
|
||||
},
|
||||
() => {
|
||||
showGenericErrorModal({
|
||||
title: i18n._(COULDN_T_UPLOAD_AVATAR_DESCRIPTOR),
|
||||
message: i18n._(FAILED_TO_PROCESS_CROPPED_IMAGE_DESCRIPTOR),
|
||||
dataFlx: 'user.user-error-modal-utils.show-user-error-modal.generic-error-modal',
|
||||
});
|
||||
},
|
||||
);
|
||||
}}
|
||||
onSkip={
|
||||
isAnimatedAvif
|
||||
? undefined
|
||||
: () => {
|
||||
onAvatarChange(base64);
|
||||
}
|
||||
}
|
||||
data-flx="user.my-profile-tab.avatar-uploader.handle-avatar-upload.asset-crop-modal"
|
||||
/>
|
||||
)),
|
||||
);
|
||||
},
|
||||
[canUploadAnimatedAvatar, onAvatarChange, i18n],
|
||||
);
|
||||
const showAvatarUploadError = useCallback(() => {
|
||||
showGenericErrorModal({
|
||||
title: i18n._(COULDN_T_UPLOAD_AVATAR_DESCRIPTOR),
|
||||
message: i18n._(INVALID_IMAGE_TRY_ANOTHER_DESCRIPTOR),
|
||||
dataFlx: 'user.user-error-modal-utils.show-user-error-modal.generic-error-modal',
|
||||
});
|
||||
}, [i18n]);
|
||||
const handlePickAvatarFile = useCallback(async () => {
|
||||
try {
|
||||
const [file] = await openFilePicker({accept: getAcceptString('avatar')});
|
||||
if (!file) return;
|
||||
await processAvatarFile(file);
|
||||
} catch {
|
||||
showAvatarUploadError();
|
||||
}
|
||||
}, [processAvatarFile, showAvatarUploadError]);
|
||||
const handleSelectAvatarGif = useCallback(
|
||||
async (gif: Gif) => {
|
||||
try {
|
||||
await processAvatarFile(await downloadGifAsImageFile(gif));
|
||||
} catch {
|
||||
showAvatarUploadError();
|
||||
}
|
||||
},
|
||||
[processAvatarFile, showAvatarUploadError],
|
||||
);
|
||||
const handleAvatarUpload = useCallback(() => {
|
||||
const uploadHint = canUploadAnimatedAvatar
|
||||
? formatImageUploadRecommendedHint(i18n, {
|
||||
formats: ANIMATED_IMAGE_FORMATS,
|
||||
maxSize: formatFileSize(i18n.locale, GlobalLimits.getAvatarMaxSize()),
|
||||
recommendedSize: AVATAR_RECOMMENDED_SIZE_LABEL,
|
||||
})
|
||||
: shouldShowPremiumFeatures()
|
||||
? formatImageUploadRecommendedHintWithNote(i18n, {
|
||||
formats: STATIC_IMAGE_FORMATS,
|
||||
maxSize: formatFileSize(i18n.locale, GlobalLimits.getAvatarMaxSize()),
|
||||
recommendedSize: AVATAR_RECOMMENDED_SIZE_LABEL,
|
||||
note: i18n._(ANIMATED_AVATARS_REQUIRE_PREMIUM_NOTE_DESCRIPTOR, {
|
||||
animatedAvatarFormats: ANIMATED_AVATAR_FORMATS,
|
||||
premiumProductName: getPremiumProductName(),
|
||||
}),
|
||||
})
|
||||
: formatImageUploadRecommendedHint(i18n, {
|
||||
formats: STATIC_IMAGE_FORMATS,
|
||||
maxSize: formatFileSize(i18n.locale, GlobalLimits.getAvatarMaxSize()),
|
||||
recommendedSize: AVATAR_RECOMMENDED_SIZE_LABEL,
|
||||
});
|
||||
openAssetSourceModal({
|
||||
title: i18n._(CHANGE_AVATAR_DESCRIPTOR),
|
||||
uploadHint,
|
||||
onPickUpload: handlePickAvatarFile,
|
||||
onSelectGif: (gif) => void handleSelectAvatarGif(gif),
|
||||
});
|
||||
}, [canUploadAnimatedAvatar, handlePickAvatarFile, handleSelectAvatarGif, i18n]);
|
||||
return {canUploadAnimatedAvatar, openChangeAvatar: handleAvatarUpload};
|
||||
}
|
||||
|
||||
export const AvatarUploader = observer(
|
||||
({
|
||||
hasAvatar,
|
||||
@@ -129,13 +326,11 @@ export const AvatarUploader = observer(
|
||||
avatarMode = 'inherit',
|
||||
onAvatarModeChange,
|
||||
}: AvatarUploaderProps) => {
|
||||
const PREMIUM_PRODUCT_NAME = getPremiumProductName();
|
||||
const {i18n} = useLingui();
|
||||
const hasAnimatedAvatarEntitlement = isLimitToggleEnabled(
|
||||
{feature_animated_avatar: LimitResolver.resolve({key: 'feature_animated_avatar', fallback: 0})},
|
||||
'feature_animated_avatar',
|
||||
);
|
||||
const canUploadAnimatedAvatar = !requireAnimatedAvatarEntitlement || hasAnimatedAvatarEntitlement;
|
||||
const {canUploadAnimatedAvatar, openChangeAvatar: handleAvatarUpload} = useChangeAvatarAction({
|
||||
onAvatarChange,
|
||||
requireAnimatedAvatarEntitlement,
|
||||
});
|
||||
const getAvatarModeOptions = useCallback(
|
||||
() => [
|
||||
{
|
||||
@@ -157,184 +352,6 @@ export const AvatarUploader = observer(
|
||||
],
|
||||
[canUploadAnimatedAvatar, i18n],
|
||||
);
|
||||
const processAvatarFile = useCallback(
|
||||
async (file: File) => {
|
||||
if (file.size > GlobalLimits.getAvatarMaxSize()) {
|
||||
showGenericErrorModal({
|
||||
title: i18n._(COULDN_T_UPLOAD_AVATAR_DESCRIPTOR),
|
||||
message: i18n._(AVATAR_FILE_IS_TOO_LARGE_PLEASE_CHOOSE_A_DESCRIPTOR, {
|
||||
imageMaxSizeLabel: formatFileSize(i18n.locale, GlobalLimits.getAvatarMaxSize()),
|
||||
}),
|
||||
dataFlx: 'user.user-error-modal-utils.show-user-error-modal.generic-error-modal',
|
||||
});
|
||||
return;
|
||||
}
|
||||
const svg = isSvgFile(file);
|
||||
if (!svg && !(await canCropFile(file))) {
|
||||
showGenericErrorModal({
|
||||
title: i18n._(COULDN_T_UPLOAD_AVATAR_DESCRIPTOR),
|
||||
message: getAssetFormatErrorMessage(i18n, 'avatar', 'unsupported_mime'),
|
||||
dataFlx: 'user.user-error-modal-utils.show-user-error-modal.generic-error-modal',
|
||||
});
|
||||
return;
|
||||
}
|
||||
const {format, animated} = svg ? {format: 'unknown', animated: false} : await inspectImageFile(file);
|
||||
if (animated && !canUploadAnimatedAvatar) {
|
||||
if (shouldShowPremiumFeatures()) {
|
||||
ModalCommands.push(
|
||||
modal(() => (
|
||||
<ConfirmModal
|
||||
title={i18n._(ANIMATED_AVATARS_REQUIRE_DESCRIPTOR, {premiumProductName: getPremiumProductName()})}
|
||||
description={
|
||||
<>
|
||||
<p data-flx="user.my-profile-tab.avatar-uploader.handle-avatar-upload.p">
|
||||
<Trans>
|
||||
Animated avatars ({ANIMATED_AVATAR_FORMATS}) are exclusively available to{' '}
|
||||
{PREMIUM_PRODUCT_NAME} subscribers.
|
||||
</Trans>
|
||||
</p>
|
||||
<p
|
||||
className={styles.spacedParagraph}
|
||||
data-flx="user.my-profile-tab.avatar-uploader.handle-avatar-upload.spaced-paragraph"
|
||||
>
|
||||
{RuntimeConfig.usesUniqueUsernames ? (
|
||||
<Trans>
|
||||
With {PREMIUM_PRODUCT_NAME}, you can use animated avatars ({ANIMATED_AVATAR_FORMATS}) and
|
||||
profile banners, and unlock many other {PREMIUM_PRODUCT_NAME} perks.
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans>
|
||||
With {PREMIUM_PRODUCT_NAME}, you can use animated avatars ({ANIMATED_AVATAR_FORMATS}) and
|
||||
profile banners, customize your tag, and unlock many other {PREMIUM_PRODUCT_NAME} perks.
|
||||
</Trans>
|
||||
)}
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
primaryText={i18n._(GET_PREMIUM_DESCRIPTOR, {premiumProductName: getPremiumProductName()})}
|
||||
primaryVariant="primary"
|
||||
secondaryText={i18n._(CANCEL_DESCRIPTOR)}
|
||||
onPrimary={() => {
|
||||
window.setTimeout(() => {
|
||||
PremiumModalCommands.open();
|
||||
}, 0);
|
||||
}}
|
||||
data-flx="user.my-profile-tab.avatar-uploader.handle-avatar-upload.confirm-modal"
|
||||
/>
|
||||
)),
|
||||
);
|
||||
} else {
|
||||
ModalCommands.push(
|
||||
modal(() => (
|
||||
<GenericErrorModal
|
||||
title={i18n._(ANIMATED_AVATARS_NOT_AVAILABLE_DESCRIPTOR)}
|
||||
message={
|
||||
<Trans>
|
||||
Animated avatars ({ANIMATED_AVATAR_FORMATS}) are not available on this instance. Upload a static
|
||||
image instead.
|
||||
</Trans>
|
||||
}
|
||||
data-flx="user.my-profile-tab.avatar-uploader.handle-avatar-upload.confirm-modal--2"
|
||||
/>
|
||||
)),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const isAnimatedAvif = animated && format === 'avif';
|
||||
if (isAnimatedAvif && !(await canDecodeAnimatedAvif())) {
|
||||
showAnimatedAvifUnsupportedModal({i18n});
|
||||
return;
|
||||
}
|
||||
const base64 = svg ? await readImageFileAsUploadDataUrl(file) : await readBlobAsDataUrl(file);
|
||||
ModalCommands.push(
|
||||
modal(() => (
|
||||
<AssetCropModal
|
||||
assetType={AssetType.AVATAR}
|
||||
imageUrl={base64}
|
||||
onCropComplete={(croppedBlob) => {
|
||||
void readBlobAsDataUrl(croppedBlob).then(
|
||||
(croppedBase64) => {
|
||||
onAvatarChange(croppedBase64);
|
||||
},
|
||||
() => {
|
||||
showGenericErrorModal({
|
||||
title: i18n._(COULDN_T_UPLOAD_AVATAR_DESCRIPTOR),
|
||||
message: i18n._(FAILED_TO_PROCESS_CROPPED_IMAGE_DESCRIPTOR),
|
||||
dataFlx: 'user.user-error-modal-utils.show-user-error-modal.generic-error-modal',
|
||||
});
|
||||
},
|
||||
);
|
||||
}}
|
||||
onSkip={
|
||||
isAnimatedAvif
|
||||
? undefined
|
||||
: () => {
|
||||
onAvatarChange(base64);
|
||||
}
|
||||
}
|
||||
data-flx="user.my-profile-tab.avatar-uploader.handle-avatar-upload.asset-crop-modal"
|
||||
/>
|
||||
)),
|
||||
);
|
||||
},
|
||||
[canUploadAnimatedAvatar, onAvatarChange, i18n],
|
||||
);
|
||||
const showAvatarUploadError = useCallback(() => {
|
||||
showGenericErrorModal({
|
||||
title: i18n._(COULDN_T_UPLOAD_AVATAR_DESCRIPTOR),
|
||||
message: i18n._(INVALID_IMAGE_TRY_ANOTHER_DESCRIPTOR),
|
||||
dataFlx: 'user.user-error-modal-utils.show-user-error-modal.generic-error-modal',
|
||||
});
|
||||
}, [i18n]);
|
||||
const handlePickAvatarFile = useCallback(async () => {
|
||||
try {
|
||||
const [file] = await openFilePicker({accept: getAcceptString('avatar')});
|
||||
if (!file) return;
|
||||
await processAvatarFile(file);
|
||||
} catch {
|
||||
showAvatarUploadError();
|
||||
}
|
||||
}, [processAvatarFile, showAvatarUploadError]);
|
||||
const handleSelectAvatarGif = useCallback(
|
||||
async (gif: Gif) => {
|
||||
try {
|
||||
await processAvatarFile(await downloadGifAsImageFile(gif));
|
||||
} catch {
|
||||
showAvatarUploadError();
|
||||
}
|
||||
},
|
||||
[processAvatarFile, showAvatarUploadError],
|
||||
);
|
||||
const handleAvatarUpload = useCallback(() => {
|
||||
const uploadHint = canUploadAnimatedAvatar
|
||||
? formatImageUploadRecommendedHint(i18n, {
|
||||
formats: ANIMATED_IMAGE_FORMATS,
|
||||
maxSize: formatFileSize(i18n.locale, GlobalLimits.getAvatarMaxSize()),
|
||||
recommendedSize: AVATAR_RECOMMENDED_SIZE_LABEL,
|
||||
})
|
||||
: shouldShowPremiumFeatures()
|
||||
? formatImageUploadRecommendedHintWithNote(i18n, {
|
||||
formats: STATIC_IMAGE_FORMATS,
|
||||
maxSize: formatFileSize(i18n.locale, GlobalLimits.getAvatarMaxSize()),
|
||||
recommendedSize: AVATAR_RECOMMENDED_SIZE_LABEL,
|
||||
note: i18n._(ANIMATED_AVATARS_REQUIRE_PREMIUM_NOTE_DESCRIPTOR, {
|
||||
animatedAvatarFormats: ANIMATED_AVATAR_FORMATS,
|
||||
premiumProductName: getPremiumProductName(),
|
||||
}),
|
||||
})
|
||||
: formatImageUploadRecommendedHint(i18n, {
|
||||
formats: STATIC_IMAGE_FORMATS,
|
||||
maxSize: formatFileSize(i18n.locale, GlobalLimits.getAvatarMaxSize()),
|
||||
recommendedSize: AVATAR_RECOMMENDED_SIZE_LABEL,
|
||||
});
|
||||
openAssetSourceModal({
|
||||
title: i18n._(CHANGE_AVATAR_DESCRIPTOR),
|
||||
uploadHint,
|
||||
onPickUpload: handlePickAvatarFile,
|
||||
onSelectGif: (gif) => void handleSelectAvatarGif(gif),
|
||||
});
|
||||
}, [canUploadAnimatedAvatar, handlePickAvatarFile, handleSelectAvatarGif, i18n]);
|
||||
const handleModeChange = useCallback(
|
||||
(mode: AvatarMode) => {
|
||||
if (mode === 'custom') {
|
||||
|
||||
@@ -65,6 +65,7 @@ interface ProfilePreviewProps {
|
||||
showMessageButton?: boolean;
|
||||
showPreviewLabel?: boolean;
|
||||
previewCustomStatus?: CustomStatus | null;
|
||||
onAvatarClick?: () => void;
|
||||
}
|
||||
|
||||
export const ProfilePreview: React.FC<ProfilePreviewProps> = observer(
|
||||
@@ -90,6 +91,7 @@ export const ProfilePreview: React.FC<ProfilePreviewProps> = observer(
|
||||
showMessageButton = true,
|
||||
showPreviewLabel = true,
|
||||
previewCustomStatus,
|
||||
onAvatarClick,
|
||||
}) => {
|
||||
const {i18n} = useLingui();
|
||||
const previewOverrides = useMemo<ProfilePreviewOverrides>(
|
||||
@@ -245,7 +247,7 @@ export const ProfilePreview: React.FC<ProfilePreviewProps> = observer(
|
||||
avatarUrl={finalAvatarUrl}
|
||||
hoverAvatarUrl={finalHoverAvatarUrl}
|
||||
isClickable={true}
|
||||
onAvatarClick={openMockProfile}
|
||||
onAvatarClick={onAvatarClick ?? openMockProfile}
|
||||
data-flx="user.profile.profile-preview.profile-card-banner"
|
||||
/>
|
||||
<UserProfileBadges
|
||||
|
||||
Reference in new issue
Block a user