import { useState, useEffect, useRef } from 'react'; import { ChatObject, MessageObject, PersonObject, UserAuthHeaders, } from '../interfaces'; import { getDateTime } from '../components/util/dateTime'; import { getChatsBefore, newChat, deleteChat, getPeopleToInvite, invitePerson, removePerson, getMessages, newMessage, readMessage, } from '../actions'; import { animateScroll } from 'react-scroll'; import { scrollToBottom } from './shared'; const chatCountIterator = 20; const messageCountIterator = 50; export const sortChats = (chats: ChatObject[]) => { return chats.sort((a: ChatObject, b: ChatObject) => { const aDate = a.last_message && a.last_message.created ? getDateTime(a.last_message.created) : getDateTime(a.created); const bDate = b.last_message && b.last_message.created ? getDateTime(b.last_message.created) : getDateTime(b.created); return new Date(bDate).getTime() - new Date(aDate).getTime(); }); }; export const sortMessages = (messages: MessageObject[]) => { return messages.sort((a: MessageObject, b: MessageObject) => { return new Date(b.created).getTime() - new Date(a.created).getTime(); }); }; export const useMultiChatLogic = ( projectId: string, username: string, secret: string, httpUrl?: string ) => { const host = httpUrl ? httpUrl : 'https://api.chatengine.io'; const headers: UserAuthHeaders = { 'Public-Key': projectId, 'User-Name': username, 'User-Secret': secret, }; // Data const [activeChatId, setActiveChatId] = useState(); const [chats, setChats] = useState([]); const [messages, setMessages] = useState([]); const [peopleToInvite, setPeopleToInvite] = useState([]); // State const [hasMoreChats, setHasMoreChats] = useState(false); const [hasMoreMessages, setHasMoreMessages] = useState(false); const [isChatFeedAtBottom, setIsChatFeedAtBottom] = useState(false); const [isChatListLoading, setIsChatListLoading] = useState(true); const [isChatFeedLoading, setIsChatFeedLoading] = useState(true); const [isChatSettingsLoading, setIsChatSettingsLoading] = useState(true); const [isMobileChatListOpen, setIsMobileChatListOpen] = useState(false); const [isMobileChatSettingsOpen, setIsMobileChatSettingsOpen] = useState(false); // Subscribe to Chat & Message Count const chatCountRef = useRef(0); chatCountRef.current = chats.length; const messageCountRef = useRef(0); messageCountRef.current = messages.length; const chat = chats.find((chat) => chat.id === activeChatId); // Fetch data on mount const didMountRef = useRef(false); useEffect(() => { if (!didMountRef.current) { didMountRef.current = true; fetchMultiChatData(); } }, []); useEffect(() => { const chat = chats.find((chat) => chat.id === activeChatId); const chatPerson = chat?.people.find( (chatPerson) => chatPerson.person.username === username ); if ( activeChatId && chat?.last_message.id && // If there is a message chat.last_message.id !== chatPerson?.last_read && isChatFeedAtBottom ) { readMessage(host, headers, activeChatId, chat.last_message.id, () => {}); } }, [chats, activeChatId, isChatFeedAtBottom]); async function fetchMultiChatData() { const now = new Date() .toISOString() .replace('T', ' ') .replace('Z', `${Math.floor(Math.random() * 1000)}+00:00`); getChatsBefore( host, headers, now, chatCountRef.current > 0 ? chatCountRef.current : chatCountIterator, (chats) => { onGetChats(chats); let currentChat = activeChatId; if (!activeChatId && chats.length > 0) { currentChat = chats[0].id; } currentChat ? onChatCardClick(currentChat) : setIsChatFeedLoading(false); } ); return; } async function onGetChats(chats: ChatObject[] = []) { setHasMoreChats(chats.length >= chatCountRef.current + chatCountIterator); const sortedChats = sortChats(chats); setChats(sortedChats); setIsChatListLoading(false); return; } async function onNewChat(chat: ChatObject) { const newChats = [chat].concat(chats ? chats : []); setChats(newChats); return; } async function onEditChat(newChat: ChatObject) { const otherChats = chats ? chats.filter((chat) => chat.id !== newChat.id) : []; const newChats = [newChat].concat(otherChats); const sortedChats = sortChats(newChats); setChats(sortedChats); return; } async function onDeleteChat(oldChat: ChatObject) { const newChats = chats ? chats.filter((chat) => chat.id !== oldChat.id) : []; setChats(newChats); if (newChats.length > 0 && activeChatId === oldChat.id) onChatCardClick(newChats[0].id); return; } async function onGetMessages(chatId: number, messages: MessageObject[]) { setHasMoreMessages( messages.length >= messageCountRef.current + messageCountIterator ); setMessages(messages); void chatId; return; } async function onNewMessage(chatId: number, newMessage: MessageObject) { if (activeChatId === chatId) { const otherMessages = messages ? messages.filter((message) => message.created !== newMessage.created) : []; const newMessages = otherMessages.concat(newMessage); const sortedMessages = sortMessages(newMessages); setMessages(sortedMessages); if (isChatFeedAtBottom) { scrollToBottom(333, `ce-message-list-${activeChatId}`); } } return; } async function onEditMessage(chatId: number, newMessage: MessageObject) { if (chatId === activeChatId) { const otherMessages = messages ? messages.filter((message) => message.id !== newMessage.id) : []; const newMessages = [newMessage].concat(otherMessages); const sortedMessages = sortMessages(newMessages); setMessages(sortedMessages); } return; } async function onDeleteMessage(chatId: number, oldMessage: MessageObject) { if (chatId === activeChatId) { const newMessages = messages ? messages.filter((message) => message.id !== oldMessage.id) : []; setMessages(newMessages); } return; } async function onConnect() { fetchMultiChatData(); return; } async function onAuthFail() {} async function onChatFormSubmit(title: string) { newChat(host, headers, title, (chat) => { onNewChat(chat); onChatCardClick(chat.id); }); return; } async function onChatCardClick(newActiveChatId: number) { if (newActiveChatId !== activeChatId) { setIsChatFeedLoading(true); setIsChatSettingsLoading(true); } setActiveChatId(newActiveChatId); setIsMobileChatListOpen(false); getMessages( host, headers, newActiveChatId, messageCountIterator, (chatId, messages) => { onGetMessages(chatId, messages); setIsChatFeedLoading(false); setIsChatSettingsLoading(false); scrollToBottom(0, `ce-message-list-${activeChatId}`); } ); getPeopleToInvite(host, headers, newActiveChatId, setPeopleToInvite); return; } async function onChatLoaderShow() { const now = new Date() .toISOString() .replace('T', ' ') .replace('Z', `0000+00:00`); getChatsBefore( host, headers, now, chatCountRef.current + chatCountIterator, onGetChats ); return; } async function onMessageFormSubmit(message: MessageObject) { const newMessages = messages?.concat(message); setMessages(newMessages); newMessage(host, headers, activeChatId, message, () => {}); setTimeout(() => { scrollToBottom(333, `ce-message-list-${activeChatId}`); }, 100); return; } async function onInvitePersonClick(person: PersonObject) { activeChatId && invitePerson(host, headers, activeChatId, person.username, () => onChatCardClick(activeChatId) ); } async function onRemovePersonClick(person: PersonObject) { activeChatId && removePerson(host, headers, activeChatId, person.username, () => onChatCardClick(activeChatId) ); return; } async function onDeleteChatClick(chat: ChatObject) { deleteChat(host, headers, chat.id, onDeleteChat); return; } async function onBottomMessageShow() { setIsChatFeedAtBottom(true); return; } async function onBottomMessageHide() { setIsChatFeedAtBottom(false); return; } async function onMessageLoaderShow() { const scrollContainerId = `ce-message-list-${activeChatId}`; const messageListId = `ce-message-list-content-${activeChatId}`; const currentElement = document.getElementById(messageListId); let currentHeight = currentElement ? currentElement.clientHeight : 0; activeChatId && getMessages( host, headers, activeChatId, messageCountRef.current + messageCountIterator, (chatId, messages) => { onGetMessages(chatId, messages); setTimeout(() => { const element = document.getElementById(messageListId); if (element) { animateScroll.scrollTo(element.clientHeight - currentHeight, { duration: 333, containerId: scrollContainerId, }); } }, 1000); } ); return; } async function onMessageLoaderHide() { return; } async function onIsTyping(id: number, person: PersonObject) { void id, person; return; } async function onMobileChatListClick() { setIsMobileChatListOpen(true); return; } async function onMobileChatSettingsClick() { setIsMobileChatSettingsOpen(true); return; } async function onCloseMobileChatSettingsClick() { setIsMobileChatSettingsOpen(false); return; } return { // Socket Hooks onConnect, onAuthFail, onGetChats, onNewChat, onEditChat, onDeleteChat, onGetMessages, onNewMessage, onEditMessage, onDeleteMessage, onIsTyping, // Auth Data projectId, username, // file deepcode ignore HardcodedNonCryptoSecret: secret, httpUrl, wsUrl: httpUrl && httpUrl.replace('http', 'ws'), // Chat Data chats, setChats, messages, setMessages, peopleToInvite, setPeopleToInvite, // Chat State activeChatId, setActiveChatId, chat, hasMoreChats, setHasMoreChats, hasMoreMessages, setHasMoreMessages, isChatFeedAtBottom, setIsChatFeedAtBottom, isChatListLoading, setIsChatListLoading, isChatFeedLoading, setIsChatFeedLoading, isChatSettingsLoading, setIsChatSettingsLoading, isMobileChatListOpen, setIsMobileChatListOpen, isMobileChatSettingsOpen, setIsMobileChatSettingsOpen, // Component Hooks onChatFormSubmit, onChatCardClick, onChatLoaderShow, onMessageLoaderShow, onMessageLoaderHide, onBottomMessageShow, onBottomMessageHide, onMessageFormSubmit, onInvitePersonClick, onRemovePersonClick, onDeleteChatClick, onMobileChatListClick, onMobileChatSettingsClick, onCloseMobileChatSettingsClick, }; };