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