|
@@ -19,11 +19,9 @@ import {
|
|
|
IMessage,
|
|
|
Send,
|
|
|
BubbleProps,
|
|
|
- QuickRepliesProps,
|
|
|
Composer
|
|
|
} from 'react-native-gifted-chat';
|
|
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
|
-import EmojiSelector from 'react-native-emoji-selector';
|
|
|
import * as ImagePicker from 'expo-image-picker';
|
|
|
import { useActionSheet } from '@expo/react-native-action-sheet';
|
|
|
import {
|
|
@@ -37,14 +35,7 @@ import { useFocusEffect, useNavigation } from '@react-navigation/native';
|
|
|
import { Video } from 'expo-av';
|
|
|
import ChatMessageBox from '../Components/ChatMessageBox';
|
|
|
import ReplyMessageBar from '../Components/ReplyMessageBar';
|
|
|
-import Animated, {
|
|
|
- FadeIn,
|
|
|
- FadeOut,
|
|
|
- SlideInDown,
|
|
|
- SlideOutDown,
|
|
|
- useSharedValue,
|
|
|
- withTiming
|
|
|
-} from 'react-native-reanimated';
|
|
|
+import { useSharedValue, withTiming } from 'react-native-reanimated';
|
|
|
import { BlurView } from 'expo-blur';
|
|
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
|
import Clipboard from '@react-native-clipboard/clipboard';
|
|
@@ -55,11 +46,15 @@ import {
|
|
|
usePostDeleteMessageMutation,
|
|
|
usePostGetChatWithQuery,
|
|
|
usePostMessagesReadMutation,
|
|
|
+ usePostReactToMessageMutation,
|
|
|
usePostSendMessageMutation
|
|
|
} from '@api/chat';
|
|
|
import { Message } from '../types';
|
|
|
import { API_HOST } from 'src/constants';
|
|
|
import { getFontSize } from 'src/utils';
|
|
|
+import ReactionBar from '../Components/ReactionBar';
|
|
|
+import OptionsMenu from '../Components/OptionsMenu';
|
|
|
+import EmojiSelectorModal from '../Components/EmojiSelectorModal';
|
|
|
|
|
|
const options = {
|
|
|
enableVibrateFallback: true,
|
|
@@ -86,7 +81,6 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
const [replyMessage, setReplyMessage] = useState<IMessage | null>(null);
|
|
|
|
|
|
const [selectedMessage, setSelectedMessage] = useState<IMessage | null>(null);
|
|
|
- const [showReactions, setShowReactions] = useState<number | null>(null);
|
|
|
const [emojiSelectorVisible, setEmojiSelectorVisible] = useState(false);
|
|
|
const [messagePosition, setMessagePosition] = useState<{
|
|
|
x: number;
|
|
@@ -100,6 +94,9 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
const [unreadMessageIndex, setUnreadMessageIndex] = useState<number | null>(null);
|
|
|
const { mutateAsync: markMessagesAsRead } = usePostMessagesReadMutation();
|
|
|
const { mutateAsync: deleteMessage } = usePostDeleteMessageMutation();
|
|
|
+ const { mutateAsync: reactToMessage } = usePostReactToMessageMutation();
|
|
|
+
|
|
|
+ const [highlightedMessageId, setHighlightedMessageId] = useState<number | null>(null);
|
|
|
|
|
|
const messageRefs = useRef<{ [key: string]: any }>({});
|
|
|
const flatList = useRef<FlatList | null>(null);
|
|
@@ -114,7 +111,13 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
_id: message.sender,
|
|
|
name: message.sender === id ? name : 'Me'
|
|
|
},
|
|
|
- replyMessage: message.reply_to_id !== -1 ? { text: message.reply_to.text } : null,
|
|
|
+ replyMessage:
|
|
|
+ message.reply_to_id !== -1
|
|
|
+ ? {
|
|
|
+ text: message.reply_to.text,
|
|
|
+ id: message.reply_to.id
|
|
|
+ }
|
|
|
+ : null,
|
|
|
reactions: JSON.parse(message.reactions || '{}'),
|
|
|
attachment: message.attachement !== -1 ? message.attachement : null,
|
|
|
pending: message.status === 1,
|
|
@@ -253,7 +256,7 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
if (emoji === '+') {
|
|
|
openEmojiSelector();
|
|
|
} else {
|
|
|
- // addReaction(messageId, emoji);
|
|
|
+ addReaction(messageId, emoji);
|
|
|
}
|
|
|
};
|
|
|
|
|
@@ -274,10 +277,12 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
const handleOptionPress = (option: string) => {
|
|
|
switch (option) {
|
|
|
case 'reply':
|
|
|
- Alert.alert(option);
|
|
|
+ setReplyMessage(selectedMessage?.currentMessage);
|
|
|
+ setIsModalVisible(false);
|
|
|
break;
|
|
|
case 'copy':
|
|
|
Clipboard.setString(selectedMessage?.currentMessage?.text ?? '');
|
|
|
+ setIsModalVisible(false);
|
|
|
Alert.alert('copied');
|
|
|
break;
|
|
|
case 'delete':
|
|
@@ -285,6 +290,7 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
setIsModalVisible(false);
|
|
|
break;
|
|
|
case 'pin':
|
|
|
+ setIsModalVisible(false);
|
|
|
Alert.alert(option);
|
|
|
break;
|
|
|
default:
|
|
@@ -293,83 +299,6 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
closeEmojiSelector();
|
|
|
};
|
|
|
|
|
|
- const renderReactionsBar = () =>
|
|
|
- selectedMessage &&
|
|
|
- messagePosition && (
|
|
|
- <Animated.View
|
|
|
- entering={FadeIn}
|
|
|
- exiting={FadeOut}
|
|
|
- style={[
|
|
|
- styles.reactionBar,
|
|
|
- {
|
|
|
- top: messagePosition.y - 50, // - reaction bar height
|
|
|
- left: messagePosition.isMine
|
|
|
- ? Dimensions.get('window').width - Dimensions.get('window').width * 0.75 - 8 // reaction bar width
|
|
|
- : messagePosition.x // + padding
|
|
|
- }
|
|
|
- ]}
|
|
|
- >
|
|
|
- {reactionEmojis.map((emoji) => (
|
|
|
- <TouchableOpacity
|
|
|
- key={emoji}
|
|
|
- onPress={() => handleReactionPress(emoji, selectedMessage?.currentMessage?._id)}
|
|
|
- >
|
|
|
- <Text style={styles.reactionEmoji}>{emoji}</Text>
|
|
|
- </TouchableOpacity>
|
|
|
- ))}
|
|
|
- <TouchableOpacity
|
|
|
- onPress={() => openEmojiSelector()}
|
|
|
- style={{
|
|
|
- alignItems: 'center',
|
|
|
- justifyContent: 'center',
|
|
|
- backgroundColor: Colors.FILL_LIGHT,
|
|
|
- borderRadius: 15,
|
|
|
- borderWidth: 1,
|
|
|
- borderColor: Colors.BORDER_LIGHT,
|
|
|
- width: 30,
|
|
|
- height: 30
|
|
|
- }}
|
|
|
- >
|
|
|
- <MaterialCommunityIcons name="plus" size={28} color={Colors.DARK_BLUE} />
|
|
|
- </TouchableOpacity>
|
|
|
- </Animated.View>
|
|
|
- );
|
|
|
-
|
|
|
- const renderOptionsMenu = () =>
|
|
|
- selectedMessage &&
|
|
|
- messagePosition && (
|
|
|
- <Animated.View
|
|
|
- entering={FadeIn}
|
|
|
- exiting={FadeOut}
|
|
|
- style={[
|
|
|
- styles.optionsMenu,
|
|
|
- {
|
|
|
- top: messagePosition.y + messagePosition.height + 10,
|
|
|
- left: messagePosition.isMine
|
|
|
- ? Dimensions.get('window').width - Dimensions.get('window').width * 0.75 - 8
|
|
|
- : messagePosition.x
|
|
|
- }
|
|
|
- ]}
|
|
|
- >
|
|
|
- <TouchableOpacity style={styles.optionButton} onPress={() => handleOptionPress('reply')}>
|
|
|
- <Text style={styles.optionText}>Reply</Text>
|
|
|
- <MaterialCommunityIcons name="reply" size={20} color={Colors.DARK_BLUE} />
|
|
|
- </TouchableOpacity>
|
|
|
- <TouchableOpacity style={styles.optionButton} onPress={() => handleOptionPress('copy')}>
|
|
|
- <Text style={styles.optionText}>Copy</Text>
|
|
|
- <MaterialCommunityIcons name="content-copy" size={20} color={Colors.DARK_BLUE} />
|
|
|
- </TouchableOpacity>
|
|
|
- <TouchableOpacity style={styles.optionButton} onPress={() => handleOptionPress('delete')}>
|
|
|
- <Text style={styles.optionText}>Delete</Text>
|
|
|
- <MaterialCommunityIcons name="delete" size={20} color={Colors.DARK_BLUE} />
|
|
|
- </TouchableOpacity>
|
|
|
- <TouchableOpacity style={styles.optionButton} onPress={() => handleOptionPress('pin')}>
|
|
|
- <Text style={styles.optionText}>Pin message</Text>
|
|
|
- <MaterialCommunityIcons name="pin" size={20} color={Colors.DARK_BLUE} />
|
|
|
- </TouchableOpacity>
|
|
|
- </Animated.View>
|
|
|
- );
|
|
|
-
|
|
|
const renderSelectedMessage = () =>
|
|
|
selectedMessage && (
|
|
|
<View
|
|
@@ -407,6 +336,7 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
</View>
|
|
|
) : null;
|
|
|
}}
|
|
|
+ renderCustomView={renderReplyMessageView}
|
|
|
/>
|
|
|
</ScrollView>
|
|
|
</View>
|
|
@@ -438,13 +368,18 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
(newMessages: IMessage[] = []) => {
|
|
|
if (replyMessage) {
|
|
|
newMessages[0].replyMessage = {
|
|
|
- text: replyMessage.text
|
|
|
+ text: replyMessage.text,
|
|
|
+ id: replyMessage._id
|
|
|
};
|
|
|
}
|
|
|
const message = { ...newMessages[0], pending: true };
|
|
|
|
|
|
sendMessage(
|
|
|
- { to_uid: id, text: message.text },
|
|
|
+ {
|
|
|
+ to_uid: id,
|
|
|
+ text: message.text,
|
|
|
+ reply_to_id: replyMessage ? (replyMessage._id as number) : -1
|
|
|
+ },
|
|
|
{
|
|
|
onSuccess: (res) => console.log('res', res),
|
|
|
onError: (err) => console.log('err', err)
|
|
@@ -554,18 +489,33 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
return null;
|
|
|
};
|
|
|
|
|
|
- const addReaction = (messageId: number, reaction: any) => {
|
|
|
- const updatedMessages = messages.map((msg: any) => {
|
|
|
+ const addReaction = (messageId: number, reaction: string) => {
|
|
|
+ const updatedMessages = messages.map((msg) => {
|
|
|
if (msg._id === messageId) {
|
|
|
+ const updatedReactions = [
|
|
|
+ ...(Array.isArray(msg.reactions) ? msg.reactions : []),
|
|
|
+ { datetime: new Date().toISOString(), reaction: reaction, uid: +currentUserId }
|
|
|
+ ];
|
|
|
+
|
|
|
return {
|
|
|
...msg,
|
|
|
- reactions: [...(msg.reactions ?? []), reaction]
|
|
|
+ reactions: updatedReactions
|
|
|
};
|
|
|
}
|
|
|
return msg;
|
|
|
});
|
|
|
+
|
|
|
setMessages(updatedMessages);
|
|
|
- setShowReactions(null);
|
|
|
+
|
|
|
+ reactToMessage(
|
|
|
+ { message_id: messageId, reaction: reaction, conversation_with_user: id },
|
|
|
+ {
|
|
|
+ onSuccess: (res) => console.log('res', res),
|
|
|
+ onError: (err) => console.log('err', err)
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ setIsModalVisible(false);
|
|
|
};
|
|
|
|
|
|
const updateRowRef = useCallback(
|
|
@@ -581,14 +531,88 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
[replyMessage]
|
|
|
);
|
|
|
|
|
|
- const renderReplyMessageView = (props: BubbleProps<IMessage>) =>
|
|
|
- props.currentMessage &&
|
|
|
- props.currentMessage?.replyMessage && (
|
|
|
- <View style={styles.replyMessageContainer}>
|
|
|
- <Text>{props.currentMessage.replyMessage.text}</Text>
|
|
|
- <View style={styles.replyMessageDivider} />
|
|
|
+ const renderCustomView = (props: BubbleProps<IMessage>) => {
|
|
|
+ if (!props.currentMessage) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ const { currentMessage } = props;
|
|
|
+
|
|
|
+ if (!currentMessage) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <View style={{ position: 'relative' }}>
|
|
|
+ {renderReplyMessageView(props)}
|
|
|
+
|
|
|
+ {currentMessage.reactions &&
|
|
|
+ Array.isArray(currentMessage.reactions) &&
|
|
|
+ currentMessage.reactions.length > 0 && (
|
|
|
+ <View
|
|
|
+ style={[
|
|
|
+ styles.reactionsContainer,
|
|
|
+ currentMessage.user._id === id ? { right: -15 } : { left: -15 }
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ {Object.entries(
|
|
|
+ currentMessage.reactions.reduce((acc, { reaction }) => {
|
|
|
+ acc[reaction] = (acc[reaction] || 0) + 1;
|
|
|
+ return acc;
|
|
|
+ }, {})
|
|
|
+ ).map(([emoji, count]) => (
|
|
|
+ <Text key={emoji} style={{}}>
|
|
|
+ {emoji}
|
|
|
+ {(count as number) > 1 ? ` ${count}` : ''}
|
|
|
+ </Text>
|
|
|
+ ))}
|
|
|
+ </View>
|
|
|
+ )}
|
|
|
</View>
|
|
|
);
|
|
|
+ };
|
|
|
+
|
|
|
+ const renderReplyMessageView = (props: BubbleProps<IMessage>) => {
|
|
|
+ if (!props.currentMessage) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ const { currentMessage } = props;
|
|
|
+
|
|
|
+ if (!currentMessage || !currentMessage?.replyMessage) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <TouchableOpacity
|
|
|
+ style={styles.replyMessageContainer}
|
|
|
+ onPress={() => {
|
|
|
+ if (currentMessage?.replyMessage?.id) {
|
|
|
+ scrollToMessage(currentMessage.replyMessage.id);
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <Text>{currentMessage.replyMessage.text}</Text>
|
|
|
+ <View style={styles.replyMessageDivider} />
|
|
|
+ </TouchableOpacity>
|
|
|
+ );
|
|
|
+ };
|
|
|
+
|
|
|
+ const scrollToMessage = (messageId: number) => {
|
|
|
+ const messageIndex = messages.findIndex((message) => message._id === messageId);
|
|
|
+
|
|
|
+ if (messageIndex !== -1 && flatList.current) {
|
|
|
+ flatList.current.scrollToIndex({
|
|
|
+ index: messageIndex,
|
|
|
+ animated: true,
|
|
|
+ viewPosition: 0.5
|
|
|
+ });
|
|
|
+
|
|
|
+ setHighlightedMessageId(messageId);
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ setHighlightedMessageId(null);
|
|
|
+ }, 1000);
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
useEffect(() => {
|
|
|
if (replyMessage && swipeableRowRef.current) {
|
|
@@ -647,57 +671,77 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
);
|
|
|
}
|
|
|
|
|
|
+ const isHighlighted = currentMessage._id === highlightedMessageId;
|
|
|
+ const backgroundColor = isHighlighted
|
|
|
+ ? Colors.ORANGE
|
|
|
+ : currentMessage.user._id === +currentUserId
|
|
|
+ ? Colors.DARK_BLUE
|
|
|
+ : Colors.FILL_LIGHT;
|
|
|
+
|
|
|
return (
|
|
|
<View
|
|
|
- ref={(ref) => {
|
|
|
- if (ref && currentMessage) {
|
|
|
- messageRefs.current[currentMessage._id] = ref;
|
|
|
- }
|
|
|
- }}
|
|
|
- collapsable={false}
|
|
|
+ style={[
|
|
|
+ currentMessage.reactions &&
|
|
|
+ Array.isArray(currentMessage.reactions) &&
|
|
|
+ currentMessage.reactions.length > 0
|
|
|
+ ? { paddingBottom: 15 }
|
|
|
+ : {}
|
|
|
+ ]}
|
|
|
>
|
|
|
- <Bubble
|
|
|
- {...props}
|
|
|
- wrapperStyle={{
|
|
|
- right: {
|
|
|
- backgroundColor: Colors.DARK_BLUE
|
|
|
- },
|
|
|
- left: {
|
|
|
- backgroundColor: Colors.FILL_LIGHT
|
|
|
- }
|
|
|
- }}
|
|
|
- textStyle={{
|
|
|
- left: {
|
|
|
- color: Colors.DARK_BLUE
|
|
|
- },
|
|
|
- right: {
|
|
|
- color: Colors.FILL_LIGHT
|
|
|
+ <View
|
|
|
+ ref={(ref) => {
|
|
|
+ if (ref && currentMessage) {
|
|
|
+ messageRefs.current[currentMessage._id] = ref;
|
|
|
}
|
|
|
}}
|
|
|
- onLongPress={() => handleLongPress(currentMessage, props)}
|
|
|
- renderTicks={(message: IMessage) => {
|
|
|
- return message.user._id === +currentUserId && message.received ? (
|
|
|
- <View style={{ paddingRight: 8, bottom: 2 }}>
|
|
|
- <MaterialCommunityIcons name="check-all" size={16} color={Colors.WHITE} />
|
|
|
- </View>
|
|
|
- ) : message.user._id === +currentUserId && message.sent ? (
|
|
|
- <View style={{ paddingRight: 8, bottom: 2 }}>
|
|
|
- <MaterialCommunityIcons name="check" size={16} color={Colors.WHITE} />
|
|
|
- </View>
|
|
|
- ) : message.user._id === +currentUserId && message.pending ? (
|
|
|
- <View style={{ paddingRight: 8, bottom: 2 }}>
|
|
|
- <MaterialCommunityIcons name="check" size={16} color={Colors.LIGHT_GRAY} />
|
|
|
- </View>
|
|
|
- ) : null;
|
|
|
- }}
|
|
|
- // renderQuickReplies={(quickReplies: QuickRepliesProps<IMessage>) => null}
|
|
|
- // renderQuickReplies={(quickReplies: QuickRepliesProps<IMessage>) => (
|
|
|
- // <View style={{height: 20, width: 20, backgroundColor: 'green', bottom: 0, right: 0}}>
|
|
|
- // <Text>{currentMessage.quickReplies.values[0].title}</Text>
|
|
|
- // </View>
|
|
|
-
|
|
|
- // )}
|
|
|
- />
|
|
|
+ collapsable={false}
|
|
|
+ >
|
|
|
+ <Bubble
|
|
|
+ {...props}
|
|
|
+ wrapperStyle={{
|
|
|
+ right: {
|
|
|
+ backgroundColor: backgroundColor
|
|
|
+ },
|
|
|
+ left: {
|
|
|
+ backgroundColor: backgroundColor
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ textStyle={{
|
|
|
+ left: {
|
|
|
+ color: Colors.DARK_BLUE
|
|
|
+ },
|
|
|
+ right: {
|
|
|
+ color: Colors.FILL_LIGHT
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ onLongPress={() => handleLongPress(currentMessage, props)}
|
|
|
+ renderTicks={(message: IMessage) => {
|
|
|
+ return message.user._id === +currentUserId && message.received ? (
|
|
|
+ <View style={{ paddingRight: 8, bottom: 2 }}>
|
|
|
+ <MaterialCommunityIcons name="check-all" size={16} color={Colors.WHITE} />
|
|
|
+ </View>
|
|
|
+ ) : message.user._id === +currentUserId && message.sent ? (
|
|
|
+ <View style={{ paddingRight: 8, bottom: 2 }}>
|
|
|
+ <MaterialCommunityIcons name="check" size={16} color={Colors.WHITE} />
|
|
|
+ </View>
|
|
|
+ ) : message.user._id === +currentUserId && message.pending ? (
|
|
|
+ <View style={{ paddingRight: 8, bottom: 2 }}>
|
|
|
+ <MaterialCommunityIcons name="check" size={16} color={Colors.LIGHT_GRAY} />
|
|
|
+ </View>
|
|
|
+ ) : null;
|
|
|
+ }}
|
|
|
+ renderCustomView={renderCustomView}
|
|
|
+ isCustomViewBottom={true} // should be false to show reply on top
|
|
|
+ // bottomContainerStyle={{
|
|
|
+ // right: {
|
|
|
+ // backgroundColor: 'red'
|
|
|
+ // },
|
|
|
+ // left: {
|
|
|
+ // backgroundColor: 'red'
|
|
|
+ // }
|
|
|
+ // }}
|
|
|
+ />
|
|
|
+ </View>
|
|
|
</View>
|
|
|
);
|
|
|
};
|
|
@@ -714,28 +758,6 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
/>
|
|
|
);
|
|
|
|
|
|
- const renderEmojiSelector = () => (
|
|
|
- <Animated.View
|
|
|
- entering={SlideInDown}
|
|
|
- exiting={SlideOutDown}
|
|
|
- style={styles.emojiSelectorContainer}
|
|
|
- >
|
|
|
- <EmojiSelector
|
|
|
- onEmojiSelected={(emoji) => {
|
|
|
- addReaction(selectedMessage?._id, emoji);
|
|
|
- closeEmojiSelector();
|
|
|
- }}
|
|
|
- showSearchBar={true}
|
|
|
- columns={8}
|
|
|
- />
|
|
|
- <TouchableOpacity style={styles.closeModalButton} onPress={closeEmojiSelector}>
|
|
|
- <MaterialCommunityIcons name="close" size={30} color={Colors.DARK_BLUE} />
|
|
|
- </TouchableOpacity>
|
|
|
- </Animated.View>
|
|
|
- );
|
|
|
-
|
|
|
- if (!messages.length) return null;
|
|
|
-
|
|
|
return (
|
|
|
<PageWrapper style={{ marginLeft: 0, marginRight: 0 }}>
|
|
|
<View style={{ paddingHorizontal: '5%' }}>
|
|
@@ -769,7 +791,11 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
onScrollToIndexFailed: (info: any) => {
|
|
|
const wait = new Promise((resolve) => setTimeout(resolve, 300));
|
|
|
wait.then(() => {
|
|
|
- flatList.current?.scrollToIndex({ index: info.index, animated: true });
|
|
|
+ flatList.current?.scrollToIndex({
|
|
|
+ index: info.index,
|
|
|
+ animated: true,
|
|
|
+ viewPosition: 0.5
|
|
|
+ });
|
|
|
});
|
|
|
}
|
|
|
}}
|
|
@@ -831,13 +857,15 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
renderChatFooter={() => (
|
|
|
<ReplyMessageBar clearReply={clearReplyMessage} message={replyMessage} />
|
|
|
)}
|
|
|
- renderCustomView={renderReplyMessageView}
|
|
|
renderMessageVideo={renderMessageVideo}
|
|
|
renderAvatar={null}
|
|
|
maxComposerHeight={100}
|
|
|
renderComposer={(props) => <Composer {...props} />}
|
|
|
isCustomViewBottom={true}
|
|
|
keyboardShouldPersistTaps="handled"
|
|
|
+ shouldUpdateMessage={(props, nextProps) =>
|
|
|
+ highlightedMessageId === nextProps.currentMessage._id
|
|
|
+ }
|
|
|
// inverted={true}
|
|
|
// isTyping={true}
|
|
|
/>
|
|
@@ -848,7 +876,6 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
<Video
|
|
|
source={{ uri: selectedMedia }}
|
|
|
style={styles.fullScreenMedia}
|
|
|
- // resizeMode="cover"
|
|
|
useNativeControls
|
|
|
/>
|
|
|
) : (
|
|
@@ -879,10 +906,25 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
|
activeOpacity={1}
|
|
|
onPress={handleBackgroundPress}
|
|
|
>
|
|
|
- {renderReactionsBar()}
|
|
|
+ <ReactionBar
|
|
|
+ messagePosition={messagePosition}
|
|
|
+ selectedMessage={selectedMessage}
|
|
|
+ reactionEmojis={reactionEmojis}
|
|
|
+ handleReactionPress={handleReactionPress}
|
|
|
+ openEmojiSelector={openEmojiSelector}
|
|
|
+ />
|
|
|
{renderSelectedMessage()}
|
|
|
- {renderOptionsMenu()}
|
|
|
- {emojiSelectorVisible ? renderEmojiSelector() : null}
|
|
|
+ <OptionsMenu
|
|
|
+ selectedMessage={selectedMessage}
|
|
|
+ handleOptionPress={handleOptionPress}
|
|
|
+ messagePosition={messagePosition}
|
|
|
+ />
|
|
|
+ <EmojiSelectorModal
|
|
|
+ visible={emojiSelectorVisible}
|
|
|
+ selectedMessage={selectedMessage}
|
|
|
+ addReaction={addReaction}
|
|
|
+ closeEmojiSelector={closeEmojiSelector}
|
|
|
+ />
|
|
|
</TouchableOpacity>
|
|
|
</BlurView>
|
|
|
</ReactModal>
|
|
@@ -902,73 +944,12 @@ const styles = StyleSheet.create({
|
|
|
fontWeight: '700',
|
|
|
fontSize: getFontSize(12)
|
|
|
},
|
|
|
- emojiSelectorContainer: {
|
|
|
- position: 'absolute',
|
|
|
- bottom: 0,
|
|
|
- width: '100%',
|
|
|
- height: '50%',
|
|
|
- backgroundColor: 'white',
|
|
|
- borderTopLeftRadius: 15,
|
|
|
- borderTopRightRadius: 15,
|
|
|
- shadowColor: '#000',
|
|
|
- shadowOpacity: 0.3,
|
|
|
- shadowOffset: { width: 0, height: -2 },
|
|
|
- shadowRadius: 5,
|
|
|
- elevation: 5,
|
|
|
- padding: 10
|
|
|
- },
|
|
|
-
|
|
|
modalBackground: {
|
|
|
flex: 1
|
|
|
},
|
|
|
modalContent: {
|
|
|
backgroundColor: 'transparent'
|
|
|
},
|
|
|
- reactionBar: {
|
|
|
- position: 'absolute',
|
|
|
- width: Dimensions.get('window').width * 0.75,
|
|
|
- flexDirection: 'row',
|
|
|
- backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
|
|
- justifyContent: 'space-between',
|
|
|
- alignItems: 'center',
|
|
|
- borderRadius: 20,
|
|
|
- padding: 5,
|
|
|
- paddingHorizontal: 12,
|
|
|
- shadowColor: '#000',
|
|
|
- shadowOpacity: 0.3,
|
|
|
- shadowOffset: { width: 0, height: 2 },
|
|
|
- shadowRadius: 5,
|
|
|
- elevation: 5
|
|
|
- },
|
|
|
- reactionEmoji: {
|
|
|
- fontSize: 28
|
|
|
- },
|
|
|
- closeModalButton: {
|
|
|
- position: 'absolute',
|
|
|
- top: 10,
|
|
|
- right: 10
|
|
|
- },
|
|
|
- optionsMenu: {
|
|
|
- position: 'absolute',
|
|
|
- backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
|
|
- borderRadius: 10,
|
|
|
- padding: 8,
|
|
|
- shadowColor: '#000',
|
|
|
- shadowOpacity: 0.3,
|
|
|
- shadowOffset: { width: 0, height: 2 },
|
|
|
- shadowRadius: 5,
|
|
|
- elevation: 5,
|
|
|
- width: Dimensions.get('window').width * 0.75
|
|
|
- },
|
|
|
- optionButton: {
|
|
|
- paddingVertical: 10,
|
|
|
- paddingHorizontal: 12,
|
|
|
- flexDirection: 'row',
|
|
|
- justifyContent: 'space-between'
|
|
|
- },
|
|
|
- optionText: {
|
|
|
- fontSize: 16
|
|
|
- },
|
|
|
mediaContainer: {
|
|
|
borderRadius: 10,
|
|
|
overflow: 'hidden',
|
|
@@ -1045,10 +1026,21 @@ const styles = StyleSheet.create({
|
|
|
fontStyle: 'italic'
|
|
|
},
|
|
|
reactionsContainer: {
|
|
|
+ height: 25,
|
|
|
+ paddingHorizontal: 6,
|
|
|
+ backgroundColor: Colors.FILL_LIGHT,
|
|
|
+ bottom: -30,
|
|
|
+ position: 'absolute',
|
|
|
+ alignItems: 'center',
|
|
|
+ justifyContent: 'center',
|
|
|
flexDirection: 'row',
|
|
|
- justifyContent: 'flex-start',
|
|
|
- paddingHorizontal: 5,
|
|
|
- marginTop: -10
|
|
|
+ gap: 6,
|
|
|
+ borderRadius: 15,
|
|
|
+ shadowColor: Colors.DARK_BLUE,
|
|
|
+ shadowOpacity: 0.2,
|
|
|
+ shadowOffset: { width: 0, height: 1 },
|
|
|
+ shadowRadius: 2,
|
|
|
+ elevation: 1
|
|
|
},
|
|
|
reactionText: {
|
|
|
fontSize: 16,
|