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

396 lines (384 loc) 14.3 kB
// DisplaySettingsModal.tsx import React, { useState } from 'react'; import { Modal, View, Text, Pressable, StyleSheet, Dimensions, } from 'react-native'; import { FontAwesome } from '@expo/vector-icons'; import RNPickerSelect from 'react-native-picker-select'; // Install using: npm install react-native-picker-select import { modifyDisplaySettings, } from '../../methods/displaySettingsMethods/modifyDisplaySettings'; import { createThemedPickerSelectStyles, getModalBodyTheme } from '../../components_modern/core/modalBodyTheme'; /** * DisplaySettingsModal - Visual display and optimization settings interface * * DisplaySettingsModal is a React Native component that provides controls for configuring * how the meeting content is displayed and rendered. It offers settings for display mode, * participant animations, full-screen forcing, and video performance optimization. * * **Key Features:** * - Display mode selection (video, media, all) * - Auto-wave animation toggle for participants * - Force full-screen display option * - Video rendering optimization toggle * - Position-configurable modal (4 corners) * - Real-time settings persistence * - Custom settings handler support * * **UI Customization:** * This component can be replaced via `uiOverrides.displaySettingsModal` to * provide a completely custom display settings interface. * * @component * @param {DisplaySettingsModalOptions} props - Configuration options * * @returns {JSX.Element} Rendered display settings modal * * @example * ```tsx * // Basic usage with default settings * import React, { useState } from 'react'; * import { DisplaySettingsModal } from 'mediasfu-reactnative-expo'; * * const [showSettings, setShowSettings] = useState(false); * * const displayParams = { * meetingDisplayType: 'video', * autoWave: true, * forceFullDisplay: false, * meetingVideoOptimized: false, * getUpdatedAllParams: () => ({ ...displayParams }), * }; * * return ( * <DisplaySettingsModal * isDisplaySettingsModalVisible={showSettings} * onDisplaySettingsClose={() => setShowSettings(false)} * parameters={displayParams} * /> * ); * ``` * * @example * ```tsx * // With custom position and styling * const handleModifySettings = async (options: ModifyDisplaySettingsOptions) => { * await modifyDisplaySettings(options); * console.log('Display settings updated'); * }; * * return ( * <DisplaySettingsModal * isDisplaySettingsModalVisible={showDisplaySettings} * onDisplaySettingsClose={() => setShowDisplaySettings(false)} * onModifyDisplaySettings={handleModifySettings} * parameters={displayParameters} * position="bottomRight" * backgroundColor="#2c3e50" * /> * ); * ``` * * @example * ```tsx * // With performance optimization enabled * const optimizedParams = { * meetingDisplayType: 'video', * autoWave: false, // Disable for better performance * forceFullDisplay: true, * meetingVideoOptimized: true, // Enable video optimization * getUpdatedAllParams: () => ({ ...optimizedParams }), * }; * * return ( * <DisplaySettingsModal * isDisplaySettingsModalVisible={isVisible} * onDisplaySettingsClose={handleClose} * parameters={optimizedParams} * /> * ); * ``` * * @example * ```tsx * // Using custom UI via uiOverrides * const config = { * uiOverrides: { * displaySettingsModal: { * component: MyCustomDisplaySettings, * injectedProps: { * theme: 'dark', * showAdvancedOptions: true, * }, * }, * }, * }; * * return <MyMeetingComponent config={config} />; * ``` */ const DisplaySettingsModal = ({ isDisplaySettingsModalVisible, onDisplaySettingsClose, onModifyDisplaySettings = modifyDisplaySettings, parameters, position = 'topRight', backgroundColor = '#83c0e9', isDarkMode, style, renderContent, renderContainer, }) => { const { meetingDisplayType, autoWave, forceFullDisplay, showSubtitlesOnCards, meetingVideoOptimized, } = parameters; const [meetingDisplayTypeState, setMeetingDisplayTypeState] = useState(meetingDisplayType); const [autoWaveState, setAutoWaveState] = useState(autoWave); const [forceFullDisplayState, setForceFullDisplayState] = useState(forceFullDisplay); const [showSubtitlesOnCardsState, setShowSubtitlesOnCardsState] = useState(showSubtitlesOnCards ?? true); const [meetingVideoOptimizedState, setMeetingVideoOptimizedState] = useState(meetingVideoOptimized); const screenWidth = Dimensions.get('window').width; let modalWidth = 0.8 * screenWidth; if (modalWidth > 400) { modalWidth = 400; } /** * Handles saving the modified display settings. */ const handleSaveSettings = async () => { await onModifyDisplaySettings({ parameters: { ...parameters, meetingDisplayType: meetingDisplayTypeState, autoWave: autoWaveState, forceFullDisplay: forceFullDisplayState, showSubtitlesOnCards: showSubtitlesOnCardsState, meetingVideoOptimized: meetingVideoOptimizedState, }, }); onDisplaySettingsClose(); // Close modal after saving }; /** * Determines the alignment style based on the 'position' prop. * * @param {string} pos - The position string ('topRight', 'topLeft', 'bottomRight', 'bottomLeft'). * @returns {StyleProp<ViewStyle>} - The corresponding alignment style. */ const getModalPosition = (pos) => { const styles = { topRight: { justifyContent: 'flex-start', alignItems: 'flex-end', }, topLeft: { justifyContent: 'flex-start', alignItems: 'flex-start', }, bottomRight: { justifyContent: 'flex-end', alignItems: 'flex-end', }, bottomLeft: { justifyContent: 'flex-end', alignItems: 'flex-start', }, }; return styles[pos] || styles.topRight; }; const dimensions = { width: modalWidth, height: 0 }; const theme = getModalBodyTheme(isDarkMode); const shouldUseModernTheme = typeof isDarkMode === 'boolean'; const themedPickerSelectStyles = createThemedPickerSelectStyles(theme); const defaultContent = (<> {/* Header */} <View style={styles.modalHeader}> <Text style={[styles.modalTitle, { color: theme.textColor }]}>Display Settings</Text> <Pressable onPress={onDisplaySettingsClose} style={styles.btnCloseSettings} accessibilityRole="button" accessibilityLabel="Close Display Settings"> <FontAwesome name="times" style={[styles.icon, { color: theme.iconColor }]}/> </Pressable> </View> {/* Divider */} <View style={[styles.hr, { backgroundColor: theme.dividerColor }]}/> {/* Body */} <View style={styles.modalBody}> {/* Display Option Picker */} <View style={styles.formGroup}> <Text style={[styles.label, { color: theme.textColor }]}>Display Option:</Text> <RNPickerSelect onValueChange={(value) => setMeetingDisplayTypeState(value)} items={[ { label: 'Video Participants Only', value: 'video' }, { label: 'Media Participants Only', value: 'media' }, { label: 'Show All Participants', value: 'all' }, ]} value={meetingDisplayTypeState} style={themedPickerSelectStyles} placeholder={{}} useNativeAndroidPickerStyle={false}/> </View> {/* Separator */} <View style={[styles.sep, { backgroundColor: theme.dividerColor }]}/> {/* Display Audiographs Toggle */} <View style={styles.formCheck}> <Text style={[styles.label, { color: theme.textColor }]}>Display Audiographs</Text> <Pressable onPress={() => setAutoWaveState(!autoWaveState)} accessibilityRole="switch" accessibilityLabel="Toggle Display Audiographs"> <FontAwesome name="check" size={24} color={autoWaveState ? theme.successColor : theme.iconColor}/> </Pressable> </View> {/* Separator */} <View style={[styles.sep, { backgroundColor: theme.dividerColor }]}/> {/* Force Full Display Toggle */} <View style={styles.formCheck}> <Text style={[styles.label, { color: theme.textColor }]}>Force Full Display</Text> <Pressable onPress={() => setForceFullDisplayState(!forceFullDisplayState)} accessibilityRole="switch" accessibilityLabel="Toggle Force Full Display"> <FontAwesome name="check" size={24} color={forceFullDisplayState ? theme.successColor : theme.iconColor}/> </Pressable> </View> {/* Separator */} <View style={[styles.sep, { backgroundColor: theme.dividerColor }]}/> {/* Force Video Participants Toggle */} <View style={styles.formCheck}> <Text style={[styles.label, { color: theme.textColor }]}>Force Video Participants</Text> <Pressable onPress={() => setMeetingVideoOptimizedState(!meetingVideoOptimizedState)} accessibilityRole="switch" accessibilityLabel="Toggle Force Video Participants"> <FontAwesome name="check" size={24} color={meetingVideoOptimizedState ? theme.successColor : theme.iconColor}/> </Pressable> </View> {/* Separator */} <View style={[styles.sep, { backgroundColor: theme.dividerColor }]}/> {/* Show Subtitles on Cards Toggle */} <View style={styles.formCheck}> <Text style={[styles.label, { color: theme.textColor }]}>Show Subtitles on Cards</Text> <Pressable onPress={() => setShowSubtitlesOnCardsState(!showSubtitlesOnCardsState)} accessibilityRole="switch" accessibilityLabel="Toggle Show Subtitles on Cards"> <FontAwesome name="check" size={24} color={showSubtitlesOnCardsState ? theme.successColor : theme.iconColor}/> </Pressable> </View> {/* Separator */} <View style={[styles.sep, { backgroundColor: theme.dividerColor }]}/> </View> {/* Footer */} <View style={styles.modalFooter}> <Pressable onPress={handleSaveSettings} style={[styles.btnApplySettings, { backgroundColor: theme.buttonBackgroundColor }]} accessibilityRole="button" accessibilityLabel="Save Display Settings"> <Text style={[styles.btnText, { color: theme.buttonTextColor }]}>Save</Text> </Pressable> </View> </>); const content = renderContent ? renderContent({ defaultContent, dimensions }) : defaultContent; const defaultContainer = (<Modal transparent animationType="fade" visible={isDisplaySettingsModalVisible} onRequestClose={onDisplaySettingsClose}> <View style={[styles.modalContainer, getModalPosition(position)]}> <View style={[styles.modalContent, { backgroundColor, width: modalWidth }, shouldUseModernTheme ? { borderColor: theme.borderColor } : null, style]}> {content} </View> </View> </Modal>); return renderContainer ? renderContainer({ defaultContainer, dimensions }) : defaultContainer; }; export default DisplaySettingsModal; /** * Stylesheet for the DisplaySettingsModal component. */ const styles = StyleSheet.create({ modalContainer: { flex: 1, justifyContent: 'flex-end', alignItems: 'flex-end', zIndex: 9, elevation: 9, }, modalContent: { height: '65%', backgroundColor: '#83c0e9', borderRadius: 0, padding: 20, maxHeight: '65%', maxWidth: '70%', zIndex: 9, elevation: 9, borderWidth: 2, borderColor: 'black', borderStyle: 'solid', }, modalHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 15, }, modalTitle: { fontSize: 18, fontWeight: 'bold', color: 'black', }, btnCloseSettings: { padding: 5, }, icon: { fontSize: 24, color: 'black', }, hr: { height: 1, backgroundColor: 'black', marginVertical: 5, }, modalBody: { flex: 1, }, formGroup: { marginBottom: 10, }, label: { fontSize: 14, color: 'black', marginBottom: 5, fontWeight: 'bold', }, formCheck: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10, }, sep: { height: 1, backgroundColor: '#ffffff', marginVertical: 2, }, modalFooter: { marginTop: 10, flexDirection: 'row', justifyContent: 'flex-end', }, btnApplySettings: { flex: 1, padding: 5, borderRadius: 5, justifyContent: 'center', alignItems: 'center', backgroundColor: 'black', }, btnText: { color: 'white', fontSize: 14, }, }); /** * Stylesheet for the RNPickerSelect component. */ const pickerSelectStyles = StyleSheet.create({ inputIOS: { fontSize: 16, paddingVertical: 4, paddingHorizontal: 10, borderWidth: 1, borderColor: 'gray', borderRadius: 4, color: 'black', paddingRight: 30, // To ensure the text is never behind the icon backgroundColor: 'white', }, inputAndroid: { fontSize: 16, paddingHorizontal: 10, paddingVertical: 4, borderWidth: 0.5, borderColor: 'purple', borderRadius: 8, color: 'black', paddingRight: 30, // To ensure the text is never behind the icon backgroundColor: 'white', }, inputWeb: { fontSize: 14, paddingHorizontal: 5, paddingVertical: 1, borderWidth: 0.5, borderColor: 'purple', borderRadius: 8, color: 'black', paddingRight: 30, // To ensure the text is never behind the icon backgroundColor: 'white', marginBottom: 10, }, }); //# sourceMappingURL=DisplaySettingsModal.js.map