react-native-srschat 0.1.26 → 0.1.28

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.
@@ -1,8 +1,9 @@
1
1
  // VoiceButton.js
2
2
 
3
3
  import React, { useState, useContext, useEffect } from 'react';
4
- import { TouchableOpacity, ActivityIndicator, StyleSheet, Alert } from 'react-native';
4
+ import { TouchableOpacity, ActivityIndicator, StyleSheet, Alert, Linking, Platform } from 'react-native';
5
5
  import Ionicons from 'react-native-vector-icons/Ionicons';
6
+ import useAsyncStorage from '../hooks/useAsyncStorage';
6
7
 
7
8
  import {
8
9
  startRecording,
@@ -10,60 +11,129 @@ import {
10
11
  cancelRecording,
11
12
  requestAudioPermission,
12
13
  cleanup,
13
- initVoice
14
+ initVoice,
15
+ resetStoredPermission,
16
+ setPermissionStatusHandlers
14
17
  } from '../utils/audioRecorder';
15
18
  import { AppContext } from '../contexts/AppContext';
16
19
 
20
+ const PERMISSION_STORAGE_KEY = '@voice_permission_status';
21
+
17
22
  export const VoiceButton = () => {
18
23
  const { handleVoiceSend } = useContext(AppContext);
19
24
  const [isListening, setIsListening] = useState(false);
20
25
  const [loading, setLoading] = useState(false);
26
+ const [permissionChecked, setPermissionChecked] = useState(false);
27
+ const [hasPermission, setHasPermission] = useState(null);
28
+
29
+ // Use your custom AsyncStorage hook
30
+ const [permissionStatus, setPermissionStatus] = useAsyncStorage(PERMISSION_STORAGE_KEY, null);
21
31
 
22
32
  useEffect(() => {
33
+ // Register our permission handlers
34
+ setPermissionStatusHandlers(() => permissionStatus, setPermissionStatus);
35
+
23
36
  const setupVoice = async () => {
24
- const initialized = await initVoice((result, error) => {
25
- if (error) {
26
- Alert.alert('Error', error);
27
- setIsListening(false);
28
- setLoading(false);
37
+ try {
38
+ // Check stored permission first
39
+ if (permissionStatus === 'denied') {
40
+ // We already know permission was denied, don't show alert again
41
+ setHasPermission(false);
42
+ setPermissionChecked(true);
29
43
  return;
30
44
  }
31
- if (result) {
32
- handleVoiceSend(null, result);
33
- setIsListening(false);
34
- setLoading(false);
35
- }
36
- });
45
+
46
+ // Only request permission if not already denied
47
+ const permissionResult = await requestAudioPermission();
48
+ setHasPermission(permissionResult);
49
+
50
+ if (permissionResult) {
51
+ const initialized = await initVoice((result, error) => {
52
+ if (error) {
53
+ // Don't show alert for permission errors since we handle that elsewhere
54
+ if (!error.includes('permission')) {
55
+ Alert.alert('Error', error);
56
+ }
57
+ setIsListening(false);
58
+ setLoading(false);
59
+ return;
60
+ }
61
+ if (result) {
62
+ handleVoiceSend(null, result);
63
+ }
64
+ // Always reset states when the recognition ends
65
+ setIsListening(false);
66
+ setLoading(false);
67
+ });
37
68
 
38
- if (!initialized) {
39
- Alert.alert(
40
- 'Error',
41
- 'Speech recognition is not available on this device'
42
- );
69
+ if (!initialized) {
70
+ // Only show this alert once per session
71
+ if (!permissionChecked) {
72
+ Alert.alert(
73
+ 'Error',
74
+ 'Speech recognition is not available on this device'
75
+ );
76
+ }
77
+ }
78
+ }
79
+ } catch (error) {
80
+ console.error('Error in setupVoice:', error);
81
+ } finally {
82
+ setPermissionChecked(true);
43
83
  }
44
84
  };
45
85
 
46
- setupVoice();
86
+ if (!permissionChecked) {
87
+ setupVoice();
88
+ }
47
89
 
48
90
  return () => {
49
91
  cleanup();
50
92
  };
51
- }, []);
93
+ }, [permissionStatus, permissionChecked]);
52
94
 
53
95
  const toggleRecording = async () => {
54
96
  try {
55
97
  if (!isListening) {
56
- const hasPermission = await requestAudioPermission();
57
- if (!hasPermission) {
58
- Alert.alert('Permission Denied', 'Microphone permission is required for voice recognition');
98
+ // If we already know we don't have permission, show a better message
99
+ if (hasPermission === false) {
100
+ Alert.alert(
101
+ 'Permission Required',
102
+ 'Voice recognition requires microphone permission. Would you like to update your settings?',
103
+ [
104
+ {
105
+ text: 'Cancel',
106
+ style: 'cancel'
107
+ },
108
+ {
109
+ text: 'Settings',
110
+ onPress: () => {
111
+ // Reset stored permission so we can check again
112
+ setPermissionStatus(null);
113
+ // Open device settings
114
+ openAppSettings();
115
+ // Reset our permission check
116
+ setPermissionChecked(false);
117
+ }
118
+ }
119
+ ]
120
+ );
59
121
  return;
60
122
  }
61
123
 
62
124
  setLoading(true);
125
+ const checkPermission = await requestAudioPermission();
126
+ if (!checkPermission) {
127
+ setHasPermission(false);
128
+ setLoading(false);
129
+ return;
130
+ }
131
+
63
132
  const started = await startRecording();
64
133
  if (started) {
65
134
  setIsListening(true);
66
135
  } else {
136
+ // Only show error if not permission related
67
137
  Alert.alert('Error', 'Failed to start voice recognition');
68
138
  }
69
139
  } else {
@@ -78,7 +148,21 @@ export const VoiceButton = () => {
78
148
  await cleanup();
79
149
  } finally {
80
150
  setLoading(false);
151
+ }
152
+ };
81
153
 
154
+ const openAppSettings = async () => {
155
+ try {
156
+ if (Platform.OS === 'ios') {
157
+ // For iOS
158
+ await Linking.openURL('app-settings://');
159
+ } else {
160
+ // For Android
161
+ await Linking.openSettings();
162
+ }
163
+ } catch (error) {
164
+ console.error('Cannot open settings', error);
165
+ Alert.alert('Error', 'Unable to open settings. Please open settings manually.');
82
166
  }
83
167
  };
84
168
 
@@ -76,8 +76,9 @@ export function useWebSocketMessage() {
76
76
 
77
77
  ws.onmessage = (event) => {
78
78
  const response = JSON.parse(event.data);
79
- if (response.type != "chunk") {
80
- console.log(response)
79
+
80
+ if (response.type !== "chunk") {
81
+ console.log(JSON.stringify(response, null, 2));
81
82
  }
82
83
  switch (response.type) {
83
84
  case 'middle_message':
@@ -179,7 +180,8 @@ export function useWebSocketMessage() {
179
180
  product_description: prod.product_details.product_description || "",
180
181
  product_name: prod.product_details.product_name || "",
181
182
  type: prod.product_details.type || "",
182
- vertical: prod.product_details.vertical || ""
183
+ vertical: prod.product_details.vertical || "",
184
+ sku: prod.product_details.sku || ""
183
185
  } : {}
184
186
  }));
185
187
 
@@ -3,6 +3,7 @@
3
3
  import { Platform, PermissionsAndroid } from 'react-native';
4
4
  import Voice from '@react-native-community/voice';
5
5
  import { check, PERMISSIONS, request, RESULTS } from 'react-native-permissions';
6
+ import useAsyncStorage from '../hooks/useAsyncStorage';
6
7
 
7
8
  let resultCallback = null;
8
9
  let silenceTimer = null;
@@ -10,6 +11,19 @@ let isCurrentlyRecording = false;
10
11
  let finalResult = '';
11
12
  const SILENCE_DURATION = 1500; // 1.5 seconds of silence before stopping
12
13
 
14
+ // Add this constant for AsyncStorage key
15
+ const PERMISSION_STORAGE_KEY = '@voice_permission_status';
16
+
17
+ // Create a function that can be called to get permission status
18
+ // This needs to be outside the React component lifecycle
19
+ let permissionStatusGetter = null;
20
+ let permissionStatusSetter = null;
21
+
22
+ export function setPermissionStatusHandlers(getter, setter) {
23
+ permissionStatusGetter = getter;
24
+ permissionStatusSetter = setter;
25
+ }
26
+
13
27
  // Initialize Voice handlers
14
28
  export async function initVoice(onResult) {
15
29
  try {
@@ -60,15 +74,25 @@ export async function initVoice(onResult) {
60
74
  Voice.onSpeechError = async (e) => {
61
75
  console.error('onSpeechError: ', e);
62
76
 
63
-
64
77
  if (silenceTimer) {
65
78
  clearTimeout(silenceTimer);
66
79
  silenceTimer = null;
67
80
  }
68
81
 
82
+ // Check for "No speech detected" error
83
+ const isNoSpeechError = e.error?.code === "recognition_fail" &&
84
+ e.error?.message?.includes("No speech detected");
69
85
 
70
86
  await cleanupVoiceSession();
71
- resultCallback(null, e.error?.message || 'Speech recognition error');
87
+
88
+ // Only send error to callback if it's not a "No speech detected" error
89
+ if (!isNoSpeechError) {
90
+ resultCallback(null, e.error?.message || 'Speech recognition error');
91
+ } else {
92
+ console.log('No speech detected, ignoring error');
93
+ // Optionally, call the callback with null parameters or a special indicator
94
+ resultCallback(null, null); // This won't trigger an error alert in the component
95
+ }
72
96
  };
73
97
 
74
98
  Voice.onSpeechResults = (e) => {
@@ -245,13 +269,32 @@ export async function cancelRecording() {
245
269
  }
246
270
 
247
271
  export async function requestAudioPermission() {
248
- if (Platform.OS === 'android') {
249
- return await requestAndroidPermission();
250
- } else if (Platform.OS === 'ios') {
251
- return await requestIOSPermission();
272
+ try {
273
+ // Get stored permission if available
274
+ const storedPermission = permissionStatusGetter ? permissionStatusGetter() : null;
275
+
276
+ if (storedPermission === 'denied') {
277
+ console.log('Permission previously denied by user');
278
+ return false;
279
+ }
280
+
281
+ let permissionResult = false;
282
+ if (Platform.OS === 'android') {
283
+ permissionResult = await requestAndroidPermission();
284
+ } else if (Platform.OS === 'ios') {
285
+ permissionResult = await requestIOSPermission();
286
+ }
287
+
288
+ // Store the result
289
+ if (permissionStatusSetter) {
290
+ permissionStatusSetter(permissionResult ? 'granted' : 'denied');
291
+ }
292
+
293
+ return permissionResult;
294
+ } catch (error) {
295
+ console.error('Error checking stored permission:', error);
296
+ return false;
252
297
  }
253
-
254
- return false;
255
298
  }
256
299
 
257
300
  async function requestAndroidPermission() {
@@ -267,23 +310,19 @@ async function requestAndroidPermission() {
267
310
  PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
268
311
  {
269
312
  title: 'Microphone Permission',
270
-
271
313
  message: 'This app needs access to your microphone for voice recognition.',
272
-
273
314
  buttonPositive: 'OK',
274
315
  buttonNegative: 'Cancel',
275
316
  }
276
317
  );
277
-
318
+
278
319
  return granted === PermissionsAndroid.RESULTS.GRANTED;
279
320
  } catch (error) {
280
321
  console.error('Error requesting Android permission:', error);
281
-
282
322
  return false;
283
323
  }
284
324
  }
285
325
 
286
-
287
326
  async function requestIOSPermission() {
288
327
  try {
289
328
  // Request microphone permission
@@ -307,6 +346,14 @@ async function requestIOSPermission() {
307
346
  }
308
347
  }
309
348
 
349
+ export function resetStoredPermission() {
350
+ if (permissionStatusSetter) {
351
+ permissionStatusSetter(null);
352
+ return true;
353
+ }
354
+ return false;
355
+ }
356
+
310
357
  export function cleanup() {
311
358
  Voice.destroy().then(() => {
312
359
  Voice.removeAllListeners();