diff --git a/src/lib/statsig/events.ts b/src/lib/statsig/events.ts index b434b40f19..2de5ffb787 100644 --- a/src/lib/statsig/events.ts +++ b/src/lib/statsig/events.ts @@ -13,7 +13,7 @@ export type LogEvents = { withPassword: boolean } 'account:loggedOut': { - logContext: 'SwitchAccount' | 'Settings' | 'SignupQueued' + logContext: 'SwitchAccount' | 'Settings' | 'SignupQueued' | 'Deactivated' } 'notifications:openApp': {} 'notifications:request': { diff --git a/src/screens/Deactivated.tsx b/src/screens/Deactivated.tsx new file mode 100644 index 0000000000..faee517cb8 --- /dev/null +++ b/src/screens/Deactivated.tsx @@ -0,0 +1,174 @@ +import React from 'react' +import {View} from 'react-native' +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 {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher' +import {isWeb} from '#/platform/detection' +import {type SessionAccount, 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 {Divider} from '#/components/Divider' +import {Text} from '#/components/Typography' + +const COL_WIDTH = 400 + +export function Deactivated() { + const {_} = useLingui() + const t = useTheme() + const insets = useSafeAreaInsets() + const {currentAccount, accounts} = useSession() + const {onPressSwitchAccount, pendingDid} = useAccountSwitcher() + const {setShowLoggedOut} = useLoggedOutViewControls() + const hasOtherAccounts = accounts.length > 1 + const setMinimalShellMode = useSetMinimalShellMode() + const {logout} = useSessionApi() + + useFocusEffect( + React.useCallback(() => { + setMinimalShellMode(true) + }, [setMinimalShellMode]), + ) + + const onSelectAccount = React.useCallback( + (account: SessionAccount) => { + if (account.did !== currentAccount?.did) { + onPressSwitchAccount(account, 'SwitchAccount') + } + }, + [currentAccount, onPressSwitchAccount], + ) + + const onPressAddAccount = React.useCallback(() => { + setShowLoggedOut(true) + }, [setShowLoggedOut]) + + const onPressLogout = React.useCallback(() => { + if (isWeb) { + // We're switching accounts, which remounts the entire app. + // On mobile, this gets us Home, but on the web we also need reset the URL. + // We can't change the URL via a navigate() call because the navigator + // itself is about to unmount, and it calls pushState() too late. + // So we change the URL ourselves. The navigator will pick it up on remount. + history.pushState(null, '', '/') + } + logout('Deactivated') + }, [logout]) + + return ( + + + + + + + + + + + + Welcome back! + + + + You previously deactivated @{currentAccount?.handle}. + + + + + You can reactivate your account to continue logging in. Your + profile and posts will be visible to other users. + + + + + + + + + + + + + + {hasOtherAccounts ? ( + <> + + Or, log into one of your other accounts. + + + + ) : ( + <> + + Or, continue with another account. + + + + )} + + + + + + ) +} diff --git a/src/state/persisted/schema.ts b/src/state/persisted/schema.ts index 95f3acdbbd..1fa2066042 100644 --- a/src/state/persisted/schema.ts +++ b/src/state/persisted/schema.ts @@ -18,6 +18,9 @@ const accountSchema = z.object({ refreshJwt: z.string().optional(), // optional because it can expire accessJwt: z.string().optional(), // optional because it can expire signupQueued: z.boolean().optional(), + status: z + .enum(['active', 'takendown', 'suspended', 'deactivated']) + .optional(), pdsUrl: z.string().optional(), }) export type PersistedAccount = z.infer diff --git a/src/state/session/__tests__/session-test.ts b/src/state/session/__tests__/session-test.ts index f71129ffb2..c8c1e103fb 100644 --- a/src/state/session/__tests__/session-test.ts +++ b/src/state/session/__tests__/session-test.ts @@ -59,6 +59,7 @@ describe('session', () => { "refreshJwt": "alice-refresh-jwt-1", "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -96,6 +97,7 @@ describe('session', () => { "refreshJwt": undefined, "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -145,6 +147,7 @@ describe('session', () => { "refreshJwt": "alice-refresh-jwt-1", "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -192,6 +195,7 @@ describe('session', () => { "refreshJwt": "bob-refresh-jwt-1", "service": "https://bob.com/", "signupQueued": false, + "status": "active", }, { "accessJwt": "alice-access-jwt-1", @@ -204,6 +208,7 @@ describe('session', () => { "refreshJwt": "alice-refresh-jwt-1", "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -251,6 +256,7 @@ describe('session', () => { "refreshJwt": "alice-refresh-jwt-2", "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, { "accessJwt": "bob-access-jwt-1", @@ -263,6 +269,7 @@ describe('session', () => { "refreshJwt": "bob-refresh-jwt-1", "service": "https://bob.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -308,6 +315,7 @@ describe('session', () => { "refreshJwt": "jay-refresh-jwt-1", "service": "https://jay.com/", "signupQueued": false, + "status": "active", }, { "accessJwt": "alice-access-jwt-2", @@ -320,6 +328,7 @@ describe('session', () => { "refreshJwt": "alice-refresh-jwt-2", "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, { "accessJwt": "bob-access-jwt-1", @@ -332,6 +341,7 @@ describe('session', () => { "refreshJwt": "bob-refresh-jwt-1", "service": "https://bob.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -373,6 +383,7 @@ describe('session', () => { "refreshJwt": undefined, "service": "https://jay.com/", "signupQueued": false, + "status": "active", }, { "accessJwt": undefined, @@ -385,6 +396,7 @@ describe('session', () => { "refreshJwt": undefined, "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, { "accessJwt": undefined, @@ -397,6 +409,7 @@ describe('session', () => { "refreshJwt": undefined, "service": "https://bob.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -455,6 +468,7 @@ describe('session', () => { "refreshJwt": undefined, "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -499,6 +513,7 @@ describe('session', () => { "refreshJwt": "alice-refresh-jwt-2", "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -610,6 +625,7 @@ describe('session', () => { "refreshJwt": "bob-refresh-jwt-1", "service": "https://bob.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -690,6 +706,7 @@ describe('session', () => { "refreshJwt": "alice-refresh-jwt-2", "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -740,6 +757,7 @@ describe('session', () => { "refreshJwt": "alice-refresh-jwt-3", "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -790,6 +808,7 @@ describe('session', () => { "refreshJwt": "alice-refresh-jwt-4", "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -946,6 +965,7 @@ describe('session', () => { "refreshJwt": "bob-refresh-jwt-1", "service": "https://bob.com/", "signupQueued": false, + "status": "active", }, { "accessJwt": "alice-access-jwt-2", @@ -958,6 +978,7 @@ describe('session', () => { "refreshJwt": "alice-refresh-jwt-2", "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -1006,6 +1027,7 @@ describe('session', () => { "refreshJwt": "bob-refresh-jwt-2", "service": "https://bob.com/", "signupQueued": false, + "status": "active", }, { "accessJwt": "alice-access-jwt-2", @@ -1018,6 +1040,7 @@ describe('session', () => { "refreshJwt": "alice-refresh-jwt-2", "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -1165,6 +1188,7 @@ describe('session', () => { "refreshJwt": "alice-refresh-jwt-1", "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -1227,6 +1251,7 @@ describe('session', () => { "refreshJwt": undefined, "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -1289,6 +1314,7 @@ describe('session', () => { "refreshJwt": undefined, "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -1380,6 +1406,7 @@ describe('session', () => { "refreshJwt": "jay-refresh-jwt-1", "service": "https://jay.com/", "signupQueued": false, + "status": "active", }, { "accessJwt": "bob-access-jwt-2", @@ -1392,6 +1419,7 @@ describe('session', () => { "refreshJwt": "bob-refresh-jwt-2", "service": "https://alice.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { @@ -1438,6 +1466,7 @@ describe('session', () => { "refreshJwt": "clarence-refresh-jwt-2", "service": "https://clarence.com/", "signupQueued": false, + "status": "active", }, ], "currentAgentState": { diff --git a/src/state/session/agent.ts b/src/state/session/agent.ts index 704ef1c9a9..cdd24cd15a 100644 --- a/src/state/session/agent.ts +++ b/src/state/session/agent.ts @@ -235,6 +235,8 @@ export function agentToSessionAccount( refreshJwt: agent.session.refreshJwt, accessJwt: agent.session.accessJwt, signupQueued: isSignupQueued(agent.session.accessJwt), + // @ts-expect-error TODO remove when backend is ready + status: agent.session.status || 'active', pdsUrl: agent.pdsUrl?.toString(), } } diff --git a/src/view/shell/createNativeStackNavigatorWithAuth.tsx b/src/view/shell/createNativeStackNavigatorWithAuth.tsx index b51ad1de53..3c611351dc 100644 --- a/src/view/shell/createNativeStackNavigatorWithAuth.tsx +++ b/src/view/shell/createNativeStackNavigatorWithAuth.tsx @@ -30,6 +30,7 @@ import { useLoggedOutViewControls, } from '#/state/shell/logged-out' import {isWeb} from 'platform/detection' +import {Deactivated} from '#/screens/Deactivated' import {Onboarding} from '#/screens/Onboarding' import {SignupQueued} from '#/screens/SignupQueued' import {LoggedOut} from '../com/auth/LoggedOut' @@ -108,6 +109,9 @@ function NativeStackNavigator({ if (showLoggedOut) { return setShowLoggedOut(false)} /> } + if (currentAccount?.status === 'deactivated') { + return + } if (onboardingState.isActive) { return }