Android_Retail/src/Pages/VoiceToText/VoiceToTextItemCard.jsx

275 lines
8.5 KiB
React
Raw Normal View History

2026-01-27 18:27:29 +05:30
import React, { useState, useEffect, useRef, forwardRef, useImperativeHandle } from "react";
import { FaMicrophone, FaMicrophoneSlash } from "react-icons/fa";
import './VoiceToText.scss'
2026-03-10 14:06:32 +05:30
import { Capacitor } from "@capacitor/core";
import { SpeechRecognition } from "@capacitor-community/speech-recognition";
2026-01-27 18:27:29 +05:30
const VoiceToTextItemCard = forwardRef(({ setVoiceData, setprodQty, uomNames = [] }, ref) => {
const [formData, setFormData] = useState({ product: "", qty: "", uom: "" });
const [transcript, setTranscript] = useState("");
const [isListening, setIsListening] = useState(false);
const recognitionRef = useRef(null);
const numberWords = {
one: "1", two: "2", three: "3", four: "4", five: "5",
six: "6", seven: "7", eight: "8", nine: "9", ten: "10"
};
const listening = () => {
console.log('Voice listening started');
// your voice start logic here
if (isListening) {
stopListening();
} else {
startListening();
}
};
useImperativeHandle(ref, () => ({
listening,
}));
const extractEntities = (text) => {
if (typeof text !== "string") return { product: "", qty: "", uom: "" };
const lowerUomNames = (uomNames?.length ? uomNames : [
"kg", "kgs", "gram", "grams", "litre", "litres", "pack", "packs", "piece", "pieces"
]).map(u => u?.toLowerCase());
const words = text?.toLowerCase().split(/\s+/);
const numberWords = {
one: "1", two: "2", three: "3", four: "4", five: "5",
six: "6", seven: "7", eight: "8", nine: "9", ten: "10"
};
let qty = "";
let uom = "";
const productWords = [];
for (let i = 0; i < words.length; i++) {
const word = words[i];
const numVal = numberWords[word] || (isNaN(word) ? null : word);
if (lowerUomNames.includes(word)) {
uom = word;
} else if (numVal) {
qty = numVal;
} else {
productWords.push(word);
}
}
const cleanProduct = productWords
.filter(w => w !== qty && w !== uom)
.join(" ")
.trim();
return {
product: cleanProduct,
qty,
uom
};
};
useEffect(() => {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) {
alert("Speech Recognition not supported");
return;
}
const recognition = new SpeechRecognition();
recognition.lang = "en-US";
recognition.continuous = false;
recognition.onresult = (event) => {
const spokenText = event.results[0][0].transcript;
setTranscript(spokenText);
const { product, qty, uom } = extractEntities(spokenText);
setFormData({ product, qty, uom });
// Send to parent component
if (setVoiceData) setVoiceData({ product, qty, uom });
if (setprodQty) setprodQty(qty); // optional
};
recognition.onerror = (err) => console.error("Speech recognition error:", err);
recognition.onend = () => {
setIsListening(false);
};
recognitionRef.current = recognition;
}, [setVoiceData, setprodQty, uomNames]);
// const startListening = () => {
// recognitionRef.current?.start();
// setIsListening(true);
// setTranscript("");
// setVoiceData && setVoiceData("");
// };
2026-03-10 14:06:32 +05:30
//sree coment line
// const startListening = async () => {
// try {
// // Step 1: Check microphone access
// const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
// stream.getTracks().forEach(track => track.stop()); // stop test mic stream
// // Step 2: Try starting speech recognition
// try {
// recognitionRef.current?.start();
// setIsListening(true);
// setTranscript("");
// if (setVoiceData) setVoiceData("");
// } catch (err) {
// console.error("SpeechRecognition start error:", err);
// alert("Unable to start speech recognition. Please check mic access or try again.");
// setIsListening(false);
// }
// } catch (error) {
// console.error("Microphone access denied or not available", error);
// alert("Microphone not connected or permission denied.");
// setIsListening(false);
// }
// };
2026-01-27 18:27:29 +05:30
const startListening = async () => {
2026-03-10 14:06:32 +05:30
// Detect platform
const platform = Capacitor.getPlatform();
if (platform === "web") {
2026-01-27 18:27:29 +05:30
try {
2026-03-10 14:06:32 +05:30
// Check microphone permission
2026-01-27 18:27:29 +05:30
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
2026-03-10 14:06:32 +05:30
stream.getTracks().forEach(track => track.stop());
2026-01-27 18:27:29 +05:30
recognitionRef.current?.start();
setIsListening(true);
setTranscript("");
2026-03-10 14:06:32 +05:30
2026-01-27 18:27:29 +05:30
if (setVoiceData) setVoiceData("");
2026-03-10 14:06:32 +05:30
} catch (error) {
console.error("Microphone access denied or not available", error);
alert("Microphone not connected or permission denied.");
2026-01-27 18:27:29 +05:30
setIsListening(false);
}
2026-03-10 14:06:32 +05:30
}
else {
// 📱 Capacitor Android / iOS
try {
const permission = await SpeechRecognition.requestPermissions();
if (!permission.speechRecognition) {
alert("Speech recognition permission denied");
return;
}
setIsListening(true);
const result = await SpeechRecognition.start({
language: "en-US",
maxResults: 1,
partialResults: false,
});
if (result.matches && result.matches.length > 0) {
const spokenText = result.matches[0];
setTranscript(spokenText);
const { product, qty, uom } = extractEntities(spokenText);
setFormData({ product, qty, uom });
if (setVoiceData) setVoiceData({ product, qty, uom });
if (setprodQty) setprodQty(qty);
}
2026-01-27 18:27:29 +05:30
2026-03-10 14:06:32 +05:30
setIsListening(false);
} catch (err) {
console.error(err);
2026-01-27 18:27:29 +05:30
setIsListening(false);
}
2026-03-10 14:06:32 +05:30
}
2026-01-27 18:27:29 +05:30
};
2026-03-10 14:06:32 +05:30
// const stopListening = () => {
// if (recognitionRef.current) {
// console.log("Stopping voice recognition...");
// recognitionRef.current.stop();
// } else {
// console.warn("Recognition not initialized");
// }
// setIsListening(false);
// };
const stopListening = async () => {
const platform = Capacitor.getPlatform();
try {
if (platform === "web") {
2026-01-27 18:27:29 +05:30
if (recognitionRef.current) {
recognitionRef.current.stop();
2026-03-10 14:06:32 +05:30
console.log("Stopping voice recognition...");
}
2026-01-27 18:27:29 +05:30
} else {
2026-03-10 14:06:32 +05:30
await SpeechRecognition.stop();
console.log("Stopping native speech recognition...");
}
} catch (error) {
console.error("Error stopping speech recognition:", error);
2026-01-27 18:27:29 +05:30
}
setIsListening(false);
};
return (
<div style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
<button>
{isListening ? <FaMicrophone /> : <FaMicrophoneSlash />}
</button>
<div style={{ display: 'flex', flexDirection: 'column-reverse', alignItems: 'center', gap: '5px' }}>
{isListening && (
<div style={{ display: "flex", gap: "5px" }}>
<div style={dotStyle(0)}></div>
<div style={dotStyle(0.2)}></div>
<div style={dotStyle(0.4)}></div>
</div>
)}
{!isListening && <div>Voice</div>}
</div>
{/* <div style={{ marginTop: "20px" }}>
<strong>Transcript:</strong> {transcript}
<br />
<strong>Product:</strong> {formData.product} <br />
<strong>Qty:</strong> {formData.qty} <br />
<strong>UOM:</strong> {formData.uom}
</div> */}
</div>
);
});
export default VoiceToTextItemCard;
// Style for dots (voice loader)
const dotStyle = (delay = 0) => ({
width: "8px",
height: "8px",
borderRadius: "50%",
background: "#ff1744",
animation: "pulse-color 1s infinite ease-in-out",
animationDelay: `${delay}s`,
});