import { useState, useEffect, useRef } from 'react'; import { Mic, MicOff, Loader } from 'lucide-react'; import api from '../api/axios'; const VoiceInput = ({ onTransactionAdded }) => { const [isListening, setIsListening] = useState(false); const [isProcessing, setIsProcessing] = useState(false); const [error, setError] = useState(null); const recognitionRef = useRef(null); const silenceTimerRef = useRef(null); const stopSilenceTimer = () => { if (silenceTimerRef.current) { clearTimeout(silenceTimerRef.current); silenceTimerRef.current = null; } }; const startSilenceTimer = () => { stopSilenceTimer(); silenceTimerRef.current = setTimeout(() => { console.log("Silence timeout reached. Stopping recognition."); if (recognitionRef.current) recognitionRef.current.stop(); }, 6000); // 2 seconds of silence }; useEffect(() => { if ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) { const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; recognitionRef.current = new SpeechRecognition(); recognitionRef.current.continuous = true; // Changed to true recognitionRef.current.interimResults = true; // Changed to true for better feedback recognitionRef.current.lang = 'en-US'; recognitionRef.current.onstart = () => { setIsListening(true); setError(null); startSilenceTimer(); }; recognitionRef.current.onend = () => { setIsListening(false); stopSilenceTimer(); }; recognitionRef.current.onerror = (event) => { console.error("Speech recognition error", event.error); if (event.error !== 'no-speech') { setError(`Voice error: ${event.error}`); } setIsListening(false); stopSilenceTimer(); }; recognitionRef.current.onresult = (event) => { startSilenceTimer(); // Reset timer on every result let finalTranscript = ''; for (let i = event.resultIndex; i < event.results.length; ++i) { if (event.results[i].isFinal) { finalTranscript += event.results[i][0].transcript; } } if (finalTranscript) { console.log("Heard Final:", finalTranscript); sendTextCheck(finalTranscript); recognitionRef.current.stop(); // Process immediately if we have a full final result } }; } else { setError("Browser does not support Web Speech API."); } return () => { if (recognitionRef.current) recognitionRef.current.stop(); stopSilenceTimer(); }; }, []); const toggleListening = () => { if (isListening) { recognitionRef.current.stop(); } else { recognitionRef.current.start(); } }; const sendTextCheck = async (text) => { setIsProcessing(true); try { const response = await api.post('finance/voice/command/', { text }); console.log("Voice Response:", response.data); if (response.data.transaction) { if (onTransactionAdded) onTransactionAdded(); alert(`Added: ${response.data.transaction.title} - ${response.data.parsed.amount}`); } else { alert(`Heard: "${response.data.text}". Could not verify details.`); } } catch (err) { console.error("Voice Command Error:", err); setError(err.response?.data?.error || "Failed to process voice command"); } finally { setIsProcessing(false); } }; return (
{error && (
{error}
)} {/* Voice Tip */}
Add Expense/Income for Amount
); }; export default VoiceInput;