Files
noteflow/.rag/08-typescript-hooks-contexts.md
Travis Vasceannie 1ce24cdf7b feat: reorganize Claude hooks and add RAG documentation structure with error handling policies
- Moved all hookify configuration files from `.claude/` to `.claude/hooks/` subdirectory for better organization
- Added four new blocking hooks to prevent common error handling anti-patterns:
  - `block-broad-exception-handler`: Prevents catching generic `Exception` with only logging
  - `block-datetime-now-fallback`: Blocks returning `datetime.now()` as fallback on parse failures to prevent data corruption
  - `block-default
2026-01-15 15:58:06 +00:00

5.2 KiB

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.

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.

interface WorkspaceContextValue {
  currentWorkspace: Workspace | null;
  workspaces: Workspace[];
  currentUser: GetCurrentUserResponse | null;
  switchWorkspace: (workspaceId: string) => Promise<void>;
  isLoading: boolean;
  error: string | null;
}

// Usage
const { currentWorkspace, currentUser, switchWorkspace } = useWorkspace();

Project Context (project-context.tsx)

Active project and project list.

interface ProjectContextValue {
  projects: Project[];
  activeProject: Project | null;
  switchProject: (projectId: string) => Promise<void>;
  isLoading: boolean;
  error: string | null;
}

// Usage
const { activeProject, projects, switchProject } = useProjects();

Custom Hooks

Diarization (use-diarization.ts)

Diarization job lifecycle with polling and recovery.

interface UseDiarizationOptions {
  onComplete?: (status: DiarizationJobStatus) => void;
  onError?: (error: string) => void;
  pollInterval?: number;
  maxRetries?: number;
  showToasts?: boolean;
  autoRecover?: boolean;  // Auto-recovery after restart
}

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<void>;
  cancel: () => Promise<void>;
  reset: () => void;
  recover: () => Promise<DiarizationJobStatus | null>;
}

Audio Devices (use-audio-devices.ts)

Audio device enumeration and selection.

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;
}

Project Hooks

  • useProject() — Access project from context
  • useActiveProject() — Get active project
  • useProjectMembers() — Project membership queries

Cloud AI consent state management.

function useCloudConsent(): {
  hasConsent: boolean;
  grantConsent: () => Promise<void>;
  revokeConsent: () => Promise<void>;
  isLoading: boolean;
}

Integration Hooks

  • useWebhooks() — Webhook CRUD
  • useEntityExtraction() — NER extraction & updates
  • useCalendarSync() — Calendar integration sync
  • useOAuthFlow() — OAuth authentication flow
  • useAuthFlow() — General auth flow
  • useOidcProviders() — OIDC provider management
  • useIntegrationSync() — Integration sync state polling
  • useIntegrationValidation() — Integration config validation

Recording Hooks

  • useRecordingAppPolicy() — App recording policy detection
  • usePostProcessing() — Post-recording processing state

Utility Hooks

  • useAsyncData<T>() — Generic async data loading with retry
  • useGuardedMutation() — Mutation with offline/permissions guard
  • useToast() — Toast notifications (shadcn/ui)
  • usePanelPreferences() — Panel layout preferences
  • useMobile() — Mobile/responsive detection

Hook Patterns

Polling with Backoff (Diarization)

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]);

Async Data Loading

const { data, isLoading, error, retry } = useAsyncData(
  () => getAPI().getMeeting({ meeting_id: meetingId }),
  {
    onError: (e) => toast.error(e.message),
    deps: [meetingId],
  }
);

Connection-Aware Components

function MyComponent() {
  const { isConnected, isReadOnly } = useConnection();
  const { activeProject } = useProjects();

  if (isReadOnly) {
    return <OfflineBanner />;
  }

  return <ConnectedUI project={activeProject} />;
}

Context Provider Pattern

// Root app setup
function App() {
  return (
    <ConnectionProvider>
      <WorkspaceProvider>
        <ProjectProvider>
          <Routes />
        </ProjectProvider>
      </WorkspaceProvider>
    </ConnectionProvider>
  );
}