UNPKG

mediasfu-reactnative

Version:
348 lines (340 loc) 11.9 kB
// MediaSettingsModal.tsx import React, { useEffect, useState } from 'react'; import { Modal, View, Text, Pressable, StyleSheet, Dimensions, } from 'react-native'; import FontAwesome5 from 'react-native-vector-icons/FontAwesome5'; import RNPickerSelect from 'react-native-picker-select'; // Install using: npm install react-native-picker-select import { switchAudio } from '../../methods/streamMethods/switchAudio'; import { switchVideo } from '../../methods/streamMethods/switchVideo'; import { switchVideoAlt } from '../../methods/streamMethods/switchVideoAlt'; import { getModalPosition } from '../../methods/utils/getModalPosition'; /** * MediaSettingsModal provides a modal interface for users to adjust media settings, such as selecting audio and video input devices and switching cameras. * * @example * ```tsx * import React, { useState } from 'react'; * import { MediaSettingsModal } from 'mediasfu-reactnative'; * import { io } from 'socket.io-client'; * * const socket = io('https://your-server-url.com'); * const videoInputs = [ * { deviceId: 'videoDevice1', label: 'Front Camera' }, * { deviceId: 'videoDevice2', label: 'Back Camera' } * ]; * const audioInputs = [ * { deviceId: 'audioDevice1', label: 'Built-in Microphone' }, * { deviceId: 'audioDevice2', label: 'External Microphone' } * ]; * * function App() { * const [isModalVisible, setModalVisible] = useState(true); * * return ( * <View> * <Button title="Open Media Settings" onPress={() => setModalVisible(true)} /> * <MediaSettingsModal * isMediaSettingsModalVisible={isModalVisible} * onMediaSettingsClose={() => setModalVisible(false)} * position="bottomLeft" * backgroundColor="#f0f0f0" * parameters={{ * userDefaultVideoInputDevice: 'videoDevice1', * userDefaultAudioInputDevice: 'audioDevice1', * videoInputs, * audioInputs, * getUpdatedAllParams: () => ({ * userDefaultVideoInputDevice: 'videoDevice1', * userDefaultAudioInputDevice: 'audioDevice1', * videoInputs, * audioInputs, * }), * }} * /> * </View> * ); * } * * export default App; * ``` */ const MediaSettingsModal = ({ isMediaSettingsModalVisible, onMediaSettingsClose, switchCameraOnPress = switchVideoAlt, switchVideoOnPress = switchVideo, switchAudioOnPress = switchAudio, parameters, position = 'topRight', backgroundColor = '#83c0e9', }) => { const { userDefaultVideoInputDevice, videoInputs, audioInputs, userDefaultAudioInputDevice, // isBackgroundModalVisible, // updateIsBackgroundModalVisible, } = parameters; const [selectedVideoInput, setSelectedVideoInput] = useState(userDefaultVideoInputDevice); const [selectedAudioInput, setSelectedAudioInput] = useState(userDefaultAudioInputDevice); const [modalWidth, setModalWidth] = useState(0.8 * Dimensions.get('window').width); useEffect(() => { const updateDimensions = () => { let width = 0.8 * Dimensions.get('window').width; if (width > 400) { width = 400; } setModalWidth(width); }; const subscribe = Dimensions.addEventListener('change', updateDimensions); // Initial call updateDimensions(); return () => { subscribe.remove(); }; }, []); /** * Handles switching the camera. */ const handleSwitchCamera = async () => { try { await switchCameraOnPress({ parameters }); } catch (error) { console.error('Failed to switch camera:', error); // Optionally, implement alert or toast } }; /** * Handles switching the video input device. * @param {string} value - The device ID of the selected video input. */ const handleVideoSwitch = async (value) => { if (value !== selectedVideoInput) { setSelectedVideoInput(value); try { await switchVideoOnPress({ videoPreference: value, parameters }); } catch (error) { console.error('Failed to switch video input:', error); // Optionally, implement alert or toast } } }; /** * Handles switching the audio input device. * @param {string} value - The device ID of the selected audio input. */ const handleAudioSwitch = async (value) => { if (value !== selectedAudioInput) { setSelectedAudioInput(value); try { await switchAudioOnPress({ audioPreference: value, parameters }); } catch (error) { console.error('Failed to switch audio input:', error); // Optionally, implement alert or toast } } }; /** * Toggles the virtual background modal visibility. */ // const toggleVirtualBackground = () => { // updateIsBackgroundModalVisible(!isBackgroundModalVisible); // }; return (<Modal transparent animationType="fade" visible={isMediaSettingsModalVisible} onRequestClose={onMediaSettingsClose}> <View style={[styles.modalContainer, getModalPosition({ position })]}> <View style={[styles.modalContent, { backgroundColor, width: modalWidth }]}> {/* Header */} <View style={styles.modalHeader}> <Text style={styles.modalTitle}>Media Settings</Text> <Pressable onPress={onMediaSettingsClose} style={styles.btnCloseMediaSettings} accessibilityRole="button" accessibilityLabel="Close Media Settings Modal"> <FontAwesome5 name="times" style={styles.icon}/> </Pressable> </View> {/* Divider */} <View style={styles.hr}/> {/* Body */} <View style={styles.modalBody}> {/* Select Camera */} <View style={styles.formGroup}> <Text style={styles.label}> <FontAwesome5 name="camera" size={16} color="black"/> Select Camera: </Text> <RNPickerSelect onValueChange={(value) => handleVideoSwitch(value)} items={videoInputs.map((input) => ({ label: input.label || `Camera ${input.deviceId}`, value: input.deviceId, }))} value={selectedVideoInput || ''} style={pickerSelectStyles} placeholder={{ label: 'Select a camera...', value: '' }} useNativeAndroidPickerStyle={false}/> </View> {/* Separator */} <View style={styles.sep}/> {/* Select Microphone */} <View style={styles.formGroup}> <Text style={styles.label}> <FontAwesome5 name="microphone" size={16} color="black"/> Select Microphone: </Text> <RNPickerSelect onValueChange={(value) => handleAudioSwitch(value)} items={audioInputs.map((input) => ({ label: input.label || `Microphone ${input.deviceId}`, value: input.deviceId, }))} value={selectedAudioInput || ''} style={pickerSelectStyles} placeholder={{ label: 'Select a microphone...', value: '' }} useNativeAndroidPickerStyle={false}/> </View> {/* Separator */} <View style={styles.sep}/> {/* Switch Camera Button */} <View style={styles.formGroup}> <Pressable onPress={handleSwitchCamera} style={styles.switchCameraButton} accessibilityRole="button" accessibilityLabel="Switch Camera"> <Text style={styles.switchCameraButtonText}> <FontAwesome5 name="sync-alt" size={16} color="black"/> Switch Camera </Text> </Pressable> </View> {/* Separator */} {/* <View style={styles.sep} /> */} {/* Virtual Background Button - Not implemented */} {/* <View style={styles.formGroup}> <Pressable onPress={toggleVirtualBackground} style={styles.virtualBackgroundButton} accessibilityRole="button" accessibilityLabel="Toggle Virtual Background" > <Text style={styles.virtualBackgroundButtonText}> <FontAwesome5 name="photo-video" size={16} color="black" /> {' '} Virtual Background </Text> </Pressable> </View> */} </View> </View> </View> </Modal>); }; export default MediaSettingsModal; /** * Stylesheet for the MediaSettingsModal component. */ const styles = StyleSheet.create({ modalContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', }, modalContent: { height: '65%', backgroundColor: '#83c0e9', borderRadius: 10, padding: 10, maxHeight: '65%', maxWidth: '80%', overflow: 'scroll', borderWidth: 2, borderColor: 'black', // Shadow for iOS shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.25, shadowRadius: 4, // Elevation for Android elevation: 9, zIndex: 9, }, modalHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10, }, modalTitle: { fontSize: 18, fontWeight: 'bold', color: 'black', }, btnCloseMediaSettings: { padding: 5, }, icon: { fontSize: 20, color: 'black', }, hr: { borderBottomColor: 'black', borderBottomWidth: 1, marginVertical: 15, }, modalBody: { padding: 10, }, formGroup: { marginBottom: 20, }, label: { fontSize: 16, color: 'black', marginBottom: 5, fontWeight: 'bold', }, picker: { backgroundColor: 'white', borderRadius: 5, padding: 10, }, switchCameraButton: { backgroundColor: '#8cd3ff', paddingHorizontal: 5, paddingVertical: 15, borderRadius: 5, alignItems: 'center', }, switchCameraButtonText: { color: 'black', fontSize: 20, }, virtualBackgroundButton: { backgroundColor: '#8cd3ff', padding: 10, borderRadius: 5, alignItems: 'center', }, virtualBackgroundButtonText: { color: 'black', fontSize: 16, }, sep: { height: 1, backgroundColor: '#ffffff', marginVertical: 5, }, }); /** * Stylesheet for the RNPickerSelect component. */ const pickerSelectStyles = StyleSheet.create({ inputIOS: { fontSize: 16, paddingVertical: 12, paddingHorizontal: 5, 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: 5, paddingVertical: 8, borderWidth: 0.5, borderColor: 'purple', borderRadius: 8, color: 'black', paddingRight: 30, // To ensure the text is never behind the icon backgroundColor: 'white', marginVertical: 5, }, 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=MediaSettingsModal.js.map