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 (394 loc) • 13.3 kB
JavaScript
import React, { useState, useRef, useEffect } from 'react';
import { View, Text, TextInput, Pressable, ScrollView, StyleSheet, } from 'react-native';
import { FontAwesome5 } from "@expo/vector-icons";
import { getModalBodyTheme } from '../../components_modern/core/modalBodyTheme';
/**
* MessagePanel component renders a panel for displaying and sending messages.
*
* @component
* @param {MessagePanelOptions} props - The properties for the MessagePanel component.
* @returns {JSX.Element} The rendered MessagePanel component.
*
* @example
* ```tsx
* import React from 'react';
* import { MessagePanel } from 'mediasfu-reactnative-expo';
*
* function App() {
* const messages = [
* { sender: 'Alice', message: 'Hello!', timestamp: '10:00', receivers: [], group: true },
* { sender: 'Bob', message: 'Hi Alice!', timestamp: '10:01', receivers: [], group: true },
* ];
*
* const handleSendMessage = async (options) => {
* // Logic to handle message sending
* console.log('Message sent:', options);
* };
*
* return (
* <MessagePanel
* messages={messages}
* messagesLength={messages.length}
* type="group"
* username="john_doe"
* onSendMessagePress={handleSendMessage}
* backgroundColor="#f5f5f5"
* focusedInput={true}
* eventType="conference"
* member="john_doe"
* islevel="1"
* startDirectMessage={false}
* updateStartDirectMessage={(start) => console.log('Start Direct Message:', start)}
* directMessageDetails={null}
* updateDirectMessageDetails={(participant) => console.log('Direct Message Participant:', participant)}
* coHostResponsibility={[{ name: 'chat', value: true }]}
* coHost="jane_doe"
* roomName="MainRoom"
* socket={socketInstance}
* chatSetting="default"
* />
* );
* }
*
* export default App;
* ```
*/
const MessagePanel = ({ messages, messagesLength, type, username, onSendMessagePress, backgroundColor = '#f5f5f5', focusedInput, showAlert, eventType, member, islevel, startDirectMessage, updateStartDirectMessage, directMessageDetails, updateDirectMessageDetails, coHostResponsibility, coHost, roomName, socket, chatSetting, isDarkMode, }) => {
const inputRef = useRef(null);
const theme = getModalBodyTheme(isDarkMode);
const shouldUseModernTheme = typeof isDarkMode === 'boolean';
const selfBubbleColor = shouldUseModernTheme
? isDarkMode
? 'rgba(37, 99, 235, 0.26)'
: 'rgba(219, 234, 254, 0.95)'
: '#DCF8C6';
const otherBubbleColor = shouldUseModernTheme
? isDarkMode
? 'rgba(15, 23, 42, 0.72)'
: 'rgba(241, 245, 249, 0.95)'
: '#1ce5c7';
const [replyInfo, setReplyInfo] = useState(null);
const [senderId, setSenderId] = useState(null);
const [directMessageText, setDirectMessageText] = useState('');
const [groupMessageText, setGroupMessageText] = useState('');
/**
* Handles changes in the message input field.
* @param {string} text - The text input value.
*/
const handleTextInputChange = (text) => {
if (type === 'direct') {
setDirectMessageText(text);
}
else {
setGroupMessageText(text);
}
};
const getDirectReceiverLabel = (receivers) => {
const normalizedReceivers = Array.isArray(receivers)
? receivers.filter((receiver) => receiver && receiver.trim().length > 0)
: [];
if (normalizedReceivers.length > 0) {
return normalizedReceivers.join(', ');
}
return '';
};
/**
* Opens the reply input field for a specific sender.
* @param {string} senderId - The username of the message sender.
*/
const openReplyInput = (senderId) => {
const replyInfoContainer = {
text: 'Replying to: ',
username: senderId,
};
setReplyInfo(replyInfoContainer);
setSenderId(senderId);
};
/**
* Handles the sending of messages.
*/
const handleSendButton = async () => {
const message = type === 'direct' ? directMessageText : groupMessageText;
if (!message) {
showAlert?.({
message: 'Please enter a message',
type: 'danger',
duration: 3000,
});
return;
}
if (message.length > 350) {
showAlert?.({
message: 'Message is too long',
type: 'danger',
duration: 3000,
});
return;
}
if (message.trim() === '') {
showAlert?.({
message: 'Message is not valid.',
type: 'danger',
duration: 3000,
});
return;
}
if (type === 'direct' && !senderId && islevel === '2') {
showAlert?.({
message: 'Please select a message to reply to',
type: 'danger',
duration: 3000,
});
return;
}
try {
await onSendMessagePress({
message,
receivers: type === 'direct' && senderId ? [senderId] : [],
group: type === 'group',
messagesLength,
member,
sender: username,
islevel,
showAlert,
coHostResponsibility,
coHost,
roomName,
socket,
chatSetting,
});
if (type === 'direct') {
setDirectMessageText('');
}
else {
setGroupMessageText('');
}
if (inputRef.current) {
inputRef.current.clear();
}
if (replyInfo) {
setReplyInfo(null);
setSenderId(null);
}
if (focusedInput) {
updateDirectMessageDetails(null);
updateStartDirectMessage(false);
}
}
catch {
showAlert?.({
message: 'Failed to send message.',
type: 'danger',
duration: 3000,
});
}
};
useEffect(() => {
if (startDirectMessage && directMessageDetails && focusedInput) {
inputRef.current?.focus();
const replyInfoContainer = {
text: 'Replying to: ',
username: directMessageDetails.name,
};
setReplyInfo(replyInfoContainer);
setSenderId(directMessageDetails.name);
}
else {
setReplyInfo(null);
if (inputRef.current) {
inputRef.current.clear();
}
}
}, [directMessageDetails, focusedInput, startDirectMessage]);
return (<View style={[styles.container, { backgroundColor }]}>
<ScrollView style={styles.messagesContainer}>
{messages.map((message, index) => (<View key={index} style={styles.messageWrapper}>
<View style={[
styles.messageContainer,
message.sender === username
? styles.selfMessage
: styles.otherMessage,
]}>
<View style={styles.messageHeader}>
{message.sender === username && !message.group && getDirectReceiverLabel(message.receivers) && (<Text style={[styles.receiverText, { color: theme.textColor }]}>
To: {getDirectReceiverLabel(message.receivers)}
</Text>)}
<Text style={[styles.senderText, { color: theme.textColor }]}>
{message.sender === username ? '' : message.sender}
</Text>
<Text style={[styles.timestampText, { color: theme.mutedTextColor }]}>{message.timestamp}</Text>
{message.sender !== username && !message.group && (<Pressable style={styles.replyButton} onPress={() => openReplyInput(message.sender)}>
<FontAwesome5 name="reply" size={12} color={theme.iconColor}/>
</Pressable>)}
</View>
<View style={[
message.sender === member
? styles.contentSelf
: styles.contentOther,
{ backgroundColor: message.sender === member ? selfBubbleColor : otherBubbleColor },
]}>
<Text style={[styles.messageText, { color: theme.textColor }]}>{message.message}</Text>
</View>
</View>
</View>))}
{/* Reply Info */}
{replyInfo && (<View style={[styles.replyInfoContainer, { backgroundColor: theme.rowBackgroundColor }]}>
<Text style={[styles.replyText, { color: theme.textColor }]}>{replyInfo.text}</Text>
<Text style={[styles.replyUsername, { color: theme.dangerColor }]}>{replyInfo.username}</Text>
</View>)}
</ScrollView>
{/* Input Area */}
<View style={[styles.inputContainer, { borderColor: theme.dividerColor }]}>
<TextInput ref={focusedInput && startDirectMessage && directMessageDetails
? inputRef
: null} style={[
styles.input,
{
backgroundColor: theme.inputBackgroundColor,
borderColor: theme.borderColor,
color: theme.inputTextColor,
},
]} placeholder={type === 'direct'
? senderId
? `Send a direct message to ${senderId}`
: directMessageDetails
? `Send a direct message to ${directMessageDetails.name}`
: islevel === '2'
? 'Select a message to reply to'
: 'Send a direct message to the host'
: eventType === 'chat'
? 'Send a message'
: 'Send a message to everyone'} maxLength={350} multiline onChangeText={handleTextInputChange} value={type === 'direct' ? directMessageText : groupMessageText} placeholderTextColor={theme.placeholderTextColor}/>
<Pressable style={[styles.sendButton, { backgroundColor: theme.buttonBackgroundColor }]} onPress={handleSendButton}>
<FontAwesome5 name="paper-plane" size={16} color={theme.buttonTextColor}/>
</Pressable>
</View>
</View>);
};
export default MessagePanel;
/**
* Stylesheet for the MessagePanel component.
*/
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 10,
},
messagesContainer: {
flex: 1,
marginBottom: 10,
},
messageWrapper: {
marginBottom: 5,
},
messageContainer: {
maxWidth: 200,
padding: 5,
borderRadius: 10,
},
selfMessage: {
flexDirection: 'column',
alignSelf: 'flex-end',
marginBottom: 5,
},
otherMessage: {
flexDirection: 'column',
alignSelf: 'flex-start',
marginBottom: 5,
},
messageHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 3,
},
receiverText: {
fontWeight: 'bold',
color: 'black',
fontSize: 8,
marginLeft: 6,
},
senderText: {
fontWeight: 'bold',
color: 'black',
fontSize: 8,
marginRight: 10,
},
timestampText: {
fontSize: 8,
color: '#999999',
},
replyButton: {
padding: 2,
marginLeft: 5,
borderRadius: 2,
backgroundColor: 'transparent',
},
messageContent: {
paddingTop: 5,
},
messageText: {
color: 'black',
fontSize: 12,
maxWidth: 300,
},
replyInfoContainer: {
flexDirection: 'row',
alignItems: 'center',
padding: 2,
backgroundColor: '#e6e6e6',
borderRadius: 5,
marginBottom: 1,
},
replyText: {
fontWeight: 'bold',
marginRight: 2,
fontSize: 8,
},
replyUsername: {
color: 'red',
fontSize: 8,
},
inputContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginTop: 'auto',
borderTopWidth: 1,
borderColor: '#cccccc',
paddingTop: 2,
},
input: {
flex: 1,
height: '100%',
borderColor: 'gray',
borderWidth: 1,
borderRadius: 20,
paddingLeft: 15,
paddingRight: 15,
backgroundColor: '#ffffff',
color: 'black',
fontSize: 14,
justifyContent: 'center',
alignItems: 'center',
},
sendButton: {
backgroundColor: '#83c0e9',
padding: 10,
borderRadius: 5,
justifyContent: 'center',
alignItems: 'center',
},
contentSelf: {
backgroundColor: '#DCF8C6',
alignSelf: 'flex-end',
padding: 10,
borderRadius: 10,
},
contentOther: {
backgroundColor: '#1ce5c7',
alignSelf: 'flex-start',
padding: 10,
borderRadius: 10,
},
});
//# sourceMappingURL=MessagePanel.js.map