fix(useTextToSpeechBrowser): handle undefined window.speechSynthesis on some android devices

This commit is contained in:
Danny Avila 2024-08-16 02:49:59 -04:00
parent dba704079c
commit 16e1f74a6c
No known key found for this signature in database
GPG key ID: 2DD9CC89B9B50364

View file

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