|
|
@@ -1,4 +1,4 @@
|
|
|
-import React, { useState, useEffect, useRef, useCallback } from 'react';
|
|
|
+import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
|
|
import {
|
|
|
View,
|
|
|
Text,
|
|
|
@@ -43,6 +43,11 @@ import { useMessagesStore } from 'src/stores/unreadMessagesStore';
|
|
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
|
import GroupIcon from 'assets/icons/messages/group-chat.svg';
|
|
|
import { usePushNotification } from 'src/contexts/PushNotificationContext';
|
|
|
+import { useChatsListLive } from 'src/watermelondb/features/chat/hooks/useChatsList';
|
|
|
+import { syncChatsIncremental } from 'src/watermelondb/features/chat/data/chat.sync';
|
|
|
+import NetInfo from '@react-native-community/netinfo';
|
|
|
+import { useBlockedUsersLive } from 'src/watermelondb/features/chat/hooks/useBlockedUsersLive';
|
|
|
+import { testConnectionSpeed } from 'src/database/speedService';
|
|
|
|
|
|
const TypingIndicator = ({ name }: { name?: string }) => {
|
|
|
const [dots, setDots] = useState('');
|
|
|
@@ -73,12 +78,8 @@ const MessagesScreen = () => {
|
|
|
const insets = useSafeAreaInsets();
|
|
|
const navigation = useNavigation<any>();
|
|
|
const token = storage.get('token', StoreType.STRING) as string;
|
|
|
- const [chats, setChats] = useState<Chat[]>([]);
|
|
|
const [index, setIndex] = useState(0);
|
|
|
const [cacheKey, setCacheKey] = useState(Date.now());
|
|
|
- const { data: chatsData, refetch } = usePostGetChatsListQuery(token, index === 2 ? 1 : 0, true);
|
|
|
- const { data: blockedData, refetch: refetchBlocked } = usePostGetBlockedQuery(token, true);
|
|
|
- const [blocked, setBlocked] = useState<Blocked[]>([]);
|
|
|
const updateUnreadMessagesCount = useMessagesStore((state) => state.updateUnreadMessagesCount);
|
|
|
const { isSubscribed } = usePushNotification();
|
|
|
const [isBannerVisible, setIsBannerVisible] = useState(false);
|
|
|
@@ -98,6 +99,9 @@ const MessagesScreen = () => {
|
|
|
{ [key: string]: boolean } | { [key: string]: { firstName: string; isTyping: boolean } }
|
|
|
>({});
|
|
|
|
|
|
+ const chats = useChatsListLive({ archived: index === 2 ? 1 : 0 });
|
|
|
+ const blocked = useBlockedUsersLive();
|
|
|
+
|
|
|
const appState = useRef(AppState.currentState);
|
|
|
|
|
|
const socket = useRef<WebSocket | null>(null);
|
|
|
@@ -200,11 +204,11 @@ const MessagesScreen = () => {
|
|
|
return () => clearInterval(pingInterval);
|
|
|
}, []);
|
|
|
|
|
|
- const handleWebSocketMessage = (data: any) => {
|
|
|
+ const handleWebSocketMessage = async (data: any) => {
|
|
|
switch (data.action) {
|
|
|
case 'new_message':
|
|
|
case 'messages_read':
|
|
|
- refetch();
|
|
|
+ await syncChatsIncremental(token);
|
|
|
break;
|
|
|
case 'is_typing':
|
|
|
if (data.conversation_with) {
|
|
|
@@ -264,21 +268,11 @@ const MessagesScreen = () => {
|
|
|
});
|
|
|
});
|
|
|
|
|
|
- useEffect(() => {
|
|
|
- if (chatsData && chatsData.conversations) {
|
|
|
- setChats(chatsData.conversations);
|
|
|
- }
|
|
|
- }, [chatsData]);
|
|
|
-
|
|
|
- useEffect(() => {
|
|
|
- if (blockedData && blockedData.blocked) {
|
|
|
- setBlocked(blockedData.blocked);
|
|
|
- }
|
|
|
- }, [blockedData]);
|
|
|
-
|
|
|
useFocusEffect(
|
|
|
useCallback(() => {
|
|
|
- refetch();
|
|
|
+ (async () => {
|
|
|
+ await syncChatsIncremental(token);
|
|
|
+ })();
|
|
|
initializeSocket();
|
|
|
updateUnreadMessagesCount();
|
|
|
|
|
|
@@ -291,113 +285,104 @@ const MessagesScreen = () => {
|
|
|
}, [token])
|
|
|
);
|
|
|
|
|
|
- const filterChatsByTab = () => {
|
|
|
- let filteredList = chats;
|
|
|
+ const getFilteredChats = (key: keyof typeof filteredChats) => {
|
|
|
+ if (key === 'blocked') return blocked;
|
|
|
|
|
|
- if (index === 3) {
|
|
|
- setFilteredChats((prev) => ({ ...prev, blocked }));
|
|
|
- return;
|
|
|
- }
|
|
|
+ let list = chats;
|
|
|
|
|
|
- if (index === 1) {
|
|
|
- filteredList = chats.filter((chat) => chat.unread_count > 0);
|
|
|
+ if (key === 'unread') {
|
|
|
+ list = list.filter((c) => c.unreadCount > 0);
|
|
|
}
|
|
|
|
|
|
- filteredList.sort((a, b) => {
|
|
|
- if (b.pin - a.pin !== 0) {
|
|
|
- return b.pin - a.pin;
|
|
|
- }
|
|
|
- if (b.pin_order - a.pin_order !== 0) {
|
|
|
- return b.pin_order - a.pin_order;
|
|
|
- }
|
|
|
+ if (key === 'archived') {
|
|
|
+ list = list.filter((c) => c.archive === 1);
|
|
|
+ }
|
|
|
|
|
|
+ return list.slice().sort((a, b) => {
|
|
|
+ if (b.pin - a.pin !== 0) return b.pin - a.pin;
|
|
|
+ if (b.pinOrder - a.pinOrder !== 0) return b.pinOrder - a.pinOrder;
|
|
|
return new Date(b.updated).getTime() - new Date(a.updated).getTime();
|
|
|
});
|
|
|
- setFilteredChats((prev) => ({ ...prev, [routes[index].key]: filteredList }));
|
|
|
};
|
|
|
|
|
|
- useEffect(() => {
|
|
|
- filterChatsByTab();
|
|
|
- }, [chats, index, blocked]);
|
|
|
-
|
|
|
- const searchFilter = (text: string) => {
|
|
|
- if (text) {
|
|
|
- const newData =
|
|
|
- chats?.filter((item: Chat) => {
|
|
|
- const itemData = item.short ? item.short.toLowerCase() : ''.toLowerCase();
|
|
|
- const textData = text.toLowerCase();
|
|
|
- return itemData.indexOf(textData) > -1;
|
|
|
- }) ?? [];
|
|
|
- setFilteredChats((prev) => ({ ...prev, [routes[index].key]: newData }));
|
|
|
- setSearch(text);
|
|
|
- } else {
|
|
|
- filterChatsByTab();
|
|
|
- setSearch(text);
|
|
|
- }
|
|
|
- };
|
|
|
+ const filteredChatsForCurrentTab = useMemo(() => {
|
|
|
+ return getFilteredChats(routes[index].key);
|
|
|
+ }, [index, chats]);
|
|
|
+
|
|
|
+ // const searchFilter = (text: string) => {
|
|
|
+ // if (text) {
|
|
|
+ // const newData =
|
|
|
+ // chats?.filter((item: Chat) => {
|
|
|
+ // const itemData = item.short ? item.short.toLowerCase() : ''.toLowerCase();
|
|
|
+ // const textData = text.toLowerCase();
|
|
|
+ // return itemData.indexOf(textData) > -1;
|
|
|
+ // }) ?? [];
|
|
|
+ // setFilteredChats((prev) => ({ ...prev, [routes[index].key]: newData }));
|
|
|
+ // setSearch(text);
|
|
|
+ // } else {
|
|
|
+ // filterChatsByTab();
|
|
|
+ // setSearch(text);
|
|
|
+ // }
|
|
|
+ // };
|
|
|
|
|
|
const renderChatItem = ({ item }: { item: Chat }) => {
|
|
|
const name =
|
|
|
- item.user_type === 'blocked'
|
|
|
+ item.userType === 'blocked'
|
|
|
? 'Account is blocked'
|
|
|
- : item.user_type === 'not_exist'
|
|
|
+ : item.userType === 'not_exist'
|
|
|
? 'Account does not exist'
|
|
|
: item.name;
|
|
|
|
|
|
return (
|
|
|
<SwipeableRow
|
|
|
chat={{
|
|
|
- uid: item.uid,
|
|
|
- groupToken: item.group_chat_token,
|
|
|
+ uid: item.chatUid,
|
|
|
+ groupToken: item.groupChatToken,
|
|
|
name: item.name,
|
|
|
avatar: item.avatar,
|
|
|
pin: item.pin,
|
|
|
archive: item.archive,
|
|
|
muted: item.muted,
|
|
|
- userType: item.user_type ?? 'normal'
|
|
|
+ userType: item.userType ?? 'normal',
|
|
|
+ announcement: item.announcement
|
|
|
}}
|
|
|
token={token}
|
|
|
onRowOpen={handleRowOpen}
|
|
|
- refetch={refetch}
|
|
|
- refetchBlocked={refetchBlocked}
|
|
|
>
|
|
|
<TouchableHighlight
|
|
|
key={
|
|
|
- item.uid
|
|
|
- ? `${item.uid}-${typingUsers[item.uid]}`
|
|
|
- : `${item.group_chat_token}-${typingUsers[item.group_chat_token ?? '']}`
|
|
|
+ item.chatUid
|
|
|
+ ? `${item.chatUid}-${typingUsers[item.chatUid]}`
|
|
|
+ : `${item.groupChatToken}-${typingUsers[item.groupChatToken ?? '']}`
|
|
|
}
|
|
|
activeOpacity={0.8}
|
|
|
onPress={() => {
|
|
|
- if (!item.uid) {
|
|
|
- navigation.navigate(NAVIGATION_PAGES.GROUP_CHAT, {
|
|
|
- group_token: item.group_chat_token,
|
|
|
- name: item.name,
|
|
|
- avatar: item.avatar,
|
|
|
- userType: item.user_type
|
|
|
- });
|
|
|
- } else {
|
|
|
- navigation.navigate(NAVIGATION_PAGES.CHAT, {
|
|
|
- id: item.uid,
|
|
|
+ navigation.navigate(
|
|
|
+ item.groupChatToken ? NAVIGATION_PAGES.GROUP_CHAT : NAVIGATION_PAGES.CHAT,
|
|
|
+ {
|
|
|
+ id: item.chatUid,
|
|
|
+ group_token: item.groupChatToken,
|
|
|
name: item.name,
|
|
|
avatar: item.avatar,
|
|
|
- userType: item.user_type
|
|
|
- });
|
|
|
- }
|
|
|
+ userType: item.userType ?? 'normal',
|
|
|
+ announcement: item.announcement
|
|
|
+ }
|
|
|
+ );
|
|
|
}}
|
|
|
underlayColor={Colors.FILL_LIGHT}
|
|
|
>
|
|
|
<View style={styles.chatItem}>
|
|
|
- {item.avatar && (item.user_type === 'normal' || !item.user_type) ? (
|
|
|
+ {item.avatar && (item.userType === 'normal' || !item.userType) ? (
|
|
|
<Image
|
|
|
source={{
|
|
|
- uri: item.group_chat_token
|
|
|
+ uri: item.groupChatToken
|
|
|
? `${API_HOST}${item.avatar}?cacheBust=${cacheKey}`
|
|
|
: `${API_HOST}${item.avatar}`
|
|
|
}}
|
|
|
style={styles.avatar}
|
|
|
+ onError={(e) => console.warn('Image error', e.nativeEvent)}
|
|
|
/>
|
|
|
- ) : item.uid && (item.user_type === 'normal' || !item.user_type) ? (
|
|
|
+ ) : item.chatUid && (item.userType === 'normal' || !item.userType) ? (
|
|
|
<AvatarWithInitials
|
|
|
text={
|
|
|
item.name
|
|
|
@@ -408,7 +393,7 @@ const MessagesScreen = () => {
|
|
|
flag={API_HOST + item?.flag}
|
|
|
size={54}
|
|
|
/>
|
|
|
- ) : item.user_type === 'normal' || !item.user_type ? (
|
|
|
+ ) : item.userType === 'normal' || !item.userType ? (
|
|
|
<GroupIcon fill={Colors.DARK_BLUE} width={54} height={54} />
|
|
|
) : (
|
|
|
<BanIcon fill={Colors.RED} width={54} height={54} />
|
|
|
@@ -419,7 +404,7 @@ const MessagesScreen = () => {
|
|
|
<Text
|
|
|
style={[
|
|
|
styles.chatName,
|
|
|
- item.user_type === 'not_exist' || item.user_type === 'blocked'
|
|
|
+ item.userType === 'not_exist' || item.userType === 'blocked'
|
|
|
? { color: Colors.RED }
|
|
|
: {}
|
|
|
]}
|
|
|
@@ -431,10 +416,9 @@ const MessagesScreen = () => {
|
|
|
{item.pin === 1 ? <PinIcon height={12} fill={Colors.DARK_BLUE} /> : null}
|
|
|
{item.muted === 1 ? <BellSlashIcon height={12} fill={Colors.DARK_BLUE} /> : null}
|
|
|
|
|
|
- {item.sent_by === +currentUserId && item.status === 3 ? (
|
|
|
+ {item.sentBy === +currentUserId && item.status === 3 ? (
|
|
|
<ReadIcon fill={Colors.DARK_BLUE} />
|
|
|
- ) : item.sent_by === +currentUserId &&
|
|
|
- (item.status === 2 || item.status === 1) ? (
|
|
|
+ ) : item.sentBy === +currentUserId && (item.status === 2 || item.status === 1) ? (
|
|
|
<UnreadIcon fill={Colors.LIGHT_GRAY} />
|
|
|
) : null}
|
|
|
<Text style={styles.chatTime}>{formatDate(item.updated)}</Text>
|
|
|
@@ -442,24 +426,24 @@ const MessagesScreen = () => {
|
|
|
</View>
|
|
|
|
|
|
<View style={[styles.rowContainer, { flex: 1, gap: 6 }]}>
|
|
|
- {item.uid && typingUsers[item.uid] ? (
|
|
|
+ {item.chatUid && typingUsers[item.chatUid] ? (
|
|
|
<TypingIndicator />
|
|
|
- ) : item.group_chat_token &&
|
|
|
- typingUsers[item.group_chat_token] &&
|
|
|
- (typingUsers[item.group_chat_token] as any)?.firstName ? (
|
|
|
- <TypingIndicator name={(typingUsers[item.group_chat_token] as any).firstName} />
|
|
|
+ ) : item.groupChatToken &&
|
|
|
+ typingUsers[item.groupChatToken] &&
|
|
|
+ (typingUsers[item.groupChatToken] as any)?.firstName ? (
|
|
|
+ <TypingIndicator name={(typingUsers[item.groupChatToken] as any).firstName} />
|
|
|
) : (
|
|
|
<Text numberOfLines={2} style={styles.chatMessage}>
|
|
|
- {item.attachement_name && item.attachement_name.length
|
|
|
- ? item.attachement_name
|
|
|
+ {item.attachementName && item.attachementName.length
|
|
|
+ ? item.attachementName
|
|
|
: item.short}
|
|
|
</Text>
|
|
|
)}
|
|
|
|
|
|
- {item.unread_count > 0 ? (
|
|
|
+ {item.unreadCount > 0 ? (
|
|
|
<View style={styles.unreadBadge}>
|
|
|
<Text style={styles.unreadText}>
|
|
|
- {item.unread_count > 99 ? '99+' : item.unread_count}
|
|
|
+ {item.unreadCount > 99 ? '99+' : item.unreadCount}
|
|
|
</Text>
|
|
|
</View>
|
|
|
) : null}
|
|
|
@@ -475,19 +459,18 @@ const MessagesScreen = () => {
|
|
|
return (
|
|
|
<SwipeableBlockedRow
|
|
|
data={{
|
|
|
- id: item.id,
|
|
|
- first_name: item.first_name,
|
|
|
- last_name: item.last_name,
|
|
|
+ userId: item.userId,
|
|
|
+ firstName: item.firstName,
|
|
|
+ lastName: item.lastName,
|
|
|
avatar: item.avatar
|
|
|
}}
|
|
|
token={token}
|
|
|
onRowOpen={handleRowOpen}
|
|
|
- refetchBlocked={refetchBlocked}
|
|
|
>
|
|
|
<TouchableHighlight
|
|
|
activeOpacity={0.8}
|
|
|
onPress={() =>
|
|
|
- navigation.navigate(NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW, { userId: item.id })
|
|
|
+ navigation.navigate(NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW, { userId: item.userId })
|
|
|
}
|
|
|
underlayColor={Colors.FILL_LIGHT}
|
|
|
>
|
|
|
@@ -499,7 +482,7 @@ const MessagesScreen = () => {
|
|
|
/>
|
|
|
) : (
|
|
|
<AvatarWithInitials
|
|
|
- text={item.first_name[0] + item.last_name[0]}
|
|
|
+ text={item.firstName[0] + item.lastName[0]}
|
|
|
flag={API_HOST + item?.flag}
|
|
|
size={32}
|
|
|
fontSize={12}
|
|
|
@@ -508,7 +491,7 @@ const MessagesScreen = () => {
|
|
|
|
|
|
<View style={{ flex: 1, gap: 6 }}>
|
|
|
<View style={[styles.rowContainer, { alignItems: 'center' }]}>
|
|
|
- <Text style={styles.chatName}>{item.first_name + ' ' + item.last_name}</Text>
|
|
|
+ <Text style={styles.chatName}>{item.firstName + ' ' + item.lastName}</Text>
|
|
|
|
|
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
|
<BanIcon height={12} fill={Colors.RED} />
|
|
|
@@ -578,17 +561,19 @@ const MessagesScreen = () => {
|
|
|
routes={routes}
|
|
|
sceneStyles={{ paddingHorizontal: 0 }}
|
|
|
maxTabHeight={50}
|
|
|
- renderScene={({ route }: { route: { key: keyof typeof filteredChats } }) =>
|
|
|
- route.key === 'blocked' ? (
|
|
|
+ renderScene={({ route }) => {
|
|
|
+ const data = route.key === routes[index].key ? filteredChatsForCurrentTab : [];
|
|
|
+
|
|
|
+ return route.key === 'blocked' ? (
|
|
|
<FlashList
|
|
|
viewabilityConfig={{
|
|
|
waitForInteraction: true,
|
|
|
itemVisiblePercentThreshold: 50,
|
|
|
minimumViewTime: 1000
|
|
|
}}
|
|
|
- data={filteredChats[route.key]}
|
|
|
+ data={blocked}
|
|
|
renderItem={renderBlockedItem}
|
|
|
- keyExtractor={(item, index) => `${item.id}-${index}`}
|
|
|
+ keyExtractor={(item, i) => `${item.userId}-${i}`}
|
|
|
/>
|
|
|
) : (
|
|
|
<FlashList
|
|
|
@@ -597,13 +582,13 @@ const MessagesScreen = () => {
|
|
|
itemVisiblePercentThreshold: 50,
|
|
|
minimumViewTime: 1000
|
|
|
}}
|
|
|
- data={filteredChats[route.key]}
|
|
|
+ data={data as Chat[]}
|
|
|
renderItem={renderChatItem}
|
|
|
- keyExtractor={(item, index) => `${item.uid}-${index}`}
|
|
|
+ keyExtractor={(item, i) => `${item.chatUid}-${item.groupChatToken}-${i}`}
|
|
|
extraData={typingUsers}
|
|
|
/>
|
|
|
- )
|
|
|
- }
|
|
|
+ );
|
|
|
+ }}
|
|
|
/>
|
|
|
|
|
|
<SearchModal />
|
|
|
@@ -611,10 +596,19 @@ const MessagesScreen = () => {
|
|
|
<WarningModal
|
|
|
type={'delete'}
|
|
|
buttonTitle={isWarningModalVisible?.buttonTitle ?? 'Delete'}
|
|
|
- isVisible={!!isWarningModalVisible}
|
|
|
- onClose={() => setIsWarningModalVisible(null)}
|
|
|
- title={isWarningModalVisible?.title}
|
|
|
- message={isWarningModalVisible?.message}
|
|
|
+ isVisible={!!isWarningModalVisible?.visible}
|
|
|
+ onClose={() =>
|
|
|
+ setIsWarningModalVisible({
|
|
|
+ ...isWarningModalVisible,
|
|
|
+ visible: false,
|
|
|
+ title: isWarningModalVisible?.title ?? '',
|
|
|
+ buttonTitle: isWarningModalVisible?.buttonTitle ?? 'Delete',
|
|
|
+ message: isWarningModalVisible?.message ?? '',
|
|
|
+ action: isWarningModalVisible?.action ?? (() => {})
|
|
|
+ })
|
|
|
+ }
|
|
|
+ title={isWarningModalVisible?.title ?? ''}
|
|
|
+ message={isWarningModalVisible?.message ?? ''}
|
|
|
action={isWarningModalVisible?.action}
|
|
|
/>
|
|
|
</View>
|