diff --git a/src/components/dialogs/ChangeEmailDialog.tsx b/src/components/dialogs/ChangeEmailDialog.tsx
deleted file mode 100644
index 93397bae93..0000000000
--- a/src/components/dialogs/ChangeEmailDialog.tsx
+++ /dev/null
@@ -1,259 +0,0 @@
-import {useState} from 'react'
-import {View} from 'react-native'
-import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-
-import {cleanError} from '#/lib/strings/errors'
-import {useAgent, useSession} from '#/state/session'
-import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
-import {atoms as a, useBreakpoints, web} from '#/alf'
-import {Button, ButtonText} from '#/components/Button'
-import * as Dialog from '#/components/Dialog'
-import * as TextField from '#/components/forms/TextField'
-import {Loader} from '#/components/Loader'
-import {Text} from '#/components/Typography'
-
-export function ChangeEmailDialog({
- control,
- verifyEmailControl,
-}: {
- control: Dialog.DialogControlProps
- verifyEmailControl: Dialog.DialogControlProps
-}) {
- return (
-
-
-
-
- )
-}
-
-export function Inner({
- verifyEmailControl,
-}: {
- verifyEmailControl: Dialog.DialogControlProps
-}) {
- const {_} = useLingui()
- const {currentAccount} = useSession()
- const agent = useAgent()
- const control = Dialog.useDialogContext()
- const {gtMobile} = useBreakpoints()
-
- const [currentStep, setCurrentStep] = useState<
- 'StepOne' | 'StepTwo' | 'StepThree'
- >('StepOne')
- const [email, setEmail] = useState('')
- const [confirmationCode, setConfirmationCode] = useState('')
- const [isProcessing, setIsProcessing] = useState(false)
- const [error, setError] = useState('')
-
- const currentEmail = currentAccount?.email || '(no email)'
- const uiStrings = {
- StepOne: {
- title: _(msg`Change Your Email`),
- message: '',
- },
- StepTwo: {
- title: _(msg`Security Step Required`),
- message: _(
- msg`An email has been sent to your previous address, ${currentEmail}. It includes a confirmation code which you can enter below.`,
- ),
- },
- StepThree: {
- title: _(msg`Email Updated!`),
- message: _(
- msg`Your email address has been updated but it is not yet verified. As a next step, please verify your new email.`,
- ),
- },
- }
-
- const onRequestChange = async () => {
- if (email === currentAccount?.email) {
- setError(
- _(
- msg`The email address you entered is the same as your current email address.`,
- ),
- )
- return
- }
- setError('')
- setIsProcessing(true)
- try {
- const res = await agent.com.atproto.server.requestEmailUpdate()
- if (res.data.tokenRequired) {
- setCurrentStep('StepTwo')
- } else {
- await agent.com.atproto.server.updateEmail({email: email.trim()})
- await agent.resumeSession(agent.session!)
- setCurrentStep('StepThree')
- }
- } catch (e) {
- setError(cleanError(String(e)))
- } finally {
- setIsProcessing(false)
- }
- }
-
- const onConfirm = async () => {
- setError('')
- setIsProcessing(true)
- try {
- await agent.com.atproto.server.updateEmail({
- email: email.trim(),
- token: confirmationCode.trim(),
- })
- await agent.resumeSession(agent.session!)
- setCurrentStep('StepThree')
- } catch (e) {
- setError(cleanError(String(e)))
- } finally {
- setIsProcessing(false)
- }
- }
-
- const onVerify = async () => {
- control.close(() => {
- verifyEmailControl.open()
- })
- }
-
- return (
-
-
-
-
-
- {uiStrings[currentStep].title}
-
- {error ? (
-
-
-
- ) : null}
- {currentStep === 'StepOne' ? (
-
-
- Enter your new email address below.
-
-
-
-
-
- ) : (
-
- {uiStrings[currentStep].message}
-
- )}
-
- {currentStep === 'StepTwo' ? (
-
-
- Confirmation code
-
-
-
-
-
- ) : null}
-
- {currentStep === 'StepOne' ? (
- <>
-
-
- >
- ) : currentStep === 'StepTwo' ? (
- <>
-
-
- >
- ) : currentStep === 'StepThree' ? (
- <>
-
-
- >
- ) : null}
-
-
-
- )
-}
diff --git a/src/components/dialogs/VerifyEmailDialog.tsx b/src/components/dialogs/VerifyEmailDialog.tsx
deleted file mode 100644
index b8d1cd1925..0000000000
--- a/src/components/dialogs/VerifyEmailDialog.tsx
+++ /dev/null
@@ -1,360 +0,0 @@
-import {useState} from 'react'
-import {View} from 'react-native'
-import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-
-import {cleanError} from '#/lib/strings/errors'
-import {logger} from '#/logger'
-import {useAgent, useSession} from '#/state/session'
-import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
-import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
-import {Button, ButtonText} from '#/components/Button'
-import * as Dialog from '#/components/Dialog'
-import * as TextField from '#/components/forms/TextField'
-import {Envelope_Filled_Stroke2_Corner0_Rounded as EnvelopeIcon} from '#/components/icons/Envelope'
-import {InlineLinkText} from '#/components/Link'
-import {Loader} from '#/components/Loader'
-import {Text} from '#/components/Typography'
-import {ChangeEmailDialog} from './ChangeEmailDialog'
-
-export function VerifyEmailDialog({
- control,
- onCloseWithoutVerifying,
- onCloseAfterVerifying,
- reasonText,
- changeEmailControl,
- reminder,
-}: {
- control: Dialog.DialogControlProps
- onCloseWithoutVerifying?: () => void
- onCloseAfterVerifying?: () => void
- reasonText?: string
- /**
- * if a changeEmailControl for a ChangeEmailDialog is not provided,
- * this component will create one for you. Using this prop
- * helps reduce duplication, since these dialogs are often used together.
- */
- changeEmailControl?: Dialog.DialogControlProps
- reminder?: boolean
-}) {
- const agent = useAgent()
- const fallbackChangeEmailControl = Dialog.useDialogControl()
-
- const [didVerify, setDidVerify] = useState(false)
-
- return (
- <>
- {
- if (!didVerify) {
- onCloseWithoutVerifying?.()
- return
- }
-
- try {
- await agent.resumeSession(agent.session!)
- onCloseAfterVerifying?.()
- } catch (e: unknown) {
- logger.error(String(e))
- return
- }
- }}>
-
-
-
- {!changeEmailControl && (
-
- )}
- >
- )
-}
-
-export function Inner({
- setDidVerify,
- reasonText,
- changeEmailControl,
- reminder,
-}: {
- setDidVerify: (value: boolean) => void
- reasonText?: string
- changeEmailControl: Dialog.DialogControlProps
- reminder?: boolean
-}) {
- const control = Dialog.useDialogContext()
- const {_} = useLingui()
- const {currentAccount} = useSession()
- const agent = useAgent()
- const {gtMobile} = useBreakpoints()
- const t = useTheme()
-
- const [currentStep, setCurrentStep] = useState<
- 'Reminder' | 'StepOne' | 'StepTwo' | 'StepThree'
- >(reminder ? 'Reminder' : 'StepOne')
- const [confirmationCode, setConfirmationCode] = useState('')
- const [isProcessing, setIsProcessing] = useState(false)
- const [error, setError] = useState('')
-
- const uiStrings = {
- Reminder: {
- title: _(msg`Please Verify Your Email`),
- message: _(
- msg`Your email has not yet been verified. This is an important security step which we recommend.`,
- ),
- },
- StepOne: {
- title: _(msg`Verify Your Email`),
- message: '',
- },
- StepTwo: {
- title: _(msg`Enter Code`),
- message: _(
- msg`An email has been sent! Please enter the confirmation code included in the email below.`,
- ),
- },
- StepThree: {
- title: _(msg`Success!`),
- message: _(msg`Thank you! Your email has been successfully verified.`),
- },
- }
-
- const onSendEmail = async () => {
- setError('')
- setIsProcessing(true)
- try {
- await agent.com.atproto.server.requestEmailConfirmation()
- setCurrentStep('StepTwo')
- } catch (e: unknown) {
- setError(cleanError(e))
- } finally {
- setIsProcessing(false)
- }
- }
-
- const onVerifyEmail = async () => {
- setError('')
- setIsProcessing(true)
- try {
- await agent.com.atproto.server.confirmEmail({
- email: (currentAccount?.email || '').trim(),
- token: confirmationCode.trim(),
- })
- } catch (e: unknown) {
- setError(cleanError(String(e)))
- setIsProcessing(false)
- return
- }
-
- setIsProcessing(false)
- setDidVerify(true)
- setCurrentStep('StepThree')
- }
-
- return (
-
-
- {currentStep === 'Reminder' && (
-
-
-
- )}
-
-
- {uiStrings[currentStep].title}
-
- {error ? (
-
-
-
- ) : null}
- {currentStep === 'StepOne' ? (
-
- {reasonText ? (
-
- {reasonText}
-
- Don't have access to{' '}
-
- {currentAccount?.email}
-
- ?{' '}
- {
- e.preventDefault()
- control.close(() => {
- changeEmailControl.open()
- })
- return false
- }}>
- Change your email address
-
- .
-
-
- ) : (
-
-
- You'll receive an email at{' '}
-
- {currentAccount?.email}
- {' '}
- to verify it's you.
- {' '}
- {
- e.preventDefault()
- control.close(() => {
- changeEmailControl.open()
- })
- return false
- }}>
- Need to change it?
-
-
- )}
-
- ) : (
-
- {uiStrings[currentStep].message}
-
- )}
-
- {currentStep === 'StepTwo' ? (
-
-
- Confirmation Code
-
-
-
-
-
- ) : null}
-
- {currentStep === 'Reminder' ? (
- <>
-
-
- >
- ) : currentStep === 'StepOne' ? (
- <>
-
-
- >
- ) : currentStep === 'StepTwo' ? (
- <>
-
-
- >
- ) : currentStep === 'StepThree' ? (
-
- ) : null}
-
-
-
- )
-}
diff --git a/src/view/com/modals/EditProfile.tsx b/src/view/com/modals/EditProfile.tsx
deleted file mode 100644
index cb1552fe57..0000000000
--- a/src/view/com/modals/EditProfile.tsx
+++ /dev/null
@@ -1,335 +0,0 @@
-import {useCallback, useState} from 'react'
-import {
- ActivityIndicator,
- KeyboardAvoidingView,
- ScrollView,
- StyleSheet,
- TextInput,
- TouchableOpacity,
- View,
-} from 'react-native'
-import Animated, {FadeOut} from 'react-native-reanimated'
-import {LinearGradient} from 'expo-linear-gradient'
-import {type AppBskyActorDefs} from '@atproto/api'
-import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-
-import {MAX_DESCRIPTION, MAX_DISPLAY_NAME, urls} from '#/lib/constants'
-import {usePalette} from '#/lib/hooks/usePalette'
-import {compressIfNeeded} from '#/lib/media/manip'
-import {type PickerImage} from '#/lib/media/picker.shared'
-import {cleanError} from '#/lib/strings/errors'
-import {enforceLen} from '#/lib/strings/helpers'
-import {colors, gradients, s} from '#/lib/styles'
-import {useTheme} from '#/lib/ThemeContext'
-import {logger} from '#/logger'
-import {isWeb} from '#/platform/detection'
-import {useModalControls} from '#/state/modals'
-import {useProfileUpdateMutation} from '#/state/queries/profile'
-import {Text} from '#/view/com/util/text/Text'
-import * as Toast from '#/view/com/util/Toast'
-import {EditableUserAvatar} from '#/view/com/util/UserAvatar'
-import {UserBanner} from '#/view/com/util/UserBanner'
-import {Admonition} from '#/components/Admonition'
-import {InlineLinkText} from '#/components/Link'
-import {useSimpleVerificationState} from '#/components/verification'
-import {ErrorMessage} from '../util/error/ErrorMessage'
-
-const AnimatedTouchableOpacity =
- Animated.createAnimatedComponent(TouchableOpacity)
-
-export const snapPoints = ['fullscreen']
-
-export function Component({
- profile,
- onUpdate,
-}: {
- profile: AppBskyActorDefs.ProfileViewDetailed
- onUpdate?: () => void
-}) {
- const pal = usePalette('default')
- const theme = useTheme()
- const {_} = useLingui()
- const {closeModal} = useModalControls()
- const updateMutation = useProfileUpdateMutation()
- const [imageError, setImageError] = useState('')
- const initialDisplayName = profile.displayName || ''
- const [displayName, setDisplayName] = useState(
- profile.displayName || '',
- )
- const [description, setDescription] = useState(
- profile.description || '',
- )
- const [userBanner, setUserBanner] = useState(
- profile.banner,
- )
- const [userAvatar, setUserAvatar] = useState(
- profile.avatar,
- )
- const [newUserBanner, setNewUserBanner] = useState<
- PickerImage | undefined | null
- >()
- const [newUserAvatar, setNewUserAvatar] = useState<
- PickerImage | undefined | null
- >()
- const onPressCancel = () => {
- closeModal()
- }
- const onSelectNewAvatar = useCallback(
- async (img: PickerImage | null) => {
- setImageError('')
- if (img === null) {
- setNewUserAvatar(null)
- setUserAvatar(null)
- return
- }
- try {
- const finalImg = await compressIfNeeded(img, 1000000)
- setNewUserAvatar(finalImg)
- setUserAvatar(finalImg.path)
- } catch (e: any) {
- setImageError(cleanError(e))
- }
- },
- [setNewUserAvatar, setUserAvatar, setImageError],
- )
-
- const onSelectNewBanner = useCallback(
- async (img: PickerImage | null) => {
- setImageError('')
- if (!img) {
- setNewUserBanner(null)
- setUserBanner(null)
- return
- }
- try {
- const finalImg = await compressIfNeeded(img, 1000000)
- setNewUserBanner(finalImg)
- setUserBanner(finalImg.path)
- } catch (e: any) {
- setImageError(cleanError(e))
- }
- },
- [setNewUserBanner, setUserBanner, setImageError],
- )
-
- const onPressSave = useCallback(async () => {
- setImageError('')
- try {
- await updateMutation.mutateAsync({
- profile,
- updates: {
- displayName,
- description,
- },
- newUserAvatar,
- newUserBanner,
- })
- Toast.show(_(msg({message: 'Profile updated', context: 'toast'})))
- onUpdate?.()
- closeModal()
- } catch (e: any) {
- logger.error('Failed to update user profile', {message: String(e)})
- }
- }, [
- updateMutation,
- profile,
- onUpdate,
- closeModal,
- displayName,
- description,
- newUserAvatar,
- newUserBanner,
- setImageError,
- _,
- ])
- const verification = useSimpleVerificationState({
- profile,
- })
-
- return (
-
-
-
- Edit my profile
-
-
-
-
-
-
-
- {updateMutation.isError && (
-
-
-
- )}
- {imageError !== '' && (
-
-
-
- )}
-
-
-
- Display Name
-
-
- setDisplayName(enforceLen(v, MAX_DISPLAY_NAME))
- }
- accessible={true}
- accessibilityLabel={_(msg`Display name`)}
- accessibilityHint={_(msg`Edit your display name`)}
- />
-
- {verification.isVerified &&
- verification.role === 'default' &&
- displayName !== initialDisplayName && (
-
-
-
- You are verified. You will lose your verification status
- if you change your display name.{' '}
-
- Learn more.
-
-
-
-
- )}
-
-
-
- Description
-
- setDescription(enforceLen(v, MAX_DESCRIPTION))}
- accessible={true}
- accessibilityLabel={_(msg`Description`)}
- accessibilityHint={_(msg`Edit your profile description`)}
- />
-
- {updateMutation.isPending ? (
-
-
-
- ) : (
-
-
-
- Save Changes
-
-
-
- )}
- {!updateMutation.isPending && (
-
-
-
- Cancel
-
-
-
- )}
-
-
-
- )
-}
-
-const styles = StyleSheet.create({
- title: {
- textAlign: 'center',
- fontWeight: '600',
- fontSize: 24,
- marginBottom: 18,
- },
- label: {
- fontWeight: '600',
- paddingHorizontal: 4,
- paddingBottom: 4,
- marginTop: 20,
- },
- form: {
- paddingHorizontal: 14,
- },
- textInput: {
- borderWidth: 1,
- borderRadius: 6,
- paddingHorizontal: 14,
- paddingVertical: 10,
- fontSize: 16,
- },
- textArea: {
- borderWidth: 1,
- borderRadius: 6,
- paddingHorizontal: 12,
- paddingTop: 10,
- fontSize: 16,
- height: 120,
- textAlignVertical: 'top',
- },
- btn: {
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'center',
- width: '100%',
- borderRadius: 32,
- padding: 10,
- marginBottom: 10,
- },
- avi: {
- position: 'absolute',
- top: 80,
- left: 24,
- width: 84,
- height: 84,
- borderWidth: 2,
- borderRadius: 42,
- },
- photos: {
- marginBottom: 36,
- marginHorizontal: -14,
- },
- errorContainer: {marginTop: 20},
-})