react-native-srschat 0.1.19 → 0.1.21

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.
Files changed (65) hide show
  1. package/README.md +1 -0
  2. package/lib/commonjs/components/email.js +1 -1
  3. package/lib/commonjs/components/email.js.map +1 -1
  4. package/lib/commonjs/components/header.js +1 -0
  5. package/lib/commonjs/components/header.js.map +1 -1
  6. package/lib/commonjs/components/input.js +2 -7
  7. package/lib/commonjs/components/input.js.map +1 -1
  8. package/lib/commonjs/components/voice.js +47 -38
  9. package/lib/commonjs/components/voice.js.map +1 -1
  10. package/lib/commonjs/components/welcomeInput.js +4 -9
  11. package/lib/commonjs/components/welcomeInput.js.map +1 -1
  12. package/lib/commonjs/contexts/AppContext.js +6 -1
  13. package/lib/commonjs/contexts/AppContext.js.map +1 -1
  14. package/lib/commonjs/layout/disclaimer.js +1 -1
  15. package/lib/commonjs/layout/disclaimer.js.map +1 -1
  16. package/lib/commonjs/layout/layout.js +8 -23
  17. package/lib/commonjs/layout/layout.js.map +1 -1
  18. package/lib/commonjs/layout/welcome.js +8 -4
  19. package/lib/commonjs/layout/welcome.js.map +1 -1
  20. package/lib/commonjs/layout/window.js +4 -4
  21. package/lib/commonjs/layout/window.js.map +1 -1
  22. package/lib/commonjs/utils/audioRecorder.js +234 -44
  23. package/lib/commonjs/utils/audioRecorder.js.map +1 -1
  24. package/lib/module/components/email.js +1 -1
  25. package/lib/module/components/email.js.map +1 -1
  26. package/lib/module/components/header.js +1 -0
  27. package/lib/module/components/header.js.map +1 -1
  28. package/lib/module/components/input.js +2 -7
  29. package/lib/module/components/input.js.map +1 -1
  30. package/lib/module/components/voice.js +51 -41
  31. package/lib/module/components/voice.js.map +1 -1
  32. package/lib/module/components/welcomeInput.js +4 -9
  33. package/lib/module/components/welcomeInput.js.map +1 -1
  34. package/lib/module/contexts/AppContext.js +6 -1
  35. package/lib/module/contexts/AppContext.js.map +1 -1
  36. package/lib/module/layout/disclaimer.js +1 -1
  37. package/lib/module/layout/disclaimer.js.map +1 -1
  38. package/lib/module/layout/layout.js +8 -23
  39. package/lib/module/layout/layout.js.map +1 -1
  40. package/lib/module/layout/welcome.js +6 -4
  41. package/lib/module/layout/welcome.js.map +1 -1
  42. package/lib/module/layout/window.js +4 -4
  43. package/lib/module/layout/window.js.map +1 -1
  44. package/lib/module/utils/audioRecorder.js +232 -45
  45. package/lib/module/utils/audioRecorder.js.map +1 -1
  46. package/lib/typescript/components/input.d.ts.map +1 -1
  47. package/lib/typescript/components/voice.d.ts.map +1 -1
  48. package/lib/typescript/components/welcomeInput.d.ts.map +1 -1
  49. package/lib/typescript/contexts/AppContext.d.ts.map +1 -1
  50. package/lib/typescript/layout/layout.d.ts.map +1 -1
  51. package/lib/typescript/layout/window.d.ts.map +1 -1
  52. package/lib/typescript/utils/audioRecorder.d.ts +6 -3
  53. package/lib/typescript/utils/audioRecorder.d.ts.map +1 -1
  54. package/package.json +5 -1
  55. package/src/components/email.js +1 -1
  56. package/src/components/header.js +1 -0
  57. package/src/components/input.js +2 -3
  58. package/src/components/voice.js +77 -50
  59. package/src/components/welcomeInput.js +6 -4
  60. package/src/contexts/AppContext.js +6 -1
  61. package/src/layout/disclaimer.js +1 -1
  62. package/src/layout/layout.js +8 -12
  63. package/src/layout/welcome.js +5 -2
  64. package/src/layout/window.js +5 -4
  65. package/src/utils/audioRecorder.js +266 -45
@@ -1,75 +1,102 @@
1
1
  // VoiceButton.js
2
- import React, { useState, useContext } from 'react';
3
- import { TouchableOpacity, ActivityIndicator, View, StyleSheet } from 'react-native';
2
+
3
+ import React, { useState, useContext, useEffect } from 'react';
4
+ import { TouchableOpacity, ActivityIndicator, StyleSheet, Alert } from 'react-native';
4
5
  import Ionicons from 'react-native-vector-icons/Ionicons';
5
- import axios from 'axios';
6
6
 
7
- import { startRecording, stopRecording, requestAudioPermission } from '../utils/audioRecorder';
7
+ import {
8
+ startRecording,
9
+ stopRecording,
10
+ cancelRecording,
11
+ requestAudioPermission,
12
+ cleanup,
13
+ initVoice
14
+ } from '../utils/audioRecorder';
8
15
  import { AppContext } from '../contexts/AppContext';
9
16
 
10
17
  export const VoiceButton = () => {
11
- const { data, handleVoiceSend } = useContext(AppContext);
18
+ const { handleVoiceSend } = useContext(AppContext);
12
19
  const [isListening, setIsListening] = useState(false);
13
20
  const [loading, setLoading] = useState(false);
14
21
 
15
- const toggleRecording = async () => {
16
- const hasPermission = await requestAudioPermission();
17
-
18
- if (!hasPermission) {
19
- console.error('Permission denied');
20
- return;
21
- }
22
-
23
- if (!isListening) {
24
- startRecording(handleStopRecording);
25
- setIsListening(true);
26
- } else {
27
- handleStopRecording();
28
- }
29
- };
22
+ useEffect(() => {
23
+ const setupVoice = async () => {
24
+ const initialized = await initVoice((result, error) => {
25
+ if (error) {
26
+ Alert.alert('Error', error);
27
+ setIsListening(false);
28
+ setLoading(false);
29
+ return;
30
+ }
31
+ if (result) {
32
+ handleVoiceSend(null, result);
33
+ setIsListening(false);
34
+ setLoading(false);
35
+ }
36
+ });
30
37
 
31
- const handleStopRecording = async () => {
32
- setLoading(true);
33
- const audioPath = await stopRecording();
34
- const transcription = await transcribeAudio(audioPath);
35
- setLoading(false);
36
- setIsListening(false);
37
- handleVoiceSend(audioPath, transcription);
38
- };
38
+ if (!initialized) {
39
+ Alert.alert(
40
+ 'Error',
41
+ 'Speech recognition is not available on this device'
42
+ );
43
+ }
44
+ };
45
+
46
+ setupVoice();
39
47
 
40
- const transcribeAudio = async (audioPath) => {
48
+ return () => {
49
+ cleanup();
50
+ };
51
+ }, []);
52
+
53
+ const toggleRecording = async () => {
41
54
  try {
42
- const formData = new FormData();
43
- formData.append('file', {
44
- uri: audioPath,
45
- type: 'audio/wav',
46
- name: 'audio.wav',
47
- });
48
- formData.append('model', 'whisper-1');
55
+ if (!isListening) {
56
+ const hasPermission = await requestAudioPermission();
57
+ if (!hasPermission) {
58
+ Alert.alert('Permission Denied', 'Microphone permission is required for voice recognition');
59
+ return;
60
+ }
49
61
 
50
- const response = await axios.post(
51
- 'https://api.openai.com/v1/audio/transcriptions',
52
- formData,
53
- {
54
- headers: {
55
- Authorization: `Bearer ${data.openai_key}`,
56
- 'Content-Type': 'multipart/form-data',
57
- },
62
+ setLoading(true);
63
+ const started = await startRecording();
64
+ if (started) {
65
+ setIsListening(true);
66
+ } else {
67
+ Alert.alert('Error', 'Failed to start voice recognition');
58
68
  }
59
- );
60
- return response.data.text;
69
+ } else {
70
+ setLoading(true);
71
+ setIsListening(false);
72
+ await stopRecording();
73
+ }
61
74
  } catch (error) {
62
- console.error('Error transcribing audio:', error);
63
- return '';
75
+ console.error('Error in toggleRecording:', error);
76
+ Alert.alert('Error', 'An error occurred while managing voice recognition');
77
+ setIsListening(false);
78
+ await cleanup();
79
+ } finally {
80
+ setLoading(false);
81
+
64
82
  }
65
83
  };
66
84
 
67
85
  return (
68
- <TouchableOpacity style={styles.button} onPress={toggleRecording} disabled={loading}>
86
+ <TouchableOpacity
87
+ style={styles.button}
88
+ onPress={toggleRecording}
89
+ disabled={loading}
90
+ >
69
91
  {loading ? (
70
92
  <ActivityIndicator size="small" color="#8E8E93" />
71
93
  ) : (
72
- <Ionicons name={isListening ? 'stop-circle' : 'mic-outline'} size={24} color="#8E8E93" />
94
+ <Ionicons
95
+ name={isListening ? 'stop-circle' : 'mic-outline'}
96
+ size={24}
97
+ color="#8E8E93"
98
+ />
99
+
73
100
  )}
74
101
  </TouchableOpacity>
75
102
  );
@@ -12,9 +12,11 @@ import {
12
12
  import { Header } from '../components/header';
13
13
  import { AppContext } from '../contexts/AppContext';
14
14
  import Ionicons from 'react-native-vector-icons/Ionicons';
15
+ import { VoiceButton } from './voice';
15
16
 
16
17
  export const WelcomeInput = ({ onProductCardClick, onAddToCartClick }) => {
17
- const { handleSend, input, setInput, showModal, theme, data } = useContext(AppContext);
18
+
19
+ const { data, handleSend, input, setInput, showModal, theme } = useContext(AppContext);
18
20
 
19
21
  return (
20
22
  <View style={styles.inputContainer}>
@@ -27,9 +29,9 @@ export const WelcomeInput = ({ onProductCardClick, onAddToCartClick }) => {
27
29
  multiline
28
30
  />
29
31
  {data.openai_key &&
30
- <TouchableOpacity style={styles.inputButton}>
31
- <Ionicons name="mic-outline" size={24} color="#8E8E93" />
32
- </TouchableOpacity>
32
+
33
+ <VoiceButton/>
34
+
33
35
  }
34
36
  <TouchableOpacity
35
37
  style={styles.sendButton}
@@ -250,6 +250,11 @@ export const AppProvider = ({ data, onProductCardClick, onAddToCartClick, uiConf
250
250
  };
251
251
  });
252
252
  };
253
+
254
+ function handleVoiceSend(audio, transcription) {
255
+ //setReadAloud(true)
256
+ handleSend(transcription)
257
+ }
253
258
 
254
259
  return (
255
260
  <AppContext.Provider
@@ -259,7 +264,7 @@ export const AppProvider = ({ data, onProductCardClick, onAddToCartClick, uiConf
259
264
  startStreaming, setStartStreaming, maintenance, setMaintenance, feedback, setFeedback, handleFeedback, feedbackOpen, setFeedbackOpen,
260
265
  writeFeedback, setWriteFeedback, writeAnswer, setWriteAnswer, BASE_URL, lastMessageId, setLastMessageId,
261
266
  onProductCardClick, onAddToCartClick, data, sessionId, setSessionId, handleWrittenFeedback, switchFeedbackOpen, confirmDisclaimer,
262
- formatChatHistory, uiConfig
267
+ formatChatHistory, uiConfig, handleVoiceSend
263
268
  }}
264
269
  >
265
270
  {children}
@@ -28,7 +28,7 @@ export const Disclaimer = ({ panHandlers }) => {
28
28
 
29
29
  return (
30
30
  <View style={styles.container}>
31
- <View {...panHandlers}>
31
+ <View>
32
32
  <Header />
33
33
  </View>
34
34
  <ScrollView contentContainerStyle={styles.scrollContainer}>
@@ -22,7 +22,10 @@ export const Layout = () => {
22
22
  setShowModal("Icon");
23
23
  }
24
24
  };
25
-
25
+
26
+ /* Currently removed this from the child components.
27
+ If activating drag to close, put {...panHandlers} in the view around the
28
+ header on each page (welcome, window, disclaimer) */
26
29
  const panResponder = PanResponder.create({
27
30
  onStartShouldSetPanResponder: () => true,
28
31
  onMoveShouldSetPanResponder: () => true,
@@ -80,7 +83,7 @@ export const Layout = () => {
80
83
  )}
81
84
  {showModal === "ChatWindow" && (
82
85
  <>
83
- <Pressable style={styles.outsideTouchable} onPress={() => handleClick()} />
86
+ {/* <Pressable style={styles.outsideTouchable} onPress={() => handleClick()} /> */}
84
87
  <Animated.View style={[styles.container, { transform: [{ translateY: panY }] }]}>
85
88
  <ChatWindow panHandlers={panResponder.panHandlers} />
86
89
  </Animated.View>
@@ -88,7 +91,7 @@ export const Layout = () => {
88
91
  )}
89
92
  {showModal === "Welcome" && (
90
93
  <>
91
- <Pressable style={styles.outsideTouchable} onPress={() => handleClick()} />
94
+ {/* <Pressable style={styles.outsideTouchable} onPress={() => handleClick()} /> */}
92
95
  <Animated.View style={[styles.container, { transform: [{ translateY: panY }] }]}>
93
96
  <Welcome panHandlers={panResponder.panHandlers} />
94
97
  </Animated.View>
@@ -96,7 +99,7 @@ export const Layout = () => {
96
99
  )}
97
100
  {showModal === "Form" &&
98
101
  <>
99
- <Pressable style={styles.outsideTouchable} onPress={() => handleClick()} />
102
+ {/* <Pressable style={styles.outsideTouchable} onPress={() => handleClick()} /> */}
100
103
  <Animated.View style={[styles.container, { transform: [{ translateY: panY }] }]}>
101
104
  <Disclaimer panHandlers={panResponder.panHandlers} />
102
105
  </Animated.View>
@@ -104,7 +107,7 @@ export const Layout = () => {
104
107
  }
105
108
  {showModal === "Email" &&
106
109
  <>
107
- <Pressable style={styles.outsideTouchable} onPress={() => handleClick()} />
110
+ {/* <Pressable style={styles.outsideTouchable} onPress={() => handleClick()} /> */}
108
111
  <Animated.View style={[styles.container, { transform: [{ translateY: panY }] }]}>
109
112
  <EmailForm panHandlers={panResponder.panHandlers} />
110
113
  </Animated.View>
@@ -144,11 +147,4 @@ const styles = StyleSheet.create({
144
147
  outsideTouchable: {
145
148
  flex: 1,
146
149
  },
147
- dragHandle: {
148
- height: 20,
149
- width: '100%',
150
- backgroundColor: '#DDD',
151
- borderTopLeftRadius: 16,
152
- borderTopRightRadius: 16,
153
- }
154
150
  });
@@ -24,7 +24,7 @@ export const Welcome = ({ panHandlers }) => {
24
24
 
25
25
  {/* Top section */}
26
26
  <View style={styles.topContainer}>
27
- <View style={styles.topHeader} {...panHandlers}>
27
+ <View style={styles.topHeader}>
28
28
  <Image
29
29
  source={require('../assets/heritage.png')}
30
30
  style={[styles.logo, { tintColor: "white" }]}
@@ -61,7 +61,9 @@ const styles = StyleSheet.create({
61
61
  flex: 1,
62
62
  },
63
63
  parentContainer: {
64
- flexGrow: 1
64
+ flexGrow: 1,
65
+ backgroundColor: '#004687',
66
+ paddingTop: 60
65
67
  },
66
68
  topContainer: {
67
69
  backgroundColor: '#004687',
@@ -111,6 +113,7 @@ const styles = StyleSheet.create({
111
113
  },
112
114
  bottomContainer: {
113
115
  paddingHorizontal: 25,
116
+ backgroundColor: "#f6f6f6",
114
117
  paddingTop: 30
115
118
  },
116
119
  welcomeBody: {
@@ -48,7 +48,7 @@ export const ChatWindow = ({ panHandlers }) => {
48
48
 
49
49
  return (
50
50
  <View style={styles.container}>
51
- <View {...panHandlers}>
51
+ <View>
52
52
  <Header />
53
53
  </View>
54
54
 
@@ -73,7 +73,7 @@ export const ChatWindow = ({ panHandlers }) => {
73
73
  {msg.type !== "middle" && (
74
74
  <View style={[ styles.messageBubble, msg.type === "user" ? styles.userMessage : styles.aiMessage,]}>
75
75
  <Markdown style={{ body: { color: msg.type === "user" ? "#ffffff" : "#161616",fontSize: 16, lineHeight: 22 }}}>
76
- {msg.text}
76
+ {typeof msg.text === 'string' ? msg.text : String(msg.text || '')}
77
77
  </Markdown>
78
78
  {(msg.type == 'ai' && i != 0 && msg.message_id ) &&
79
79
  <Feedback message={msg} messageId={msg.message_id}/>
@@ -85,7 +85,7 @@ export const ChatWindow = ({ panHandlers }) => {
85
85
  <View style={[styles.middleMessageBubble, styles.middleMessage]}>
86
86
  <Ionicons name="sparkles-outline" size={20} style={{marginRight: 10}}/>
87
87
  <Markdown style={{ body: { color: msg.type === "user" ? "#ffffff" : "#161616",fontSize: 16, lineHeight: 22 }}}>
88
- {msg.text}
88
+ {typeof msg.text === 'string' ? msg.text : String(msg.text || '')}
89
89
  </Markdown>
90
90
  </View>
91
91
  )}
@@ -104,7 +104,8 @@ export const ChatWindow = ({ panHandlers }) => {
104
104
  </TouchableOpacity>
105
105
  }
106
106
 
107
- {msg.suggested_questions && Array.isArray(msg.questions) && msg.questions.map((question, index) => (
107
+ {msg.suggested_questions && Array.isArray(msg.suggested_questions) && msg.suggested_questions.length > 0 &&
108
+ msg.suggested_questions.map((question, index) => (
108
109
  <TouchableOpacity key={index} style={styles.suggestedQuestionButton}
109
110
  onPress={() => handleButtonClick(question)}>
110
111
  <Text style={styles.suggestedQuestionText}>{question}</Text>