react-native-srschat 0.1.59 → 0.1.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,9 @@ import useAsyncStorage from '../hooks/useAsyncStorage';
7
7
  import { startRecording, stopRecording, cancelRecording, requestAudioPermission, cleanup, initVoice, resetStoredPermission, setPermissionStatusHandlers } from '../utils/audioRecorder';
8
8
  import { AppContext } from '../contexts/AppContext';
9
9
  const PERMISSION_STORAGE_KEY = '@voice_permission_status';
10
- export const VoiceButton = () => {
10
+ export const VoiceButton = ({
11
+ setInput
12
+ }) => {
11
13
  const {
12
14
  handleVoiceSend
13
15
  } = useContext(AppContext);
@@ -35,23 +37,37 @@ export const VoiceButton = () => {
35
37
  const permissionResult = await requestAudioPermission();
36
38
  setHasPermission(permissionResult);
37
39
  if (permissionResult) {
38
- const initialized = await initVoice((result, error) => {
40
+ const initialized = await initVoice(
41
+ // Final result callback
42
+ (result, error) => {
43
+ console.log('Voice final result:', result, 'Error:', error);
44
+
45
+ // Always reset states when the recognition ends
46
+ setIsListening(false);
47
+ setLoading(false);
39
48
  if (error) {
40
49
  // Don't show alert for permission errors since we handle that elsewhere
41
50
  if (!error.includes('permission')) {
42
51
  Alert.alert('Error', error);
43
52
  }
44
- setIsListening(false);
45
- setLoading(false);
46
53
  return;
47
54
  }
48
55
  if (result) {
49
- handleVoiceSend(null, result);
56
+ if (setInput) {
57
+ // For live transcription mode: just update input, don't auto-send
58
+ setInput(result);
59
+ } else {
60
+ // For original mode: send the message automatically
61
+ handleVoiceSend(null, result);
62
+ }
50
63
  }
51
- // Always reset states when the recognition ends
52
- setIsListening(false);
53
- setLoading(false);
54
- });
64
+ },
65
+ // Partial result callback for live transcription
66
+ setInput ? partialResult => {
67
+ if (partialResult) {
68
+ setInput(partialResult);
69
+ }
70
+ } : null);
55
71
  if (!initialized) {
56
72
  // Only show this alert once per session
57
73
  if (!permissionChecked) {
@@ -69,7 +85,8 @@ export const VoiceButton = () => {
69
85
  setupVoice();
70
86
  }
71
87
  return () => {
72
- cleanup();
88
+ // Optional: only if the entire feature is being removed from the app
89
+ // cleanup();
73
90
  };
74
91
  }, [permissionStatus, permissionChecked]);
75
92
  const toggleRecording = async () => {
@@ -116,7 +133,7 @@ export const VoiceButton = () => {
116
133
  console.error('Error in toggleRecording:', error);
117
134
  Alert.alert('Error', 'An error occurred while managing voice recognition');
118
135
  setIsListening(false);
119
- await cleanup();
136
+ // Don't call cleanup here - let the natural session cleanup handle it
120
137
  } finally {
121
138
  setLoading(false);
122
139
  }
@@ -1 +1 @@
1
- {"version":3,"names":["React","useState","useContext","useEffect","TouchableOpacity","ActivityIndicator","StyleSheet","Alert","Linking","Platform","Ionicons","useAsyncStorage","startRecording","stopRecording","cancelRecording","requestAudioPermission","cleanup","initVoice","resetStoredPermission","setPermissionStatusHandlers","AppContext","PERMISSION_STORAGE_KEY","VoiceButton","handleVoiceSend","isListening","setIsListening","loading","setLoading","permissionChecked","setPermissionChecked","hasPermission","setHasPermission","permissionStatus","setPermissionStatus","setupVoice","permissionResult","initialized","result","error","includes","alert","console","toggleRecording","text","style","onPress","openAppSettings","checkPermission","started","OS","openURL","openSettings","createElement","styles","button","disabled","size","color","name","create","justifyContent","alignItems"],"sourceRoot":"../../../src","sources":["components/voice.js"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["React","useState","useContext","useEffect","TouchableOpacity","ActivityIndicator","StyleSheet","Alert","Linking","Platform","Ionicons","useAsyncStorage","startRecording","stopRecording","cancelRecording","requestAudioPermission","cleanup","initVoice","resetStoredPermission","setPermissionStatusHandlers","AppContext","PERMISSION_STORAGE_KEY","VoiceButton","setInput","handleVoiceSend","isListening","setIsListening","loading","setLoading","permissionChecked","setPermissionChecked","hasPermission","setHasPermission","permissionStatus","setPermissionStatus","setupVoice","permissionResult","initialized","result","error","console","log","includes","alert","partialResult","toggleRecording","text","style","onPress","openAppSettings","checkPermission","started","OS","openURL","openSettings","createElement","styles","button","disabled","size","color","name","create","justifyContent","alignItems"],"sourceRoot":"../../../src","sources":["components/voice.js"],"mappings":"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","ignoreList":[]}
@@ -16,6 +16,7 @@ export const WelcomeInput = ({
16
16
  showModal,
17
17
  theme
18
18
  } = useContext(AppContext);
19
+ const inputRef = useRef(null);
19
20
  const handleKeyPress = ({
20
21
  nativeEvent
21
22
  }) => {
@@ -33,6 +34,7 @@ export const WelcomeInput = ({
33
34
  return /*#__PURE__*/React.createElement(View, {
34
35
  style: styles.inputContainer
35
36
  }, /*#__PURE__*/React.createElement(TextInput, {
37
+ ref: inputRef,
36
38
  style: styles.input,
37
39
  value: input,
38
40
  onChangeText: setInput,
@@ -42,9 +44,34 @@ export const WelcomeInput = ({
42
44
  returnKeyType: "send",
43
45
  enablesReturnKeyAutomatically: true,
44
46
  onKeyPress: handleKeyPress,
45
- onSubmitEditing: onSubmitEditing
47
+ onSubmitEditing: onSubmitEditing,
48
+ selection: undefined
46
49
  // blurOnSubmit={false}
47
- }), /*#__PURE__*/React.createElement(VoiceButton, null), /*#__PURE__*/React.createElement(TouchableOpacity, {
50
+ }), /*#__PURE__*/React.createElement(VoiceButton, {
51
+ setInput: text => {
52
+ setInput(text);
53
+ // Auto-scroll to end
54
+ if (inputRef.current) {
55
+ // For iOS and Android, blur and refocus to ensure scroll
56
+ inputRef.current.blur();
57
+ setTimeout(() => {
58
+ if (inputRef.current) {
59
+ inputRef.current.focus();
60
+ // Set selection to end - Android sometimes needs a slight delay
61
+ if (Platform.OS === 'android') {
62
+ setTimeout(() => {
63
+ if (inputRef.current) {
64
+ inputRef.current.setSelection(text.length, text.length);
65
+ }
66
+ }, 10);
67
+ } else {
68
+ inputRef.current.setSelection(text.length, text.length);
69
+ }
70
+ }
71
+ }, 50);
72
+ }
73
+ }
74
+ }), /*#__PURE__*/React.createElement(TouchableOpacity, {
48
75
  style: styles.sendButton,
49
76
  onPress: () => handleSend(input),
50
77
  disabled: !input.trim()
@@ -1 +1 @@
1
- {"version":3,"names":["React","useState","useEffect","useContext","useRef","Text","StyleSheet","View","TextInput","TouchableOpacity","Platform","KeyboardAvoidingView","Keyboard","Header","AppContext","Ionicons","VoiceButton","WelcomeInput","onProductCardClick","onAddToCartClick","data","handleSend","input","setInput","showModal","theme","handleKeyPress","nativeEvent","key","shiftKey","preventDefault","onSubmitEditing","trim","createElement","style","styles","inputContainer","value","onChangeText","placeholder","placeholderTextColor","multiline","returnKeyType","enablesReturnKeyAutomatically","onKeyPress","sendButton","onPress","disabled","name","size","color","primaryColor","create","flexDirection","alignItems","paddingHorizontal","paddingVertical","backgroundColor","borderRadius","shadowColor","shadowOffset","width","height","shadowOpacity","shadowRadius","elevation","flex","fontSize","inputButton","padding","marginLeft","disabledButton","opacity"],"sourceRoot":"../../../src","sources":["components/welcomeInput.js"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["React","useState","useEffect","useContext","useRef","Text","StyleSheet","View","TextInput","TouchableOpacity","Platform","KeyboardAvoidingView","Keyboard","Header","AppContext","Ionicons","VoiceButton","WelcomeInput","onProductCardClick","onAddToCartClick","data","handleSend","input","setInput","showModal","theme","inputRef","handleKeyPress","nativeEvent","key","shiftKey","preventDefault","onSubmitEditing","trim","createElement","style","styles","inputContainer","ref","value","onChangeText","placeholder","placeholderTextColor","multiline","returnKeyType","enablesReturnKeyAutomatically","onKeyPress","selection","undefined","text","current","blur","setTimeout","focus","OS","setSelection","length","sendButton","onPress","disabled","name","size","color","primaryColor","create","flexDirection","alignItems","paddingHorizontal","paddingVertical","backgroundColor","borderRadius","shadowColor","shadowOffset","width","height","shadowOpacity","shadowRadius","elevation","flex","fontSize","inputButton","padding","marginLeft","disabledButton","opacity"],"sourceRoot":"../../../src","sources":["components/welcomeInput.js"],"mappings":"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","ignoreList":[]}
@@ -3,12 +3,18 @@
3
3
  import { Platform } from 'react-native';
4
4
  import Voice from '@react-native-voice/voice';
5
5
  import { check, PERMISSIONS, request, RESULTS } from 'react-native-permissions';
6
- import useAsyncStorage from '../hooks/useAsyncStorage';
7
6
  let resultCallback = null;
7
+ let partialResultCallback = null;
8
8
  let silenceTimer = null;
9
- let isCurrentlyRecording = false;
10
9
  let finalResult = '';
11
- const SILENCE_DURATION = 1500; // 1.5 seconds of silence before stopping
10
+ const SILENCE_DURATION = 1500;
11
+ const State = {
12
+ IDLE: 'IDLE',
13
+ LISTENING: 'LISTENING',
14
+ FINALIZING: 'FINALIZING'
15
+ };
16
+ let state = State.IDLE;
17
+ let listenersBound = false;
12
18
 
13
19
  // Add this constant for AsyncStorage key
14
20
  const PERMISSION_STORAGE_KEY = '@voice_permission_status';
@@ -22,11 +28,13 @@ export function setPermissionStatusHandlers(getter, setter) {
22
28
  permissionStatusSetter = setter;
23
29
  }
24
30
 
25
- // Initialize Voice handlers
26
- export async function initVoice(onResult) {
31
+ // Initialize Voice handlers - modified to support live transcription
32
+ export async function initVoice(onResult, onPartialResult = null) {
27
33
  try {
28
34
  resultCallback = onResult;
35
+ partialResultCallback = onPartialResult; // Store partial callback
29
36
  finalResult = '';
37
+ if (listenersBound) return true;
30
38
 
31
39
  // Check if Voice module is available
32
40
  if (!Voice) {
@@ -40,80 +48,103 @@ export async function initVoice(onResult) {
40
48
  console.error('Speech recognition is not available on this device');
41
49
  return false;
42
50
  }
51
+ Voice.removeAllListeners();
43
52
 
44
53
  // Set up all event listeners
45
- Voice.onSpeechStart = e => {
46
- console.log('onSpeechStart: ', e);
47
- isCurrentlyRecording = true;
54
+ Voice.onSpeechStart = () => {
55
+ console.log('[onSpeechStart] Setting state to LISTENING');
56
+ state = State.LISTENING;
48
57
  finalResult = '';
49
- if (silenceTimer) {
50
- clearTimeout(silenceTimer);
51
- silenceTimer = null;
52
- }
53
- };
54
- Voice.onSpeechRecognized = e => {
55
- console.log('onSpeechRecognized: ', e);
56
- if (e.isFinal) {
57
- console.log('Speech recognition final');
58
- handleFinalResult();
59
- }
58
+ clearSilenceTimer();
60
59
  };
61
- Voice.onSpeechEnd = async e => {
62
- console.log('onSpeechEnd: ', e);
63
- if (silenceTimer) {
64
- clearTimeout(silenceTimer);
65
- silenceTimer = null;
66
- }
67
60
 
68
- // Only handle final result if we're still recording
69
- if (isCurrentlyRecording) {
70
- await handleFinalResult();
61
+ // Optional: ignore onSpeechRecognized or just log
62
+ Voice.onSpeechRecognized = () => {};
63
+ Voice.onSpeechEnd = () => {
64
+ console.log('[onSpeechEnd] Speech ended, current state:', state);
65
+ clearSilenceTimer();
66
+ // Always reset to IDLE when speech ends (sessions should be considered over)
67
+ console.log('[onSpeechEnd] Scheduling IDLE reset');
68
+ if (Platform.OS === 'android') {
69
+ setTimeout(() => {
70
+ console.log('[onSpeechEnd] Android timeout - setting state to IDLE');
71
+ state = State.IDLE;
72
+ }, 800); // Increased delay
73
+ } else {
74
+ console.log('[onSpeechEnd] iOS - setting state to IDLE immediately');
75
+ state = State.IDLE;
71
76
  }
72
77
  };
73
- Voice.onSpeechError = async e => {
78
+ Voice.onSpeechError = e => {
74
79
  var _e$error, _e$error2;
75
- // console.error('onSpeechError: ', e);
80
+ console.log('[onSpeechError] Error occurred, current state:', state, 'error:', e);
81
+ clearSilenceTimer();
82
+ const code = (_e$error = e.error) === null || _e$error === void 0 || (_e$error = _e$error.code) === null || _e$error === void 0 ? void 0 : _e$error.toString();
83
+ const msg = ((_e$error2 = e.error) === null || _e$error2 === void 0 ? void 0 : _e$error2.message) || '';
76
84
 
77
- if (silenceTimer) {
78
- clearTimeout(silenceTimer);
79
- silenceTimer = null;
85
+ // Handle callback first
86
+ if (Platform.OS === 'android' && (code === '7' || code === '5')) {
87
+ if (finalResult && resultCallback) resultCallback(finalResult, null);else if (resultCallback) resultCallback(null, null);
88
+ } else if (!msg.includes('No speech detected') && resultCallback) {
89
+ resultCallback(null, msg);
90
+ } else if (resultCallback) {
91
+ resultCallback(null, null);
80
92
  }
81
93
 
82
- // Check for "No speech detected" error
83
- const isNoSpeechError = ((_e$error = e.error) === null || _e$error === void 0 ? void 0 : _e$error.code) === "recognition_fail" && ((_e$error2 = e.error) === null || _e$error2 === void 0 || (_e$error2 = _e$error2.message) === null || _e$error2 === void 0 ? void 0 : _e$error2.includes("No speech detected"));
84
- await cleanupVoiceSession();
85
-
86
- // Only send error to callback if it's not a "No speech detected" error
87
- // if (!isNoSpeechError) {
88
- // resultCallback(null, e.error?.message || 'Speech recognition error');
89
- // } else {
90
- // console.log('No speech detected, ignoring error');
91
- // // Optionally, call the callback with null parameters or a special indicator
92
- // resultCallback(null, null); // This won't trigger an error alert in the component
93
- // }
94
+ // Errors end the session immediately, reset to IDLE with delay
95
+ console.log('[onSpeechError] Scheduling IDLE reset');
96
+ if (Platform.OS === 'android') {
97
+ setTimeout(() => {
98
+ console.log('[onSpeechError] Android timeout - setting state to IDLE');
99
+ state = State.IDLE;
100
+ }, 800); // Increased delay to match onSpeechEnd
101
+ } else {
102
+ console.log('[onSpeechError] iOS - setting state to IDLE immediately');
103
+ state = State.IDLE;
104
+ }
94
105
  };
95
106
  Voice.onSpeechResults = e => {
96
- console.log('onSpeechResults: ', e);
107
+ console.log('[onSpeechResults] Results received, current state:', state, 'results:', e);
108
+ clearSilenceTimer();
97
109
  if (e.value && e.value.length > 0) {
98
110
  finalResult = e.value[0];
99
- handleSilenceDetection();
111
+ }
112
+
113
+ // Only call callback if we haven't already (avoid double-calling)
114
+ if (state === State.LISTENING && resultCallback) {
115
+ console.log('[onSpeechResults] Calling callback with results');
116
+ resultCallback(finalResult, null);
117
+ } else {
118
+ console.log('[onSpeechResults] Not calling callback - state:', state);
119
+ }
120
+
121
+ // On Android, we must explicitly stop to avoid session corruption
122
+ if (Platform.OS === 'android') {
123
+ console.log('[onSpeechResults] Android: Explicitly calling stopRecording()');
124
+ stopRecording();
125
+ }
126
+
127
+ // Results end the session, reset to IDLE with delay
128
+ console.log('[onSpeechResults] Scheduling IDLE reset');
129
+ if (Platform.OS === 'android') {
130
+ setTimeout(() => {
131
+ console.log('[onSpeechResults] Android timeout - setting state to IDLE');
132
+ state = State.IDLE;
133
+ }, 800); // Increased delay
134
+ } else {
135
+ console.log('[onSpeechResults] iOS - setting state to IDLE immediately');
136
+ state = State.IDLE;
100
137
  }
101
138
  };
102
139
  Voice.onSpeechPartialResults = e => {
103
- console.log('onSpeechPartialResults: ', e);
104
- if (silenceTimer) {
105
- clearTimeout(silenceTimer);
106
- }
107
140
  if (e.value && e.value.length > 0) {
108
141
  finalResult = e.value[0];
142
+ if (partialResultCallback) partialResultCallback(finalResult);
109
143
  handleSilenceDetection();
110
144
  }
111
145
  };
112
- if (Platform.OS === 'android') {
113
- Voice.onSpeechVolumeChanged = e => {
114
- console.log('onSpeechVolumeChanged: ', e);
115
- };
116
- }
146
+ if (Platform.OS === 'android') Voice.onSpeechVolumeChanged = () => {};
147
+ listenersBound = true;
117
148
  return true;
118
149
  } catch (error) {
119
150
  console.error('Error initializing Voice:', error);
@@ -125,138 +156,144 @@ const handleSilenceDetection = () => {
125
156
  clearTimeout(silenceTimer);
126
157
  }
127
158
  silenceTimer = setTimeout(async () => {
128
- if (isCurrentlyRecording) {
159
+ if (state === State.LISTENING) {
129
160
  await handleFinalResult();
130
161
  }
131
162
  }, SILENCE_DURATION);
132
163
  };
133
164
  const handleFinalResult = async () => {
134
- if (!isCurrentlyRecording) return;
135
- if (finalResult) {
136
- resultCallback(finalResult);
165
+ console.log('[handleFinalResult] Called, current state:', state);
166
+ if (state !== State.LISTENING) {
167
+ console.log('[handleFinalResult] State not LISTENING, returning');
168
+ return;
137
169
  }
138
170
 
139
- // Stop recording first
171
+ // Set to FINALIZING first to prevent double callbacks
172
+ console.log('[handleFinalResult] Setting state to FINALIZING');
173
+ state = State.FINALIZING;
174
+
175
+ // Call the callback with results
176
+ if (finalResult && resultCallback) {
177
+ console.log('[handleFinalResult] Calling callback with result:', finalResult);
178
+ resultCallback(finalResult, null);
179
+ }
180
+
181
+ // Now stop recording (this will call Voice.stop())
182
+ console.log('[handleFinalResult] Calling stopRecording');
140
183
  await stopRecording();
184
+ };
185
+ const cleanupVoiceSession = () => {
186
+ console.log('[cleanupVoiceSession] Called, current state:', state);
187
+ finalResult = '';
188
+ clearSilenceTimer();
141
189
 
142
- // Then clean up the session
143
- await cleanupVoiceSession();
190
+ // Add delay before allowing next session on Android
191
+ if (Platform.OS === 'android') {
192
+ setTimeout(() => {
193
+ console.log('[cleanupVoiceSession] Android timeout - setting state to IDLE');
194
+ state = State.IDLE;
195
+ }, 800);
196
+ } else {
197
+ console.log('[cleanupVoiceSession] iOS - setting state to IDLE immediately');
198
+ state = State.IDLE;
199
+ }
144
200
  };
145
- const cleanupVoiceSession = async () => {
146
- isCurrentlyRecording = false;
201
+ const clearSilenceTimer = () => {
147
202
  if (silenceTimer) {
148
203
  clearTimeout(silenceTimer);
149
204
  silenceTimer = null;
150
205
  }
206
+ };
207
+ export async function startRecording() {
151
208
  try {
152
- // Check if Voice module is available
153
- if (!Voice) {
154
- console.log('Voice module not available during cleanup');
155
- return;
156
- }
209
+ console.log('[startRecording] Called, current state:', state);
157
210
 
158
- // First try to stop if still recognizing
159
- const isRecognizing = await Voice.isRecognizing();
160
- if (isRecognizing) {
211
+ // On Android, destroy any lingering instance before starting
212
+ if (Platform.OS === 'android') {
161
213
  try {
162
- await Voice.stop();
163
- await new Promise(resolve => setTimeout(resolve, 100));
164
- } catch (e) {
165
- console.error('Error stopping in cleanup:', e);
166
- }
167
- }
168
-
169
- // Then force destroy
170
- await Voice.destroy();
171
- await new Promise(resolve => setTimeout(resolve, 300));
172
-
173
- // Double check and force destroy again if needed
174
- const stillRecognizing = await Voice.isRecognizing();
175
- if (stillRecognizing) {
176
- await Voice.destroy();
177
- await new Promise(resolve => setTimeout(resolve, 300));
178
- }
179
- } catch (error) {
180
- console.error('Error in cleanupVoiceSession:', error);
181
- // Final attempt to destroy on error
182
- try {
183
- if (Voice) {
214
+ console.log('[startRecording] Android: Proactively destroying Voice instance');
184
215
  await Voice.destroy();
216
+ await new Promise(r => setTimeout(r, 100)); // Short delay for destroy to complete
217
+ } catch (e) {
218
+ console.log('[startRecording] Proactive destroy failed, may be okay:', e);
185
219
  }
186
- } catch (e) {
187
- console.error('Final destroy attempt failed:', e);
188
220
  }
189
- }
190
- finalResult = '';
191
- };
192
- export async function startRecording() {
193
- try {
194
- // Check if Voice module is available
195
221
  if (!Voice) {
196
- console.error('Voice module is not available');
222
+ console.log('[startRecording] Voice not available');
223
+ return false;
224
+ }
225
+ if (state !== State.IDLE) {
226
+ console.log('[startRecording] State not IDLE, returning false');
197
227
  return false;
198
228
  }
199
-
200
- // Ensure cleanup of any existing session
201
- await cleanupVoiceSession();
202
229
  const hasPermission = await requestAudioPermission();
203
230
  if (!hasPermission) {
204
231
  console.error('No permission to record audio');
205
232
  return false;
206
233
  }
234
+ const recognizing = await Voice.isRecognizing();
235
+ console.log('[startRecording] Voice.isRecognizing():', recognizing);
236
+ if (recognizing) {
237
+ console.log('[startRecording] Already recognizing, canceling first');
238
+ await Voice.cancel();
239
+ // Wait longer for cancel to take effect
240
+ await new Promise(r => setTimeout(r, 500));
241
+
242
+ // Double-check if still recognizing after cancel
243
+ const stillRecognizing = await Voice.isRecognizing();
244
+ console.log('[startRecording] After cancel, still recognizing:', stillRecognizing);
245
+ if (stillRecognizing) {
246
+ console.log('[startRecording] Still recognizing after cancel, stopping');
247
+ try {
248
+ await Voice.stop();
249
+ await new Promise(r => setTimeout(r, 300));
250
+ } catch (e) {
251
+ console.log('[startRecording] Error stopping:', e);
252
+ }
253
+ }
254
+ }
255
+ console.log('[startRecording] Calling Voice.start()');
207
256
  await Voice.start('en-US');
208
- isCurrentlyRecording = true;
257
+ console.log('[startRecording] Voice.start() completed, setting state to LISTENING');
258
+ state = State.LISTENING;
209
259
  return true;
210
260
  } catch (error) {
211
261
  console.error('Error starting voice recognition:', error);
212
- await cleanupVoiceSession();
262
+ cleanupVoiceSession();
213
263
  return false;
214
264
  }
215
265
  }
216
266
  export async function stopRecording() {
217
267
  try {
218
- if (!isCurrentlyRecording || !Voice) return;
219
-
220
- // Set this first to prevent race conditions
221
- isCurrentlyRecording = false;
222
- if (silenceTimer) {
223
- clearTimeout(silenceTimer);
224
- silenceTimer = null;
225
- }
226
-
227
- // First try to stop
228
- try {
229
- await Voice.stop();
230
- // Wait a bit for stop to complete
231
- await new Promise(resolve => setTimeout(resolve, 100));
232
- } catch (error) {
233
- console.error('Error stopping Voice:', error);
268
+ console.log('[stopRecording] Called, current state:', state);
269
+ // Can be called from LISTENING or FINALIZING state
270
+ if (state !== State.LISTENING && state !== State.FINALIZING || !Voice) {
271
+ console.log('[stopRecording] Invalid state or no Voice, returning');
272
+ return;
234
273
  }
235
274
 
236
- // Then force destroy
237
- try {
238
- await Voice.destroy();
239
- await new Promise(resolve => setTimeout(resolve, 300));
240
- } catch (error) {
241
- console.error('Error destroying Voice:', error);
275
+ // Only set to FINALIZING if not already there
276
+ if (state === State.LISTENING) {
277
+ console.log('[stopRecording] Setting state to FINALIZING');
278
+ state = State.FINALIZING;
242
279
  }
243
-
244
- // Final cleanup
245
- await cleanupVoiceSession();
280
+ clearSilenceTimer();
281
+ console.log('[stopRecording] Calling Voice.stop()');
282
+ await Voice.stop();
283
+ console.log('[stopRecording] Voice.stop() completed');
246
284
  } catch (error) {
247
285
  console.error('Error in stopRecording:', error);
248
- // Force cleanup on error
249
- await cleanupVoiceSession();
286
+ cleanupVoiceSession();
250
287
  }
251
288
  }
252
289
  export async function cancelRecording() {
253
290
  try {
254
291
  if (!Voice) return;
255
292
  await Voice.cancel();
256
- await cleanupVoiceSession();
293
+ cleanupVoiceSession();
257
294
  } catch (error) {
258
295
  console.error('Error canceling voice recognition:', error);
259
- await cleanupVoiceSession();
296
+ cleanupVoiceSession();
260
297
  }
261
298
  }
262
299
  export async function requestAudioPermission() {
@@ -293,11 +330,16 @@ async function requestAndroidPermission() {
293
330
  return false;
294
331
  }
295
332
 
296
- // Skip checking speech recognition services which is causing errors
297
- const services = await Voice.getSpeechRecognitionServices();
298
- if (!services || services.length === 0) {
299
- console.error('No speech recognition services available');
300
- return false;
333
+ // Check speech recognition services
334
+ try {
335
+ const services = await Voice.getSpeechRecognitionServices();
336
+ if (!services || services.length === 0) {
337
+ console.error('No speech recognition services available');
338
+ return false;
339
+ }
340
+ } catch (e) {
341
+ console.log('Error checking speech services:', e);
342
+ // Continue anyway - some devices report error but work fine
301
343
  }
302
344
  return true;
303
345
  } catch (error) {
@@ -334,19 +376,6 @@ export function resetStoredPermission() {
334
376
  return false;
335
377
  }
336
378
  export function cleanup() {
337
- if (!Voice) {
338
- console.log('Voice module not available during cleanup');
339
- return;
340
- }
341
- Voice.destroy().then(() => {
342
- Voice.removeAllListeners();
343
- cleanupVoiceSession();
344
- }).catch(error => {
345
- console.error('Error in cleanup:', error);
346
- // Try one more time
347
- if (Voice) {
348
- Voice.destroy().catch(e => console.error('Final cleanup attempt failed:', e));
349
- }
350
- });
379
+ cleanupVoiceSession();
351
380
  }
352
381
  //# sourceMappingURL=audioRecorder.js.map