diff --git a/mobile/src/app/_layout.tsx b/mobile/src/app/_layout.tsx index 340dfd6..4f614d8 100644 --- a/mobile/src/app/_layout.tsx +++ b/mobile/src/app/_layout.tsx @@ -3,10 +3,10 @@ import { Stack } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; import { AppState, Modal, Pressable, ScrollView, StyleSheet, Text, TextInput, View } from 'react-native'; import { useAuthStore, type AuthState } from '../store/auth-store'; -import { useInboxStore, type InboxState } from '../store/inbox-store'; -import { useNotesStore, type NotesState } from '../store/notes-store'; -import { useWorkspaceStore, type WorkspaceState } from '../store/workspace-store'; -import { checkKillSwitch, initPlatform } from '../lib/platform'; +import { useInboxStore } from '../store/inbox-store'; +import { useNotesStore } from '../store/notes-store'; +import { useWorkspaceStore } from '../store/workspace-store'; +import { checkKillSwitch, flushTelemetry, initPlatform } from '../lib/platform'; import { getBroadcastClient } from '../lib/broadcast-client'; import { getSurveyClient } from '../lib/survey-client'; import { flushNoteQueue, getNoteQueueSize } from '../lib/offline-queue'; @@ -37,9 +37,8 @@ export default function RootLayout() { }); const bootstrapAuth = useAuthStore((state: AuthState) => state.bootstrap); - const hydrateInbox = useInboxStore((state: InboxState) => state.hydrate); - const hydrateNotes = useNotesStore((state: NotesState) => state.hydrate); - const hydrateWorkspaces = useWorkspaceStore((state: WorkspaceState) => state.hydrate); + const hasBootstrapped = useAuthStore((state: AuthState) => state.hasBootstrapped); + const isAuthenticated = useAuthStore((state: AuthState) => state.isAuthenticated); function toSurveyAnswer(question: Question, value: string): QuestionAnswer { if (question.type === 'single_choice' || question.type === 'dropdown') { @@ -99,15 +98,45 @@ export default function RootLayout() { .catch(() => { setKillSwitchState({ checked: true, disabled: false, message: null }); }); + }, []); + useEffect(() => { void bootstrapAuth(); + }, [bootstrapAuth]); + + useEffect(() => { void initPlatform(); - void hydrateNotes(); - void hydrateWorkspaces(); - void hydrateInbox(); - void flushQueuedNoteMutations(); - void loadBroadcasts(); - void loadSurvey(); + }, []); + + useEffect(() => { + const sub = AppState.addEventListener('change', (nextState) => { + if (nextState === 'background' || nextState === 'inactive') { + flushTelemetry(); + } + }); + return () => sub.remove(); + }, []); + + useEffect(() => { + if (!hasBootstrapped || !isAuthenticated) { + return undefined; + } + + let cancelled = false; + + void (async () => { + await Promise.all([ + useNotesStore.getState().hydrate(), + useWorkspaceStore.getState().hydrate(), + useInboxStore.getState().hydrate(), + ]); + if (cancelled) { + return; + } + void flushQueuedNoteMutations(); + void loadBroadcasts(); + void loadSurvey(); + })(); const broadcastTimer = setInterval(() => { void loadBroadcasts(); @@ -124,11 +153,12 @@ export default function RootLayout() { }); return () => { + cancelled = true; clearInterval(broadcastTimer); clearInterval(surveyTimer); appStateSubscription.remove(); }; - }, [bootstrapAuth, hydrateInbox, hydrateNotes, hydrateWorkspaces]); + }, [hasBootstrapped, isAuthenticated]); if (killSwitchState.checked && killSwitchState.disabled) { return (