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
287 lines (284 loc) • 9.82 kB
JavaScript
import React, { useEffect, useState } from "react";
import { Modal, View, Text, Pressable, StyleSheet, ScrollView, Dimensions, TextInput, } from "react-native";
import { FontAwesome } from "@expo/vector-icons";
import RenderRequestComponent from "./RenderRequestComponent";
import { respondToRequests, } from "../../methods/requestsMethods/respondToRequests";
import { getModalPosition } from "../../methods/utils/getModalPosition";
import { getModalBodyTheme } from "../../components_modern/core/modalBodyTheme";
/**
* RequestsModal - Participant request management interface
*
* RequestsModal is a React Native component that displays pending participant requests
* (microphone, video, screenshare) with options to filter, accept, or reject each request.
* Host users can review and respond to requests in real-time via Socket.io.
*
* **Key Features:**
* - Scrollable list of participant requests (mic, video, screenshare)
* - Accept/reject actions for each request
* - Real-time filtering by participant name or request type
* - Request counter badge display
* - Socket.io synchronization for instant responses
* - Custom request item rendering
* - Position-configurable modal (5 positions)
*
* **UI Customization:**
* This component can be replaced via `uiOverrides.requestsModal` to
* provide a completely custom requests management interface.
*
* @component
* @param {RequestsModalOptions} props - Configuration options
*
* @returns {JSX.Element} Rendered requests modal
*
* @example
* ```tsx
* // Basic usage with request handling
* import React, { useState } from 'react';
* import { RequestsModal } from 'mediasfu-reactnative-expo';
* import { io } from 'socket.io-client';
*
* const socket = io('https://your-server.com');
* const [showRequests, setShowRequests] = useState(false);
* const [filter, setFilter] = useState('');
*
* const requests = [
* { id: '1', name: 'John Doe', icon: 'fa-microphone', username: 'john' },
* { id: '2', name: 'Jane Smith', icon: 'fa-video', username: 'jane' },
* { id: '3', name: 'Bob Wilson', icon: 'fa-desktop', username: 'bob' },
* ];
*
* return (
* <RequestsModal
* isRequestsModalVisible={showRequests}
* onRequestClose={() => setShowRequests(false)}
* requestCounter={requests.length}
* onRequestFilterChange={setFilter}
* requestList={requests}
* updateRequestList={(newList) => console.log('Updated:', newList)}
* roomName="meeting-room-123"
* socket={socket}
* parameters={{
* getUpdatedAllParams: () => ({ filteredRequestList: requests }),
* }}
* />
* );
* ```
*
* @example
* ```tsx
* // With custom positioning and request handling
* const handleRequestResponse = async (options: RespondToRequestsOptions) => {
* await respondToRequests(options);
* console.log('Request handled');
* };
*
* return (
* <RequestsModal
* isRequestsModalVisible={isVisible}
* onRequestClose={handleClose}
* requestCounter={pendingCount}
* onRequestFilterChange={handleFilterChange}
* onRequestItemPress={handleRequestResponse}
* requestList={pendingRequests}
* updateRequestList={setPendingRequests}
* roomName={roomId}
* socket={socketConnection}
* parameters={requestParams}
* position="bottomLeft"
* backgroundColor="#2c3e50"
* />
* );
* ```
*
* @example
* ```tsx
* // Using custom UI via uiOverrides
* const config = {
* uiOverrides: {
* requestsModal: {
* component: MyCustomRequestsManager,
* injectedProps: {
* theme: 'dark',
* showAutoApprove: true,
* },
* },
* },
* };
*
* return <MyMeetingComponent config={config} />;
* ```
*/
const RequestsModal = ({ isRequestsModalVisible, onRequestClose, requestCounter, onRequestFilterChange, onRequestItemPress = respondToRequests, requestList, updateRequestList, roomName, socket, renderRequestComponent = RenderRequestComponent, backgroundColor = "#83c0e9", isDarkMode, position = "topRight", parameters, style, renderContent, renderContainer, }) => {
const [filteredRequestList, setFilteredRequestList] = useState(requestList);
const [localRequestCounter, setLocalRequestCounter] = useState(requestCounter);
const [filterText, setFilterText] = useState("");
useEffect(() => {
if (!isRequestsModalVisible) {
return;
}
const { getUpdatedAllParams } = parameters;
const updatedParams = getUpdatedAllParams();
setFilteredRequestList(updatedParams.filteredRequestList);
setLocalRequestCounter(updatedParams.filteredRequestList.length);
}, [isRequestsModalVisible, requestList, parameters]);
if (!isRequestsModalVisible) {
return null;
}
const modalWidth = 0.8 * Dimensions.get("window").width > 350 ? 350 : 0.8 * Dimensions.get("window").width;
const dimensions = { width: modalWidth, height: 0 };
const theme = getModalBodyTheme(isDarkMode);
const shouldUseModernTheme = typeof isDarkMode === "boolean";
const requestRenderer = renderRequestComponent;
const defaultContent = (<>
{/* Header */}
<View style={styles.modalHeader}>
<Text style={[styles.modalTitle, { color: theme.textColor }]}>
Requests <Text style={[styles.badge, { backgroundColor: theme.badgeBackgroundColor, color: theme.badgeTextColor }]}>{localRequestCounter}</Text>
</Text>
<Pressable onPress={onRequestClose} style={styles.closeButton}>
<FontAwesome name="times" size={20} color={theme.iconColor}/>
</Pressable>
</View>
<View style={[styles.separator, { backgroundColor: theme.dividerColor }]}/>
{/* Filter Input */}
<View style={styles.modalBody}>
<View style={styles.filterContainer}>
<TextInput style={[
styles.input,
{
backgroundColor: theme.inputBackgroundColor,
borderColor: theme.borderColor,
color: theme.inputTextColor,
},
]} placeholder="Search ..." placeholderTextColor={theme.placeholderTextColor} value={filterText} onChangeText={(text) => {
setFilterText(text);
onRequestFilterChange(text);
}}/>
</View>
</View>
{/* Request List */}
<ScrollView style={styles.scrollView}>
<View style={styles.requestList}>
{filteredRequestList && filteredRequestList.length > 0 ? (filteredRequestList.map((requestItem, index) => (<View key={index} style={styles.requestItem}>
{requestRenderer({
request: requestItem,
onRequestItemPress,
requestList: filteredRequestList,
updateRequestList,
roomName,
socket,
isDarkMode,
})}
</View>))) : (<Text style={[styles.noRequestsText, { color: theme.mutedTextColor }]}>No requests found.</Text>)}
</View>
</ScrollView>
</>);
const content = renderContent
? renderContent({ defaultContent, dimensions })
: defaultContent;
const defaultContainer = (<Modal transparent animationType="fade" visible={isRequestsModalVisible} onRequestClose={onRequestClose}>
<View style={[styles.modalContainer, getModalPosition({ position }), shouldUseModernTheme ? { borderColor: theme.borderColor } : null]}>
<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 RequestsModal;
/**
* Stylesheet for the RequestsModal component.
*/
const styles = StyleSheet.create({
modalContainer: {
flex: 1,
...StyleSheet.absoluteFillObject,
justifyContent: "flex-end",
alignItems: "flex-end",
zIndex: 9,
elevation: 9,
borderWidth: 2,
borderColor: "black",
},
modalContent: {
height: "65%",
backgroundColor: "#fff",
borderRadius: 0,
padding: 20,
maxHeight: "65%",
maxWidth: "70%",
zIndex: 9,
elevation: 9,
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 4,
borderWidth: 2,
borderColor: "black",
},
modalHeader: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 15,
},
modalTitle: {
fontSize: 18,
fontWeight: "bold",
color: "black",
},
badge: {
backgroundColor: "#ffffff",
color: "#000",
borderRadius: 5,
paddingHorizontal: 10,
paddingVertical: 2,
marginLeft: 5,
},
closeButton: {
padding: 5,
},
modalBody: {
flex: 1,
},
separator: {
height: 1,
backgroundColor: "#ffffff",
marginVertical: 10,
},
filterContainer: {
marginBottom: 15,
},
input: {
height: 40,
borderColor: "gray",
borderWidth: 1,
borderRadius: 5,
paddingHorizontal: 10,
fontSize: 16,
},
scrollView: {
flex: 1,
maxHeight: "100%",
maxWidth: "100%",
},
requestList: {
flexGrow: 1,
},
requestItem: {
marginBottom: 10,
},
noRequestsText: {
textAlign: "center",
color: "gray",
fontSize: 16,
},
});
//# sourceMappingURL=RequestsModal.js.map