@qafka/react-native 2.3.0 → 2.3.2
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/dist/hooks/useVoiceChat.js +34 -2
- package/package.json +1 -1
|
@@ -20,6 +20,13 @@ function useVoiceChat({ apiUrl, apiKey, userContext, contextDescription, onToolS
|
|
|
20
20
|
(0, react_1.useEffect)(() => {
|
|
21
21
|
navigationSuggestionRef.current = navigationSuggestion;
|
|
22
22
|
}, [navigationSuggestion]);
|
|
23
|
+
// Set when an explicit (trigger:'user') navigation fires: we navigate
|
|
24
|
+
// immediately but defer closing the voice session until the AI finishes
|
|
25
|
+
// its turn (response.done), so the closing remark isn't cut off.
|
|
26
|
+
const pendingNavDisconnectRef = (0, react_1.useRef)(false);
|
|
27
|
+
// Ref mirror of `disconnect` so handleEvent (useCallback([])) can close the
|
|
28
|
+
// session without going stale.
|
|
29
|
+
const disconnectRef = (0, react_1.useRef)(undefined);
|
|
23
30
|
const serviceRef = (0, react_1.useRef)(null);
|
|
24
31
|
// Snapshot of the live AI transcript so `response.done` can push the
|
|
25
32
|
// finished text into history without depending on stale closure state.
|
|
@@ -116,11 +123,18 @@ function useVoiceChat({ apiUrl, apiKey, userContext, contextDescription, onToolS
|
|
|
116
123
|
transcriptRef.current = '';
|
|
117
124
|
setTranscript('');
|
|
118
125
|
setState('listening');
|
|
126
|
+
// Explicit navigation deferred its session close to here, so the AI's
|
|
127
|
+
// closing remark could finish first.
|
|
128
|
+
if (pendingNavDisconnectRef.current) {
|
|
129
|
+
pendingNavDisconnectRef.current = false;
|
|
130
|
+
disconnectRef.current?.();
|
|
131
|
+
}
|
|
119
132
|
return;
|
|
120
133
|
}
|
|
121
134
|
case 'session.closed':
|
|
122
135
|
setState('idle');
|
|
123
136
|
setNavigationSuggestion(null);
|
|
137
|
+
pendingNavDisconnectRef.current = false;
|
|
124
138
|
return;
|
|
125
139
|
case 'error':
|
|
126
140
|
setState('idle');
|
|
@@ -330,7 +344,11 @@ function useVoiceChat({ apiUrl, apiKey, userContext, contextDescription, onToolS
|
|
|
330
344
|
case 'navigation': {
|
|
331
345
|
const decision = (0, decide_voice_navigation_1.decideVoiceNavigation)(event.navigation);
|
|
332
346
|
if (decision.action === 'callback') {
|
|
347
|
+
// Explicit request: navigate now, then close the voice session once the
|
|
348
|
+
// AI finishes speaking (handled in response.done) so the closing remark
|
|
349
|
+
// plays and the session doesn't linger in the background.
|
|
333
350
|
onNavigationSuggestRef.current?.(decision.suggestion);
|
|
351
|
+
pendingNavDisconnectRef.current = true;
|
|
334
352
|
}
|
|
335
353
|
else {
|
|
336
354
|
setNavigationSuggestion(decision.suggestion);
|
|
@@ -340,8 +358,14 @@ function useVoiceChat({ apiUrl, apiKey, userContext, contextDescription, onToolS
|
|
|
340
358
|
}
|
|
341
359
|
}, []);
|
|
342
360
|
const connect = (0, react_1.useCallback)(async () => {
|
|
343
|
-
if (serviceRef.current?.isConnected)
|
|
361
|
+
if (serviceRef.current?.isConnected) {
|
|
362
|
+
// Re-entering the voice page after a swipe to chat (which calls pauseMic
|
|
363
|
+
// and stops native capture). The socket is still open, so the fresh-
|
|
364
|
+
// connect path below would no-op and leave the mic stopped — resume
|
|
365
|
+
// capture so the user can be heard again.
|
|
366
|
+
await serviceRef.current.resumeMic();
|
|
344
367
|
return;
|
|
368
|
+
}
|
|
345
369
|
setState('connecting');
|
|
346
370
|
const service = new RealtimeService_1.RealtimeService(apiUrl, apiKey);
|
|
347
371
|
if (getSessionToken)
|
|
@@ -391,11 +415,15 @@ function useVoiceChat({ apiUrl, apiKey, userContext, contextDescription, onToolS
|
|
|
391
415
|
transcriptRef.current = '';
|
|
392
416
|
setAmplitude(0);
|
|
393
417
|
setNavigationSuggestion(null);
|
|
418
|
+
pendingNavDisconnectRef.current = false;
|
|
394
419
|
// Reset both mute layers so the next session starts clean.
|
|
395
420
|
userMutedRef.current = false;
|
|
396
421
|
systemMutedRef.current = false;
|
|
397
422
|
setIsMuted(false);
|
|
398
423
|
}, []);
|
|
424
|
+
(0, react_1.useEffect)(() => {
|
|
425
|
+
disconnectRef.current = disconnect;
|
|
426
|
+
}, [disconnect]);
|
|
399
427
|
const pauseMic = (0, react_1.useCallback)(async () => {
|
|
400
428
|
await serviceRef.current?.pauseMic();
|
|
401
429
|
}, []);
|
|
@@ -407,7 +435,11 @@ function useVoiceChat({ apiUrl, apiKey, userContext, contextDescription, onToolS
|
|
|
407
435
|
setNavigationSuggestion(null);
|
|
408
436
|
if (current)
|
|
409
437
|
onNavigationSuggestRef.current?.(current);
|
|
410
|
-
|
|
438
|
+
// Consistent with explicit navigation: leaving for a screen ends the
|
|
439
|
+
// voice session. The AI turn is already finished here (button was shown
|
|
440
|
+
// after response.done), so close immediately.
|
|
441
|
+
disconnect();
|
|
442
|
+
}, [disconnect]);
|
|
411
443
|
const dismissNavigationSuggestion = (0, react_1.useCallback)(() => {
|
|
412
444
|
setNavigationSuggestion(null);
|
|
413
445
|
}, []);
|
package/package.json
CHANGED