@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
141 lines (130 loc) • 4.24 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _reactNative = require("@livekit/react-native");
var _react = require("react");
var _jsxRuntime = require("react/jsx-runtime");
// export interface VoiceProps {
// url: string;
// token: string;
// onDisconnected: (data: string) => void;
// onConnected: (data: string) => void;
// }
// 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] = (0, _react.useState)(audioSessionActive);
const mountedRef = (0, _react.useRef)(true);
const connectedRef = (0, _react.useRef)(false);
// Start audio session safely - prevent multiple simultaneous starts
const startAudioSession = async () => {
if (audioSessionActive || audioSessionStarting) {
console.log('Audio session already active or starting');
return;
}
try {
audioSessionStarting = true;
await _reactNative.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 _reactNative.AudioSession.stopAudioSession();
audioSessionActive = false;
} catch (err) {
// todo: handle failed to stop audio session
} finally {
audioSessionStopping = false;
}
};
// Setup audio session
(0, _react.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
(0, _react.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) {
console.warn('Voice component missing url or 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__*/(0, _jsxRuntime.jsx)(_reactNative.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');
}
});
};
var _default = exports.default = Voice;
//# sourceMappingURL=EmbedVoice.js.map