diff options
author | Eric Bailey <git@esb.lol> | 2024-06-04 20:02:22 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-05 02:02:22 +0100 |
commit | 3ece21cb45e8b74795f4eac33a1551b303196946 (patch) | |
tree | 8259d8cbae98ef7493197af8d810ca9a0fdfae97 /src/screens | |
parent | e64b7cf69869ad5d984037ced7f81fc400e1daa0 (diff) | |
download | voidsky-3ece21cb45e8b74795f4eac33a1551b303196946.tar.zst |
[🙅] Integrate deactivate (#4308)
* Update types (cherry picked from commit 27deac1f367825771ba76fa098ec1b0a62dcf64a) * Integrate into deactivate dialog (cherry picked from commit 84f299a447259cc1fbfc7be607e28197779e4ec1) * Integrate into Deactivated screen (cherry picked from commit 29193f34822ecdf11e2a407197fa230285dfe846) * Bump api sdk (cherry picked from commit 738c622d3e5a23bfbb0d3bdce3a6bdf01e54ca60) * Update permalink (cherry picked from commit c10bf5c071d76c3054bc4ce9d313c10b1820f038) * Bump sdk pkg * Update types to match backend * Loosen types for forwards compat * Hydrate status from persisted data * Refresh session when re-activating, clear query cache * Show app password error * Refactor dialog to clear state when closed * Add app password error to Deactivated screen
Diffstat (limited to 'src/screens')
-rw-r--r-- | src/screens/Deactivated.tsx | 63 | ||||
-rw-r--r-- | src/screens/Settings/components/DeactivateAccountDialog.tsx | 84 |
2 files changed, 137 insertions, 10 deletions
diff --git a/src/screens/Deactivated.tsx b/src/screens/Deactivated.tsx index faee517cb..add550f93 100644 --- a/src/screens/Deactivated.tsx +++ b/src/screens/Deactivated.tsx @@ -4,18 +4,27 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useFocusEffect} from '@react-navigation/native' +import {useQueryClient} from '@tanstack/react-query' import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher' +import {logger} from '#/logger' import {isWeb} from '#/platform/detection' -import {type SessionAccount, useSession, useSessionApi} from '#/state/session' +import { + type SessionAccount, + useAgent, + useSession, + useSessionApi, +} from '#/state/session' import {useSetMinimalShellMode} from '#/state/shell' import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {ScrollView} from '#/view/com/util/Views' import {Logo} from '#/view/icons/Logo' import {atoms as a, useTheme} from '#/alf' import {AccountList} from '#/components/AccountList' -import {Button, ButtonText} from '#/components/Button' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {Divider} from '#/components/Divider' +import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' +import {Loader} from '#/components/Loader' import {Text} from '#/components/Typography' const COL_WIDTH = 400 @@ -30,6 +39,10 @@ export function Deactivated() { const hasOtherAccounts = accounts.length > 1 const setMinimalShellMode = useSetMinimalShellMode() const {logout} = useSessionApi() + const agent = useAgent() + const [pending, setPending] = React.useState(false) + const [error, setError] = React.useState<string | undefined>() + const queryClient = useQueryClient() useFocusEffect( React.useCallback(() => { @@ -62,6 +75,34 @@ export function Deactivated() { logout('Deactivated') }, [logout]) + const handleActivate = React.useCallback(async () => { + try { + setPending(true) + await agent.com.atproto.server.activateAccount() + await queryClient.resetQueries() + await agent.resumeSession(agent.session!) + } catch (e: any) { + switch (e.message) { + case 'Bad token scope': + setError( + _( + msg`You're logged in with an App Password. Please log in with your main password to continue deactivating your account.`, + ), + ) + break + default: + setError(_(msg`Something went wrong, please try again`)) + break + } + + logger.error(e, { + context: 'Failed to activate account', + }) + } finally { + setPending(false) + } + }, [_, agent, setPending, setError, queryClient]) + return ( <View style={[a.h_full_vh, a.flex_1, t.atoms.bg]}> <ScrollView @@ -104,10 +145,11 @@ export function Deactivated() { size="medium" variant="solid" color="primary" - onPress={() => setShowLoggedOut(true)}> + onPress={handleActivate}> <ButtonText> <Trans>Yes, reactivate my account</Trans> </ButtonText> + {pending && <ButtonIcon icon={Loader} position="right" />} </Button> <Button label={_(msg`Cancel reactivation and log out`)} @@ -120,6 +162,21 @@ export function Deactivated() { </ButtonText> </Button> </View> + + {error && ( + <View + style={[ + a.flex_row, + a.gap_sm, + a.mt_md, + a.p_md, + a.rounded_sm, + t.atoms.bg_contrast_25, + ]}> + <CircleInfo size="md" fill={t.palette.negative_400} /> + <Text style={[a.flex_1, a.leading_snug]}>{error}</Text> + </View> + )} </View> <View style={[a.pb_3xl]}> diff --git a/src/screens/Settings/components/DeactivateAccountDialog.tsx b/src/screens/Settings/components/DeactivateAccountDialog.tsx index 4330ffcaa..99999d068 100644 --- a/src/screens/Settings/components/DeactivateAccountDialog.tsx +++ b/src/screens/Settings/components/DeactivateAccountDialog.tsx @@ -3,9 +3,14 @@ import {View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {atoms as a, useTheme} from '#/alf' +import {logger} from '#/logger' +import {useAgent, useSessionApi} from '#/state/session' +import {atoms as a, useBreakpoints, useTheme} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {DialogOuterProps} from '#/components/Dialog' import {Divider} from '#/components/Divider' +import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' +import {Loader} from '#/components/Loader' import * as Prompt from '#/components/Prompt' import {Text} from '#/components/Typography' @@ -14,11 +19,57 @@ export function DeactivateAccountDialog({ }: { control: DialogOuterProps['control'] }) { + return ( + <Prompt.Outer control={control}> + <DeactivateAccountDialogInner control={control} /> + </Prompt.Outer> + ) +} + +function DeactivateAccountDialogInner({ + control, +}: { + control: DialogOuterProps['control'] +}) { const t = useTheme() + const {gtMobile} = useBreakpoints() const {_} = useLingui() + const agent = useAgent() + const {logout} = useSessionApi() + const [pending, setPending] = React.useState(false) + const [error, setError] = React.useState<string | undefined>() + + const handleDeactivate = React.useCallback(async () => { + try { + setPending(true) + await agent.com.atproto.server.deactivateAccount({}) + control.close(() => { + logout('Deactivated') + }) + } catch (e: any) { + switch (e.message) { + case 'Bad token scope': + setError( + _( + msg`You're logged in with an App Password. Please log in with your main password to continue deactivating your account.`, + ), + ) + break + default: + setError(_(msg`Something went wrong, please try again`)) + break + } + + logger.error(e, { + context: 'Failed to deactivate account', + }) + } finally { + setPending(false) + } + }, [agent, control, logout, _, setPending]) return ( - <Prompt.Outer control={control} testID="confirmModal"> + <> <Prompt.TitleText>{_(msg`Deactivate account`)}</Prompt.TitleText> <Prompt.DescriptionText> <Trans> @@ -48,13 +99,32 @@ export function DeactivateAccountDialog({ <Divider /> </View> <Prompt.Actions> - <Prompt.Action - cta={_(msg`Yes, deactivate`)} - onPress={() => {}} + <Button + variant="solid" color="negative" - /> + size={gtMobile ? 'small' : 'medium'} + label={_(msg`Yes, deactivate`)} + onPress={handleDeactivate}> + <ButtonText>{_(msg`Yes, deactivate`)}</ButtonText> + {pending && <ButtonIcon icon={Loader} position="right" />} + </Button> <Prompt.Cancel /> </Prompt.Actions> - </Prompt.Outer> + + {error && ( + <View + style={[ + a.flex_row, + a.gap_sm, + a.mt_md, + a.p_md, + a.rounded_sm, + t.atoms.bg_contrast_25, + ]}> + <CircleInfo size="md" fill={t.palette.negative_400} /> + <Text style={[a.flex_1, a.leading_snug]}>{error}</Text> + </View> + )} + </> ) } |