# NoteFlow TypeScript Hooks & Contexts ## Location `client/src/hooks/` and `client/src/contexts/` ## React Contexts ### Connection Context (`connection-context.tsx`) gRPC connection state and mode detection. ```typescript interface ConnectionHelpers { state: ConnectionState; mode: ConnectionMode; // connected | disconnected | cached | mock | reconnecting isConnected: boolean; isReadOnly: boolean; // cached | disconnected | mock | reconnecting isReconnecting: boolean; isSimulating: boolean; // Simulation mode from preferences } // Usage const { isConnected, isReadOnly, mode } = useConnection(); ``` ### Workspace Context (`workspace-context.tsx`) User and workspace state. ```typescript interface WorkspaceContextValue { currentWorkspace: Workspace | null; workspaces: Workspace[]; currentUser: GetCurrentUserResponse | null; switchWorkspace: (workspaceId: string) => Promise; isLoading: boolean; error: string | null; } // Usage const { currentWorkspace, currentUser, switchWorkspace } = useWorkspace(); ``` ### Project Context (`project-context.tsx`) Active project and project list. ```typescript interface ProjectContextValue { projects: Project[]; activeProject: Project | null; switchProject: (projectId: string) => Promise; isLoading: boolean; error: string | null; } // Usage const { activeProject, projects, switchProject } = useProject(); ``` ### Storage Context (`storage.ts`) Persistent storage utilities. ## Hook Organization (Domain Folders) ### Audio Hooks (`hooks/audio/`) | Hook | Purpose | |------|---------| | `use-audio-devices.ts` | Audio device enumeration and selection | | `use-audio-devices.helpers.ts` | Device helper functions | | `use-audio-devices.types.ts` | Device type definitions | | `use-asr-config.ts` | ASR configuration management | | `use-streaming-config.ts` | Streaming configuration | | `use-audio-testing.ts` | Audio testing utilities | ```typescript // use-audio-devices.ts interface AudioDevice { id: string; name: string; kind: 'input' | 'output'; } function useAudioDevices(options: UseAudioDevicesOptions): { devices: AudioDevice[]; selectedInput: AudioDevice | null; selectedOutput: AudioDevice | null; setSelectedInput: (id: string) => void; setSelectedOutput: (id: string) => void; isLoading: boolean; } ``` ### Auth Hooks (`hooks/auth/`) | Hook | Purpose | |------|---------| | `use-cloud-consent.ts` | Cloud AI consent management | | `use-oauth-flow.ts` | OAuth authentication flow | | `use-auth-flow.ts` | General auth flow | | `use-oidc-providers.ts` | OIDC provider management | | `use-huggingface-token.ts` | HuggingFace token management | | `use-secure-integration-secrets.ts` | Secure secret storage | ```typescript // use-cloud-consent.ts function useCloudConsent(): { hasConsent: boolean; grantConsent: () => Promise; revokeConsent: () => Promise; isLoading: boolean; } ``` ### Data Hooks (`hooks/data/`) | Hook | Purpose | |------|---------| | `use-async-data.ts` | Generic async data loading with retry | | `use-guarded-mutation.ts` | Mutation with offline/permissions guard | | `use-project.ts` | Project access from context | | `use-project-members.ts` | Project membership queries | ```typescript // use-async-data.ts const { data, isLoading, error, retry } = useAsyncData( () => getAPI().getMeeting({ meeting_id: meetingId }), { onError: (e) => toast.error(e.message), deps: [meetingId], } ); ``` ### Processing Hooks (`hooks/processing/`) | Hook | Purpose | |------|---------| | `use-diarization.ts` | Diarization job lifecycle with polling | | `use-entity-extraction.ts` | NER extraction & updates | | `use-post-processing.ts` | Post-recording processing state | | `use-assistant.ts` | AI assistant interactions | | `state.ts` | Processing state management | | `events.ts` | Processing event handling | ```typescript // use-diarization.ts interface UseDiarizationOptions { onComplete?: (status: DiarizationJobStatus) => void; onError?: (error: string) => void; pollInterval?: number; maxRetries?: number; showToasts?: boolean; autoRecover?: boolean; } interface DiarizationState { jobId: string | null; status: JobStatus | null; progress: number; // 0-100 error: string | null; speakerIds: string[]; segmentsUpdated: number; isActive: boolean; } function useDiarization(options?: UseDiarizationOptions): { state: DiarizationState; start: (meetingId: string, numSpeakers?: number) => Promise; cancel: () => Promise; reset: () => void; recover: () => Promise; } ``` ### Recording Hooks (`hooks/recording/`) | Hook | Purpose | |------|---------| | `use-recording-session.ts` | Recording session management | | `use-recording-app-policy.ts` | App recording policy detection | ### Sync Hooks (`hooks/sync/`) | Hook | Purpose | |------|---------| | `use-webhooks.ts` | Webhook CRUD | | `use-calendar-sync.ts` | Calendar integration sync | | `use-integration-sync.ts` | Integration sync state polling | | `use-integration-validation.ts` | Integration config validation | | `use-preferences-sync.ts` | Preferences synchronization | | `use-meeting-reminders.ts` | Meeting reminder notifications | | `sync-notifications.ts` | Sync notification handling | ### UI Hooks (`hooks/ui/`) | Hook | Purpose | |------|---------| | `use-toast.ts` | Toast notifications (shadcn/ui) | | `use-panel-preferences.ts` | Panel layout preferences | | `use-recording-panels.ts` | Recording panel state | | `use-animated-words.ts` | Word animation for transcription | ## Hook Patterns ### Polling with Backoff (Diarization) ```typescript const { state, start, cancel, recover } = useDiarization({ pollInterval: 2000, maxRetries: 10, autoRecover: true, onComplete: (status) => { toast.success(`Diarization complete: ${status.segmentsUpdated} segments updated`); }, }); // Start job await start(meetingId, 2); // 2 speakers // Monitor progress useEffect(() => { console.log(`Progress: ${state.progress}%`); }, [state.progress]); ``` ### Connection-Aware Components ```typescript function MyComponent() { const { isConnected, isReadOnly } = useConnection(); const { activeProject } = useProject(); if (isReadOnly) { return ; } return ; } ``` ### Guarded Mutations ```typescript const { mutate, isLoading } = useGuardedMutation( async () => { await getAPI().deleteMeeting(meetingId); }, { requiresConnection: true, onError: (e) => toast.error(e.message), } ); ``` ## Context Provider Pattern ```typescript // Root app setup function App() { return ( ); } ``` ## Hook Re-exports (`index.ts`) All hooks are re-exported from domain-specific index files: ```typescript // hooks/index.ts export * from './audio'; export * from './auth'; export * from './data'; export * from './processing'; export * from './recording'; export * from './sync'; export * from './ui'; ```