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
JavaScript
// 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