Refactor delete account dialog using ALF (#9863)
This commit is contained in:
@@ -32,8 +32,11 @@ export function TokenField({
|
||||
<TextField.Root>
|
||||
<TextField.Icon icon={Shield} />
|
||||
<TextField.Input
|
||||
autoComplete="off"
|
||||
autoCorrect={false}
|
||||
isInvalid={isInvalid}
|
||||
label={_(msg`Confirmation code`)}
|
||||
maxLength={11}
|
||||
placeholder="XXXXX-XXXXX"
|
||||
value={value}
|
||||
onChangeText={handleOnChangeText}
|
||||
|
||||
@@ -3,7 +3,6 @@ import {useLingui} from '@lingui/react'
|
||||
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||
|
||||
import {type CommonNavigatorParams} from '#/lib/routes/types'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useSession} from '#/state/session'
|
||||
import * as SettingsList from '#/screens/Settings/components/SettingsList'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
@@ -27,6 +26,7 @@ import * as Layout from '#/components/Layout'
|
||||
import {ChangeHandleDialog} from './components/ChangeHandleDialog'
|
||||
import {ChangePasswordDialog} from './components/ChangePasswordDialog'
|
||||
import {DeactivateAccountDialog} from './components/DeactivateAccountDialog'
|
||||
import {DeleteAccountDialog} from './components/DeleteAccountDialog'
|
||||
import {ExportCarDialog} from './components/ExportCarDialog'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'AccountSettings'>
|
||||
@@ -34,13 +34,13 @@ export function AccountSettingsScreen({}: Props) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const {currentAccount} = useSession()
|
||||
const {openModal} = useModalControls()
|
||||
const emailDialogControl = useEmailDialogControl()
|
||||
const birthdayControl = useDialogControl()
|
||||
const changeHandleControl = useDialogControl()
|
||||
const changePasswordControl = useDialogControl()
|
||||
const exportCarControl = useDialogControl()
|
||||
const deactivateAccountControl = useDialogControl()
|
||||
const deleteAccountControl = useDialogControl()
|
||||
|
||||
return (
|
||||
<Layout.Screen>
|
||||
@@ -169,7 +169,7 @@ export function AccountSettingsScreen({}: Props) {
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.PressableItem
|
||||
label={_(msg`Delete account`)}
|
||||
onPress={() => openModal({name: 'delete-account'})}
|
||||
onPress={() => deleteAccountControl.open()}
|
||||
destructive>
|
||||
<SettingsList.ItemIcon icon={Trash_Stroke2_Corner2_Rounded} />
|
||||
<SettingsList.ItemText>
|
||||
@@ -185,6 +185,10 @@ export function AccountSettingsScreen({}: Props) {
|
||||
<ChangePasswordDialog control={changePasswordControl} />
|
||||
<ExportCarDialog control={exportCarControl} />
|
||||
<DeactivateAccountDialog control={deactivateAccountControl} />
|
||||
<DeleteAccountDialog
|
||||
control={deleteAccountControl}
|
||||
deactivateDialogControl={deactivateAccountControl}
|
||||
/>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,371 @@
|
||||
import {useCallback, useRef, useState} from 'react'
|
||||
import {type TextInput, View} from 'react-native'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {DM_SERVICE_HEADERS} from '#/lib/constants'
|
||||
import {useCleanError} from '#/lib/hooks/useCleanError'
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
import {logger} from '#/logger'
|
||||
import {useAgent, useSession, useSessionApi} from '#/state/session'
|
||||
import {atoms as a, useTheme, web} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import {type DialogOuterProps} from '#/components/Dialog'
|
||||
import {
|
||||
isValidCode,
|
||||
TokenField,
|
||||
} from '#/components/dialogs/EmailDialog/components/TokenField'
|
||||
import * as TextField from '#/components/forms/TextField'
|
||||
import {Envelope_Stroke2_Corner0_Rounded as Envelope} from '#/components/icons/Envelope'
|
||||
import {Lock_Stroke2_Corner0_Rounded as Lock} from '#/components/icons/Lock'
|
||||
import {createStaticClick, InlineLinkText} from '#/components/Link'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import * as toast from '#/components/Toast'
|
||||
import {Span, Text} from '#/components/Typography'
|
||||
import {resetToTab} from '#/Navigation'
|
||||
|
||||
const WHITESPACE_RE = /\s/gu
|
||||
const PASSWORD_MIN_LENGTH = 8
|
||||
|
||||
enum Step {
|
||||
SEND_CODE,
|
||||
VERIFY_CODE,
|
||||
CONFIRM_DELETION,
|
||||
}
|
||||
|
||||
enum EmailState {
|
||||
DEFAULT,
|
||||
PENDING,
|
||||
}
|
||||
|
||||
function isPasswordValid(password: string) {
|
||||
return password.length >= PASSWORD_MIN_LENGTH
|
||||
}
|
||||
|
||||
export function DeleteAccountDialog({
|
||||
control,
|
||||
deactivateDialogControl,
|
||||
}: {
|
||||
control: DialogOuterProps['control']
|
||||
deactivateDialogControl: DialogOuterProps['control']
|
||||
}) {
|
||||
return (
|
||||
<Prompt.Outer control={control}>
|
||||
<DeleteAccountDialogInner
|
||||
control={control}
|
||||
deactivateDialogControl={deactivateDialogControl}
|
||||
/>
|
||||
</Prompt.Outer>
|
||||
)
|
||||
}
|
||||
|
||||
function DeleteAccountDialogInner({
|
||||
control,
|
||||
deactivateDialogControl,
|
||||
}: {
|
||||
control: DialogOuterProps['control']
|
||||
deactivateDialogControl: DialogOuterProps['control']
|
||||
}) {
|
||||
const passwordRef = useRef<TextInput | null>(null)
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const cleanError = useCleanError()
|
||||
const agent = useAgent()
|
||||
const {currentAccount} = useSession()
|
||||
const {removeAccount} = useSessionApi()
|
||||
|
||||
const [emailState, setEmailState] = useState(EmailState.DEFAULT)
|
||||
const [emailSentCount, setEmailSentCount] = useState(0)
|
||||
const [step, setStep] = useState(Step.SEND_CODE)
|
||||
const [confirmCode, setConfirmCode] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const [error, setError] = useState('')
|
||||
|
||||
const sendEmail = useCallback(async () => {
|
||||
if (emailState === EmailState.PENDING) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
setEmailState(EmailState.PENDING)
|
||||
await agent.com.atproto.server.requestAccountDelete()
|
||||
setError('')
|
||||
setEmailSentCount(prevCount => prevCount + 1)
|
||||
setStep(Step.VERIFY_CODE)
|
||||
} catch (e: any) {
|
||||
const {clean, raw} = cleanError(e)
|
||||
const error = clean || raw || e
|
||||
setError(error)
|
||||
logger.error(raw || e, {
|
||||
message: 'Failed to send account deletion verification email',
|
||||
})
|
||||
} finally {
|
||||
setEmailState(EmailState.DEFAULT)
|
||||
}
|
||||
}, [agent, cleanError, emailState, setEmailState])
|
||||
|
||||
const confirmDeletion = useCallback(async () => {
|
||||
try {
|
||||
setError('')
|
||||
if (!currentAccount?.did) {
|
||||
throw new Error('Invalid did')
|
||||
}
|
||||
const token = confirmCode.replace(WHITESPACE_RE, '')
|
||||
// Inform chat service of intent to delete account.
|
||||
const {success} = await agent.api.chat.bsky.actor.deleteAccount(
|
||||
undefined,
|
||||
{
|
||||
headers: DM_SERVICE_HEADERS,
|
||||
},
|
||||
)
|
||||
if (!success) {
|
||||
throw new Error('Failed to inform chat service of account deletion')
|
||||
}
|
||||
await agent.com.atproto.server.deleteAccount({
|
||||
did: currentAccount.did,
|
||||
password,
|
||||
token,
|
||||
})
|
||||
control.close(() => {
|
||||
toast.show(_(msg`Your account has been deleted, see ya! ✌️`))
|
||||
resetToTab('HomeTab')
|
||||
removeAccount(currentAccount)
|
||||
})
|
||||
} catch (e: any) {
|
||||
const {clean, raw} = cleanError(e)
|
||||
const error = clean || raw || e
|
||||
setError(error)
|
||||
logger.error(raw || e, {
|
||||
message: 'Failed to delete account',
|
||||
})
|
||||
setConfirmCode('')
|
||||
setPassword('')
|
||||
setStep(Step.VERIFY_CODE)
|
||||
}
|
||||
}, [
|
||||
_,
|
||||
agent,
|
||||
cleanError,
|
||||
confirmCode,
|
||||
control,
|
||||
currentAccount,
|
||||
password,
|
||||
removeAccount,
|
||||
])
|
||||
|
||||
const handleDeactivate = useCallback(() => {
|
||||
control.close(() => deactivateDialogControl.open())
|
||||
}, [control, deactivateDialogControl])
|
||||
|
||||
const handleSendEmail = useCallback(() => {
|
||||
void sendEmail()
|
||||
}, [sendEmail])
|
||||
|
||||
const handleSubmitConfirmCode = useCallback(() => {
|
||||
passwordRef.current?.focus()
|
||||
}, [])
|
||||
|
||||
const handleDeleteAccount = useCallback(() => {
|
||||
setStep(Step.CONFIRM_DELETION)
|
||||
}, [setStep])
|
||||
|
||||
const handleConfirmDeletion = useCallback(() => {
|
||||
void confirmDeletion()
|
||||
}, [confirmDeletion])
|
||||
|
||||
const currentHandle = sanitizeHandle(currentAccount?.handle ?? '', '@')
|
||||
const currentEmail = currentAccount?.email ?? '(no email)'
|
||||
|
||||
switch (step) {
|
||||
case Step.SEND_CODE:
|
||||
return (
|
||||
<>
|
||||
<Prompt.Content>
|
||||
<Prompt.TitleText>
|
||||
{_(msg`Delete account “${currentHandle}”`)}
|
||||
</Prompt.TitleText>
|
||||
<Prompt.DescriptionText>
|
||||
<Trans>
|
||||
For security reasons, we’ll need to send a confirmation code to
|
||||
your email address{' '}
|
||||
<Span style={[a.font_semi_bold, t.atoms.text]}>
|
||||
{currentEmail}
|
||||
</Span>
|
||||
.
|
||||
</Trans>
|
||||
</Prompt.DescriptionText>
|
||||
</Prompt.Content>
|
||||
<Prompt.Actions>
|
||||
<Button
|
||||
color="primary"
|
||||
label={_(msg`Send email`)}
|
||||
size="large"
|
||||
onPress={handleSendEmail}>
|
||||
<ButtonText>{_(msg`Send email`)}</ButtonText>
|
||||
<ButtonIcon
|
||||
icon={emailState === EmailState.PENDING ? Loader : Envelope}
|
||||
/>
|
||||
</Button>
|
||||
<Prompt.Cancel />
|
||||
</Prompt.Actions>
|
||||
{error && (
|
||||
<Admonition style={[a.mt_lg]} type="error">
|
||||
<Text style={[a.flex_1, a.leading_snug]}>{error}</Text>
|
||||
</Admonition>
|
||||
)}
|
||||
<Admonition style={[a.mt_lg]} type="tip">
|
||||
<Trans>
|
||||
You can also{' '}
|
||||
<Span
|
||||
style={[{color: t.palette.primary_500}, web(a.underline)]}
|
||||
onPress={handleDeactivate}>
|
||||
temporarily deactivate
|
||||
</Span>{' '}
|
||||
your account instead. Your profile, posts, feeds, and lists will
|
||||
no longer be visible to other Bluesky users. You can reactivate
|
||||
your account at any time by logging in.
|
||||
</Trans>
|
||||
</Admonition>
|
||||
</>
|
||||
)
|
||||
case Step.VERIFY_CODE:
|
||||
return (
|
||||
<>
|
||||
<Prompt.Content>
|
||||
<Prompt.TitleText>
|
||||
{_(msg`Delete account “${currentHandle}”`)}
|
||||
</Prompt.TitleText>
|
||||
<Prompt.DescriptionText>
|
||||
<Trans>
|
||||
Check{' '}
|
||||
<Span style={[a.font_semi_bold, t.atoms.text]}>
|
||||
{currentEmail}
|
||||
</Span>{' '}
|
||||
for an email with the confirmation code to enter below:
|
||||
</Trans>
|
||||
</Prompt.DescriptionText>
|
||||
</Prompt.Content>
|
||||
<View style={[a.mb_xs]}>
|
||||
<TextField.LabelText>
|
||||
<Trans>Confirmation code</Trans>
|
||||
</TextField.LabelText>
|
||||
<TokenField
|
||||
value={confirmCode}
|
||||
onChangeText={setConfirmCode}
|
||||
onSubmitEditing={handleSubmitConfirmCode}
|
||||
/>
|
||||
</View>
|
||||
<Text
|
||||
style={[
|
||||
a.text_sm,
|
||||
a.leading_snug,
|
||||
a.mb_lg,
|
||||
t.atoms.text_contrast_medium,
|
||||
]}>
|
||||
{emailSentCount > 1 ? (
|
||||
<Trans>
|
||||
Email sent!{' '}
|
||||
<InlineLinkText
|
||||
label={_(msg`Resend`)}
|
||||
{...createStaticClick(() => {
|
||||
void handleSendEmail()
|
||||
})}>
|
||||
Click here to resend.
|
||||
</InlineLinkText>
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans>
|
||||
Don’t see a code?{' '}
|
||||
<InlineLinkText
|
||||
label={_(msg`Resend`)}
|
||||
{...createStaticClick(() => {
|
||||
void handleSendEmail()
|
||||
})}>
|
||||
Click here to resend.
|
||||
</InlineLinkText>
|
||||
</Trans>
|
||||
)}{' '}
|
||||
<Span style={{top: 1}}>
|
||||
{emailState === EmailState.PENDING ? <Loader size="xs" /> : null}
|
||||
</Span>
|
||||
</Text>
|
||||
<View style={[a.mb_xl]}>
|
||||
<TextField.LabelText>
|
||||
<Trans>Password</Trans>
|
||||
</TextField.LabelText>
|
||||
<TextField.Root>
|
||||
<TextField.Icon icon={Lock} />
|
||||
<TextField.Input
|
||||
inputRef={passwordRef}
|
||||
testID="newPasswordInput"
|
||||
label={_(msg`Enter your password`)}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
returnKeyType="done"
|
||||
secureTextEntry={true}
|
||||
autoComplete="off"
|
||||
clearButtonMode="while-editing"
|
||||
passwordRules={`minlength: ${PASSWORD_MIN_LENGTH}};`}
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
onSubmitEditing={handleDeleteAccount}
|
||||
/>
|
||||
</TextField.Root>
|
||||
</View>
|
||||
<Prompt.Actions>
|
||||
<Button
|
||||
color="negative"
|
||||
disabled={!isValidCode(confirmCode) || !isPasswordValid(password)}
|
||||
size="large"
|
||||
label={_(msg`Delete My Account`)}
|
||||
onPress={handleDeleteAccount}>
|
||||
<ButtonText>{_(msg`Delete My Account`)}</ButtonText>
|
||||
</Button>
|
||||
<Prompt.Cancel />
|
||||
</Prompt.Actions>
|
||||
{error && (
|
||||
<Admonition style={[a.mt_lg]} type="error">
|
||||
<Text style={[a.flex_1, a.leading_snug]}>{error}</Text>
|
||||
</Admonition>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
case Step.CONFIRM_DELETION:
|
||||
return (
|
||||
<>
|
||||
<Prompt.Content>
|
||||
<Prompt.TitleText>
|
||||
{_(msg`Are you really, really sure?`)}
|
||||
</Prompt.TitleText>
|
||||
<Prompt.DescriptionText>
|
||||
<Trans>
|
||||
This will irreversably delete your Bluesky account{' '}
|
||||
<Span style={[a.font_semi_bold, t.atoms.text]}>
|
||||
{currentHandle}
|
||||
</Span>{' '}
|
||||
and all associated data. Note that this will affect any other{' '}
|
||||
<InlineLinkText
|
||||
label={_(msg`Learn more about the AT Protocol.`)}
|
||||
style={[a.text_md]}
|
||||
to="https://bsky.social/about/faq">
|
||||
AT Protocol
|
||||
</InlineLinkText>{' '}
|
||||
services you use with this account.
|
||||
</Trans>
|
||||
</Prompt.DescriptionText>
|
||||
</Prompt.Content>
|
||||
<Prompt.Actions>
|
||||
<Button
|
||||
color="negative"
|
||||
size="large"
|
||||
label={_(msg`Yes, delete my account`)}
|
||||
onPress={handleConfirmDeletion}>
|
||||
<ButtonText>{_(msg`Yes, delete my account`)}</ButtonText>
|
||||
</Button>
|
||||
<Prompt.Cancel />
|
||||
</Prompt.Actions>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,6 @@ export interface UserAddRemoveListsModal {
|
||||
onRemove?: (listUri: string) => void
|
||||
}
|
||||
|
||||
export interface DeleteAccountModal {
|
||||
name: 'delete-account'
|
||||
}
|
||||
|
||||
export interface ContentLanguagesSettingsModal {
|
||||
name: 'content-languages-settings'
|
||||
}
|
||||
@@ -23,9 +19,6 @@ export interface ContentLanguagesSettingsModal {
|
||||
* @deprecated DO NOT ADD NEW MODALS
|
||||
*/
|
||||
export type Modal =
|
||||
// Account
|
||||
| DeleteAccountModal
|
||||
|
||||
// Curation
|
||||
| ContentLanguagesSettingsModal
|
||||
|
||||
|
||||
@@ -1,342 +0,0 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
ActivityIndicator,
|
||||
SafeAreaView,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import {LinearGradient} from 'expo-linear-gradient'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {DM_SERVICE_HEADERS} from '#/lib/constants'
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {colors, gradients, s} from '#/lib/styles'
|
||||
import {useTheme} from '#/lib/ThemeContext'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useAgent, useSession, useSessionApi} from '#/state/session'
|
||||
import {atoms as a, useTheme as useNewTheme} from '#/alf'
|
||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||
import {Text as NewText} from '#/components/Typography'
|
||||
import {IS_ANDROID, IS_WEB} from '#/env'
|
||||
import {resetToTab} from '../../../Navigation'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {Text} from '../util/text/Text'
|
||||
import * as Toast from '../util/Toast'
|
||||
import {ScrollView, TextInput} from './util'
|
||||
|
||||
export const snapPoints = IS_ANDROID ? ['90%'] : ['55%']
|
||||
|
||||
export function Component({}: {}) {
|
||||
const pal = usePalette('default')
|
||||
const theme = useTheme()
|
||||
const t = useNewTheme()
|
||||
const {currentAccount} = useSession()
|
||||
const agent = useAgent()
|
||||
const {removeAccount} = useSessionApi()
|
||||
const {_} = useLingui()
|
||||
const {closeModal} = useModalControls()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const [isEmailSent, setIsEmailSent] = React.useState<boolean>(false)
|
||||
const [confirmCode, setConfirmCode] = React.useState<string>('')
|
||||
const [password, setPassword] = React.useState<string>('')
|
||||
const [isProcessing, setIsProcessing] = React.useState<boolean>(false)
|
||||
const [error, setError] = React.useState<string>('')
|
||||
const onPressSendEmail = async () => {
|
||||
setError('')
|
||||
setIsProcessing(true)
|
||||
try {
|
||||
await agent.com.atproto.server.requestAccountDelete()
|
||||
setIsEmailSent(true)
|
||||
} catch (e: any) {
|
||||
setError(cleanError(e))
|
||||
}
|
||||
setIsProcessing(false)
|
||||
}
|
||||
const onPressConfirmDelete = async () => {
|
||||
if (!currentAccount?.did) {
|
||||
throw new Error(`DeleteAccount modal: currentAccount.did is undefined`)
|
||||
}
|
||||
|
||||
setError('')
|
||||
setIsProcessing(true)
|
||||
const token = confirmCode.replace(/\s/g, '')
|
||||
|
||||
try {
|
||||
// inform chat service of intent to delete account
|
||||
const {success} = await agent.api.chat.bsky.actor.deleteAccount(
|
||||
undefined,
|
||||
{
|
||||
headers: DM_SERVICE_HEADERS,
|
||||
},
|
||||
)
|
||||
if (!success) {
|
||||
throw new Error('Failed to inform chat service of account deletion')
|
||||
}
|
||||
await agent.com.atproto.server.deleteAccount({
|
||||
did: currentAccount.did,
|
||||
password,
|
||||
token,
|
||||
})
|
||||
Toast.show(_(msg`Your account has been deleted`))
|
||||
resetToTab('HomeTab')
|
||||
removeAccount(currentAccount)
|
||||
closeModal()
|
||||
} catch (e: any) {
|
||||
setError(cleanError(e))
|
||||
}
|
||||
setIsProcessing(false)
|
||||
}
|
||||
const onCancel = () => {
|
||||
closeModal()
|
||||
}
|
||||
return (
|
||||
<SafeAreaView style={[s.flex1]}>
|
||||
<ScrollView style={[pal.view]} keyboardShouldPersistTaps="handled">
|
||||
<View style={[styles.titleContainer, pal.view]}>
|
||||
<Text type="title-xl" style={[s.textCenter, pal.text]}>
|
||||
<Trans>
|
||||
Delete Account{' '}
|
||||
<Text type="title-xl" style={[pal.text, s.bold]}>
|
||||
"
|
||||
</Text>
|
||||
<Text
|
||||
type="title-xl"
|
||||
numberOfLines={1}
|
||||
style={[
|
||||
isMobile ? styles.titleMobile : styles.titleDesktop,
|
||||
pal.text,
|
||||
s.bold,
|
||||
]}>
|
||||
{currentAccount?.handle}
|
||||
</Text>
|
||||
<Text type="title-xl" style={[pal.text, s.bold]}>
|
||||
"
|
||||
</Text>
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
{!isEmailSent ? (
|
||||
<>
|
||||
<Text type="lg" style={[styles.description, pal.text]}>
|
||||
<Trans>
|
||||
For security reasons, we'll need to send a confirmation code to
|
||||
your email address.
|
||||
</Trans>
|
||||
</Text>
|
||||
{error ? (
|
||||
<View style={s.mt10}>
|
||||
<ErrorMessage message={error} />
|
||||
</View>
|
||||
) : undefined}
|
||||
{isProcessing ? (
|
||||
<View style={[styles.btn, s.mt10]}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
<TouchableOpacity
|
||||
style={styles.mt20}
|
||||
onPress={onPressSendEmail}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Send email`)}
|
||||
accessibilityHint={_(
|
||||
msg`Sends email with confirmation code for account deletion`,
|
||||
)}>
|
||||
<LinearGradient
|
||||
colors={[
|
||||
gradients.blueLight.start,
|
||||
gradients.blueLight.end,
|
||||
]}
|
||||
start={{x: 0, y: 0}}
|
||||
end={{x: 1, y: 1}}
|
||||
style={[styles.btn]}>
|
||||
<Text type="button-lg" style={[s.white, s.bold]}>
|
||||
<Trans context="action">Send Email</Trans>
|
||||
</Text>
|
||||
</LinearGradient>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.btn, s.mt10]}
|
||||
onPress={onCancel}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Cancel account deletion`)}
|
||||
accessibilityHint=""
|
||||
onAccessibilityEscape={onCancel}>
|
||||
<Text type="button-lg" style={pal.textLight}>
|
||||
<Trans context="action">Cancel</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
)}
|
||||
|
||||
<View style={[!IS_WEB && a.px_xl]}>
|
||||
<View
|
||||
style={[
|
||||
a.w_full,
|
||||
a.flex_row,
|
||||
a.gap_sm,
|
||||
a.mt_lg,
|
||||
a.p_lg,
|
||||
a.rounded_sm,
|
||||
t.atoms.bg_contrast_25,
|
||||
]}>
|
||||
<CircleInfo
|
||||
size="md"
|
||||
style={[
|
||||
a.relative,
|
||||
{
|
||||
top: -1,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<NewText style={[a.leading_snug, a.flex_1]}>
|
||||
<Trans>
|
||||
You can also temporarily deactivate your account instead,
|
||||
and reactivate it at any time.
|
||||
</Trans>
|
||||
</NewText>
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{/* TODO: Update this label to be more concise */}
|
||||
<Text
|
||||
type="lg"
|
||||
style={[pal.text, styles.description]}
|
||||
nativeID="confirmationCode">
|
||||
<Trans>
|
||||
Check your inbox for an email with the confirmation code to
|
||||
enter below:
|
||||
</Trans>
|
||||
</Text>
|
||||
<TextInput
|
||||
style={[styles.textInput, pal.borderDark, pal.text, styles.mb20]}
|
||||
placeholder={_(msg`Confirmation code`)}
|
||||
placeholderTextColor={pal.textLight.color}
|
||||
keyboardAppearance={theme.colorScheme}
|
||||
value={confirmCode}
|
||||
onChangeText={setConfirmCode}
|
||||
accessibilityLabelledBy="confirmationCode"
|
||||
accessibilityLabel={_(msg`Confirmation code`)}
|
||||
accessibilityHint={_(
|
||||
msg`Input confirmation code for account deletion`,
|
||||
)}
|
||||
/>
|
||||
<Text
|
||||
type="lg"
|
||||
style={[pal.text, styles.description]}
|
||||
nativeID="password">
|
||||
<Trans>Please enter your password as well:</Trans>
|
||||
</Text>
|
||||
<TextInput
|
||||
style={[styles.textInput, pal.borderDark, pal.text]}
|
||||
placeholder={_(msg`Password`)}
|
||||
placeholderTextColor={pal.textLight.color}
|
||||
keyboardAppearance={theme.colorScheme}
|
||||
secureTextEntry
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
accessibilityLabelledBy="password"
|
||||
accessibilityLabel={_(msg`Password`)}
|
||||
accessibilityHint={_(msg`Input password for account deletion`)}
|
||||
/>
|
||||
{error ? (
|
||||
<View style={styles.mt20}>
|
||||
<ErrorMessage message={error} />
|
||||
</View>
|
||||
) : undefined}
|
||||
{isProcessing ? (
|
||||
<View style={[styles.btn, s.mt10]}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
<TouchableOpacity
|
||||
style={[styles.btn, styles.evilBtn, styles.mt20]}
|
||||
onPress={onPressConfirmDelete}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Confirm delete account`)}
|
||||
accessibilityHint="">
|
||||
<Text type="button-lg" style={[s.white, s.bold]}>
|
||||
<Trans>Delete my account</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.btn, s.mt10]}
|
||||
onPress={onCancel}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Cancel account deletion`)}
|
||||
accessibilityHint={_(msg`Exits account deletion process`)}
|
||||
onAccessibilityEscape={onCancel}>
|
||||
<Text type="button-lg" style={pal.textLight}>
|
||||
<Trans context="action">Cancel</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
titleContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
flexWrap: 'wrap',
|
||||
marginTop: 12,
|
||||
marginBottom: 12,
|
||||
marginLeft: 20,
|
||||
marginRight: 20,
|
||||
},
|
||||
titleMobile: {
|
||||
textAlign: 'center',
|
||||
},
|
||||
titleDesktop: {
|
||||
textAlign: 'center',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
// @ts-ignore only rendered on web
|
||||
maxWidth: '400px',
|
||||
},
|
||||
description: {
|
||||
textAlign: 'center',
|
||||
paddingHorizontal: 22,
|
||||
marginBottom: 10,
|
||||
},
|
||||
mt20: {
|
||||
marginTop: 20,
|
||||
},
|
||||
mb20: {
|
||||
marginBottom: 20,
|
||||
},
|
||||
textInput: {
|
||||
borderWidth: 1,
|
||||
borderRadius: 6,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 12,
|
||||
fontSize: 20,
|
||||
marginHorizontal: 20,
|
||||
},
|
||||
btn: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 32,
|
||||
padding: 14,
|
||||
marginHorizontal: 20,
|
||||
},
|
||||
evilBtn: {
|
||||
backgroundColor: colors.red4,
|
||||
},
|
||||
})
|
||||
@@ -7,7 +7,6 @@ import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {useModalControls, useModals} from '#/state/modals'
|
||||
import {FullWindowOverlay} from '#/components/FullWindowOverlay'
|
||||
import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop'
|
||||
import * as DeleteAccountModal from './DeleteAccount'
|
||||
import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguagesSettings'
|
||||
import * as UserAddRemoveListsModal from './UserAddRemoveLists'
|
||||
|
||||
@@ -45,9 +44,6 @@ export function ModalsContainer() {
|
||||
if (activeModal?.name === 'user-add-remove-lists') {
|
||||
snapPoints = UserAddRemoveListsModal.snapPoints
|
||||
element = <UserAddRemoveListsModal.Component {...activeModal} />
|
||||
} else if (activeModal?.name === 'delete-account') {
|
||||
snapPoints = DeleteAccountModal.snapPoints
|
||||
element = <DeleteAccountModal.Component />
|
||||
} else if (activeModal?.name === 'content-languages-settings') {
|
||||
snapPoints = ContentLanguagesSettingsModal.snapPoints
|
||||
element = <ContentLanguagesSettingsModal.Component />
|
||||
|
||||
@@ -6,7 +6,6 @@ import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||
import {type Modal as ModalIface} from '#/state/modals'
|
||||
import {useModalControls, useModals} from '#/state/modals'
|
||||
import * as DeleteAccountModal from './DeleteAccount'
|
||||
import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguagesSettings'
|
||||
import * as UserAddRemoveLists from './UserAddRemoveLists'
|
||||
|
||||
@@ -48,8 +47,6 @@ function Modal({modal}: {modal: ModalIface}) {
|
||||
let element
|
||||
if (modal.name === 'user-add-remove-lists') {
|
||||
element = <UserAddRemoveLists.Component {...modal} />
|
||||
} else if (modal.name === 'delete-account') {
|
||||
element = <DeleteAccountModal.Component />
|
||||
} else if (modal.name === 'content-languages-settings') {
|
||||
element = <ContentLanguagesSettingsModal.Component />
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user