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/events.ts38
-rw-r--r--src/state/models/ui/shell.ts12
-rw-r--r--src/state/session/index.tsx9
-rw-r--r--src/state/shell/index.tsx7
-rw-r--r--src/state/shell/reminders.ts22
-rw-r--r--src/state/shell/tick-every-minute.tsx20
6 files changed, 88 insertions, 20 deletions
diff --git a/src/state/events.ts b/src/state/events.ts
new file mode 100644
index 000000000..5441aafef
--- /dev/null
+++ b/src/state/events.ts
@@ -0,0 +1,38 @@
+import EventEmitter from 'eventemitter3'
+import {BskyAgent} from '@atproto/api'
+import {SessionAccount} from './session'
+
+type UnlistenFn = () => void
+
+const emitter = new EventEmitter()
+
+// a "soft reset" typically means scrolling to top and loading latest
+// but it can depend on the screen
+export function emitSoftReset() {
+  emitter.emit('soft-reset')
+}
+export function listenSoftReset(fn: () => void): UnlistenFn {
+  emitter.on('soft-reset', fn)
+  return () => emitter.off('soft-reset', fn)
+}
+
+export function emitSessionLoaded(
+  sessionAccount: SessionAccount,
+  agent: BskyAgent,
+) {
+  emitter.emit('session-loaded', sessionAccount, agent)
+}
+export function listenSessionLoaded(
+  fn: (sessionAccount: SessionAccount, agent: BskyAgent) => void,
+): UnlistenFn {
+  emitter.on('session-loaded', fn)
+  return () => emitter.off('session-loaded', fn)
+}
+
+export function emitSessionDropped() {
+  emitter.emit('session-dropped')
+}
+export function listenSessionDropped(fn: () => void): UnlistenFn {
+  emitter.on('session-dropped', fn)
+  return () => emitter.off('session-dropped', fn)
+}
diff --git a/src/state/models/ui/shell.ts b/src/state/models/ui/shell.ts
index 310d4f0f9..223c20625 100644
--- a/src/state/models/ui/shell.ts
+++ b/src/state/models/ui/shell.ts
@@ -1,6 +1,6 @@
 import {AppBskyActorDefs} from '@atproto/api'
 import {RootStoreModel} from '../root-store'
-import {makeAutoObservable, runInAction} from 'mobx'
+import {makeAutoObservable} from 'mobx'
 import {
   shouldRequestEmailConfirmation,
   setEmailConfirmationRequested,
@@ -40,14 +40,12 @@ export class ImagesLightbox implements LightboxModel {
 export class ShellUiModel {
   isLightboxActive = false
   activeLightbox: ProfileImageLightbox | ImagesLightbox | null = null
-  tickEveryMinute = Date.now()
 
   constructor(public rootStore: RootStoreModel) {
     makeAutoObservable(this, {
       rootStore: false,
     })
 
-    this.setupClock()
     this.setupLoginModals()
   }
 
@@ -83,14 +81,6 @@ export class ShellUiModel {
     this.activeLightbox = null
   }
 
-  setupClock() {
-    setInterval(() => {
-      runInAction(() => {
-        this.tickEveryMinute = Date.now()
-      })
-    }, 60_000)
-  }
-
   setupLoginModals() {
     this.rootStore.onSessionReady(() => {
       if (shouldRequestEmailConfirmation(this.rootStore.session)) {
diff --git a/src/state/session/index.tsx b/src/state/session/index.tsx
index e01e841f6..b8422553c 100644
--- a/src/state/session/index.tsx
+++ b/src/state/session/index.tsx
@@ -1,5 +1,4 @@
 import React from 'react'
-import {DeviceEventEmitter} from 'react-native'
 import {BskyAgent, AtpPersistSessionHandler} from '@atproto/api'
 
 import {networkRetry} from '#/lib/async/retry'
@@ -7,6 +6,7 @@ import {logger} from '#/logger'
 import * as persisted from '#/state/persisted'
 import {PUBLIC_BSKY_AGENT} from '#/state/queries'
 import {IS_PROD} from '#/lib/constants'
+import {emitSessionLoaded, emitSessionDropped} from '../events'
 
 export type SessionAccount = persisted.PersistedAccount
 
@@ -98,7 +98,9 @@ function createPersistSessionHandler(
       logger.DebugContext.session,
     )
 
-    if (expired) DeviceEventEmitter.emit('session-dropped')
+    if (expired) {
+      emitSessionDropped()
+    }
 
     persistSessionCallback({
       expired,
@@ -180,6 +182,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
 
       setState(s => ({...s, agent}))
       upsertAccount(account)
+      emitSessionLoaded(account, agent)
 
       logger.debug(
         `session: created account`,
@@ -230,6 +233,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
 
       setState(s => ({...s, agent}))
       upsertAccount(account)
+      emitSessionLoaded(account, agent)
 
       logger.debug(
         `session: logged in`,
@@ -291,6 +295,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
 
       setState(s => ({...s, agent}))
       upsertAccount(account)
+      emitSessionLoaded(account, agent)
     },
     [upsertAccount],
   )
diff --git a/src/state/shell/index.tsx b/src/state/shell/index.tsx
index 63c3763d1..53f05055c 100644
--- a/src/state/shell/index.tsx
+++ b/src/state/shell/index.tsx
@@ -6,6 +6,7 @@ import {Provider as MinimalModeProvider} from './minimal-mode'
 import {Provider as ColorModeProvider} from './color-mode'
 import {Provider as OnboardingProvider} from './onboarding'
 import {Provider as ComposerProvider} from './composer'
+import {Provider as TickEveryMinuteProvider} from './tick-every-minute'
 
 export {useIsDrawerOpen, useSetDrawerOpen} from './drawer-open'
 export {
@@ -15,6 +16,8 @@ export {
 export {useMinimalShellMode, useSetMinimalShellMode} from './minimal-mode'
 export {useColorMode, useSetColorMode} from './color-mode'
 export {useOnboardingState, useOnboardingDispatch} from './onboarding'
+export {useComposerState, useComposerControls} from './composer'
+export {useTickEveryMinute} from './tick-every-minute'
 
 export function Provider({children}: React.PropsWithChildren<{}>) {
   return (
@@ -24,7 +27,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
           <MinimalModeProvider>
             <ColorModeProvider>
               <OnboardingProvider>
-                <ComposerProvider>{children}</ComposerProvider>
+                <ComposerProvider>
+                  <TickEveryMinuteProvider>{children}</TickEveryMinuteProvider>
+                </ComposerProvider>
               </OnboardingProvider>
             </ColorModeProvider>
           </MinimalModeProvider>
diff --git a/src/state/shell/reminders.ts b/src/state/shell/reminders.ts
index e7ee7a5fe..88d0a5d85 100644
--- a/src/state/shell/reminders.ts
+++ b/src/state/shell/reminders.ts
@@ -1,14 +1,24 @@
 import * as persisted from '#/state/persisted'
-import {SessionModel} from '../models/session'
 import {toHashCode} from 'lib/strings/helpers'
 import {isOnboardingActive} from './onboarding'
+import {SessionAccount} from '../session'
+import {listenSessionLoaded} from '../events'
+import {unstable__openModal} from '../modals'
 
-export function shouldRequestEmailConfirmation(session: SessionModel) {
-  const sess = session.currentSession
-  if (!sess) {
+export function init() {
+  listenSessionLoaded(account => {
+    if (shouldRequestEmailConfirmation(account)) {
+      unstable__openModal({name: 'verify-email', showReminder: true})
+      setEmailConfirmationRequested()
+    }
+  })
+}
+
+export function shouldRequestEmailConfirmation(account: SessionAccount) {
+  if (!account) {
     return false
   }
-  if (sess.emailConfirmed) {
+  if (account.emailConfirmed) {
     return false
   }
   if (isOnboardingActive()) {
@@ -22,7 +32,7 @@ export function shouldRequestEmailConfirmation(session: SessionModel) {
   // shard the users into 2 day of the week buckets
   // (this is to avoid a sudden influx of email updates when
   // this feature rolls out)
-  const code = toHashCode(sess.did) % 7
+  const code = toHashCode(account.did) % 7
   if (code !== today.getDay() && code !== (today.getDay() + 1) % 7) {
     return false
   }
diff --git a/src/state/shell/tick-every-minute.tsx b/src/state/shell/tick-every-minute.tsx
new file mode 100644
index 000000000..c37221c90
--- /dev/null
+++ b/src/state/shell/tick-every-minute.tsx
@@ -0,0 +1,20 @@
+import React from 'react'
+
+type StateContext = number
+
+const stateContext = React.createContext<StateContext>(0)
+
+export function Provider({children}: React.PropsWithChildren<{}>) {
+  const [tick, setTick] = React.useState(Date.now())
+  React.useEffect(() => {
+    const i = setInterval(() => {
+      setTick(Date.now())
+    }, 60_000)
+    return () => clearInterval(i)
+  }, [])
+  return <stateContext.Provider value={tick}>{children}</stateContext.Provider>
+}
+
+export function useTickEveryMinute() {
+  return React.useContext(stateContext)
+}