about summary refs log tree commit diff
path: root/src/state
diff options
context:
space:
mode:
Diffstat (limited to 'src/state')
-rw-r--r--src/state/ageAssurance/index.tsx12
-rw-r--r--src/state/ageAssurance/useAgeAssurance.ts3
-rw-r--r--src/state/ageAssurance/useIsAgeAssuranceEnabled.ts7
-rw-r--r--src/state/session/index.tsx22
-rw-r--r--src/state/session/reducer.ts42
-rw-r--r--src/state/session/types.ts8
6 files changed, 77 insertions, 17 deletions
diff --git a/src/state/ageAssurance/index.tsx b/src/state/ageAssurance/index.tsx
index 3451b1139..6cdd8d929 100644
--- a/src/state/ageAssurance/index.tsx
+++ b/src/state/ageAssurance/index.tsx
@@ -4,7 +4,6 @@ import {useQuery} from '@tanstack/react-query'
 
 import {networkRetry} from '#/lib/async/retry'
 import {useGetAndRegisterPushToken} from '#/lib/notifications/notifications'
-import {useGate} from '#/lib/statsig/statsig'
 import {isNetworkError} from '#/lib/strings/errors'
 import {
   type AgeAssuranceAPIContextType,
@@ -41,7 +40,6 @@ const AgeAssuranceAPIContext = createContext<AgeAssuranceAPIContextType>({
  * performance.
  */
 export function Provider({children}: {children: React.ReactNode}) {
-  const gate = useGate()
   const agent = useAgent()
   const {geolocation} = useGeolocation()
   const isAgeAssuranceEnabled = useIsAgeAssuranceEnabled()
@@ -78,12 +76,10 @@ export function Provider({children}: {children: React.ReactNode}) {
           account: agent.session?.did,
         })
 
-        if (gate('age_assurance')) {
-          await getAndRegisterPushToken({
-            isAgeRestricted:
-              !!geolocation?.isAgeRestrictedGeo && data.status !== 'assured',
-          })
-        }
+        await getAndRegisterPushToken({
+          isAgeRestricted:
+            !!geolocation?.isAgeRestrictedGeo && data.status !== 'assured',
+        })
 
         return data
       } catch (e) {
diff --git a/src/state/ageAssurance/useAgeAssurance.ts b/src/state/ageAssurance/useAgeAssurance.ts
index 0215cc88d..061384868 100644
--- a/src/state/ageAssurance/useAgeAssurance.ts
+++ b/src/state/ageAssurance/useAgeAssurance.ts
@@ -28,7 +28,8 @@ export function useAgeAssurance(): AgeAssurance {
 
   return useMemo(() => {
     const isReady = aa.isReady && preferencesLoaded
-    const isDeclaredUnderage = (declaredAge || 0) < 18
+    const isDeclaredUnderage =
+      declaredAge !== undefined ? declaredAge < 18 : false
     const state: AgeAssurance = {
       isReady,
       status: aa.status,
diff --git a/src/state/ageAssurance/useIsAgeAssuranceEnabled.ts b/src/state/ageAssurance/useIsAgeAssuranceEnabled.ts
index 06fe46d23..b020e3c57 100644
--- a/src/state/ageAssurance/useIsAgeAssuranceEnabled.ts
+++ b/src/state/ageAssurance/useIsAgeAssuranceEnabled.ts
@@ -1,14 +1,11 @@
 import {useMemo} from 'react'
 
-import {useGate} from '#/lib/statsig/statsig'
 import {useGeolocation} from '#/state/geolocation'
 
 export function useIsAgeAssuranceEnabled() {
-  const gate = useGate()
   const {geolocation} = useGeolocation()
 
   return useMemo(() => {
-    const enabled = gate('age_assurance')
-    return enabled && !!geolocation?.isAgeRestrictedGeo
-  }, [geolocation, gate])
+    return !!geolocation?.isAgeRestrictedGeo
+  }, [geolocation])
 }
diff --git a/src/state/session/index.tsx b/src/state/session/index.tsx
index 45384c4f5..8223a7b3a 100644
--- a/src/state/session/index.tsx
+++ b/src/state/session/index.tsx
@@ -40,6 +40,7 @@ const ApiContext = React.createContext<SessionApiContext>({
   logoutEveryAccount: async () => {},
   resumeSession: async () => {},
   removeAccount: () => {},
+  partialRefreshSession: async () => {},
 })
 
 export function Provider({children}: React.PropsWithChildren<{}>) {
@@ -119,7 +120,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
   )
 
   const logoutCurrentAccount = React.useCallback<
-    SessionApiContext['logoutEveryAccount']
+    SessionApiContext['logoutCurrentAccount']
   >(
     logContext => {
       addSessionDebugLog({type: 'method:start', method: 'logout'})
@@ -182,6 +183,23 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
     [onAgentSessionChange, cancelPendingTask],
   )
 
+  const partialRefreshSession = React.useCallback<
+    SessionApiContext['partialRefreshSession']
+  >(async () => {
+    const agent = state.currentAgentState.agent as BskyAppAgent
+    const signal = cancelPendingTask()
+    const {data} = await agent.com.atproto.server.getSession()
+    if (signal.aborted) return
+    dispatch({
+      type: 'partial-refresh-session',
+      accountDid: agent.session!.did,
+      patch: {
+        emailConfirmed: data.emailConfirmed,
+        emailAuthFactor: data.emailAuthFactor,
+      },
+    })
+  }, [state, cancelPendingTask])
+
   const removeAccount = React.useCallback<SessionApiContext['removeAccount']>(
     account => {
       addSessionDebugLog({
@@ -262,6 +280,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
       logoutEveryAccount,
       resumeSession,
       removeAccount,
+      partialRefreshSession,
     }),
     [
       createAccount,
@@ -270,6 +289,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
       logoutEveryAccount,
       resumeSession,
       removeAccount,
+      partialRefreshSession,
     ],
   )
 
diff --git a/src/state/session/reducer.ts b/src/state/session/reducer.ts
index 22ba47162..f6452a391 100644
--- a/src/state/session/reducer.ts
+++ b/src/state/session/reducer.ts
@@ -1,8 +1,8 @@
-import {AtpSessionEvent} from '@atproto/api'
+import {type AtpSessionEvent, type BskyAgent} from '@atproto/api'
 
 import {createPublicAgent} from './agent'
 import {wrapSessionReducerForLogging} from './logging'
-import {SessionAccount} from './types'
+import {type SessionAccount} from './types'
 
 // A hack so that the reducer can't read anything from the agent.
 // From the reducer's point of view, it should be a completely opaque object.
@@ -52,6 +52,11 @@ export type Action =
       syncedAccounts: SessionAccount[]
       syncedCurrentDid: string | undefined
     }
+  | {
+      type: 'partial-refresh-session'
+      accountDid: string
+      patch: Pick<SessionAccount, 'emailConfirmed' | 'emailAuthFactor'>
+    }
 
 function createPublicAgentState(): AgentState {
   return {
@@ -180,6 +185,39 @@ let reducer = (state: State, action: Action): State => {
         needsPersist: false, // Synced from another tab. Don't persist to avoid cycles.
       }
     }
+    case 'partial-refresh-session': {
+      const {accountDid, patch} = action
+      const agent = state.currentAgentState.agent as BskyAgent
+
+      /*
+       * Only mutating values that are safe. Be very careful with this.
+       */
+      if (agent.session) {
+        agent.session.emailConfirmed =
+          patch.emailConfirmed ?? agent.session.emailConfirmed
+        agent.session.emailAuthFactor =
+          patch.emailAuthFactor ?? agent.session.emailAuthFactor
+      }
+
+      return {
+        ...state,
+        currentAgentState: {
+          ...state.currentAgentState,
+          agent,
+        },
+        accounts: state.accounts.map(a => {
+          if (a.did === accountDid) {
+            return {
+              ...a,
+              emailConfirmed: patch.emailConfirmed ?? a.emailConfirmed,
+              emailAuthFactor: patch.emailAuthFactor ?? a.emailAuthFactor,
+            }
+          }
+          return a
+        }),
+        needsPersist: true,
+      }
+    }
   }
 }
 reducer = wrapSessionReducerForLogging(reducer)
diff --git a/src/state/session/types.ts b/src/state/session/types.ts
index aa8b9a99e..4621b4f04 100644
--- a/src/state/session/types.ts
+++ b/src/state/session/types.ts
@@ -40,4 +40,12 @@ export type SessionApiContext = {
   ) => void
   resumeSession: (account: SessionAccount) => Promise<void>
   removeAccount: (account: SessionAccount) => void
+  /**
+   * Calls `getSession` and updates select fields on the current account and
+   * `BskyAgent`. This is an alternative to `resumeSession`, which updates
+   * current account/agent using the `persistSessionHandler`, but is more load
+   * bearing. This patches in updates without causing any side effects via
+   * `persistSessionHandler`.
+   */
+  partialRefreshSession: () => Promise<void>
 }