diff --git a/src/Navigation.tsx b/src/Navigation.tsx
index 3d6a15c4..83aede72 100644
--- a/src/Navigation.tsx
+++ b/src/Navigation.tsx
@@ -565,7 +565,11 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
}
function getCurrentRouteName() {
- return navigationRef.getCurrentRoute()?.name
+ if (navigationRef.isReady()) {
+ return navigationRef.getCurrentRoute()?.name
+ } else {
+ return undefined
+ }
}
/**
diff --git a/src/lib/hooks/useAccountSwitcher.ts b/src/lib/hooks/useAccountSwitcher.ts
index 74b5674d..eb1685a0 100644
--- a/src/lib/hooks/useAccountSwitcher.ts
+++ b/src/lib/hooks/useAccountSwitcher.ts
@@ -6,6 +6,7 @@ import {useSessionApi, SessionAccount} from '#/state/session'
import * as Toast from '#/view/com/util/Toast'
import {useCloseAllActiveElements} from '#/state/util'
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
+import {LogEvents} from '../statsig/statsig'
export function useAccountSwitcher() {
const {track} = useAnalytics()
@@ -14,7 +15,10 @@ export function useAccountSwitcher() {
const {requestSwitchToAccount} = useLoggedOutViewControls()
const onPressSwitchAccount = useCallback(
- async (account: SessionAccount) => {
+ async (
+ account: SessionAccount,
+ logContext: LogEvents['account:loggedIn']['logContext'],
+ ) => {
track('Settings:SwitchAccountButtonClicked')
try {
@@ -28,7 +32,7 @@ export function useAccountSwitcher() {
// So we change the URL ourselves. The navigator will pick it up on remount.
history.pushState(null, '', '/')
}
- await selectAccount(account)
+ await selectAccount(account, logContext)
setTimeout(() => {
Toast.show(`Signed in as @${account.handle}`)
}, 100)
diff --git a/src/lib/statsig/events.ts b/src/lib/statsig/events.ts
index 420c58ed..f57c8d41 100644
--- a/src/lib/statsig/events.ts
+++ b/src/lib/statsig/events.ts
@@ -2,6 +2,13 @@ export type LogEvents = {
init: {
initMs: number
}
+ 'account:loggedIn': {
+ logContext: 'LoginForm' | 'SwitchAccount' | 'ChooseAccountForm' | 'Settings'
+ withPassword: boolean
+ }
+ 'account:loggedOut': {
+ logContext: 'SwitchAccount' | 'Settings' | 'Deactivated'
+ }
'notifications:openApp': {}
'state:background': {}
'state:foreground': {}
diff --git a/src/screens/Deactivated.tsx b/src/screens/Deactivated.tsx
index f4c20147..7e87973c 100644
--- a/src/screens/Deactivated.tsx
+++ b/src/screens/Deactivated.tsx
@@ -147,7 +147,7 @@ export function Deactivated() {
variant="ghost"
size="large"
label={_(msg`Log out`)}
- onPress={logout}>
+ onPress={() => logout('Deactivated')}>
Log out
@@ -176,7 +176,7 @@ export function Deactivated() {
variant="ghost"
size="large"
label={_(msg`Log out`)}
- onPress={logout}>
+ onPress={() => logout('Deactivated')}>
Log out
diff --git a/src/state/session/index.tsx b/src/state/session/index.tsx
index 6b147483..b6748bfa 100644
--- a/src/state/session/index.tsx
+++ b/src/state/session/index.tsx
@@ -20,6 +20,7 @@ import {useCloseAllActiveElements} from '#/state/util'
import {track} from '#/lib/analytics/analytics'
import {hasProp} from '#/lib/type-guards'
import {readLabelers} from './agent-config'
+import {logEvent, LogEvents} from '#/lib/statsig/statsig'
let __globalAgent: BskyAgent = PUBLIC_BSKY_AGENT
@@ -54,17 +55,22 @@ export type ApiContext = {
verificationPhone?: string
verificationCode?: string
}) => Promise
- login: (props: {
- service: string
- identifier: string
- password: string
- }) => Promise
+ login: (
+ props: {
+ service: string
+ identifier: string
+ password: string
+ },
+ logContext: LogEvents['account:loggedIn']['logContext'],
+ ) => Promise
/**
* A full logout. Clears the `currentAccount` from session, AND removes
* access tokens from all accounts, so that returning as any user will
* require a full login.
*/
- logout: () => Promise
+ logout: (
+ logContext: LogEvents['account:loggedOut']['logContext'],
+ ) => Promise
/**
* A partial logout. Clears the `currentAccount` from session, but DOES NOT
* clear access tokens from accounts, allowing the user to return to their
@@ -76,7 +82,10 @@ export type ApiContext = {
initSession: (account: SessionAccount) => Promise
resumeSession: (account?: SessionAccount) => Promise
removeAccount: (account: SessionAccount) => void
- selectAccount: (account: SessionAccount) => Promise
+ selectAccount: (
+ account: SessionAccount,
+ logContext: LogEvents['account:loggedIn']['logContext'],
+ ) => Promise
updateCurrentAccount: (
account: Partial<
Pick
@@ -286,7 +295,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
)
const login = React.useCallback(
- async ({service, identifier, password}) => {
+ async ({service, identifier, password}, logContext) => {
logger.debug(`session: login`, {}, logger.DebugContext.session)
const agent = new BskyAgent({service})
@@ -329,24 +338,29 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
logger.debug(`session: logged in`, {}, logger.DebugContext.session)
track('Sign In', {resumedSession: false})
+ logEvent('account:loggedIn', {logContext, withPassword: true})
},
[upsertAccount, queryClient, clearCurrentAccount],
)
- const logout = React.useCallback(async () => {
- logger.debug(`session: logout`)
- clearCurrentAccount()
- setStateAndPersist(s => {
- return {
- ...s,
- accounts: s.accounts.map(a => ({
- ...a,
- refreshJwt: undefined,
- accessJwt: undefined,
- })),
- }
- })
- }, [clearCurrentAccount, setStateAndPersist])
+ const logout = React.useCallback(
+ async logContext => {
+ logger.debug(`session: logout`)
+ clearCurrentAccount()
+ setStateAndPersist(s => {
+ return {
+ ...s,
+ accounts: s.accounts.map(a => ({
+ ...a,
+ refreshJwt: undefined,
+ accessJwt: undefined,
+ })),
+ }
+ })
+ logEvent('account:loggedOut', {logContext})
+ },
+ [clearCurrentAccount, setStateAndPersist],
+ )
const initSession = React.useCallback(
async account => {
@@ -540,11 +554,12 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
)
const selectAccount = React.useCallback(
- async account => {
+ async (account, logContext) => {
setState(s => ({...s, isSwitchingAccounts: true}))
try {
await initSession(account)
setState(s => ({...s, isSwitchingAccounts: false}))
+ logEvent('account:loggedIn', {logContext, withPassword: false})
} catch (e) {
// reset this in case of error
setState(s => ({...s, isSwitchingAccounts: false}))
diff --git a/src/view/com/auth/login/ChooseAccountForm.tsx b/src/view/com/auth/login/ChooseAccountForm.tsx
index d3b075fd..e754c848 100644
--- a/src/view/com/auth/login/ChooseAccountForm.tsx
+++ b/src/view/com/auth/login/ChooseAccountForm.tsx
@@ -16,6 +16,7 @@ import {useSession, useSessionApi, SessionAccount} from '#/state/session'
import {useProfileQuery} from '#/state/queries/profile'
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
import * as Toast from '#/view/com/util/Toast'
+import {logEvent} from '#/lib/statsig/statsig'
function AccountItem({
account,
@@ -102,6 +103,10 @@ export const ChooseAccountForm = ({
Toast.show(_(msg`Already signed in as @${account.handle}`))
} else {
await initSession(account)
+ logEvent('account:loggedIn', {
+ logContext: 'ChooseAccountForm',
+ withPassword: false,
+ })
track('Sign In', {resumedSession: true})
setTimeout(() => {
Toast.show(_(msg`Signed in as @${account.handle}`))
diff --git a/src/view/com/auth/login/LoginForm.tsx b/src/view/com/auth/login/LoginForm.tsx
index 3202d69c..92f49557 100644
--- a/src/view/com/auth/login/LoginForm.tsx
+++ b/src/view/com/auth/login/LoginForm.tsx
@@ -98,11 +98,14 @@ export const LoginForm = ({
}
// TODO remove double login
- await login({
- service: serviceUrl,
- identifier: fullIdent,
- password,
- })
+ await login(
+ {
+ service: serviceUrl,
+ identifier: fullIdent,
+ password,
+ },
+ 'LoginForm',
+ )
} catch (e: any) {
const errMsg = e.toString()
setIsProcessing(false)
diff --git a/src/view/com/modals/SwitchAccount.tsx b/src/view/com/modals/SwitchAccount.tsx
index 0658805b..892b07c9 100644
--- a/src/view/com/modals/SwitchAccount.tsx
+++ b/src/view/com/modals/SwitchAccount.tsx
@@ -39,7 +39,7 @@ function SwitchAccountCard({account}: {account: SessionAccount}) {
track('Settings:SignOutButtonClicked')
closeAllActiveElements()
// needs to be in timeout or the modal re-opens
- setTimeout(() => logout(), 0)
+ setTimeout(() => logout('SwitchAccount'), 0)
}, [track, logout, closeAllActiveElements])
const contents = (
@@ -95,7 +95,9 @@ function SwitchAccountCard({account}: {account: SessionAccount}) {
key={account.did}
style={[isSwitchingAccounts && styles.dimmed]}
onPress={
- isSwitchingAccounts ? undefined : () => onPressSwitchAccount(account)
+ isSwitchingAccounts
+ ? undefined
+ : () => onPressSwitchAccount(account, 'SwitchAccount')
}
accessibilityRole="button"
accessibilityLabel={_(msg`Switch to ${account.handle}`)}
diff --git a/src/view/com/testing/TestCtrls.e2e.tsx b/src/view/com/testing/TestCtrls.e2e.tsx
index e1e89948..1eb99c4f 100644
--- a/src/view/com/testing/TestCtrls.e2e.tsx
+++ b/src/view/com/testing/TestCtrls.e2e.tsx
@@ -22,18 +22,24 @@ export function TestCtrls() {
const {mutate: setFeedViewPref} = useSetFeedViewPreferencesMutation()
const {setShowLoggedOut} = useLoggedOutViewControls()
const onPressSignInAlice = async () => {
- await login({
- service: 'http://localhost:3000',
- identifier: 'alice.test',
- password: 'hunter2',
- })
+ await login(
+ {
+ service: 'http://localhost:3000',
+ identifier: 'alice.test',
+ password: 'hunter2',
+ },
+ 'LoginForm',
+ )
}
const onPressSignInBob = async () => {
- await login({
- service: 'http://localhost:3000',
- identifier: 'bob.test',
- password: 'hunter2',
- })
+ await login(
+ {
+ service: 'http://localhost:3000',
+ identifier: 'bob.test',
+ password: 'hunter2',
+ },
+ 'LoginForm',
+ )
}
return (
@@ -51,7 +57,7 @@ export function TestCtrls() {
/>
logout()}
+ onPress={() => logout('Settings')}
accessibilityRole="button"
style={BTN}
/>
diff --git a/src/view/screens/Settings/index.tsx b/src/view/screens/Settings/index.tsx
index 7e808f91..3967678b 100644
--- a/src/view/screens/Settings/index.tsx
+++ b/src/view/screens/Settings/index.tsx
@@ -100,7 +100,9 @@ function SettingsAccountCard({account}: {account: SessionAccount}) {
{isCurrentAccount ? (
{
+ logout('Settings')
+ }}
accessibilityRole="button"
accessibilityLabel={_(msg`Sign out`)}
accessibilityHint={`Signs ${profile?.displayName} out of Bluesky`}>
@@ -129,7 +131,9 @@ function SettingsAccountCard({account}: {account: SessionAccount}) {
testID={`switchToAccountBtn-${account.handle}`}
key={account.did}
onPress={
- isSwitchingAccounts ? undefined : () => onPressSwitchAccount(account)
+ isSwitchingAccounts
+ ? undefined
+ : () => onPressSwitchAccount(account, 'Settings')
}
accessibilityRole="button"
accessibilityLabel={_(msg`Switch to ${account.handle}`)}