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
}