react-native-voice-ts 1.0.2 → 1.0.3
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.
- package/README.md +287 -77
- package/dist/NativeVoiceAndroid.d.ts +0 -1
- package/dist/NativeVoiceAndroid.js +0 -1
- package/dist/NativeVoiceIOS.d.ts +0 -1
- package/dist/NativeVoiceIOS.js +0 -1
- package/dist/VoiceModuleTypes.d.ts +0 -1
- package/dist/VoiceModuleTypes.js +0 -1
- package/dist/VoiceUtilTypes.d.ts +0 -1
- package/dist/VoiceUtilTypes.js +0 -1
- package/dist/components/MicIcon.d.ts +24 -5
- package/dist/components/MicIcon.js +71 -13
- package/dist/components/VoiceMicrophone.d.ts +0 -1
- package/dist/components/VoiceMicrophone.js +0 -1
- package/dist/components/index.d.ts +1 -2
- package/dist/components/index.js +1 -2
- package/dist/hooks/index.d.ts +0 -1
- package/dist/hooks/index.js +0 -1
- package/dist/hooks/useVoiceRecognition.d.ts +0 -1
- package/dist/hooks/useVoiceRecognition.js +0 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/package.json +3 -7
- package/CONTRIBUTING.md +0 -293
- package/dist/NativeVoiceAndroid.d.ts.map +0 -1
- package/dist/NativeVoiceAndroid.js.map +0 -1
- package/dist/NativeVoiceIOS.d.ts.map +0 -1
- package/dist/NativeVoiceIOS.js.map +0 -1
- package/dist/VoiceModuleTypes.d.ts.map +0 -1
- package/dist/VoiceModuleTypes.js.map +0 -1
- package/dist/VoiceUtilTypes.d.ts.map +0 -1
- package/dist/VoiceUtilTypes.js.map +0 -1
- package/dist/components/MicIcon.d.ts.map +0 -1
- package/dist/components/MicIcon.js.map +0 -1
- package/dist/components/VoiceMicrophone.d.ts.map +0 -1
- package/dist/components/VoiceMicrophone.js.map +0 -1
- package/dist/components/index.d.ts.map +0 -1
- package/dist/components/index.js.map +0 -1
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/hooks/index.js.map +0 -1
- package/dist/hooks/useVoiceRecognition.d.ts.map +0 -1
- package/dist/hooks/useVoiceRecognition.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/ios/Voice.xcodeproj/project.xcworkspace/xcuserdata/olumayowadaniel.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/Voice.xcodeproj/project.xcworkspace/xcuserdata/rudie_shahinian.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/plugin/src/withVoice.ts +0 -74
- package/plugin/tsconfig.json +0 -10
- package/plugin/tsconfig.tsbuildinfo +0 -1
- package/src/NativeVoiceAndroid.ts +0 -28
- package/src/NativeVoiceIOS.ts +0 -24
- package/src/VoiceModuleTypes.ts +0 -64
- package/src/VoiceUtilTypes.ts +0 -46
- package/src/components/MicIcon.tsx +0 -72
- package/src/components/VoiceMicrophone.tsx +0 -345
- package/src/components/index.ts +0 -4
- package/src/hooks/index.ts +0 -5
- package/src/hooks/useVoiceRecognition.ts +0 -333
- package/src/images/mic.svg +0 -16
- package/src/index.ts +0 -515
package/src/VoiceModuleTypes.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
export type SpeechEvents = {
|
|
2
|
-
onSpeechStart?: (e: SpeechStartEvent) => void;
|
|
3
|
-
onSpeechRecognized?: (e: SpeechRecognizedEvent) => void;
|
|
4
|
-
onSpeechEnd?: (e: SpeechEndEvent) => void;
|
|
5
|
-
onSpeechError?: (e: SpeechErrorEvent) => void;
|
|
6
|
-
onSpeechResults?: (e: SpeechResultsEvent) => void;
|
|
7
|
-
onSpeechPartialResults?: (e: SpeechResultsEvent) => void;
|
|
8
|
-
onSpeechVolumeChanged?: (e: SpeechVolumeChangeEvent) => void;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export type TranscriptionEvents = {
|
|
12
|
-
onTranscriptionStart?: (e: TranscriptionStartEvent) => void;
|
|
13
|
-
onTranscriptionEnd?: (e: TranscriptionEndEvent) => void;
|
|
14
|
-
onTranscriptionError?: (e: TranscriptionErrorEvent) => void;
|
|
15
|
-
onTranscriptionResults?: (e: TranscriptionResultsEvent) => void;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export type SpeechStartEvent = {
|
|
19
|
-
error?: boolean;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export type TranscriptionStartEvent = {
|
|
23
|
-
error?: boolean;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export type SpeechRecognizedEvent = {
|
|
27
|
-
isFinal?: boolean;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export type SpeechResultsEvent = {
|
|
31
|
-
value?: string[];
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export type TranscriptionResultsEvent = {
|
|
35
|
-
segments?: string[];
|
|
36
|
-
transcription?: string;
|
|
37
|
-
isFinal?: boolean;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export type SpeechErrorEvent = {
|
|
41
|
-
error?: {
|
|
42
|
-
code?: string;
|
|
43
|
-
message?: string;
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export type TranscriptionErrorEvent = {
|
|
48
|
-
error?: {
|
|
49
|
-
code?: string;
|
|
50
|
-
message?: string;
|
|
51
|
-
};
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
export type SpeechEndEvent = {
|
|
55
|
-
error?: boolean;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export type TranscriptionEndEvent = {
|
|
59
|
-
error?: boolean;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export type SpeechVolumeChangeEvent = {
|
|
63
|
-
value?: number;
|
|
64
|
-
};
|
package/src/VoiceUtilTypes.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* React Native Voice - Speech Recognition Utility Types
|
|
3
|
-
*
|
|
4
|
-
* This file contains helper types and interfaces for working with
|
|
5
|
-
* speech recognition functionality.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export interface VoiceOptions {
|
|
9
|
-
/** Android: Language model type (LANGUAGE_MODEL_FREE_FORM or LANGUAGE_MODEL_WEB_SEARCH) */
|
|
10
|
-
EXTRA_LANGUAGE_MODEL?: string;
|
|
11
|
-
/** Android: Maximum number of results */
|
|
12
|
-
EXTRA_MAX_RESULTS?: number;
|
|
13
|
-
/** Android: Enable partial results */
|
|
14
|
-
EXTRA_PARTIAL_RESULTS?: boolean;
|
|
15
|
-
/** Android: Auto request permissions */
|
|
16
|
-
REQUEST_PERMISSIONS_AUTO?: boolean;
|
|
17
|
-
/** iOS: Detection mode (automatic, manual) */
|
|
18
|
-
iosCategory?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface RecognitionStats {
|
|
22
|
-
/** Duration of recognition in milliseconds */
|
|
23
|
-
duration: number;
|
|
24
|
-
/** Whether recognition is currently active */
|
|
25
|
-
isActive: boolean;
|
|
26
|
-
/** Last recognized results */
|
|
27
|
-
lastResults: string[];
|
|
28
|
-
/** Start timestamp */
|
|
29
|
-
startTime: number;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface PermissionResult {
|
|
33
|
-
/** Whether permission was granted */
|
|
34
|
-
granted: boolean;
|
|
35
|
-
/** Error message if permission check failed */
|
|
36
|
-
error?: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface Language {
|
|
40
|
-
/** Language code (e.g., 'en-US') */
|
|
41
|
-
code: string;
|
|
42
|
-
/** Display name */
|
|
43
|
-
name: string;
|
|
44
|
-
/** Whether language is available */
|
|
45
|
-
available: boolean;
|
|
46
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import Svg, { Path, Rect } from 'react-native-svg';
|
|
3
|
-
|
|
4
|
-
export interface MicIconProps {
|
|
5
|
-
size?: number;
|
|
6
|
-
color?: string;
|
|
7
|
-
strokeWidth?: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Microphone Icon Component
|
|
12
|
-
* Based on Lucide mic icon
|
|
13
|
-
*/
|
|
14
|
-
export const MicIcon: React.FC<MicIconProps> = ({
|
|
15
|
-
size = 24,
|
|
16
|
-
color = 'currentColor',
|
|
17
|
-
strokeWidth = 2,
|
|
18
|
-
}) => {
|
|
19
|
-
return (
|
|
20
|
-
<Svg
|
|
21
|
-
width={size}
|
|
22
|
-
height={size}
|
|
23
|
-
viewBox="0 0 24 24"
|
|
24
|
-
fill="none"
|
|
25
|
-
stroke={color}
|
|
26
|
-
strokeWidth={strokeWidth}
|
|
27
|
-
strokeLinecap="round"
|
|
28
|
-
strokeLinejoin="round"
|
|
29
|
-
>
|
|
30
|
-
<Path d="M12 19v3" />
|
|
31
|
-
<Path d="M19 10v2a7 7 0 0 1-14 0v-2" />
|
|
32
|
-
<Rect x="9" y="2" width="6" height="13" rx="3" />
|
|
33
|
-
</Svg>
|
|
34
|
-
);
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export interface MicOffIconProps {
|
|
38
|
-
size?: number;
|
|
39
|
-
color?: string;
|
|
40
|
-
strokeWidth?: number;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Microphone Off Icon Component
|
|
45
|
-
* For recording/stop state
|
|
46
|
-
*/
|
|
47
|
-
export const MicOffIcon: React.FC<MicOffIconProps> = ({
|
|
48
|
-
size = 24,
|
|
49
|
-
color = 'currentColor',
|
|
50
|
-
strokeWidth = 2,
|
|
51
|
-
}) => {
|
|
52
|
-
return (
|
|
53
|
-
<Svg
|
|
54
|
-
width={size}
|
|
55
|
-
height={size}
|
|
56
|
-
viewBox="0 0 24 24"
|
|
57
|
-
fill="none"
|
|
58
|
-
stroke={color}
|
|
59
|
-
strokeWidth={strokeWidth}
|
|
60
|
-
strokeLinecap="round"
|
|
61
|
-
strokeLinejoin="round"
|
|
62
|
-
>
|
|
63
|
-
<Path d="M2 2l20 20" />
|
|
64
|
-
<Path d="M12 12a3 3 0 0 0 3-3V5a3 3 0 1 0-6 0v1" />
|
|
65
|
-
<Path d="M19 10v2a7 7 0 0 1-11.18 5.66" />
|
|
66
|
-
<Path d="M4.27 16.73A7 7 0 0 1 5 12v-2" />
|
|
67
|
-
<Path d="M12 17v5" />
|
|
68
|
-
</Svg>
|
|
69
|
-
);
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export default MicIcon;
|
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState, useCallback } from 'react';
|
|
2
|
-
import Voice from '../index';
|
|
3
|
-
import type { SpeechErrorEvent, SpeechResultsEvent } from '../VoiceModuleTypes';
|
|
4
|
-
|
|
5
|
-
export interface VoiceMicrophoneProps {
|
|
6
|
-
/**
|
|
7
|
-
* Callback fired when speech is recognized and converted to text
|
|
8
|
-
*/
|
|
9
|
-
onSpeechResult?: (text: string) => void;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Callback fired when partial results are available (real-time)
|
|
13
|
-
*/
|
|
14
|
-
onPartialResult?: (text: string) => void;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Callback fired when recording starts
|
|
18
|
-
*/
|
|
19
|
-
onStart?: () => void;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Callback fired when recording stops
|
|
23
|
-
*/
|
|
24
|
-
onStop?: () => void;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Callback fired when an error occurs
|
|
28
|
-
*/
|
|
29
|
-
onError?: (error: string) => void;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Language locale for speech recognition
|
|
33
|
-
* @default 'en-US'
|
|
34
|
-
*/
|
|
35
|
-
locale?: string;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Whether to automatically start recording on mount
|
|
39
|
-
* @default false
|
|
40
|
-
*/
|
|
41
|
-
autoStart?: boolean;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Whether to enable partial results (real-time transcription)
|
|
45
|
-
* @default true
|
|
46
|
-
*/
|
|
47
|
-
enablePartialResults?: boolean;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Whether to continue listening after getting results (continuous mode)
|
|
51
|
-
* When enabled, the microphone will automatically restart after getting results
|
|
52
|
-
* @default false
|
|
53
|
-
*/
|
|
54
|
-
continuous?: boolean;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Maximum silence duration in milliseconds before stopping (continuous mode)
|
|
58
|
-
* Only applies when continuous mode is enabled
|
|
59
|
-
* @default 5000 (5 seconds)
|
|
60
|
-
*/
|
|
61
|
-
maxSilenceDuration?: number;
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Custom render function for the component
|
|
65
|
-
* Receives isRecording state and control functions
|
|
66
|
-
*/
|
|
67
|
-
children?: (props: {
|
|
68
|
-
isRecording: boolean;
|
|
69
|
-
recognizedText: string;
|
|
70
|
-
partialText: string;
|
|
71
|
-
start: () => Promise<void>;
|
|
72
|
-
stop: () => Promise<void>;
|
|
73
|
-
cancel: () => Promise<void>;
|
|
74
|
-
error: string | null;
|
|
75
|
-
}) => React.ReactNode;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* VoiceMicrophone Component
|
|
80
|
-
*
|
|
81
|
-
* A ready-to-use voice recognition component that handles microphone access,
|
|
82
|
-
* speech recognition, and provides real-time text results.
|
|
83
|
-
*
|
|
84
|
-
* @example
|
|
85
|
-
* ```tsx
|
|
86
|
-
* // Simple usage with callback
|
|
87
|
-
* <VoiceMicrophone
|
|
88
|
-
* onSpeechResult={(text) => setSearchQuery(text)}
|
|
89
|
-
* />
|
|
90
|
-
*
|
|
91
|
-
* // Custom render with full control
|
|
92
|
-
* <VoiceMicrophone locale="en-US">
|
|
93
|
-
* {({ isRecording, recognizedText, start, stop }) => (
|
|
94
|
-
* <View>
|
|
95
|
-
* <Text>{recognizedText}</Text>
|
|
96
|
-
* <Button
|
|
97
|
-
* onPress={isRecording ? stop : start}
|
|
98
|
-
* title={isRecording ? 'Stop' : 'Start'}
|
|
99
|
-
* />
|
|
100
|
-
* </View>
|
|
101
|
-
* )}
|
|
102
|
-
* </VoiceMicrophone>
|
|
103
|
-
* ```
|
|
104
|
-
*/
|
|
105
|
-
const VoiceMicrophone: React.FC<VoiceMicrophoneProps> = ({
|
|
106
|
-
onSpeechResult,
|
|
107
|
-
onPartialResult,
|
|
108
|
-
onStart,
|
|
109
|
-
onStop,
|
|
110
|
-
onError,
|
|
111
|
-
locale = 'en-US',
|
|
112
|
-
autoStart = false,
|
|
113
|
-
enablePartialResults = true,
|
|
114
|
-
continuous = false,
|
|
115
|
-
maxSilenceDuration = 5000,
|
|
116
|
-
children,
|
|
117
|
-
}) => {
|
|
118
|
-
const [isRecording, setIsRecording] = useState(false);
|
|
119
|
-
const [recognizedText, setRecognizedText] = useState('');
|
|
120
|
-
const [partialText, setPartialText] = useState('');
|
|
121
|
-
const [error, setError] = useState<string | null>(null);
|
|
122
|
-
const [shouldContinue, setShouldContinue] = useState(false);
|
|
123
|
-
const silenceTimerRef = React.useRef<NodeJS.Timeout | null>(null);
|
|
124
|
-
|
|
125
|
-
useEffect(() => {
|
|
126
|
-
// Clear any existing timers on cleanup
|
|
127
|
-
return () => {
|
|
128
|
-
if (silenceTimerRef.current) {
|
|
129
|
-
clearTimeout(silenceTimerRef.current);
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
}, []);
|
|
133
|
-
|
|
134
|
-
useEffect(() => {
|
|
135
|
-
// Set up event listeners
|
|
136
|
-
Voice.onSpeechStart = () => {
|
|
137
|
-
setIsRecording(true);
|
|
138
|
-
setError(null);
|
|
139
|
-
if (silenceTimerRef.current) {
|
|
140
|
-
clearTimeout(silenceTimerRef.current);
|
|
141
|
-
}
|
|
142
|
-
onStart?.();
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
Voice.onSpeechEnd = async () => {
|
|
146
|
-
setIsRecording(false);
|
|
147
|
-
|
|
148
|
-
// In continuous mode, restart listening after results
|
|
149
|
-
if (continuous && shouldContinue) {
|
|
150
|
-
// Small delay before restarting
|
|
151
|
-
setTimeout(async () => {
|
|
152
|
-
if (shouldContinue) {
|
|
153
|
-
try {
|
|
154
|
-
await Voice.start(locale, {
|
|
155
|
-
EXTRA_PARTIAL_RESULTS: enablePartialResults,
|
|
156
|
-
});
|
|
157
|
-
} catch (err) {
|
|
158
|
-
console.error('Failed to restart voice recognition:', err);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}, 100);
|
|
162
|
-
} else {
|
|
163
|
-
onStop?.();
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
Voice.onSpeechError = (e: SpeechErrorEvent) => {
|
|
168
|
-
const errorMessage = e.error?.message || 'Unknown error';
|
|
169
|
-
setError(errorMessage);
|
|
170
|
-
setIsRecording(false);
|
|
171
|
-
setShouldContinue(false);
|
|
172
|
-
if (silenceTimerRef.current) {
|
|
173
|
-
clearTimeout(silenceTimerRef.current);
|
|
174
|
-
}
|
|
175
|
-
onError?.(errorMessage);
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
Voice.onSpeechResults = (e: SpeechResultsEvent) => {
|
|
179
|
-
if (e.value && e.value.length > 0) {
|
|
180
|
-
const text = e.value[0];
|
|
181
|
-
|
|
182
|
-
// In continuous mode, append new text to existing
|
|
183
|
-
if (continuous && recognizedText) {
|
|
184
|
-
const updatedText = recognizedText + ' ' + text;
|
|
185
|
-
setRecognizedText(updatedText);
|
|
186
|
-
onSpeechResult?.(updatedText);
|
|
187
|
-
} else {
|
|
188
|
-
setRecognizedText(text);
|
|
189
|
-
onSpeechResult?.(text);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
setPartialText('');
|
|
193
|
-
}
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
if (enablePartialResults) {
|
|
197
|
-
Voice.onSpeechPartialResults = (e: SpeechResultsEvent) => {
|
|
198
|
-
if (e.value && e.value.length > 0) {
|
|
199
|
-
const text = e.value[0];
|
|
200
|
-
setPartialText(text);
|
|
201
|
-
onPartialResult?.(text);
|
|
202
|
-
|
|
203
|
-
// Reset silence timer on partial results (user is speaking)
|
|
204
|
-
if (continuous && silenceTimerRef.current) {
|
|
205
|
-
clearTimeout(silenceTimerRef.current);
|
|
206
|
-
silenceTimerRef.current = setTimeout(() => {
|
|
207
|
-
if (shouldContinue) {
|
|
208
|
-
stop();
|
|
209
|
-
}
|
|
210
|
-
}, maxSilenceDuration);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Cleanup
|
|
217
|
-
return () => {
|
|
218
|
-
Voice.destroy().then(Voice.removeAllListeners);
|
|
219
|
-
};
|
|
220
|
-
}, [
|
|
221
|
-
onSpeechResult,
|
|
222
|
-
onPartialResult,
|
|
223
|
-
onStart,
|
|
224
|
-
onStop,
|
|
225
|
-
onError,
|
|
226
|
-
enablePartialResults,
|
|
227
|
-
continuous,
|
|
228
|
-
shouldContinue,
|
|
229
|
-
recognizedText,
|
|
230
|
-
locale,
|
|
231
|
-
maxSilenceDuration,
|
|
232
|
-
]);
|
|
233
|
-
|
|
234
|
-
// Auto-start if enabled
|
|
235
|
-
useEffect(() => {
|
|
236
|
-
if (autoStart) {
|
|
237
|
-
start();
|
|
238
|
-
}
|
|
239
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
240
|
-
}, [autoStart]);
|
|
241
|
-
|
|
242
|
-
const start = useCallback(async () => {
|
|
243
|
-
try {
|
|
244
|
-
setError(null);
|
|
245
|
-
if (!continuous) {
|
|
246
|
-
setRecognizedText('');
|
|
247
|
-
setPartialText('');
|
|
248
|
-
}
|
|
249
|
-
setShouldContinue(true);
|
|
250
|
-
|
|
251
|
-
// Check permission (Android only)
|
|
252
|
-
const hasPermission = await Voice.checkMicrophonePermission();
|
|
253
|
-
if (!hasPermission) {
|
|
254
|
-
const granted = await Voice.requestMicrophonePermission();
|
|
255
|
-
if (!granted) {
|
|
256
|
-
setError('Microphone permission denied');
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
await Voice.start(locale, {
|
|
262
|
-
EXTRA_PARTIAL_RESULTS: enablePartialResults,
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
// Start silence timer if in continuous mode
|
|
266
|
-
if (continuous) {
|
|
267
|
-
silenceTimerRef.current = setTimeout(() => {
|
|
268
|
-
if (shouldContinue) {
|
|
269
|
-
stop();
|
|
270
|
-
}
|
|
271
|
-
}, maxSilenceDuration);
|
|
272
|
-
}
|
|
273
|
-
} catch (e) {
|
|
274
|
-
const errorMessage =
|
|
275
|
-
e instanceof Error ? e.message : 'Failed to start recording';
|
|
276
|
-
setError(errorMessage);
|
|
277
|
-
setShouldContinue(false);
|
|
278
|
-
onError?.(errorMessage);
|
|
279
|
-
}
|
|
280
|
-
}, [
|
|
281
|
-
locale,
|
|
282
|
-
enablePartialResults,
|
|
283
|
-
onError,
|
|
284
|
-
continuous,
|
|
285
|
-
maxSilenceDuration,
|
|
286
|
-
shouldContinue,
|
|
287
|
-
]);
|
|
288
|
-
|
|
289
|
-
const stop = useCallback(async () => {
|
|
290
|
-
try {
|
|
291
|
-
setShouldContinue(false);
|
|
292
|
-
if (silenceTimerRef.current) {
|
|
293
|
-
clearTimeout(silenceTimerRef.current);
|
|
294
|
-
silenceTimerRef.current = null;
|
|
295
|
-
}
|
|
296
|
-
await Voice.stop();
|
|
297
|
-
onStop?.();
|
|
298
|
-
} catch (e) {
|
|
299
|
-
const errorMessage =
|
|
300
|
-
e instanceof Error ? e.message : 'Failed to stop recording';
|
|
301
|
-
setError(errorMessage);
|
|
302
|
-
onError?.(errorMessage);
|
|
303
|
-
}
|
|
304
|
-
}, [onError, onStop]);
|
|
305
|
-
|
|
306
|
-
const cancel = useCallback(async () => {
|
|
307
|
-
try {
|
|
308
|
-
setShouldContinue(false);
|
|
309
|
-
if (silenceTimerRef.current) {
|
|
310
|
-
clearTimeout(silenceTimerRef.current);
|
|
311
|
-
silenceTimerRef.current = null;
|
|
312
|
-
}
|
|
313
|
-
await Voice.cancel();
|
|
314
|
-
setRecognizedText('');
|
|
315
|
-
setPartialText('');
|
|
316
|
-
} catch (e) {
|
|
317
|
-
const errorMessage =
|
|
318
|
-
e instanceof Error ? e.message : 'Failed to cancel recording';
|
|
319
|
-
setError(errorMessage);
|
|
320
|
-
onError?.(errorMessage);
|
|
321
|
-
}
|
|
322
|
-
}, [onError]);
|
|
323
|
-
|
|
324
|
-
// If children render prop is provided, use it
|
|
325
|
-
if (children) {
|
|
326
|
-
return (
|
|
327
|
-
<>
|
|
328
|
-
{children({
|
|
329
|
-
isRecording,
|
|
330
|
-
recognizedText,
|
|
331
|
-
partialText,
|
|
332
|
-
start,
|
|
333
|
-
stop,
|
|
334
|
-
cancel,
|
|
335
|
-
error,
|
|
336
|
-
})}
|
|
337
|
-
</>
|
|
338
|
-
);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Default: render nothing (headless component)
|
|
342
|
-
return null;
|
|
343
|
-
};
|
|
344
|
-
|
|
345
|
-
export default VoiceMicrophone;
|
package/src/components/index.ts
DELETED