about summary refs log tree commit diff
path: root/src/state/session
diff options
context:
space:
mode:
Diffstat (limited to 'src/state/session')
-rw-r--r--src/state/session/agent-config.ts12
-rw-r--r--src/state/session/index.tsx102
2 files changed, 90 insertions, 24 deletions
diff --git a/src/state/session/agent-config.ts b/src/state/session/agent-config.ts
new file mode 100644
index 000000000..3ee2718a3
--- /dev/null
+++ b/src/state/session/agent-config.ts
@@ -0,0 +1,12 @@
+import AsyncStorage from '@react-native-async-storage/async-storage'
+
+const PREFIX = 'agent-labelers'
+
+export async function saveLabelers(did: string, value: string[]) {
+  await AsyncStorage.setItem(`${PREFIX}:${did}`, JSON.stringify(value))
+}
+
+export async function readLabelers(did: string): Promise<string[] | undefined> {
+  const rawData = await AsyncStorage.getItem(`${PREFIX}:${did}`)
+  return rawData ? JSON.parse(rawData) : undefined
+}
diff --git a/src/state/session/index.tsx b/src/state/session/index.tsx
index 46628318c..b6748bfad 100644
--- a/src/state/session/index.tsx
+++ b/src/state/session/index.tsx
@@ -1,8 +1,15 @@
 import React from 'react'
-import {BskyAgent, AtpPersistSessionHandler} from '@atproto/api'
+import {
+  BskyAgent,
+  AtpPersistSessionHandler,
+  BSKY_LABELER_DID,
+} from '@atproto/api'
 import {useQueryClient} from '@tanstack/react-query'
 import {jwtDecode} from 'jwt-decode'
 
+import {IS_DEV} from '#/env'
+import {IS_TEST_USER} from '#/lib/constants'
+import {isWeb} from '#/platform/detection'
 import {networkRetry} from '#/lib/async/retry'
 import {logger} from '#/logger'
 import * as persisted from '#/state/persisted'
@@ -12,6 +19,8 @@ import {useLoggedOutViewControls} from '#/state/shell/logged-out'
 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
 
@@ -46,17 +55,22 @@ export type ApiContext = {
     verificationPhone?: string
     verificationCode?: string
   }) => Promise<void>
-  login: (props: {
-    service: string
-    identifier: string
-    password: string
-  }) => Promise<void>
+  login: (
+    props: {
+      service: string
+      identifier: string
+      password: string
+    },
+    logContext: LogEvents['account:loggedIn']['logContext'],
+  ) => Promise<void>
   /**
    * 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<void>
+  logout: (
+    logContext: LogEvents['account:loggedOut']['logContext'],
+  ) => Promise<void>
   /**
    * A partial logout. Clears the `currentAccount` from session, but DOES NOT
    * clear access tokens from accounts, allowing the user to return to their
@@ -68,7 +82,10 @@ export type ApiContext = {
   initSession: (account: SessionAccount) => Promise<void>
   resumeSession: (account?: SessionAccount) => Promise<void>
   removeAccount: (account: SessionAccount) => void
-  selectAccount: (account: SessionAccount) => Promise<void>
+  selectAccount: (
+    account: SessionAccount,
+    logContext: LogEvents['account:loggedIn']['logContext'],
+  ) => Promise<void>
   updateCurrentAccount: (
     account: Partial<
       Pick<SessionAccount, 'handle' | 'email' | 'emailConfirmed'>
@@ -255,6 +272,8 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
         deactivated,
       }
 
+      await configureModeration(agent, account)
+
       agent.setPersistSessionHandler(
         createPersistSessionHandler(
           account,
@@ -276,7 +295,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
   )
 
   const login = React.useCallback<ApiContext['login']>(
-    async ({service, identifier, password}) => {
+    async ({service, identifier, password}, logContext) => {
       logger.debug(`session: login`, {}, logger.DebugContext.session)
 
       const agent = new BskyAgent({service})
@@ -298,6 +317,8 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
         deactivated: isSessionDeactivated(agent.session.accessJwt),
       }
 
+      await configureModeration(agent, account)
+
       agent.setPersistSessionHandler(
         createPersistSessionHandler(
           account,
@@ -309,30 +330,37 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
       )
 
       __globalAgent = agent
+      // @ts-ignore
+      if (IS_DEV && isWeb) window.agent = agent
       queryClient.clear()
       upsertAccount(account)
 
       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<ApiContext['logout']>(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<ApiContext['logout']>(
+    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<ApiContext['initSession']>(
     async account => {
@@ -348,6 +376,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
           {networkErrorCallback: clearCurrentAccount},
         ),
       })
+      // @ts-ignore
+      if (IS_DEV && isWeb) window.agent = agent
+      await configureModeration(agent, account)
 
       let canReusePrevSession = false
       try {
@@ -523,11 +554,12 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
   )
 
   const selectAccount = React.useCallback<ApiContext['selectAccount']>(
-    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}))
@@ -643,6 +675,28 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
   )
 }
 
+async function configureModeration(agent: BskyAgent, account: SessionAccount) {
+  if (IS_TEST_USER(account.handle)) {
+    const did = (
+      await agent
+        .resolveHandle({handle: 'mod-authority.test'})
+        .catch(_ => undefined)
+    )?.data.did
+    if (did) {
+      console.warn('USING TEST ENV MODERATION')
+      BskyAgent.configure({appLabelers: [did]})
+    }
+  } else {
+    BskyAgent.configure({appLabelers: [BSKY_LABELER_DID]})
+    const labelerDids = await readLabelers(account.did).catch(_ => {})
+    if (labelerDids) {
+      agent.configureLabelersHeader(
+        labelerDids.filter(did => did !== BSKY_LABELER_DID),
+      )
+    }
+  }
+}
+
 export function useSession() {
   return React.useContext(StateContext)
 }