react-native-voice-ts 1.0.3 → 1.0.5

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.
@@ -32,7 +32,7 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
32
32
  const [recognizedText, setRecognizedText] = useState('');
33
33
  const [partialText, setPartialText] = useState('');
34
34
  const [error, setError] = useState(null);
35
- const [shouldContinue, setShouldContinue] = useState(false);
35
+ const shouldContinueRef = React.useRef(false);
36
36
  const silenceTimerRef = React.useRef(null);
37
37
  useEffect(() => {
38
38
  // Clear any existing timers on cleanup
@@ -55,10 +55,10 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
55
55
  Voice.onSpeechEnd = async () => {
56
56
  setIsRecording(false);
57
57
  // In continuous mode, restart listening after results
58
- if (continuous && shouldContinue) {
58
+ if (continuous && shouldContinueRef.current) {
59
59
  // Small delay before restarting
60
60
  setTimeout(async () => {
61
- if (shouldContinue) {
61
+ if (shouldContinueRef.current) {
62
62
  try {
63
63
  await Voice.start(locale, {
64
64
  EXTRA_PARTIAL_RESULTS: enablePartialResults,
@@ -78,7 +78,7 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
78
78
  const errorMessage = e.error?.message || 'Unknown error';
79
79
  setError(errorMessage);
80
80
  setIsRecording(false);
81
- setShouldContinue(false);
81
+ shouldContinueRef.current = false;
82
82
  if (silenceTimerRef.current) {
83
83
  clearTimeout(silenceTimerRef.current);
84
84
  }
@@ -109,9 +109,15 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
109
109
  // Reset silence timer on partial results (user is speaking)
110
110
  if (continuous && silenceTimerRef.current) {
111
111
  clearTimeout(silenceTimerRef.current);
112
- silenceTimerRef.current = setTimeout(() => {
113
- if (shouldContinue) {
114
- stop();
112
+ silenceTimerRef.current = setTimeout(async () => {
113
+ if (shouldContinueRef.current) {
114
+ shouldContinueRef.current = false;
115
+ if (silenceTimerRef.current) {
116
+ clearTimeout(silenceTimerRef.current);
117
+ silenceTimerRef.current = null;
118
+ }
119
+ await Voice.stop();
120
+ onStop?.();
115
121
  }
116
122
  }, maxSilenceDuration);
117
123
  }
@@ -130,7 +136,6 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
130
136
  onError,
131
137
  enablePartialResults,
132
138
  continuous,
133
- shouldContinue,
134
139
  recognizedText,
135
140
  locale,
136
141
  maxSilenceDuration,
@@ -149,7 +154,7 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
149
154
  setRecognizedText('');
150
155
  setPartialText('');
151
156
  }
152
- setShouldContinue(true);
157
+ shouldContinueRef.current = true;
153
158
  // Check permission (Android only)
154
159
  const hasPermission = await Voice.checkMicrophonePermission();
155
160
  if (!hasPermission) {
@@ -164,9 +169,15 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
164
169
  });
165
170
  // Start silence timer if in continuous mode
166
171
  if (continuous) {
167
- silenceTimerRef.current = setTimeout(() => {
168
- if (shouldContinue) {
169
- stop();
172
+ silenceTimerRef.current = setTimeout(async () => {
173
+ if (shouldContinueRef.current) {
174
+ shouldContinueRef.current = false;
175
+ if (silenceTimerRef.current) {
176
+ clearTimeout(silenceTimerRef.current);
177
+ silenceTimerRef.current = null;
178
+ }
179
+ await Voice.stop();
180
+ onStop?.();
170
181
  }
171
182
  }, maxSilenceDuration);
172
183
  }
@@ -174,7 +185,7 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
174
185
  catch (e) {
175
186
  const errorMessage = e instanceof Error ? e.message : 'Failed to start recording';
176
187
  setError(errorMessage);
177
- setShouldContinue(false);
188
+ shouldContinueRef.current = false;
178
189
  onError?.(errorMessage);
179
190
  }
180
191
  }, [
@@ -183,11 +194,11 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
183
194
  onError,
184
195
  continuous,
185
196
  maxSilenceDuration,
186
- shouldContinue,
197
+ onStop,
187
198
  ]);
188
199
  const stop = useCallback(async () => {
189
200
  try {
190
- setShouldContinue(false);
201
+ shouldContinueRef.current = false;
191
202
  if (silenceTimerRef.current) {
192
203
  clearTimeout(silenceTimerRef.current);
193
204
  silenceTimerRef.current = null;
@@ -203,7 +214,7 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
203
214
  }, [onError, onStop]);
204
215
  const cancel = useCallback(async () => {
205
216
  try {
206
- setShouldContinue(false);
217
+ shouldContinueRef.current = false;
207
218
  if (silenceTimerRef.current) {
208
219
  clearTimeout(silenceTimerRef.current);
209
220
  silenceTimerRef.current = null;
@@ -25,10 +25,30 @@ export interface UseVoiceRecognitionOptions {
25
25
  * Callback fired when speech is recognized
26
26
  */
27
27
  onResult?: (text: string) => void;
28
+ /**
29
+ * Callback fired when partial results are available (real-time)
30
+ */
31
+ onPartialResult?: (text: string) => void;
28
32
  /**
29
33
  * Callback fired when an error occurs
30
34
  */
31
35
  onError?: (error: string) => void;
36
+ /**
37
+ * Callback fired when recording starts
38
+ */
39
+ onStart?: () => void;
40
+ /**
41
+ * Callback fired when recording ends
42
+ */
43
+ onEnd?: () => void;
44
+ /**
45
+ * Callback fired when speech is recognized (before results)
46
+ */
47
+ onRecognized?: () => void;
48
+ /**
49
+ * Callback fired when volume changes (0-10)
50
+ */
51
+ onVolumeChanged?: (value: number) => void;
32
52
  }
33
53
  export interface UseVoiceRecognitionReturn {
34
54
  /**
@@ -22,12 +22,12 @@ import Voice from '../index';
22
22
  * ```
23
23
  */
24
24
  export const useVoiceRecognition = (options = {}) => {
25
- const { locale = 'en-US', enablePartialResults = true, continuous = false, maxSilenceDuration = 5000, onResult, onError, } = options;
25
+ const { locale = 'en-US', enablePartialResults = true, continuous = false, maxSilenceDuration = 5000, onResult, onPartialResult, onError, onStart, onEnd, onRecognized, onVolumeChanged, } = options;
26
26
  const [isRecording, setIsRecording] = useState(false);
27
27
  const [results, setResults] = useState([]);
28
28
  const [partialResults, setPartialResults] = useState([]);
29
29
  const [error, setError] = useState(null);
30
- const [shouldContinue, setShouldContinue] = useState(false);
30
+ const shouldContinueRef = React.useRef(false);
31
31
  const silenceTimerRef = React.useRef(null);
32
32
  const accumulatedTextRef = React.useRef('');
33
33
  useEffect(() => {
@@ -46,13 +46,15 @@ export const useVoiceRecognition = (options = {}) => {
46
46
  if (silenceTimerRef.current) {
47
47
  clearTimeout(silenceTimerRef.current);
48
48
  }
49
+ onStart?.();
49
50
  };
50
51
  Voice.onSpeechEnd = async () => {
51
52
  setIsRecording(false);
53
+ onEnd?.();
52
54
  // In continuous mode, restart listening after results
53
- if (continuous && shouldContinue) {
55
+ if (continuous && shouldContinueRef.current) {
54
56
  setTimeout(async () => {
55
- if (shouldContinue) {
57
+ if (shouldContinueRef.current) {
56
58
  try {
57
59
  await Voice.start(locale, {
58
60
  EXTRA_PARTIAL_RESULTS: enablePartialResults,
@@ -65,11 +67,14 @@ export const useVoiceRecognition = (options = {}) => {
65
67
  }, 100);
66
68
  }
67
69
  };
70
+ Voice.onSpeechRecognized = () => {
71
+ onRecognized?.();
72
+ };
68
73
  Voice.onSpeechError = (e) => {
69
74
  const errorMessage = e.error?.message || 'Unknown error';
70
75
  setError(errorMessage);
71
76
  setIsRecording(false);
72
- setShouldContinue(false);
77
+ shouldContinueRef.current = false;
73
78
  if (silenceTimerRef.current) {
74
79
  clearTimeout(silenceTimerRef.current);
75
80
  }
@@ -95,22 +100,34 @@ export const useVoiceRecognition = (options = {}) => {
95
100
  }
96
101
  }
97
102
  };
98
- if (enablePartialResults) {
99
- Voice.onSpeechPartialResults = (e) => {
100
- if (e.value && e.value.length > 0) {
101
- setPartialResults(e.value);
102
- // Reset silence timer on partial results (user is speaking)
103
- if (continuous && silenceTimerRef.current) {
104
- clearTimeout(silenceTimerRef.current);
105
- silenceTimerRef.current = setTimeout(() => {
106
- if (shouldContinue) {
107
- stop();
103
+ Voice.onSpeechPartialResults = (e) => {
104
+ if (e.value && e.value.length > 0) {
105
+ setPartialResults(e.value);
106
+ const firstPartial = e.value[0];
107
+ if (firstPartial) {
108
+ onPartialResult?.(firstPartial);
109
+ }
110
+ // Reset silence timer on partial results (user is speaking)
111
+ if (continuous && silenceTimerRef.current) {
112
+ clearTimeout(silenceTimerRef.current);
113
+ silenceTimerRef.current = setTimeout(async () => {
114
+ if (shouldContinueRef.current) {
115
+ shouldContinueRef.current = false;
116
+ if (silenceTimerRef.current) {
117
+ clearTimeout(silenceTimerRef.current);
118
+ silenceTimerRef.current = null;
108
119
  }
109
- }, maxSilenceDuration);
110
- }
120
+ await Voice.stop();
121
+ }
122
+ }, maxSilenceDuration);
111
123
  }
112
- };
113
- }
124
+ }
125
+ };
126
+ Voice.onSpeechVolumeChanged = (e) => {
127
+ if (e.value !== undefined) {
128
+ onVolumeChanged?.(e.value);
129
+ }
130
+ };
114
131
  // Cleanup
115
132
  return () => {
116
133
  Voice.destroy().then(Voice.removeAllListeners);
@@ -118,9 +135,13 @@ export const useVoiceRecognition = (options = {}) => {
118
135
  }, [
119
136
  enablePartialResults,
120
137
  onResult,
138
+ onPartialResult,
121
139
  onError,
140
+ onStart,
141
+ onEnd,
142
+ onRecognized,
143
+ onVolumeChanged,
122
144
  continuous,
123
- shouldContinue,
124
145
  locale,
125
146
  maxSilenceDuration,
126
147
  ]);
@@ -132,7 +153,7 @@ export const useVoiceRecognition = (options = {}) => {
132
153
  setPartialResults([]);
133
154
  accumulatedTextRef.current = '';
134
155
  }
135
- setShouldContinue(true);
156
+ shouldContinueRef.current = true;
136
157
  // Check permission (Android only)
137
158
  const hasPermission = await Voice.checkMicrophonePermission();
138
159
  if (!hasPermission) {
@@ -147,9 +168,14 @@ export const useVoiceRecognition = (options = {}) => {
147
168
  });
148
169
  // Start silence timer if in continuous mode
149
170
  if (continuous) {
150
- silenceTimerRef.current = setTimeout(() => {
151
- if (shouldContinue) {
152
- stop();
171
+ silenceTimerRef.current = setTimeout(async () => {
172
+ if (shouldContinueRef.current) {
173
+ shouldContinueRef.current = false;
174
+ if (silenceTimerRef.current) {
175
+ clearTimeout(silenceTimerRef.current);
176
+ silenceTimerRef.current = null;
177
+ }
178
+ await Voice.stop();
153
179
  }
154
180
  }, maxSilenceDuration);
155
181
  }
@@ -157,20 +183,13 @@ export const useVoiceRecognition = (options = {}) => {
157
183
  catch (e) {
158
184
  const errorMessage = e instanceof Error ? e.message : 'Failed to start recording';
159
185
  setError(errorMessage);
160
- setShouldContinue(false);
186
+ shouldContinueRef.current = false;
161
187
  onError?.(errorMessage);
162
188
  }
163
- }, [
164
- locale,
165
- enablePartialResults,
166
- onError,
167
- continuous,
168
- maxSilenceDuration,
169
- shouldContinue,
170
- ]);
189
+ }, [locale, enablePartialResults, onError, continuous, maxSilenceDuration]);
171
190
  const stop = useCallback(async () => {
172
191
  try {
173
- setShouldContinue(false);
192
+ shouldContinueRef.current = false;
174
193
  if (silenceTimerRef.current) {
175
194
  clearTimeout(silenceTimerRef.current);
176
195
  silenceTimerRef.current = null;
@@ -185,7 +204,7 @@ export const useVoiceRecognition = (options = {}) => {
185
204
  }, [onError]);
186
205
  const cancel = useCallback(async () => {
187
206
  try {
188
- setShouldContinue(false);
207
+ shouldContinueRef.current = false;
189
208
  if (silenceTimerRef.current) {
190
209
  clearTimeout(silenceTimerRef.current);
191
210
  silenceTimerRef.current = null;
@@ -207,7 +226,7 @@ export const useVoiceRecognition = (options = {}) => {
207
226
  setError(null);
208
227
  setIsRecording(false);
209
228
  accumulatedTextRef.current = '';
210
- setShouldContinue(false);
229
+ shouldContinueRef.current = false;
211
230
  if (silenceTimerRef.current) {
212
231
  clearTimeout(silenceTimerRef.current);
213
232
  silenceTimerRef.current = null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-native-voice-ts",
3
3
  "description": "Advanced Speech-to-Text library for React Native with TypeScript support. Features ready-to-use components (VoiceMicrophone), custom hooks (useVoiceRecognition), real-time transcription, multi-language support, and comprehensive voice recognition capabilities for iOS and Android.",
4
- "version": "1.0.3",
4
+ "version": "1.0.5",
5
5
  "author": "Noor Mohammad <noor.jsdivs@gmail.com>",
6
6
  "private": false,
7
7
  "homepage": "https://github.com/noorjsdivs/react-native-voice-ts",
@@ -94,7 +94,7 @@
94
94
  "lint:plugin": "eslint plugin/src/* --fix",
95
95
  "clean": "rm -rf dist && rm -rf plugin/build",
96
96
  "prepack": "yarn clean && yarn build && yarn build:plugin",
97
- "test": "jest",
97
+ "test": "echo \"No tests in library root. Run 'yarn --cwd example test' to test the example app.\"",
98
98
  "validate": "yarn type-check && yarn lint:check && yarn format:check"
99
99
  },
100
100
  "dependencies": {