diff --git a/client/src/hooks/Input/useTextToSpeechBrowser.ts b/client/src/hooks/Input/useTextToSpeechBrowser.ts index 85c3a80c00..8fb0eecd0b 100644 --- a/client/src/hooks/Input/useTextToSpeechBrowser.ts +++ b/client/src/hooks/Input/useTextToSpeechBrowser.ts @@ -1,4 +1,4 @@ -import { useRecoilState } from 'recoil'; +import { useRecoilValue } from 'recoil'; import { useState, useEffect, useCallback } from 'react'; import type { VoiceOption } from '~/common'; import store from '~/store'; @@ -8,13 +8,20 @@ function useTextToSpeechBrowser({ }: { setIsSpeaking: React.Dispatch>; }) { - const [cloudBrowserVoices] = useRecoilState(store.cloudBrowserVoices); - const [voiceName] = useRecoilState(store.voice); + const voiceName = useRecoilValue(store.voice); const [voices, setVoices] = useState([]); + const cloudBrowserVoices = useRecoilValue(store.cloudBrowserVoices); + const [isSpeechSynthesisSupported, setIsSpeechSynthesisSupported] = useState(true); const updateVoices = useCallback(() => { + const synth = window.speechSynthesis as SpeechSynthesis | undefined; + if (!synth) { + setIsSpeechSynthesisSupported(false); + return; + } + try { - const availableVoices = window.speechSynthesis.getVoices(); + const availableVoices = synth.getVoices(); if (!Array.isArray(availableVoices)) { console.error('getVoices() did not return an array'); return; @@ -31,11 +38,16 @@ function useTextToSpeechBrowser({ setVoices(voiceOptions); } catch (error) { console.error('Error updating voices:', error); + setIsSpeechSynthesisSupported(false); } }, [cloudBrowserVoices]); useEffect(() => { - const synth = window.speechSynthesis; + const synth = window.speechSynthesis as SpeechSynthesis | undefined; + if (!synth) { + setIsSpeechSynthesisSupported(false); + return; + } try { if (synth.getVoices().length) { @@ -45,14 +57,22 @@ function useTextToSpeechBrowser({ } } catch (error) { console.error('Error in useEffect:', error); + setIsSpeechSynthesisSupported(false); } return () => { - synth.onvoiceschanged = null; + if (synth.onvoiceschanged) { + synth.onvoiceschanged = null; + } }; }, [updateVoices]); const generateSpeechLocal = (text: string) => { + if (!isSpeechSynthesisSupported) { + console.warn('Speech synthesis is not supported'); + return; + } + const synth = window.speechSynthesis; const voice = voices.find((v) => v.value === voiceName); @@ -81,6 +101,10 @@ function useTextToSpeechBrowser({ }; const cancelSpeechLocal = () => { + if (!isSpeechSynthesisSupported) { + return; + } + try { window.speechSynthesis.cancel(); } catch (error) { @@ -90,7 +114,7 @@ function useTextToSpeechBrowser({ } }; - return { generateSpeechLocal, cancelSpeechLocal, voices }; + return { generateSpeechLocal, cancelSpeechLocal, voices, isSpeechSynthesisSupported }; } export default useTextToSpeechBrowser;