All files / src/utils/components ReadAloud.jsx

100% Statements 44/44
100% Branches 13/13
100% Functions 11/11
100% Lines 38/38

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116          5x 23x 23x 23x   23x   20x 20x 20x                             20x 20x 20x 20x   20x   20x 20x   20x       23x 18x   17x                       23x 20x   20x 2x 2x     18x 18x   13x 13x 13x 13x   13x     23x 42x 21x       23x 20x 20x     23x                                                      
/* eslint-disable no-irregular-whitespace */
// NOTE: this is a custom hook and not a component
import { useEffect, useRef, useState } from "react";
import { useSelector } from "react-redux";
 
const useTextToSpeech = () => {
    const { settings } = useSelector((state) => state.StudentQuestionPage);
    const utteranceRef = useRef(null);
    const [speaking, setSpeaking] = useState(false);
 
    useEffect(() => {
        // Initialize utterance
        const utterance = new SpeechSynthesisUtterance();
        utteranceRef.current = utterance;
        utterance.voice = getVoiceByName("Google US English");
 
        // var voices = speechSynthesis.getVoices();
        // console.log(voices);
        // voices.forEach(function (voice, index) {
        //     console.log(voice.name);
        //     // console.log("Voice " + index.toString());
        //     // console.log("Name: " + voice.name);
        //     // console.log("Lang: " + voice.lang);
        //     // console.log("Default: " + voice.default);
        //     // console.log("URI: " + voice.voiceURI);
        //     // console.log("--------------------------------------");
        // });
 
        // Event listener for speech start and end
        const onStart = () => setSpeaking(true);
        const onEnd = () => setSpeaking(false);
        utterance.addEventListener("start", onStart);
        utterance.addEventListener("end", onEnd);
 
        return () => {
            // Clean up: Remove event listeners
            utterance.removeEventListener("start", onStart);
            utterance.removeEventListener("end", onEnd);
            // Cancel any ongoing speech when component unmounts
            cancelSpeech();
        };
    }, []);
 
    const normalizeSpeechText = (value = "") => {
        if (typeof value !== "string") return "";
 
        return (
            value
                // remove HTML tags
                .replace(/<[^>]*>/g, "")
                // convert __blank__ placeholders to spoken word
                .replace(/__blank__/g, "blank")
                // normalize whitespace
                .replace(/\s+/g, " ")
                .trim()
        );
    };
 
    const speak = (text) => {
        cancelSpeech();
 
        if (typeof window === "undefined" || !window.speechSynthesis) {
            console.error("Speech synthesis not supported.");
            return;
        }
 
        const cleanText = normalizeSpeechText(text);
        if (!cleanText) return;
 
        const utterance = utteranceRef.current;
        utterance.text = cleanText;
        utterance.rate = settings.voice.speed;
        utterance.volume = settings.voice.volume;
 
        window.speechSynthesis.speak(utterance);
    };
 
    const cancelSpeech = () => {
        if (typeof window !== "undefined" && window.speechSynthesis) {
            window.speechSynthesis.cancel();
        }
    };
 
    const getVoiceByName = (voiceName) => {
        const voices = window.speechSynthesis.getVoices();
        return voices.find((voice) => voice.name === voiceName);
    };
 
    return { speak, cancelSpeech, speaking };
};
 
export default useTextToSpeech;
 
// Microsoft David - English (United States)
// Microsoft Mark - English (United States)
// Microsoft Zira - English (United States)
// Google Deutsch
// Google US English
// Google UK English Female
// Google UK English Male
// Google español
// Google español de Estados Unidos
// Google français
// Google हिन्दी
// Google Bahasa Indonesia
// Google italiano
// Google 日本語
// Google 한국의
// Google Nederlands
// Google polski
// Google português do Brasil
// Google русский
// Google 普通话(中国大陆)//space included
// Google 粤語(香港)//space included
// Google 國語(臺灣)//space included