UNPKG

mediasfu-reactnative-expo

Version:

mediasfu-reactnative-expo – Expo-managed React Native WebRTC SDK for video conferencing, webinars, live streaming, broadcast, screen sharing, whiteboard, chat, recording, live subtitles, translation, and AI agent rooms on iOS, Android, and web. Prebuilt r

247 lines (245 loc) 7.89 kB
// ConfirmHereModal.tsx import React, { useEffect, useState } from 'react'; import { Modal, View, Text, Pressable, StyleSheet, Dimensions, ActivityIndicator, } from 'react-native'; import { getModalBodyTheme } from '../../components_modern/core/modalBodyTheme'; /** * ConfirmHereModal - Inactivity detection and presence confirmation dialog * * ConfirmHereModal is a React Native component that displays a fullscreen modal * asking the user to confirm their presence. If no confirmation is received within * the countdown duration, it emits a 'disconnectUser' event via Socket.io to remove * the inactive user from the meeting. * * **Key Features:** * - Fullscreen presence confirmation prompt * - Countdown timer with real-time updates * - Auto-disconnect on timeout (emits 'disconnectUser' event) * - Manual confirmation button ("Yes, I'm here") * - Dual socket support (primary + local) * - Loading spinner during countdown * - Customizable countdown duration * * **UI Customization:** * This component can be replaced via `uiOverrides.confirmHereModal` to * provide a completely custom presence confirmation interface. * * @component * @param {ConfirmHereModalOptions} props - Configuration options * * @returns {JSX.Element} Rendered presence confirmation modal * * @example * ```tsx * // Basic usage with default 120-second countdown * import React, { useState } from 'react'; * import { ConfirmHereModal } from 'mediasfu-reactnative-expo'; * import { io } from 'socket.io-client'; * * const socket = io('https://your-server.com'); * const [showConfirmHere, setShowConfirmHere] = useState(false); * * return ( * <ConfirmHereModal * isConfirmHereModalVisible={showConfirmHere} * onConfirmHereClose={() => setShowConfirmHere(false)} * socket={socket} * roomName="meeting-room-123" * member="user-id-456" * /> * ); * ``` * * @example * ```tsx * // With custom countdown duration and styling * const localSocket = io('https://local-server.com'); * * return ( * <ConfirmHereModal * isConfirmHereModalVisible={isInactive} * onConfirmHereClose={handleConfirmClose} * countdownDuration={60} // 60 seconds instead of default 120 * socket={socketConnection} * localSocket={localSocket} * roomName={roomId} * member={userId} * backgroundColor="#ff6b6b" * /> * ); * ``` * * @example * ```tsx * // Using custom UI via uiOverrides * const config = { * uiOverrides: { * confirmHereModal: { * component: MyCustomPresenceCheck, * injectedProps: { * theme: 'dark', * warningSound: true, * }, * }, * }, * }; * * return <MyMeetingComponent config={config} />; * ``` */ let countdownInterval; function startCountdown({ duration, onConfirm, onUpdateCounter, socket, localSocket, roomName, member, }) { let timeRemaining = duration; countdownInterval = setInterval(() => { timeRemaining--; onUpdateCounter(timeRemaining); if (timeRemaining <= 0) { clearInterval(countdownInterval); socket.emit('disconnectUser', { member, roomName, ban: false, }); try { if (localSocket && localSocket.id) { localSocket.emit("disconnectUser", { member: member, roomName: roomName, ban: false, }); } } catch { // Do nothing } onConfirm(); } }, 1000); } const ConfirmHereModal = ({ isConfirmHereModalVisible, onConfirmHereClose, backgroundColor = '#83c0e9', isDarkMode, countdownDuration = 120, socket, roomName, member, style, renderContent, renderContainer, }) => { const [counter, setCounter] = useState(countdownDuration); const screenWidth = Dimensions.get('window').width; let modalWidth = 0.8 * screenWidth; if (modalWidth > 400) { modalWidth = 400; } useEffect(() => { if (isConfirmHereModalVisible) { startCountdown({ duration: countdownDuration, onConfirm: onConfirmHereClose, onUpdateCounter: setCounter, socket, roomName, member, }); } }, [ isConfirmHereModalVisible, countdownDuration, socket, roomName, member, onConfirmHereClose, ]); const handleConfirmHere = () => { setCounter(countdownDuration); // Reset counter if needed onConfirmHereClose(); // Close the modal // Additional logic if needed }; const dimensions = { width: modalWidth, height: 0 }; const theme = getModalBodyTheme(isDarkMode); const shouldUseModernTheme = typeof isDarkMode === 'boolean'; const defaultContent = (<View style={styles.modalBody}> {/* Spinner */} <ActivityIndicator size="large" color={theme.iconColor} style={styles.spinnerContainer}/> {/* Modal Content */} <Text style={[styles.modalTitle, { color: theme.textColor }]}>Are you still there?</Text> <Text style={[styles.modalMessage, { color: theme.textColor }]}> Please confirm if you are still present. </Text> <Text style={[styles.modalCounter, { color: theme.mutedTextColor }]}> Time remaining: <Text style={[styles.counterText, { color: theme.textColor }]}>{counter}</Text>{' '} seconds </Text> {/* Confirm Button */} <Pressable onPress={handleConfirmHere} style={[styles.confirmButton, { backgroundColor: theme.buttonBackgroundColor }]}> <Text style={[styles.confirmButtonText, { color: theme.buttonTextColor }]}>Yes</Text> </Pressable> </View>); const content = renderContent ? renderContent({ defaultContent, dimensions }) : defaultContent; const defaultContainer = (<Modal transparent animationType="slide" visible={isConfirmHereModalVisible} onRequestClose={onConfirmHereClose}> <View style={styles.modalContainer}> <View style={[styles.modalContent, { backgroundColor, width: modalWidth }, shouldUseModernTheme ? { borderColor: theme.borderColor, borderWidth: 1 } : null, style]}> {content} </View> </View> </Modal>); return renderContainer ? renderContainer({ defaultContainer, dimensions }) : defaultContainer; }; export default ConfirmHereModal; /** * Stylesheet for the ConfirmHereModal component. */ const styles = StyleSheet.create({ modalContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', }, modalContent: { height: '60%', backgroundColor: '#83c0e9', borderRadius: 10, padding: 20, maxWidth: '80%', zIndex: 9, elevation: 9, }, modalBody: { flex: 1, justifyContent: 'center', alignItems: 'center', }, spinnerContainer: { marginBottom: 20, }, spinnerIcon: { fontSize: 50, color: 'black', }, modalTitle: { fontSize: 18, fontWeight: 'bold', color: 'black', marginBottom: 10, }, modalMessage: { fontSize: 16, color: 'black', marginVertical: 15, textAlign: 'center', }, modalCounter: { fontSize: 14, color: 'black', marginBottom: 10, }, counterText: { fontWeight: 'bold', }, confirmButton: { backgroundColor: '#dc3545', padding: 10, borderRadius: 5, }, confirmButtonText: { color: 'white', fontWeight: 'bold', paddingHorizontal: 20, }, }); //# sourceMappingURL=ConfirmHereModal.js.map