|
@@ -49,7 +49,10 @@ import {
|
|
usePostSendGroupMessageMutation,
|
|
usePostSendGroupMessageMutation,
|
|
usePostReactToGroupMessageMutation,
|
|
usePostReactToGroupMessageMutation,
|
|
usePostGroupMessagesReadMutation,
|
|
usePostGroupMessagesReadMutation,
|
|
- usePostDeleteGroupMessageMutation
|
|
|
|
|
|
+ usePostDeleteGroupMessageMutation,
|
|
|
|
+ usePostGetPinnedGroupMessageQuery,
|
|
|
|
+ usePostSetPinGroupMessageMutation,
|
|
|
|
+ usePostGetGroupSettingsQuery
|
|
} from '@api/chat';
|
|
} from '@api/chat';
|
|
import { CustomMessage, GroupMessage, Reaction } from '../types';
|
|
import { CustomMessage, GroupMessage, Reaction } from '../types';
|
|
import { API_HOST, WEBSOCKET_URL } from 'src/constants';
|
|
import { API_HOST, WEBSOCKET_URL } from 'src/constants';
|
|
@@ -77,6 +80,7 @@ import MessageLocation from '../Components/MessageLocation';
|
|
import GroupIcon from 'assets/icons/messages/group-chat.svg';
|
|
import GroupIcon from 'assets/icons/messages/group-chat.svg';
|
|
import { CACHED_ATTACHMENTS_DIR } from 'src/constants/constants';
|
|
import { CACHED_ATTACHMENTS_DIR } from 'src/constants/constants';
|
|
import GroupStatusModal from '../Components/GroupStatusModal';
|
|
import GroupStatusModal from '../Components/GroupStatusModal';
|
|
|
|
+import PinIcon from 'assets/icons/messages/pin.svg';
|
|
|
|
|
|
const options = {
|
|
const options = {
|
|
enableVibrateFallback: true,
|
|
enableVibrateFallback: true,
|
|
@@ -116,12 +120,22 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
refetch: refetch,
|
|
refetch: refetch,
|
|
isFetching: isFetching
|
|
isFetching: isFetching
|
|
} = usePostGetGroupChatQuery(token, group_token, 50, prevThenMessageId, true);
|
|
} = usePostGetGroupChatQuery(token, group_token, 50, prevThenMessageId, true);
|
|
|
|
+ const { data: pinData, refetch: refetchPinned } = usePostGetPinnedGroupMessageQuery(
|
|
|
|
+ token,
|
|
|
|
+ group_token,
|
|
|
|
+ true
|
|
|
|
+ );
|
|
|
|
+ const { data } = usePostGetGroupSettingsQuery(token, group_token, true);
|
|
|
|
+
|
|
const { mutateAsync: sendMessage } = usePostSendGroupMessageMutation();
|
|
const { mutateAsync: sendMessage } = usePostSendGroupMessageMutation();
|
|
|
|
|
|
|
|
+ const [isSearchingMessage, setIsSearchingMessage] = useState<number | null>(null);
|
|
|
|
+
|
|
const swipeableRowRef = useRef<Swipeable | null>(null);
|
|
const swipeableRowRef = useRef<Swipeable | null>(null);
|
|
const messageContainerRef = useRef<FlatList<IMessage> | null>(null);
|
|
const messageContainerRef = useRef<FlatList<IMessage> | null>(null);
|
|
const [selectedMedia, setSelectedMedia] = useState<any>(null);
|
|
const [selectedMedia, setSelectedMedia] = useState<any>(null);
|
|
|
|
|
|
|
|
+ const [pinned, setPinned] = useState<any>(null);
|
|
const [replyMessage, setReplyMessage] = useState<CustomMessage | null>(null);
|
|
const [replyMessage, setReplyMessage] = useState<CustomMessage | null>(null);
|
|
const [modalInfo, setModalInfo] = useState({
|
|
const [modalInfo, setModalInfo] = useState({
|
|
visible: false,
|
|
visible: false,
|
|
@@ -147,6 +161,7 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
const { mutateAsync: markMessagesAsRead } = usePostGroupMessagesReadMutation();
|
|
const { mutateAsync: markMessagesAsRead } = usePostGroupMessagesReadMutation();
|
|
const { mutateAsync: deleteMessage } = usePostDeleteGroupMessageMutation();
|
|
const { mutateAsync: deleteMessage } = usePostDeleteGroupMessageMutation();
|
|
const { mutateAsync: reactToMessage } = usePostReactToGroupMessageMutation();
|
|
const { mutateAsync: reactToMessage } = usePostReactToGroupMessageMutation();
|
|
|
|
+ const { mutateAsync: pinMessage } = usePostSetPinGroupMessageMutation();
|
|
|
|
|
|
const [highlightedMessageId, setHighlightedMessageId] = useState<number | null>(null);
|
|
const [highlightedMessageId, setHighlightedMessageId] = useState<number | null>(null);
|
|
const [isRerendering, setIsRerendering] = useState<boolean>(false);
|
|
const [isRerendering, setIsRerendering] = useState<boolean>(false);
|
|
@@ -180,6 +195,12 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
});
|
|
});
|
|
}, []);
|
|
}, []);
|
|
|
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
+ if (pinData && pinData?.message) {
|
|
|
|
+ setPinned(pinData.message);
|
|
|
|
+ }
|
|
|
|
+ }, [pinData]);
|
|
|
|
+
|
|
const onSendMedia = useCallback(
|
|
const onSendMedia = useCallback(
|
|
async (files: { uri: string; type: 'image' | 'video' }[]) => {
|
|
async (files: { uri: string; type: 'image' | 'video' }[]) => {
|
|
for (const file of files) {
|
|
for (const file of files) {
|
|
@@ -985,6 +1006,19 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
}, [chatData])
|
|
}, [chatData])
|
|
);
|
|
);
|
|
|
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
+ if (messages) {
|
|
|
|
+ if (isSearchingMessage) {
|
|
|
|
+ const messageIndex = messages.findIndex((msg) => msg._id === isSearchingMessage);
|
|
|
|
+
|
|
|
|
+ if (messageIndex !== -1 && flatList.current) {
|
|
|
|
+ setIsSearchingMessage(null);
|
|
|
|
+ }
|
|
|
|
+ scrollToMessage(isSearchingMessage);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }, [messages]);
|
|
|
|
+
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
if (messages?.length === 0 && !modalInfo.visible) {
|
|
if (messages?.length === 0 && !modalInfo.visible) {
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
@@ -994,7 +1028,7 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
}, [modalInfo]);
|
|
}, [modalInfo]);
|
|
|
|
|
|
const loadEarlierMessages = async () => {
|
|
const loadEarlierMessages = async () => {
|
|
- if (!hasMoreMessages || isLoadingEarlier || !messages) return;
|
|
|
|
|
|
+ if (!hasMoreMessages || (isLoadingEarlier && !isSearchingMessage) || !messages) return;
|
|
|
|
|
|
setIsLoadingEarlier(true);
|
|
setIsLoadingEarlier(true);
|
|
|
|
|
|
@@ -1093,8 +1127,8 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- if (spaceBelow < 160) {
|
|
|
|
- const extraShift = 160 - spaceBelow;
|
|
|
|
|
|
+ if (spaceBelow < 220) {
|
|
|
|
+ const extraShift = 220 - spaceBelow;
|
|
finalY -= extraShift;
|
|
finalY -= extraShift;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1103,7 +1137,7 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
finalY += extraShift;
|
|
finalY += extraShift;
|
|
}
|
|
}
|
|
|
|
|
|
- if (spaceBelow < 160 || spaceAbove < 50) {
|
|
|
|
|
|
+ if (spaceBelow < 220 || spaceAbove < 50) {
|
|
const targetY = screenHeight / 2 - height / 2;
|
|
const targetY = screenHeight / 2 - height / 2;
|
|
scrollY.value = withTiming(finalY - finalY);
|
|
scrollY.value = withTiming(finalY - finalY);
|
|
}
|
|
}
|
|
@@ -1170,6 +1204,25 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
);
|
|
);
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+ const handlePinMessage = (messageId: number, pin: 0 | 1) => {
|
|
|
|
+ pinMessage(
|
|
|
|
+ {
|
|
|
|
+ token,
|
|
|
|
+ message_id: messageId,
|
|
|
|
+ group_token,
|
|
|
|
+ pin
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ onSuccess: () => {
|
|
|
|
+ refetchPinned();
|
|
|
|
+ if (pin === 0) {
|
|
|
|
+ setPinned(null);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ );
|
|
|
|
+ };
|
|
|
|
+
|
|
const handleOptionPress = (option: string) => {
|
|
const handleOptionPress = (option: string) => {
|
|
if (!selectedMessage) return;
|
|
if (!selectedMessage) return;
|
|
|
|
|
|
@@ -1202,6 +1255,10 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
setIsModalVisible(false);
|
|
setIsModalVisible(false);
|
|
setInsetsColor(Colors.WHITE);
|
|
setInsetsColor(Colors.WHITE);
|
|
break;
|
|
break;
|
|
|
|
+ case 'pin':
|
|
|
|
+ handlePinMessage(selectedMessage.currentMessage?._id, 1);
|
|
|
|
+ setIsModalVisible(false);
|
|
|
|
+ break;
|
|
default:
|
|
default:
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
@@ -1519,6 +1576,8 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
const messageIndex = messages.findIndex((message) => message._id === messageId);
|
|
const messageIndex = messages.findIndex((message) => message._id === messageId);
|
|
|
|
|
|
if (messageIndex !== -1 && flatList.current) {
|
|
if (messageIndex !== -1 && flatList.current) {
|
|
|
|
+ setIsSearchingMessage(null);
|
|
|
|
+
|
|
flatList.current.scrollToIndex({
|
|
flatList.current.scrollToIndex({
|
|
index: messageIndex,
|
|
index: messageIndex,
|
|
animated: true,
|
|
animated: true,
|
|
@@ -1526,6 +1585,21 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
});
|
|
});
|
|
|
|
|
|
setHighlightedMessageId(messageId);
|
|
setHighlightedMessageId(messageId);
|
|
|
|
+ setMessages((previousMessages) =>
|
|
|
|
+ (previousMessages ?? []).map((msg) =>
|
|
|
|
+ msg._id === messageId
|
|
|
|
+ ? {
|
|
|
|
+ ...msg,
|
|
|
|
+ isRendering: msg?.isRendering ? false : true
|
|
|
|
+ }
|
|
|
|
+ : msg
|
|
|
|
+ )
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (hasMoreMessages && messageIndex === -1) {
|
|
|
|
+ setIsSearchingMessage(messageId);
|
|
|
|
+ loadEarlierMessages();
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
@@ -1854,6 +1928,56 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
}
|
|
}
|
|
/>
|
|
/>
|
|
</View>
|
|
</View>
|
|
|
|
+ {pinned && (
|
|
|
|
+ <TouchableOpacity
|
|
|
|
+ style={{
|
|
|
|
+ height: 38,
|
|
|
|
+ flexDirection: 'row',
|
|
|
|
+ backgroundColor: Colors.FILL_LIGHT,
|
|
|
|
+ borderBottomWidth: 1,
|
|
|
|
+ borderBottomColor: Colors.DARK_LIGHT
|
|
|
|
+ }}
|
|
|
|
+ onPress={() => scrollToMessage(pinned.id)}
|
|
|
|
+ >
|
|
|
|
+ <View
|
|
|
|
+ style={{
|
|
|
|
+ height: 50,
|
|
|
|
+ width: 6,
|
|
|
|
+ backgroundColor: Colors.DARK_BLUE
|
|
|
|
+ }}
|
|
|
|
+ ></View>
|
|
|
|
+ <View
|
|
|
|
+ style={{
|
|
|
|
+ paddingLeft: 8,
|
|
|
|
+ height: '100%',
|
|
|
|
+ justifyContent: 'center'
|
|
|
|
+ }}
|
|
|
|
+ >
|
|
|
|
+ <PinIcon fill={Colors.DARK_BLUE} height={18} />
|
|
|
|
+ </View>
|
|
|
|
+
|
|
|
|
+ <View style={{ flex: 1, justifyContent: 'center' }}>
|
|
|
|
+ <Text style={{ color: Colors.DARK_BLUE, paddingLeft: 10 }} numberOfLines={1}>
|
|
|
|
+ {pinned.text}
|
|
|
|
+ </Text>
|
|
|
|
+ </View>
|
|
|
|
+
|
|
|
|
+ {data?.settings?.admin === 1 && (
|
|
|
|
+ <View style={{ alignItems: 'flex-end', justifyContent: 'center' }}>
|
|
|
|
+ <TouchableOpacity
|
|
|
|
+ style={{ paddingRight: 10 }}
|
|
|
|
+ onPress={() => handlePinMessage(pinned.id, 0)}
|
|
|
|
+ >
|
|
|
|
+ <MaterialCommunityIcons
|
|
|
|
+ name="close-circle-outline"
|
|
|
|
+ size={24}
|
|
|
|
+ color={Colors.DARK_BLUE}
|
|
|
|
+ />
|
|
|
|
+ </TouchableOpacity>
|
|
|
|
+ </View>
|
|
|
|
+ )}
|
|
|
|
+ </TouchableOpacity>
|
|
|
|
+ )}
|
|
|
|
|
|
<GestureHandlerRootView style={styles.container}>
|
|
<GestureHandlerRootView style={styles.container}>
|
|
{messages ? (
|
|
{messages ? (
|
|
@@ -1862,7 +1986,7 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
listViewProps={{
|
|
listViewProps={{
|
|
ref: flatList,
|
|
ref: flatList,
|
|
showsVerticalScrollIndicator: false,
|
|
showsVerticalScrollIndicator: false,
|
|
- initialNumToRender: 30,
|
|
|
|
|
|
+ initialNumToRender: messages.length,
|
|
onViewableItemsChanged: handleViewableItemsChanged,
|
|
onViewableItemsChanged: handleViewableItemsChanged,
|
|
viewabilityConfig: { itemVisiblePercentThreshold: 50 },
|
|
viewabilityConfig: { itemVisiblePercentThreshold: 50 },
|
|
onScrollToIndexFailed: (info: any) => {
|
|
onScrollToIndexFailed: (info: any) => {
|
|
@@ -2013,6 +2137,7 @@ const GroupChatScreen = ({ route }: { route: any }) => {
|
|
handleOptionPress={handleOptionPress}
|
|
handleOptionPress={handleOptionPress}
|
|
messagePosition={messagePosition}
|
|
messagePosition={messagePosition}
|
|
isGroup={true}
|
|
isGroup={true}
|
|
|
|
+ isAdmin={data?.settings?.admin == 1}
|
|
/>
|
|
/>
|
|
<EmojiSelectorModal
|
|
<EmojiSelectorModal
|
|
visible={emojiSelectorVisible}
|
|
visible={emojiSelectorVisible}
|