import { useLanguage } from '@/lib/i18n' import { useState, useEffect, useRef, useCallback } from 'react'; import { useDebounce } from './use-debounce'; import { TagSuggestion } from '@/lib/ai/types'; interface UseAutoTaggingProps { content: string; notebookId?: string | null; enabled?: boolean; } export function useAutoTagging({ content, notebookId, enabled = true }: UseAutoTaggingProps) { const { language } = useLanguage(); const [suggestions, setSuggestions] = useState([]); const [isAnalyzing, setIsAnalyzing] = useState(false); const [error, setError] = useState(null); // Debounce content by 1.5s const debouncedContent = useDebounce(content, 1500); // Track previous notebookId to detect when note is moved to a notebook const previousNotebookId = useRef(notebookId); // AbortController for cancelling in-flight requests const abortRef = useRef(null); const analyzeContent = useCallback(async (contentToAnalyze: string, currentNotebookId?: string | null, currentLanguage?: string) => { if (!contentToAnalyze || contentToAnalyze.length < 10) { setSuggestions([]); return; } // Cancel previous request abortRef.current?.abort(); const controller = new AbortController(); abortRef.current = controller; setIsAnalyzing(true); setError(null); try { const response = await fetch('/api/ai/tags', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content: contentToAnalyze, notebookId: currentNotebookId || undefined, language: currentLanguage || document.documentElement.lang || 'en', }), signal: controller.signal, }); if (controller.signal.aborted) return; if (!response.ok) { throw new Error('Error during analysis'); } const data = await response.json(); setSuggestions(data.tags || []); } catch (err: any) { if (err.name === 'AbortError') return; setError('Failed to generate suggestions'); } finally { if (!controller.signal.aborted) { setIsAnalyzing(false); } } }, []); // Trigger on content change useEffect(() => { if (!enabled) { setSuggestions([]); return; } analyzeContent(debouncedContent, notebookId, language); }, [debouncedContent, enabled, notebookId, language, analyzeContent]); // Trigger when notebookId changes from null/undefined to a value useEffect(() => { if (!enabled) return; const prev = previousNotebookId.current; previousNotebookId.current = notebookId; const wasMovedToNotebook = (prev === null || prev === undefined) && notebookId; if (wasMovedToNotebook && content && content.length >= 10) { analyzeContent(content, notebookId, language); } }, [notebookId, content, enabled, language, analyzeContent]); // Cleanup on unmount useEffect(() => { return () => { abortRef.current?.abort(); }; }, []); return { suggestions, isAnalyzing, error, clearSuggestions: () => setSuggestions([]), }; }