- 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
5.2 KiB
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 contextuseActiveProject()— Get active projectuseProjectMembers()— Project membership queries
Cloud Consent (use-cloud-consent.ts)
Cloud AI consent state management.
function useCloudConsent(): {
hasConsent: boolean;
grantConsent: () => Promise<void>;
revokeConsent: () => Promise<void>;
isLoading: boolean;
}
Integration Hooks
useWebhooks()— Webhook CRUDuseEntityExtraction()— NER extraction & updatesuseCalendarSync()— Calendar integration syncuseOAuthFlow()— OAuth authentication flowuseAuthFlow()— General auth flowuseOidcProviders()— OIDC provider managementuseIntegrationSync()— Integration sync state pollinguseIntegrationValidation()— Integration config validation
Recording Hooks
useRecordingAppPolicy()— App recording policy detectionusePostProcessing()— Post-recording processing state
Utility Hooks
useAsyncData<T>()— Generic async data loading with retryuseGuardedMutation()— Mutation with offline/permissions guarduseToast()— Toast notifications (shadcn/ui)usePanelPreferences()— Panel layout preferencesuseMobile()— 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>
);
}