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

261 lines (255 loc) 8.79 kB
import React, { useEffect, useState } from "react"; import { Modal, View, Text, Pressable, StyleSheet, Dimensions, ScrollView, } from "react-native"; import { FontAwesome5 } from "@expo/vector-icons"; import CustomButtons from "./CustomButtons"; import MeetingIdComponent from "./MeetingIDComponent"; import MeetingPasscodeComponent from "./MeetingPasscodeComponent"; import ShareButtonsComponent from "./ShareButtonsComponent"; import { getModalPosition } from "../../methods/utils/getModalPosition"; /** * MenuModal - Meeting menu with room info, custom actions, and share options * * MenuModal is a comprehensive React Native modal component that displays meeting * information (room name, passcode), custom action buttons, and sharing capabilities. * It's typically accessed from a menu/hamburger button in the main UI. * * **Key Features:** * - Meeting ID and passcode display (for authorized users) * - Custom action buttons with icons and handlers * - Built-in share buttons for inviting participants * - Flexible positioning (4 screen corners) * - Scrollable content for long lists * - Responsive design for different screen sizes * * **UI Customization:** * This component can be replaced via `uiOverrides.menuModalComponent` to provide * a completely custom menu modal implementation. * * @component * @param {MenuModalOptions} props - Configuration options for the MenuModal component * * @returns {JSX.Element} Rendered menu modal with meeting info and actions * * @example * // Basic usage - Display menu modal with default settings * import React, { useState } from 'react'; * import { MenuModal } from 'mediasfu-reactnative-expo'; * * function MeetingControls() { * const [menuVisible, setMenuVisible] = useState(false); * * return ( * <> * <Button title="Menu" onPress={() => setMenuVisible(true)} /> * <MenuModal * isVisible={menuVisible} * onClose={() => setMenuVisible(false)} * backgroundColor="#83c0e9" * position="bottomRight" * roomName="MeetingRoom123" * adminPasscode="456789" * islevel="2" * eventType="conference" * shareButtons={true} * /> * </> * ); * } * * @example * // With custom action buttons * <MenuModal * isVisible={showMenu} * onClose={() => setShowMenu(false)} * backgroundColor="#1a1a2e" * position="topRight" * roomName="TeamStandup" * adminPasscode="secret123" * islevel="1" * eventType="webinar" * customButtons={[ * { * action: () => console.log('Settings clicked'), * show: true, * backgroundColor: '#4CAF50', * icon: 'cog', * text: 'Settings', * }, * { * action: () => console.log('Help clicked'), * show: true, * backgroundColor: '#2196F3', * icon: 'question-circle', * text: 'Help', * }, * ]} * shareButtons={true} * localLink="https://meet.example.com/room123" * /> * * @example * // Using uiOverrides for complete modal replacement * import { MyCustomMenuModal } from './MyCustomMenuModal'; * * const sessionConfig = { * credentials: { apiKey: 'your-api-key' }, * uiOverrides: { * menuModalComponent: { * component: MyCustomMenuModal, * injectedProps: { * theme: 'dark', * compactMode: true, * }, * }, * }, * }; * * // MyCustomMenuModal.tsx * export const MyCustomMenuModal = (props: MenuModalOptions & { theme: string; compactMode: boolean }) => { * return ( * <Modal visible={props.isVisible} onRequestClose={props.onClose}> * <View style={{ backgroundColor: props.theme === 'dark' ? '#000' : '#fff' }}> * <Text>Room: {props.roomName}</Text> * <Text>Passcode: {props.adminPasscode}</Text> * <Button title="Close" onPress={props.onClose} /> * </View> * </Modal> * ); * }; */ const MenuModal = ({ backgroundColor = "#83c0e9", isVisible, onClose, customButtons = [], shareButtons = true, position = "bottomRight", roomName, adminPasscode, islevel, eventType, localLink, isDarkMode, style, renderContent, renderContainer, }) => { const [modalWidth, setModalWidth] = useState(0.7 * Dimensions.get("window").width); useEffect(() => { const updateDimensions = () => { let width = 0.7 * Dimensions.get("window").width; if (width > 450) { width = 450; } setModalWidth(width); }; const subscribe = Dimensions.addEventListener("change", updateDimensions); // Initial call updateDimensions(); return () => { subscribe.remove(); }; }, []); const dimensions = { width: modalWidth, height: 0 }; const themed = typeof isDarkMode === 'boolean'; const textColor = themed ? (isDarkMode ? '#f8fafc' : '#0f172a') : 'black'; const borderColor = themed ? (isDarkMode ? 'rgba(226, 232, 240, 0.18)' : 'rgba(71, 85, 105, 0.22)') : 'black'; const inputBackgroundColor = themed ? (isDarkMode ? '#1e293b' : '#ffffff') : '#f0f0f0'; const inputBorderColor = themed ? (isDarkMode ? 'rgba(226, 232, 240, 0.22)' : 'rgba(71, 85, 105, 0.28)') : 'gray'; const defaultContent = (<> {/* Header */} <View style={styles.modalHeader}> <Text style={[styles.modalTitle, { color: textColor }]}> <FontAwesome5 name="bars" style={[styles.icon, { color: textColor }]}/> Menu </Text> <Pressable onPress={onClose} style={styles.closeButton} accessibilityRole="button" accessibilityLabel="Close Menu Modal"> <FontAwesome5 name="times" style={[styles.icon, { color: textColor }]}/> </Pressable> </View> {/* Divider */} <View style={[styles.hr, { backgroundColor: borderColor }]}/> <View style={styles.modalBody}> <ScrollView style={styles.scrollView}> <View style={styles.listGroup}> <CustomButtons buttons={customButtons} isDarkMode={isDarkMode}/> {/* Separator */} <View style={styles.separator}/> {/* Meeting Passcode - Visible only for level 2 users */} {islevel === "2" && (<MeetingPasscodeComponent meetingPasscode={adminPasscode} isDarkMode={isDarkMode}/>)} {/* Meeting ID */} <MeetingIdComponent meetingID={roomName} isDarkMode={isDarkMode}/> {/* Share Buttons */} {shareButtons && (<ShareButtonsComponent meetingID={roomName} eventType={eventType} localLink={localLink}/>)} </View> </ScrollView> </View> </>); const content = renderContent ? renderContent({ defaultContent, dimensions }) : defaultContent; const defaultContainer = (<Modal transparent animationType="fade" visible={isVisible} onRequestClose={onClose}> <View style={[styles.modalContainer, getModalPosition({ position })]}> <View style={[styles.modalContent, { backgroundColor, width: modalWidth }, style]}> {content} </View> </View> </Modal>); return renderContainer ? renderContainer({ defaultContainer, dimensions }) : defaultContainer; }; export default MenuModal; /** * Stylesheet for the MenuModal component. */ const styles = StyleSheet.create({ modalContainer: { flex: 1, ...StyleSheet.absoluteFillObject, justifyContent: "flex-end", alignItems: "flex-end", }, modalContent: { height: "70%", backgroundColor: "#83c0e9", borderRadius: 0, padding: 10, maxHeight: "70%", maxWidth: "75%", overflow: "hidden", borderWidth: 2, borderColor: "black", // Shadow for iOS shadowColor: "#000", shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.25, shadowRadius: 4, // Elevation for Android elevation: 5, }, scrollView: { flex: 1, maxHeight: "100%", maxWidth: "100%", }, modalHeader: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", marginBottom: 15, }, modalTitle: { fontSize: 18, fontWeight: "bold", color: "black", }, closeButton: { padding: 5, }, icon: { fontSize: 20, color: "black", }, hr: { height: 1, backgroundColor: "black", marginVertical: 15, }, modalBody: { flex: 1, }, listGroup: { margin: 0, padding: 0, }, separator: { height: 1, backgroundColor: "#ffffff", marginVertical: 10, }, }); //# sourceMappingURL=MenuModal.js.map