New Edit Profile dialog on web, use new Edit Image dialog everywhere (#8220)

This commit is contained in:
Samuel Newman
2025-05-06 22:50:28 +03:00
committed by GitHub
parent 3f7dc9a8e5
commit 4c8fd006f6
21 changed files with 532 additions and 441 deletions
@@ -5,12 +5,11 @@ import {msg, Plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {urls} from '#/lib/constants'
import {compressIfNeeded} from '#/lib/media/manip'
import {type PickerImage} from '#/lib/media/picker.shared'
import {cleanError} from '#/lib/strings/errors'
import {useWarnMaxGraphemeCount} from '#/lib/strings/helpers'
import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {type ImageMeta} from '#/state/gallery'
import {useProfileUpdateMutation} from '#/state/queries/profile'
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
import * as Toast from '#/view/com/util/Toast'
@@ -18,10 +17,11 @@ import {EditableUserAvatar} from '#/view/com/util/UserAvatar'
import {UserBanner} from '#/view/com/util/UserBanner'
import {atoms as a, useTheme} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {Button, ButtonText} from '#/components/Button'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import * as TextField from '#/components/forms/TextField'
import {InlineLinkText} from '#/components/Link'
import {Loader} from '#/components/Loader'
import * as Prompt from '#/components/Prompt'
import {useSimpleVerificationState} from '#/components/verification'
@@ -127,10 +127,10 @@ function DialogInner({
profile.avatar,
)
const [newUserBanner, setNewUserBanner] = useState<
PickerImage | undefined | null
ImageMeta | undefined | null
>()
const [newUserAvatar, setNewUserAvatar] = useState<
PickerImage | undefined | null
ImageMeta | undefined | null
>()
const dirty =
@@ -144,7 +144,7 @@ function DialogInner({
}, [dirty, setDirty])
const onSelectNewAvatar = useCallback(
async (img: PickerImage | null) => {
(img: ImageMeta | null) => {
setImageError('')
if (img === null) {
setNewUserAvatar(null)
@@ -152,9 +152,8 @@ function DialogInner({
return
}
try {
const finalImg = await compressIfNeeded(img, 1000000)
setNewUserAvatar(finalImg)
setUserAvatar(finalImg.path)
setNewUserAvatar(img)
setUserAvatar(img.path)
} catch (e: any) {
setImageError(cleanError(e))
}
@@ -163,7 +162,7 @@ function DialogInner({
)
const onSelectNewBanner = useCallback(
async (img: PickerImage | null) => {
(img: ImageMeta | null) => {
setImageError('')
if (!img) {
setNewUserBanner(null)
@@ -171,9 +170,8 @@ function DialogInner({
return
}
try {
const finalImg = await compressIfNeeded(img, 1000000)
setNewUserBanner(finalImg)
setUserBanner(finalImg.path)
setNewUserBanner(img)
setUserBanner(img.path)
} catch (e: any) {
setImageError(cleanError(e))
}
@@ -258,6 +256,7 @@ function DialogInner({
<ButtonText style={[a.text_md, !dirty && t.atoms.text_contrast_low]}>
<Trans>Save</Trans>
</ButtonText>
{isUpdatingProfile && <ButtonIcon icon={Loader} />}
</Button>
),
[
@@ -1,11 +1,11 @@
import React, {memo, useMemo} from 'react'
import {View} from 'react-native'
import {
AppBskyActorDefs,
AppBskyLabelerDefs,
type AppBskyActorDefs,
type AppBskyLabelerDefs,
moderateProfile,
ModerationOpts,
RichText as RichTextAPI,
type ModerationOpts,
type RichText as RichTextAPI,
} from '@atproto/api'
import {msg, Plural, plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
@@ -15,10 +15,9 @@ import {MAX_LABELERS} from '#/lib/constants'
import {useHaptics} from '#/lib/haptics'
import {isAppLabeler} from '#/lib/moderation'
import {logger} from '#/logger'
import {isIOS, isWeb} from '#/platform/detection'
import {isIOS} from '#/platform/detection'
import {useProfileShadow} from '#/state/cache/profile-shadow'
import {Shadow} from '#/state/cache/types'
import {useModalControls} from '#/state/modals'
import {type Shadow} from '#/state/cache/types'
import {useLabelerSubscriptionMutation} from '#/state/queries/labeler'
import {useLikeMutation, useUnlikeMutation} from '#/state/queries/like'
import {usePreferencesQuery} from '#/state/queries/preferences'
@@ -27,7 +26,7 @@ import {ProfileMenu} from '#/view/com/profile/ProfileMenu'
import * as Toast from '#/view/com/util/Toast'
import {atoms as a, tokens, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import {DialogOuterProps, useDialogControl} from '#/components/Dialog'
import {type DialogOuterProps, useDialogControl} from '#/components/Dialog'
import {
Heart2_Filled_Stroke2_Corner0_Rounded as HeartFilled,
Heart2_Stroke2_Corner0_Rounded as Heart,
@@ -117,19 +116,7 @@ let ProfileHeaderLabeler = ({
}
}, [labeler, playHaptic, likeUri, unlikeMod, likeMod, _])
const {openModal} = useModalControls()
const editProfileControl = useDialogControl()
const onPressEditProfile = React.useCallback(() => {
if (isWeb) {
// temp, while we figure out the nested dialog bug
openModal({
name: 'edit-profile',
profile,
})
} else {
editProfileControl.open()
}
}, [editProfileControl, openModal, profile])
const onPressSubscribe = React.useCallback(
() =>
@@ -192,7 +179,7 @@ let ProfileHeaderLabeler = ({
size="small"
color="secondary"
variant="solid"
onPress={onPressEditProfile}
onPress={editProfileControl.open}
label={_(msg`Edit profile`)}
style={a.rounded_full}>
<ButtonText>
@@ -12,10 +12,9 @@ import {useLingui} from '@lingui/react'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles'
import {logger} from '#/logger'
import {isIOS, isWeb} from '#/platform/detection'
import {isIOS} from '#/platform/detection'
import {useProfileShadow} from '#/state/cache/profile-shadow'
import {type Shadow} from '#/state/cache/types'
import {useModalControls} from '#/state/modals'
import {
useProfileBlockMutationQueue,
useProfileFollowMutationQueue,
@@ -78,19 +77,7 @@ let ProfileHeaderStandard = ({
profile.viewer?.blockedBy ||
profile.viewer?.blockingByList
const {openModal} = useModalControls()
const editProfileControl = useDialogControl()
const onPressEditProfile = React.useCallback(() => {
if (isWeb) {
// temp, while we figure out the nested dialog bug
openModal({
name: 'edit-profile',
profile,
})
} else {
editProfileControl.open()
}
}, [editProfileControl, openModal, profile])
const onPressFollow = () => {
requireAuth(async () => {
@@ -178,7 +165,7 @@ let ProfileHeaderStandard = ({
size="small"
color="secondary"
variant="solid"
onPress={onPressEditProfile}
onPress={editProfileControl.open}
label={_(msg`Edit profile`)}
style={[a.rounded_full]}>
<ButtonText>