@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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qafka/react-native",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "description": "Drop-in AI assistant for React Native: chat, voice, and tool execution with screen-aware navigation.",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",