fix(stt): graceful degradation for Expo Go
Handle missing native module @jamsch/expo-speech-recognition gracefully. In Expo Go the native module is not available, which was causing the entire _layout.tsx to fail to export, breaking tab navigation. - Use dynamic require() with try/catch instead of static import - Initialize ExpoSpeechRecognitionModule and useSpeechRecognitionEvent as no-ops - Check module availability before calling any native methods - isAvailable state properly reflects module presence Tab navigation now works in Expo Go (with STT disabled). Full STT functionality requires a development build.
This commit is contained in:
parent
76d93abf1e
commit
f2803ca5db
@ -4,6 +4,10 @@
|
||||
* Wraps @jamsch/expo-speech-recognition for easy use in components.
|
||||
* Provides start/stop controls, recognized text, and status states.
|
||||
*
|
||||
* NOTE: Gracefully handles missing native module (Expo Go)
|
||||
* - In Expo Go: isAvailable = false, all methods are no-ops
|
||||
* - In Dev Build: Full functionality
|
||||
*
|
||||
* Usage:
|
||||
* ```typescript
|
||||
* const { startListening, stopListening, isListening, recognizedText, error } = useSpeechRecognition();
|
||||
@ -19,12 +23,20 @@
|
||||
*/
|
||||
|
||||
import { useState, useCallback, useRef, useEffect } from 'react';
|
||||
import {
|
||||
ExpoSpeechRecognitionModule,
|
||||
useSpeechRecognitionEvent,
|
||||
} from '@jamsch/expo-speech-recognition';
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
// Try to import the native module - may fail in Expo Go
|
||||
let ExpoSpeechRecognitionModule: any = null;
|
||||
let useSpeechRecognitionEvent: any = () => {}; // no-op by default
|
||||
|
||||
try {
|
||||
const speechRecognition = require('@jamsch/expo-speech-recognition');
|
||||
ExpoSpeechRecognitionModule = speechRecognition.ExpoSpeechRecognitionModule;
|
||||
useSpeechRecognitionEvent = speechRecognition.useSpeechRecognitionEvent;
|
||||
} catch (e) {
|
||||
console.warn('[SpeechRecognition] Native module not available (Expo Go?). Speech recognition disabled.');
|
||||
}
|
||||
|
||||
export interface UseSpeechRecognitionOptions {
|
||||
/** Language for recognition (default: 'en-US') */
|
||||
lang?: string;
|
||||
@ -83,7 +95,7 @@ export function useSpeechRecognition(
|
||||
} = options;
|
||||
|
||||
const [isListening, setIsListening] = useState(false);
|
||||
const [isAvailable, setIsAvailable] = useState(true);
|
||||
const [isAvailable, setIsAvailable] = useState(!!ExpoSpeechRecognitionModule);
|
||||
const [recognizedText, setRecognizedText] = useState('');
|
||||
const [partialTranscript, setPartialTranscript] = useState('');
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@ -95,6 +107,11 @@ export function useSpeechRecognition(
|
||||
|
||||
// Check availability on mount
|
||||
useEffect(() => {
|
||||
if (!ExpoSpeechRecognitionModule) {
|
||||
setIsAvailable(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const checkAvailability = async () => {
|
||||
try {
|
||||
// Check if we can get permissions (indirect availability check)
|
||||
@ -131,7 +148,7 @@ export function useSpeechRecognition(
|
||||
});
|
||||
|
||||
// Event: Result available
|
||||
useSpeechRecognitionEvent('result', (event) => {
|
||||
useSpeechRecognitionEvent('result', (event: any) => {
|
||||
const results = event.results;
|
||||
if (results && results.length > 0) {
|
||||
const result = results[results.length - 1];
|
||||
@ -159,7 +176,7 @@ export function useSpeechRecognition(
|
||||
});
|
||||
|
||||
// Event: Error occurred
|
||||
useSpeechRecognitionEvent('error', (event) => {
|
||||
useSpeechRecognitionEvent('error', (event: any) => {
|
||||
const errorMessage = event.message || event.error || 'Speech recognition error';
|
||||
console.error('[SpeechRecognition] Error:', errorMessage);
|
||||
|
||||
@ -178,6 +195,11 @@ export function useSpeechRecognition(
|
||||
* @returns true if started successfully, false otherwise
|
||||
*/
|
||||
const startListening = useCallback(async (): Promise<boolean> => {
|
||||
if (!ExpoSpeechRecognitionModule) {
|
||||
console.warn('[SpeechRecognition] Cannot start - native module not available');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isListening || isStartingRef.current) {
|
||||
console.log('[SpeechRecognition] Already listening or starting');
|
||||
return false;
|
||||
@ -239,6 +261,8 @@ export function useSpeechRecognition(
|
||||
* Stop listening and process final result
|
||||
*/
|
||||
const stopListening = useCallback(() => {
|
||||
if (!ExpoSpeechRecognitionModule) return;
|
||||
|
||||
if (!isListening && !isStartingRef.current) {
|
||||
console.log('[SpeechRecognition] Not listening, nothing to stop');
|
||||
return;
|
||||
@ -256,6 +280,8 @@ export function useSpeechRecognition(
|
||||
* Abort listening without processing
|
||||
*/
|
||||
const abortListening = useCallback(() => {
|
||||
if (!ExpoSpeechRecognitionModule) return;
|
||||
|
||||
if (!isListening && !isStartingRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user