import React, { useState, useEffect, useRef, useCallback } from 'react'; import { View, Text, TouchableOpacity, Image, Platform, TouchableHighlight, AppState, AppStateStatus } from 'react-native'; import { AvatarWithInitials, HorizontalTabView, Input, PageWrapper, WarningModal } from 'src/components'; import { NAVIGATION_PAGES } from 'src/types'; import { useFocusEffect, useNavigation } from '@react-navigation/native'; import AddChatIcon from 'assets/icons/messages/chat-plus.svg'; import { API_HOST, WEBSOCKET_URL } from 'src/constants'; import { Colors } from 'src/theme'; import SwipeableRow from './Components/SwipeableRow'; import { FlashList } from '@shopify/flash-list'; import ReadIcon from 'assets/icons/messages/check-read.svg'; import UnreadIcon from 'assets/icons/messages/check-unread.svg'; import SearchModal from './Components/SearchUsersModal'; import { SheetManager } from 'react-native-actions-sheet'; import MoreModal from './Components/MoreModal'; import SearchIcon from 'assets/icons/search.svg'; import { storage, StoreType } from 'src/storage'; import { usePostGetBlockedQuery, usePostGetChatsListQuery } from '@api/chat'; import { Blocked, Chat } from './types'; import PinIcon from 'assets/icons/messages/pin.svg'; import { formatDate } from './utils'; import { routes } from './constants'; import { styles } from './styles'; import { useChatStore } from 'src/stores/chatStore'; import BellSlashIcon from 'assets/icons/messages/bell-slash.svg'; import BanIcon from 'assets/icons/messages/ban.svg'; import SwipeableBlockedRow from './Components/SwipeableBlockedRow'; import { useMessagesStore } from 'src/stores/unreadMessagesStore'; const TypingIndicator = () => { const [dots, setDots] = useState(''); useEffect(() => { const interval = setInterval(() => { setDots((prevDots) => { if (prevDots.length >= 3) { return ''; } return prevDots + '.'; }); }, 500); return () => clearInterval(interval); }, []); return Typing{dots}; }; const MessagesScreen = () => { const navigation = useNavigation(); const token = storage.get('token', StoreType.STRING) as string; const [chats, setChats] = useState([]); const [index, setIndex] = useState(0); const { data: chatsData, refetch } = usePostGetChatsListQuery(token, index === 2 ? 1 : 0, true); const { data: blockedData, refetch: refetchBlocked } = usePostGetBlockedQuery(token, true); const [blocked, setBlocked] = useState([]); const updateUnreadMessagesCount = useMessagesStore((state) => state.updateUnreadMessagesCount); const [filteredChats, setFilteredChats] = useState<{ all: Chat[]; unread: Chat[]; archived: Chat[]; blocked: Blocked[]; }>({ all: [], unread: [], archived: [], blocked: [] }); const [search, setSearch] = useState(''); const openRowRef = useRef(null); const { isWarningModalVisible, setIsWarningModalVisible } = useChatStore(); const [typingUsers, setTypingUsers] = useState<{ [key: string]: boolean }>({}); const appState = useRef(AppState.currentState); const socket = useRef(null); const initializeSocket = () => { if (socket.current) { socket.current.close(); } setTimeout(() => { socket.current = new WebSocket(WEBSOCKET_URL); socket.current.onopen = () => { socket.current?.send(JSON.stringify({ token })); }; socket.current.onmessage = (event) => { const data = JSON.parse(event.data); handleWebSocketMessage(data); }; socket.current.onclose = () => { console.log('WebSocket connection closed'); }; }, 500); }; useEffect(() => { const handleAppStateChange = (nextAppState: AppStateStatus) => { if (appState.current.match(/inactive|background/) && nextAppState === 'active') { if (!socket.current || socket.current.readyState === WebSocket.CLOSED) { socket.current = new WebSocket(WEBSOCKET_URL); socket.current.onopen = () => { socket.current?.send(JSON.stringify({ token })); }; socket.current.onmessage = (event) => { const data = JSON.parse(event.data); handleWebSocketMessage(data); }; } } }; const subscription = AppState.addEventListener('change', handleAppStateChange); return () => { subscription.remove(); if (socket.current) { socket.current.close(); socket.current = null; } }; }, [token]); useEffect(() => { const pingInterval = setInterval(() => { if (socket.current && socket.current.readyState === WebSocket.OPEN) { socket.current.send(JSON.stringify({ action: 'ping', conversation_with: 0 })); } else { initializeSocket(); return () => { if (socket.current) { socket.current.close(); socket.current = null; } }; } }, 50000); return () => clearInterval(pingInterval); }, []); const handleWebSocketMessage = (data: any) => { switch (data.action) { case 'new_message': case 'messages_read': refetch(); break; case 'is_typing': if (data.conversation_with) { setTypingUsers((prev) => ({ ...prev, [data.conversation_with]: true })); } break; case 'stopped_typing': if (data.conversation_with) { setTypingUsers((prev) => ({ ...prev, [data.conversation_with]: false })); } break; default: break; } }; const handleRowOpen = (ref: any) => { if (openRowRef.current && openRowRef.current !== ref) { openRowRef.current.close(); } openRowRef.current = ref; }; useFocusEffect(() => { navigation.getParent()?.setOptions({ tabBarStyle: { display: 'flex', ...Platform.select({ android: { height: 58 } }) } }); }); useEffect(() => { if (chatsData && chatsData.conversations) { setChats(chatsData.conversations); } }, [chatsData]); useEffect(() => { if (blockedData && blockedData.blocked) { setBlocked(blockedData.blocked); } }, [blockedData]); useFocusEffect( useCallback(() => { refetch(); initializeSocket(); updateUnreadMessagesCount(); return () => { if (socket.current) { socket.current.close(); socket.current = null; } }; }, [token]) ); const filterChatsByTab = () => { let filteredList = chats; if (index === 3) { setFilteredChats((prev) => ({ ...prev, blocked })); return; } if (index === 1) { filteredList = chats.filter((chat) => chat.unread_count > 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; } 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 renderChatItem = ({ item }: { item: Chat }) => { return ( navigation.navigate( ...([ NAVIGATION_PAGES.CHAT, { id: item.uid, name: item.name, avatar: item.avatar } ] as never) ) } underlayColor={Colors.FILL_LIGHT} > {item.avatar ? ( ) : ( n[0]) .join('') ?? '' } flag={API_HOST + item?.flag} size={54} /> )} {item.name} {item.pin === 1 ? : null} {item.muted === 1 ? : null} {item.sent_by !== item.uid && item.status === 3 ? ( ) : item.sent_by !== item.uid && (item.status === 2 || item.status === 1) ? ( ) : null} {formatDate(item.updated)} {typingUsers[item.uid] ? ( ) : ( {item.short} )} {item.unread_count > 0 ? ( {item.unread_count > 99 ? '99+' : item.unread_count} ) : null} ); }; const renderBlockedItem = ({ item }: { item: Blocked }) => { return ( navigation.navigate( ...([NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW, { userId: item.id }] as never) ) } underlayColor={Colors.FILL_LIGHT} > {item.avatar ? ( ) : ( )} {item.first_name + ' ' + item.last_name} ); }; return ( Messages SheetManager.show('search-modal')} style={{ width: 30, alignItems: 'flex-end' }} > {/* searchFilter(text)} value={search} icon={} height={38} /> */} route.key === 'blocked' ? ( `${item.id}-${index}`} estimatedItemSize={50} /> ) : ( `${item.uid}-${index}`} estimatedItemSize={78} extraData={typingUsers} /> ) } /> setIsWarningModalVisible(null)} title={isWarningModalVisible?.title} message={isWarningModalVisible?.message} action={isWarningModalVisible?.action} /> ); }; export default MessagesScreen;