253 lines
8.4 KiB
TypeScript
253 lines
8.4 KiB
TypeScript
import MessageForm from '../components/chat/chatArea/MessageForm.tsx';
|
|
import ContactProfile from '../components/chat/chatHeader/ContactProfile.tsx';
|
|
import UserProfile from '../components/chat/leftSidebar/UserProfile.tsx';
|
|
import ContactForm from '../components/chat/leftSidebar/ContactForm.tsx';
|
|
import MessagesArea from '../components/chat/chatArea/MessagesArea.tsx';
|
|
import { createContext, useEffect, useState } from 'react';
|
|
import ContactsList from '../components/chat/leftSidebar/ContactsList.tsx';
|
|
import { initializeSocket, joinRoom } from '../socket/socket.tsx';
|
|
import Cookies from 'js-cookie';
|
|
import { getMessages, setContactStatus } from '../api/contactsApi.tsx';
|
|
import axios from 'axios';
|
|
import ParticipantsBar from '@/components/chat/rightSidebar/ParticipantsBar.tsx';
|
|
import { ChatMessagesProps, ContactsProps, MeProps } from '@/types/types.ts';
|
|
|
|
function Chat() {
|
|
const meDefaultValue = {
|
|
isGroupAdmin: false,
|
|
isGroupOwner: false,
|
|
};
|
|
const [contactsList, setContactsList] = useState<ContactsProps[]>([]);
|
|
const [currentContact, setCurrentContact] = useState<ContactsProps | null>(
|
|
null,
|
|
);
|
|
const [cursor, setCursor] = useState<number>(0);
|
|
const [messages, setMessages] = useState<ChatMessagesProps[]>([]);
|
|
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
|
const [hasMoreMessages, setHasMoreMessages] = useState<boolean>(true);
|
|
const [me, setMe] = useState<MeProps>(meDefaultValue);
|
|
const MeContext = createContext(meDefaultValue);
|
|
|
|
const [groupOwner, setGroupOwner] = useState<string | undefined>();
|
|
|
|
useEffect(() => {
|
|
const token = Cookies.get('token');
|
|
if (token) {
|
|
initializeSocket(token);
|
|
}
|
|
const storedContact = localStorage.getItem('contact');
|
|
if (storedContact) {
|
|
try {
|
|
const parsedContact = JSON.parse(storedContact);
|
|
if (parsedContact) {
|
|
initializeContact(parsedContact);
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to parse stored contact:', error);
|
|
localStorage.removeItem('contact');
|
|
}
|
|
}
|
|
}, []);
|
|
|
|
async function initializeContact(newContact: ContactsProps) {
|
|
setMessages([]); // Clear messages from previous contact
|
|
localStorage.setItem('contact', JSON.stringify(newContact)); // Set current contact in localstorage
|
|
setCurrentContact(newContact);
|
|
console.log('Initialized contact: ', newContact);
|
|
try {
|
|
const joinResult = await joinRoom(newContact.conversation_id);
|
|
if (!joinResult.success) {
|
|
setErrorMessage(joinResult.message);
|
|
return false;
|
|
}
|
|
try {
|
|
await fetchMessages(newContact.conversation_id);
|
|
} catch (e) {
|
|
console.error('Failed to fetch messages: ', e);
|
|
return false;
|
|
}
|
|
setContactsList((prevContacts) =>
|
|
prevContacts.map((c) =>
|
|
c.username === newContact.username ? { ...c, read: true } : c,
|
|
),
|
|
);
|
|
console.log('Current contact is now: ', newContact);
|
|
return true;
|
|
} catch (e) {
|
|
console.error('Failed to initialize contact:', e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const fetchMessages = async (conversation_id: string) => {
|
|
console.log('Fetching messages for: ', conversation_id);
|
|
try {
|
|
const messages = await getMessages(conversation_id);
|
|
if (messages.messages.length < 50) {
|
|
setHasMoreMessages(false);
|
|
setErrorMessage('No more messages found');
|
|
}
|
|
|
|
setCursor(() => {
|
|
return Math.min(
|
|
...messages.messages.map((message) => message.message_id),
|
|
);
|
|
});
|
|
|
|
messages.messages.forEach(messageHandler);
|
|
} catch (e) {
|
|
if (axios.isAxiosError(e)) {
|
|
setErrorMessage(e.message);
|
|
}
|
|
}
|
|
};
|
|
|
|
const fetchPreviousMessages = async (contact: string | null) => {
|
|
if (!hasMoreMessages) {
|
|
return;
|
|
}
|
|
|
|
console.log(
|
|
'Fetching messages for: ',
|
|
contact,
|
|
'with cursor: ',
|
|
cursor,
|
|
'hasmoremessages: ',
|
|
hasMoreMessages,
|
|
);
|
|
|
|
try {
|
|
const messages = await getMessages(contact, cursor, 50);
|
|
if (messages.messages.length < 50) {
|
|
setHasMoreMessages(false);
|
|
setErrorMessage('No more messages found');
|
|
}
|
|
messages.messages.forEach((msg) => {
|
|
setMessages((prevMessages) => {
|
|
if (!prevMessages.some((m) => m.message_id === msg.message_id)) {
|
|
const messageWithDate = {
|
|
...msg,
|
|
sent_at: new Date(msg.sent_at),
|
|
};
|
|
|
|
return [messageWithDate, ...prevMessages];
|
|
}
|
|
return prevMessages;
|
|
});
|
|
});
|
|
|
|
setCursor(() => {
|
|
return Math.min(
|
|
...messages.messages.map((message) => message.message_id),
|
|
);
|
|
});
|
|
} catch (e) {
|
|
if (axios.isAxiosError(e)) {
|
|
setErrorMessage(e.message);
|
|
}
|
|
}
|
|
};
|
|
|
|
function messageHandler(msg: ChatMessagesProps) {
|
|
setMessages((prevMessages) => {
|
|
// Check if the message already exists in the state
|
|
if (!prevMessages.some((m) => m.message_id === msg.message_id)) {
|
|
// Convert sent_at to Date object before adding to state
|
|
const messageWithDate = {
|
|
...msg,
|
|
sent_at: new Date(msg.sent_at),
|
|
};
|
|
return [...prevMessages, messageWithDate];
|
|
}
|
|
return prevMessages;
|
|
});
|
|
}
|
|
|
|
function updateContactStatus(contactObj: ContactsProps, read: boolean) {
|
|
console.log('Update contact status: ', contactObj);
|
|
|
|
setContactsList((prevContacts) =>
|
|
prevContacts.map((contact) => {
|
|
if (contact.conversation_id === contactObj.conversation_id) {
|
|
if (!contactObj.read) {
|
|
setContactStatus(contactObj.conversation_id, read);
|
|
}
|
|
return { ...contact, read: read };
|
|
} else {
|
|
return contact;
|
|
}
|
|
}),
|
|
);
|
|
}
|
|
|
|
return (
|
|
<MeContext.Provider value={me}>
|
|
<div className="text-white flex h-screen">
|
|
{/* Left Sidebar */}
|
|
<div className="w-64 h-screen flex-shrink-0 flex flex-col bg-zinc-950 text-center border-r border-zinc-800">
|
|
<ContactForm
|
|
setContactsList={setContactsList}
|
|
InitializeContact={initializeContact}
|
|
/>
|
|
<ContactsList
|
|
initializeContact={initializeContact}
|
|
contactsList={contactsList}
|
|
setContactsList={setContactsList}
|
|
setCurrentContact={setCurrentContact}
|
|
updateContactStatus={updateContactStatus}
|
|
setMessages={setMessages}
|
|
currentContact={currentContact}
|
|
setErrorMessage={setErrorMessage}
|
|
/>
|
|
<UserProfile />
|
|
</div>
|
|
|
|
{/*Chat area */}
|
|
<div className="flex-grow flex flex-col h-screen bg-[#0a0a0a]">
|
|
<div className="flex flex-grow overflow-hidden">
|
|
{/* Messages Container and Participants Container */}
|
|
<div className="flex-grow flex flex-col overflow-hidden">
|
|
<div className="flex-shrink-0 border-b border-zinc-800 ">
|
|
<ContactProfile contact={currentContact} />
|
|
</div>
|
|
<div className="flex-grow overflow-y-auto">
|
|
<MessagesArea
|
|
messages={messages}
|
|
setMessages={setMessages}
|
|
currentContact={currentContact}
|
|
updateContactStatus={updateContactStatus}
|
|
messageHandler={messageHandler}
|
|
setContactsList={setContactsList}
|
|
fetchPreviousMessages={fetchPreviousMessages}
|
|
errorMessage={errorMessage}
|
|
contactsList={contactsList}
|
|
/>
|
|
</div>
|
|
<div className="flex-shrink-0 p-3 border-t border-zinc-800">
|
|
{currentContact && currentContact.username?.length >= 4 ? (
|
|
<MessageForm contact={currentContact} messages={messages} />
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Right Sidebar - Participants */}
|
|
{currentContact?.type === 'group' && (
|
|
<div className="w-64 flex-shrink-0 border-l border-zinc-800 bg-zinc-950">
|
|
<ParticipantsBar
|
|
setMe={setMe}
|
|
initializeContact={initializeContact}
|
|
currentContact={currentContact}
|
|
setGroupOwner={setGroupOwner}
|
|
groupOwner={groupOwner}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</MeContext.Provider>
|
|
);
|
|
}
|
|
|
|
export default Chat;
|