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
325 lines (321 loc) • 11.6 kB
JavaScript
// RecordingModal.tsx
import React from 'react';
import { Modal, View, StyleSheet, ScrollView, Dimensions, Pressable, Text, } from 'react-native';
import { FontAwesome } from '@expo/vector-icons';
import StandardPanelComponent from './StandardPanelComponent';
import AdvancedPanelComponent from './AdvancedPanelComponent';
import { getModalPosition } from '../../methods/utils/getModalPosition';
const getRecordingDisplayAdvice = (parameters) => {
if (!parameters) {
return null;
}
const normalizedRecordingMediaOptions = parameters.recordingMediaOptions === 'all' ? 'video' : parameters.recordingMediaOptions;
if (!parameters.recordingVideoParticipantsFullRoomSupport &&
parameters.recordingVideoOptions === 'all' &&
normalizedRecordingMediaOptions === 'video' &&
parameters.meetingDisplayType === 'all' &&
!(parameters.breakOutRoomStarted && !parameters.breakOutRoomEnded)) {
return 'Meeting display is set to All. This recording setup may be blocked. Switch the meeting display to Media before confirming so only participants with active media are included.';
}
return null;
};
/**
* RecordingModal - Comprehensive recording settings with standard and advanced options
*
* RecordingModal is a feature-rich React Native modal for configuring and managing
* session recordings. It provides both standard (quick start) and advanced (detailed)
* panels for customizing video layout, background, text overlays, media options, and more.
*
* **Key Features:**
* - Tabbed interface (Standard / Advanced settings)
* - Video type selection (fullDisplay, mainScreen, custom layouts)
* - Display type options (video, media, all)
* - Background color customization
* - Participant name tags with color options
* - Custom text overlays with positioning
* - Video orientation control
* - Media/audio/video encoding options
* - HLS streaming enablement
* - Real-time preview of settings
*
* **UI Customization:**
* This component can be replaced via `uiOverrides.recordingModalComponent` to
* provide a completely custom recording modal implementation.
*
* @component
* @param {RecordingModalOptions} props - Configuration options for the RecordingModal component
*
* @returns {JSX.Element} Rendered recording modal with configuration panels
*
* @example
* // Basic usage - Display recording modal with default settings
* import React, { useState } from 'react';
* import { RecordingModal, confirmRecording, startRecording } from 'mediasfu-reactnative-expo';
*
* function RecordingControls() {
* const [showModal, setShowModal] = useState(false);
* const [recordingParams, setRecordingParams] = useState({
* recordPaused: false,
* recordingVideoType: 'fullDisplay',
* recordingDisplayType: 'video' as const,
* recordingBackgroundColor: '#ffffff',
* recordingNameTagsColor: '#000000',
* recordingOrientationVideo: 'landscape',
* recordingNameTags: true,
* recordingAddText: false,
* recordingCustomText: '',
* recordingCustomTextPosition: 'top',
* recordingCustomTextColor: '#000000',
* recordingMediaOptions: 'default',
* recordingAudioOptions: 'default',
* recordingVideoOptions: 'default',
* recordingAddHLS: false,
* eventType: 'conference' as const,
* updateRecordingVideoType: (value: string) => setRecordingParams({...recordingParams, recordingVideoType: value}),
* updateRecordingDisplayType: (value: 'video' | 'media' | 'all') => setRecordingParams({...recordingParams, recordingDisplayType: value}),
* // ... other update functions
* getUpdatedAllParams: () => recordingParams,
* });
*
* return (
* <>
* <Button title="Recording Settings" onPress={() => setShowModal(true)} />
* <RecordingModal
* isRecordingModalVisible={showModal}
* onClose={() => setShowModal(false)}
* confirmRecording={confirmRecording}
* startRecording={startRecording}
* parameters={recordingParams}
* />
* </>
* );
* }
*
* @example
* // With custom styling and positioning
* <RecordingModal
* isRecordingModalVisible={showRecordingModal}
* onClose={() => setShowRecordingModal(false)}
* backgroundColor="#1a1a2e"
* position="center"
* confirmRecording={async (options) => {
* console.log('Confirming recording settings');
* await confirmRecording(options);
* }}
* startRecording={async (options) => {
* console.log('Starting recording with settings:', options.parameters);
* await startRecording(options);
* }}
* parameters={{
* ...recordingParams,
* recordingBackgroundColor: '#0f3460',
* recordingNameTagsColor: '#e94560',
* recordingAddText: true,
* recordingCustomText: 'Company Webinar',
* recordingAddHLS: true,
* }}
* />
*
* @example
* // Using uiOverrides for complete modal replacement
* import { MyCustomRecordingModal } from './MyCustomRecordingModal';
*
* const sessionConfig = {
* credentials: { apiKey: 'your-api-key' },
* uiOverrides: {
* recordingModalComponent: {
* component: MyCustomRecordingModal,
* injectedProps: {
* theme: 'professional',
* presets: ['meeting', 'webinar', 'presentation'],
* },
* },
* },
* };
*
* // MyCustomRecordingModal.tsx
* export const MyCustomRecordingModal = (props: RecordingModalOptions & { theme: string; presets: string[] }) => {
* return (
* <Modal visible={props.isRecordingModalVisible} onRequestClose={props.onClose}>
* <View style={{ backgroundColor: props.theme === 'professional' ? '#2c3e50' : '#fff' }}>
* <Text>Recording Settings</Text>
* {props.presets.map(preset => (
* <Button key={preset} title={preset} onPress={() => applyPreset(preset)} />
* ))}
* <Button title="Start" onPress={() => props.startRecording({ parameters: props.parameters })} />
* </View>
* </Modal>
* );
* };
* );
* }
*
* export default App;
* ```
*/
const RecordingModal = ({ isRecordingModalVisible, onClose, backgroundColor = '#83c0e9', position = 'bottomRight', isDarkMode, confirmRecording, startRecording, parameters, style, renderContent, renderContainer, }) => {
if (!isRecordingModalVisible) {
return null;
}
const { recordPaused } = parameters;
const recordingDisplayAdvice = getRecordingDisplayAdvice(parameters);
const screenWidth = Dimensions.get('window').width;
let modalWidth = 0.75 * screenWidth;
if (modalWidth > 400) {
modalWidth = 400;
}
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)') : '#000000';
const defaultContent = (<>
{/* Header */}
<View style={styles.modalHeader}>
<Text style={[styles.modalTitle, { color: textColor }]}>
<FontAwesome name="bars" size={24} color={textColor}/>
{' Recording Settings'}
</Text>
<Pressable onPress={onClose} style={styles.closeButton}>
<FontAwesome name="times" size={24} color={textColor}/>
</Pressable>
</View>
<View style={[styles.separator, { backgroundColor: borderColor }]}/>
{/* Modal Body */}
<View style={styles.modalBody}>
<ScrollView style={styles.scrollView}>
<View style={styles.listGroup}>
<StandardPanelComponent parameters={{ ...parameters, isDarkMode }}/>
<AdvancedPanelComponent parameters={{ ...parameters, isDarkMode }}/>
</View>
</ScrollView>
</View>
<View style={[styles.separator, { backgroundColor: borderColor }]}/>
{/* Action Buttons */}
{recordingDisplayAdvice && (<View style={styles.adviceBox}>
<Text style={[styles.adviceText, { color: textColor }]}>{recordingDisplayAdvice}</Text>
</View>)}
<View style={styles.buttonRow}>
<Pressable style={[styles.button, styles.confirmButton]} onPress={() => confirmRecording({ parameters })}>
<Text style={styles.buttonText}>Confirm</Text>
</Pressable>
{!recordPaused && (<Pressable style={[styles.button, styles.startButton]} onPress={() => startRecording({ parameters })}>
<Text style={styles.buttonText}>
Start <FontAwesome name="play" size={16} color="#ffffff"/>
</Text>
</Pressable>)}
</View>
</>);
const content = renderContent
? renderContent({ defaultContent, dimensions })
: defaultContent;
const defaultContainer = (<Modal transparent animationType="slide" visible={isRecordingModalVisible} 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 RecordingModal;
/**
* Stylesheet for the RecordingModal component.
*/
const styles = StyleSheet.create({
modalContainer: {
flex: 1,
...StyleSheet.absoluteFillObject,
justifyContent: 'flex-end',
alignItems: 'flex-end',
zIndex: 9,
elevation: 9,
},
modalContent: {
height: '75%',
backgroundColor: '#ffffff', // Default background color
borderRadius: 10,
padding: 15,
maxHeight: '80%',
maxWidth: '75%',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 9,
zIndex: 9,
},
scrollView: {
flex: 1,
maxHeight: '100%',
maxWidth: '100%',
},
modalHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 15,
},
modalTitle: {
fontSize: 16,
fontWeight: 'bold',
color: 'black',
flexDirection: 'row',
alignItems: 'center',
},
closeButton: {
padding: 5,
},
separator: {
height: 1,
backgroundColor: '#000000',
marginVertical: 10,
},
modalBody: {
flex: 1,
},
buttonRow: {
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: 20,
},
adviceBox: {
marginTop: 12,
paddingHorizontal: 14,
paddingVertical: 12,
borderRadius: 10,
borderWidth: 1,
borderColor: 'rgba(245, 158, 11, 0.35)',
backgroundColor: 'rgba(245, 158, 11, 0.18)',
},
adviceText: {
color: 'black',
fontSize: 13,
fontWeight: '600',
lineHeight: 19,
},
button: {
flex: 1,
paddingVertical: 10,
borderRadius: 5,
justifyContent: 'center',
alignItems: 'center',
marginHorizontal: 5,
},
confirmButton: {
backgroundColor: '#4CAF50',
},
startButton: {
backgroundColor: '#f44336',
},
buttonText: {
color: 'black',
fontSize: 14,
},
listGroup: {
margin: 0,
padding: 0,
},
});
//# sourceMappingURL=RecordingModal.js.map