UNPKG

@revrag-ai/embed-react-native

Version:

A powerful React Native library for integrating AI-powered voice agents into mobile applications. Features real-time voice communication, intelligent speech processing, customizable UI components, and comprehensive event handling for building conversation

136 lines (125 loc) 4 kB
"use strict"; import { AudioSession, LiveKitRoom } from '@livekit/react-native'; import { useEffect, useRef, useState } from 'react'; // export interface VoiceProps { // url: string; // token: string; // onDisconnected: (data: string) => void; // onConnected: (data: string) => void; // } import { jsx as _jsx } from "react/jsx-runtime"; // Track audio session state globally to prevent multiple starts/stops let audioSessionActive = false; let audioSessionStarting = false; let audioSessionStopping = false; // Track connection to prevent unmounting while connected let activeConnectionToken = null; const Voice = props => { const { url, token, onDisconnected, onConnected, roomRef } = props; const [audioSessionStarted, setAudioSessionStarted] = useState(audioSessionActive); const mountedRef = useRef(true); const connectedRef = useRef(false); // Start audio session safely - prevent multiple simultaneous starts const startAudioSession = async () => { if (audioSessionActive || audioSessionStarting) { // Avoid repeated logs; just skip if already active/starting return; } try { audioSessionStarting = true; await AudioSession.startAudioSession(); audioSessionActive = true; if (mountedRef.current) { setAudioSessionStarted(true); } } catch (err) { console.error('Failed to start audio session:', err); // Set as started anyway to prevent blocking if (mountedRef.current) { setAudioSessionStarted(true); } } finally { audioSessionStarting = false; } }; // Stop audio session safely - prevent multiple simultaneous stops const stopAudioSession = async () => { if (!audioSessionActive || audioSessionStopping) { // todo: handle audio session already inactive or stopping return; } try { audioSessionStopping = true; await AudioSession.stopAudioSession(); audioSessionActive = false; } catch (err) { // todo: handle failed to stop audio session } finally { audioSessionStopping = false; } }; // Setup audio session useEffect(() => { mountedRef.current = true; // Capture room at effect setup time for cleanup const room = roomRef.current; startAudioSession(); return () => { mountedRef.current = false; // IMPORTANT: Don't stop the audio session on unmount if there might be an active call // This prevents audio session start/stop loops when components remount if (room?.state !== 'connected' && !connectedRef.current) { stopAudioSession(); } else { // todo: handle skipping audio session stop because room is still connected } }; }, [roomRef]); // Track connection state changes to avoid unmounting while connected useEffect(() => { if (token) { activeConnectionToken = token; } return () => { // Only clear token when unmounting with this specific token if (activeConnectionToken === token) { activeConnectionToken = null; } }; }, [token]); // Safety check - don't render if we don't have valid props if (!url || !token) { return null; } // Only render LiveKitRoom when audio session is ready if (!audioSessionStarted) { return null; } // IMPORTANT: Never return empty fragment when connected! // Instead, always render the LiveKitRoom component to maintain the connection return /*#__PURE__*/_jsx(LiveKitRoom, { serverUrl: url, token: token, screen: false, audio: true, onConnected: () => { // todo: handle livekit room connected connectedRef.current = true; onConnected('connected'); }, room: roomRef.current || undefined, onDisconnected: () => { // todo: handle livekit room disconnected connectedRef.current = false; onDisconnected('disconnected'); } }); }; export default Voice; //# sourceMappingURL=EmbedVoice.js.map