@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
127 lines (117 loc) • 3.79 kB
JavaScript
;
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) {
console.log('Audio session already active or starting, skipping');
return;
}
try {
audioSessionStarting = true;
console.log('Starting audio session');
await AudioSession.startAudioSession();
audioSessionActive = true;
if (mountedRef.current) {
setAudioSessionStarted(true);
}
} catch (err) {
console.error('Failed to start audio session:', err);
} finally {
audioSessionStarting = false;
}
};
// Stop audio session safely - prevent multiple simultaneous stops
const stopAudioSession = async () => {
if (!audioSessionActive || audioSessionStopping) {
console.log('Audio session already inactive or stopping, skipping');
return;
}
try {
audioSessionStopping = true;
console.log('Stopping audio session');
await AudioSession.stopAudioSession();
audioSessionActive = false;
} catch (err) {
console.error('Failed to stop audio session:', err);
} finally {
audioSessionStopping = false;
}
};
// Setup audio session
useEffect(() => {
mountedRef.current = true;
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 (roomRef.current?.state !== 'connected' && !connectedRef.current) {
stopAudioSession();
} else {
console.log('Skipping audio session stop because room is still connected');
}
};
}, []);
// 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]);
// 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: () => {
console.log('LiveKitRoom connected');
connectedRef.current = true;
onConnected('connected');
},
room: roomRef.current || undefined,
onDisconnected: () => {
console.log('LiveKitRoom disconnected');
connectedRef.current = false;
onDisconnected('disconnected');
}
});
};
export default Voice;
//# sourceMappingURL=EmbedVoice.js.map