|
@@ -26,7 +26,6 @@ import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
import EmojiSelector from 'react-native-emoji-selector';
|
|
import EmojiSelector from 'react-native-emoji-selector';
|
|
import * as ImagePicker from 'expo-image-picker';
|
|
import * as ImagePicker from 'expo-image-picker';
|
|
import { useActionSheet } from '@expo/react-native-action-sheet';
|
|
import { useActionSheet } from '@expo/react-native-action-sheet';
|
|
-import { ActionSheetProvider } from '@expo/react-native-action-sheet';
|
|
|
|
import {
|
|
import {
|
|
GestureHandlerRootView,
|
|
GestureHandlerRootView,
|
|
LongPressGestureHandler,
|
|
LongPressGestureHandler,
|
|
@@ -52,9 +51,15 @@ import Clipboard from '@react-native-clipboard/clipboard';
|
|
import { trigger } from 'react-native-haptic-feedback';
|
|
import { trigger } from 'react-native-haptic-feedback';
|
|
import ReactModal from 'react-native-modal';
|
|
import ReactModal from 'react-native-modal';
|
|
import { storage, StoreType } from 'src/storage';
|
|
import { storage, StoreType } from 'src/storage';
|
|
-import { usePostGetChatWithQuery, usePostSendMessageMutation } from '@api/chat';
|
|
|
|
|
|
+import {
|
|
|
|
+ usePostDeleteMessageMutation,
|
|
|
|
+ usePostGetChatWithQuery,
|
|
|
|
+ usePostMessagesReadMutation,
|
|
|
|
+ usePostSendMessageMutation
|
|
|
|
+} from '@api/chat';
|
|
import { Message } from '../types';
|
|
import { Message } from '../types';
|
|
import { API_HOST } from 'src/constants';
|
|
import { API_HOST } from 'src/constants';
|
|
|
|
+import { getFontSize } from 'src/utils';
|
|
|
|
|
|
const options = {
|
|
const options = {
|
|
enableVibrateFallback: true,
|
|
enableVibrateFallback: true,
|
|
@@ -92,8 +97,12 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
} | null>(null);
|
|
} | null>(null);
|
|
|
|
|
|
const [isModalVisible, setIsModalVisible] = useState(false);
|
|
const [isModalVisible, setIsModalVisible] = useState(false);
|
|
|
|
+ const [unreadMessageIndex, setUnreadMessageIndex] = useState<number | null>(null);
|
|
|
|
+ const { mutateAsync: markMessagesAsRead } = usePostMessagesReadMutation();
|
|
|
|
+ const { mutateAsync: deleteMessage } = usePostDeleteMessageMutation();
|
|
|
|
|
|
const messageRefs = useRef<{ [key: string]: any }>({});
|
|
const messageRefs = useRef<{ [key: string]: any }>({});
|
|
|
|
+ const flatList = useRef<FlatList | null>(null);
|
|
const scrollY = useSharedValue(0);
|
|
const scrollY = useSharedValue(0);
|
|
|
|
|
|
const mapApiMessageToGiftedMessage = (message: Message): IMessage => {
|
|
const mapApiMessageToGiftedMessage = (message: Message): IMessage => {
|
|
@@ -110,7 +119,8 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
attachment: message.attachement !== -1 ? message.attachement : null,
|
|
attachment: message.attachement !== -1 ? message.attachement : null,
|
|
pending: message.status === 1,
|
|
pending: message.status === 1,
|
|
sent: message.status === 2,
|
|
sent: message.status === 2,
|
|
- received: message.status === 3
|
|
|
|
|
|
+ received: message.status === 3,
|
|
|
|
+ deleted: message.status === 4
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
|
|
@@ -118,11 +128,67 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
useCallback(() => {
|
|
useCallback(() => {
|
|
if (chatData?.messages) {
|
|
if (chatData?.messages) {
|
|
const mappedMessages = chatData.messages.map(mapApiMessageToGiftedMessage);
|
|
const mappedMessages = chatData.messages.map(mapApiMessageToGiftedMessage);
|
|
|
|
+
|
|
|
|
+ const firstUnreadIndex = mappedMessages.findLastIndex(
|
|
|
|
+ (msg) => !msg.received && !msg?.deleted && msg.user._id === id
|
|
|
|
+ );
|
|
|
|
+ if (firstUnreadIndex !== -1) {
|
|
|
|
+ setUnreadMessageIndex(firstUnreadIndex);
|
|
|
|
+
|
|
|
|
+ const unreadMarker: any = {
|
|
|
|
+ _id: 'unreadMarker',
|
|
|
|
+ text: 'Unread messages',
|
|
|
|
+ system: true
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ mappedMessages.splice(firstUnreadIndex, 0, unreadMarker);
|
|
|
|
+ }
|
|
setMessages(mappedMessages);
|
|
setMessages(mappedMessages);
|
|
}
|
|
}
|
|
}, [chatData])
|
|
}, [chatData])
|
|
);
|
|
);
|
|
|
|
|
|
|
|
+ const sentToServer = useRef<Set<number>>(new Set());
|
|
|
|
+
|
|
|
|
+ const handleViewableItemsChanged = ({ viewableItems }: { viewableItems: any[] }) => {
|
|
|
|
+ const newViewableUnreadMessages = viewableItems
|
|
|
|
+ .filter(
|
|
|
|
+ (item) =>
|
|
|
|
+ !item.item.received &&
|
|
|
|
+ !item.item.deleted &&
|
|
|
|
+ !item.item.system &&
|
|
|
|
+ item.item.user._id === id &&
|
|
|
|
+ !sentToServer.current.has(item.item._id)
|
|
|
|
+ )
|
|
|
|
+ .map((item) => item.item._id);
|
|
|
|
+
|
|
|
|
+ if (newViewableUnreadMessages.length > 0) {
|
|
|
|
+ markMessagesAsRead(
|
|
|
|
+ {
|
|
|
|
+ token,
|
|
|
|
+ from_user: id,
|
|
|
|
+ messages_id: newViewableUnreadMessages
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ onSuccess: (res) => {
|
|
|
|
+ newViewableUnreadMessages.forEach((id) => sentToServer.current.add(id));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const renderSystemMessage = (props: any) => {
|
|
|
|
+ if (props.currentMessage._id === 'unreadMarker') {
|
|
|
|
+ return (
|
|
|
|
+ <View style={styles.unreadMessagesContainer}>
|
|
|
|
+ <Text style={styles.unreadMessagesText}>{props.currentMessage.text}</Text>
|
|
|
|
+ </View>
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ return null;
|
|
|
|
+ };
|
|
|
|
+
|
|
const clearReplyMessage = () => setReplyMessage(null);
|
|
const clearReplyMessage = () => setReplyMessage(null);
|
|
|
|
|
|
const handleLongPress = (message: IMessage, props) => {
|
|
const handleLongPress = (message: IMessage, props) => {
|
|
@@ -191,6 +257,20 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+ const handleDeleteMessage = (messageId: number) => {
|
|
|
|
+ deleteMessage(
|
|
|
|
+ {
|
|
|
|
+ message_id: messageId,
|
|
|
|
+ conversation_with_user: id
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ onSuccess: (res) => {
|
|
|
|
+ setMessages((prevMessages) => prevMessages.filter((msg) => msg._id !== messageId));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ );
|
|
|
|
+ };
|
|
|
|
+
|
|
const handleOptionPress = (option: string) => {
|
|
const handleOptionPress = (option: string) => {
|
|
switch (option) {
|
|
switch (option) {
|
|
case 'reply':
|
|
case 'reply':
|
|
@@ -201,10 +281,8 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
Alert.alert('copied');
|
|
Alert.alert('copied');
|
|
break;
|
|
break;
|
|
case 'delete':
|
|
case 'delete':
|
|
- setMessages((prevMessages) =>
|
|
|
|
- prevMessages.filter((msg) => msg._id !== selectedMessage?.currentMessage?._id)
|
|
|
|
- );
|
|
|
|
- Alert.alert('deleted');
|
|
|
|
|
|
+ handleDeleteMessage(selectedMessage?.currentMessage?._id);
|
|
|
|
+ setIsModalVisible(false);
|
|
break;
|
|
break;
|
|
case 'pin':
|
|
case 'pin':
|
|
Alert.alert(option);
|
|
Alert.alert(option);
|
|
@@ -534,6 +612,41 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
const renderBubble = (props: any) => {
|
|
const renderBubble = (props: any) => {
|
|
const { currentMessage } = props;
|
|
const { currentMessage } = props;
|
|
|
|
|
|
|
|
+ if (currentMessage.deleted) {
|
|
|
|
+ return (
|
|
|
|
+ <View>
|
|
|
|
+ <Bubble
|
|
|
|
+ {...props}
|
|
|
|
+ renderTime={() => null}
|
|
|
|
+ currentMessage={{ ...props.currentMessage, text: 'This message was deleted' }}
|
|
|
|
+ renderMessageText={() => (
|
|
|
|
+ <View style={{ paddingHorizontal: 12, paddingVertical: 6 }}>
|
|
|
|
+ <Text style={{ color: Colors.LIGHT_GRAY, fontStyle: 'italic', fontSize: 12 }}>
|
|
|
|
+ This message was deleted
|
|
|
|
+ </Text>
|
|
|
|
+ </View>
|
|
|
|
+ )}
|
|
|
|
+ wrapperStyle={{
|
|
|
|
+ right: {
|
|
|
|
+ backgroundColor: Colors.DARK_BLUE
|
|
|
|
+ },
|
|
|
|
+ left: {
|
|
|
|
+ backgroundColor: Colors.FILL_LIGHT
|
|
|
|
+ }
|
|
|
|
+ }}
|
|
|
|
+ textStyle={{
|
|
|
|
+ left: {
|
|
|
|
+ color: Colors.DARK_BLUE
|
|
|
|
+ },
|
|
|
|
+ right: {
|
|
|
|
+ color: Colors.FILL_LIGHT
|
|
|
|
+ }
|
|
|
|
+ }}
|
|
|
|
+ />
|
|
|
|
+ </View>
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
return (
|
|
return (
|
|
<View
|
|
<View
|
|
ref={(ref) => {
|
|
ref={(ref) => {
|
|
@@ -648,9 +761,20 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
<GiftedChat
|
|
<GiftedChat
|
|
messages={messages}
|
|
messages={messages}
|
|
listViewProps={{
|
|
listViewProps={{
|
|
|
|
+ ref: flatList,
|
|
showsVerticalScrollIndicator: false,
|
|
showsVerticalScrollIndicator: false,
|
|
- initialNumToRender: 20
|
|
|
|
|
|
+ initialNumToRender: 30,
|
|
|
|
+ onViewableItemsChanged: handleViewableItemsChanged,
|
|
|
|
+ viewabilityConfig: { itemVisiblePercentThreshold: 50 },
|
|
|
|
+ initialScrollIndex: unreadMessageIndex ?? 0,
|
|
|
|
+ onScrollToIndexFailed: (info: any) => {
|
|
|
|
+ const wait = new Promise((resolve) => setTimeout(resolve, 300));
|
|
|
|
+ wait.then(() => {
|
|
|
|
+ flatList.current?.scrollToIndex({ index: info.index, animated: true });
|
|
|
|
+ });
|
|
|
|
+ }
|
|
}}
|
|
}}
|
|
|
|
+ renderSystemMessage={renderSystemMessage}
|
|
onSend={(newMessages: IMessage[]) => onSend(newMessages)}
|
|
onSend={(newMessages: IMessage[]) => onSend(newMessages)}
|
|
user={{ _id: +currentUserId, name: 'Me' }}
|
|
user={{ _id: +currentUserId, name: 'Me' }}
|
|
renderBubble={renderBubble}
|
|
renderBubble={renderBubble}
|
|
@@ -769,6 +893,16 @@ const ChatScreen = ({ route }: { route: any }) => {
|
|
};
|
|
};
|
|
|
|
|
|
const styles = StyleSheet.create({
|
|
const styles = StyleSheet.create({
|
|
|
|
+ unreadMessagesContainer: {
|
|
|
|
+ padding: 8,
|
|
|
|
+ backgroundColor: Colors.FILL_LIGHT,
|
|
|
|
+ alignItems: 'center'
|
|
|
|
+ },
|
|
|
|
+ unreadMessagesText: {
|
|
|
|
+ color: Colors.DARK_BLUE,
|
|
|
|
+ fontWeight: '700',
|
|
|
|
+ fontSize: getFontSize(12)
|
|
|
|
+ },
|
|
emojiSelectorContainer: {
|
|
emojiSelectorContainer: {
|
|
position: 'absolute',
|
|
position: 'absolute',
|
|
bottom: 0,
|
|
bottom: 0,
|