improved ContactsList.tsx UI, return last message id for

This commit is contained in:
slawk0
2025-01-05 22:07:01 +01:00
parent 33ffc2aeb4
commit 1f7aedb0ca
6 changed files with 108 additions and 113 deletions

View File

@@ -111,6 +111,7 @@ function MessagesArea() {
type: 'direct', type: 'direct',
last_active: new Date().toString(), last_active: new Date().toString(),
last_message: msg.message, last_message: msg.message,
last_message_id: msg.message_id,
last_message_sender: msg.sender, last_message_sender: msg.sender,
last_message_time: new Date().toString(), last_message_time: new Date().toString(),
}, },
@@ -153,6 +154,7 @@ function MessagesArea() {
type: 'direct', type: 'direct',
last_active: new Date().toString(), last_active: new Date().toString(),
last_message: msg.message, last_message: msg.message,
last_message_id: msg.message_id,
last_message_sender: msg.sender, last_message_sender: msg.sender,
last_message_time: new Date().toString(), last_message_time: new Date().toString(),
}, },
@@ -184,6 +186,18 @@ function MessagesArea() {
(message) => message.message_id !== msg.message_id, (message) => message.message_id !== msg.message_id,
), ),
); );
setContactsList((prevContacts) => {
return prevContacts.map((contact) =>
contact.last_message_id === msg.message_id
? {
...contact,
last_active: new Date().toString(),
last_message: 'message deleted',
last_message_time: new Date().toString(),
}
: contact,
);
});
}, },
); );

View File

@@ -12,7 +12,7 @@ import {
AlertDialogTitle, AlertDialogTitle,
AlertDialogTrigger, AlertDialogTrigger,
} from '@/components/ui/alert-dialog.tsx'; } from '@/components/ui/alert-dialog.tsx';
import { Dot, Paperclip } from 'lucide-react'; import { Ellipsis, Paperclip } from 'lucide-react';
import LastActiveTime from '@/components/chat/leftSidebar/LastActiveTime.tsx'; import LastActiveTime from '@/components/chat/leftSidebar/LastActiveTime.tsx';
import { ContactsProps } from '@/types/types.ts'; import { ContactsProps } from '@/types/types.ts';
import { useChat } from '@/context/chat/useChat.ts'; import { useChat } from '@/context/chat/useChat.ts';
@@ -96,14 +96,14 @@ function ContactsList() {
const ContactItem = ({ contact }: { contact: ContactsProps }) => ( const ContactItem = ({ contact }: { contact: ContactsProps }) => (
<li <li
className="m-1 flex p-2 hover:bg-zinc-900 cursor-pointer transition-colors rounded-lg justify-between items-center min-h-[40px]" className="m-1 flex p-2 hover:bg-zinc-900 cursor-pointer transition-colors rounded-lg justify-between items-start min-h-[40px]"
onClick={() => { onClick={() => {
initializeContact(contact); initializeContact(contact);
updateContactStatus(contact, true); updateContactStatus(contact, true);
}} }}
> >
<div className="flex"> <div className="flex flex-col w-full">
<div className=" flex flex-col"> <div className="flex items-center justify-between">
<div className="flex items-center"> <div className="flex items-center">
<span className="text-lg">{contact.username}</span> <span className="text-lg">{contact.username}</span>
{contact.type === 'group' && ( {contact.type === 'group' && (
@@ -113,81 +113,77 @@ function ContactsList() {
className="ml-2 invert w-5" className="ml-2 invert w-5"
/> />
)} )}
<p className="text-center text-2xl text-red-300 leading-none">
{contact.read ? '•' : '•'}
</p>
</div> </div>
<div className="flex">
<span className="text-sm text-gray-500 text-left">
{contact.last_message?.length > 0 ? (
contact.last_message?.length > 15 ? (
<div className="flex">
{contact.last_message?.substring(0, 12) + '...'}
<Dot className="text-gray-200" />
</div>
) : (
<div className="flex">
{contact.last_message} <Dot className="text-gray-200" />
</div>
)
) : contact.last_message_time ? (
<div className="flex">
<Paperclip className="w-4 mr-1" /> attachment
<Dot className="text-gray-200" />
</div>
) : null}
</span>
<LastActiveTime contact={contact} />
</div>
</div>
</div>
<div className="w-4 h-4 flex items-center justify-center ml-2">
<p className="text-center text-2xl text-red-200 leading-none">
{contact.read ? '' : '•'}
</p>
</div>
{contact.type === 'group' ? ( {contact.type === 'group' ? (
<AlertDialog> <AlertDialog>
<AlertDialogTrigger asChild> <AlertDialogTrigger asChild>
<button
onClick={(e) => e.stopPropagation()}
className="flex-shrink-0 w-6 h-6 rounded-full hover:text-red-500 flex items-center justify-center text-gray-500"
>
x
</button>
</AlertDialogTrigger>
<AlertDialogContent className="bg-zinc-950">
<AlertDialogHeader>
<AlertDialogTitle className="text-white">
Leave Group?
</AlertDialogTitle>
<AlertDialogDescription className="text-white">
Are you sure you want to leave this group?
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={(e) => {
e.stopPropagation();
removeContact(contact.id, contact.conversation_id);
}}
className="bg-red-600 hover:bg-red-500"
>
Leave Group
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
) : (
<button <button
onClick={(e) => e.stopPropagation()} onClick={(e) => {
className="flex-shrink-0 w-6 h-6 rounded-full hover:text-red-500 flex items-center justify-center text-gray-500" e.stopPropagation();
removeContact(contact.id, contact.conversation_id);
}}
className="flex-shrink-0 w-6 h-6 rounded-full hover:text-red-500 flex items-center justify-center text-gray-700"
> >
x x
</button> </button>
</AlertDialogTrigger> )}
<AlertDialogContent className="bg-zinc-950"> </div>
<AlertDialogHeader>
<AlertDialogTitle className="text-white"> <div className="flex items-center justify-between text-sm text-gray-500">
Leave Group? <div className="flex items-center">
</AlertDialogTitle> {contact.last_message?.length > 0 ? (
<AlertDialogDescription className="text-white"> contact.last_message?.length > 16 ? (
Are you sure you want to leave this group? <div className="flex items-center">
</AlertDialogDescription> {contact.last_message?.substring(0, 16)}
</AlertDialogHeader> <Ellipsis className="w-3 mt-2" />
<AlertDialogFooter> </div>
<AlertDialogCancel>Cancel</AlertDialogCancel> ) : (
<AlertDialogAction contact.last_message
onClick={(e) => { )
e.stopPropagation(); ) : contact.last_message_time ? (
removeContact(contact.id, contact.conversation_id); <div className="flex items-center">
}} <Paperclip className="w-4" /> attachment
className="bg-red-600 hover:bg-red-500" </div>
> ) : null}
Leave Group </div>
</AlertDialogAction> <LastActiveTime contact={contact} />
</AlertDialogFooter> </div>
</AlertDialogContent> </div>
</AlertDialog>
) : (
<button
onClick={(e) => {
e.stopPropagation();
removeContact(contact.id, contact.conversation_id);
}}
className="flex-shrink-0 w-6 h-6 rounded-full hover:text-red-500 flex items-center justify-center text-gray-500"
>
x
</button>
)}
</li> </li>
); );

View File

@@ -1,5 +1,5 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { formatDistanceToNow, differenceInSeconds } from 'date-fns'; import { differenceInSeconds, formatDistanceToNowStrict } from 'date-fns';
import { ContactsProps } from '@/types/types.ts'; import { ContactsProps } from '@/types/types.ts';
@@ -23,7 +23,7 @@ const LastActiveTime = ({ contact }: LastActiveTimeProps) => {
return; return;
} }
setTimeAgo(formatDistanceToNow(lastActiveDate)); setTimeAgo(formatDistanceToNowStrict(lastActiveDate));
}; };
updateTime(); updateTime();
@@ -32,7 +32,7 @@ const LastActiveTime = ({ contact }: LastActiveTimeProps) => {
return () => clearInterval(intervalId); return () => clearInterval(intervalId);
}, [contact?.last_message]); }, [contact?.last_message]);
return <span className="text-sm text-gray-500">{timeAgo}</span>; return <span className="text-xs font-bold text-gray-500">{timeAgo}</span>;
}; };
export default LastActiveTime; export default LastActiveTime;

View File

@@ -23,6 +23,7 @@ export type ContactsProps = {
conversation_id: string; conversation_id: string;
last_active: string; last_active: string;
last_message: string; last_message: string;
last_message_id: number;
last_message_time: string; last_message_time: string;
last_message_sender: string; last_message_sender: string;
}; };

View File

@@ -4,13 +4,13 @@ import { AuthContext } from './AuthProvider';
import LoadingScreen from '../components/LoadingScreen'; import LoadingScreen from '../components/LoadingScreen';
function ProtectedRoutes() { function ProtectedRoutes() {
const { authorized } = useContext(AuthContext); const { authorized, user } = useContext(AuthContext);
if (authorized === null) { if (authorized === null) {
return <LoadingScreen />; return <LoadingScreen />;
} }
return authorized ? <Outlet /> : <Navigate to="/login" replace />; return authorized && user ? <Outlet /> : <Navigate to="/login" replace />;
} }
export default ProtectedRoutes; export default ProtectedRoutes;

View File

@@ -631,40 +631,22 @@ async function insertContact(initiatorId, receiverId, contactUsername, read) {
return null; return null;
} }
// Retrieve the last message, last active time, and last message sender // Retrieve the last message and related information
const lastMessageQuery = ` const latestMessage = await getLatestMessage(conversation_id);
SELECT DISTINCT ON (m.conversation_id)
m.content AS last_message,
m.sent_at AS last_message_time,
a.username AS last_message_sender
FROM Messages m
JOIN Accounts a ON m.user_id = a.user_id
WHERE m.conversation_id = $1
ORDER BY m.conversation_id, m.sent_at DESC
`;
const lastMessageResult = await client.query(lastMessageQuery, [
conversation_id,
]);
let lastMessage, lastMessageTime, lastMessageSender;
if (lastMessageResult.rows.length > 0) { // Return formatted result with all message information including message_id
lastMessage = lastMessageResult.rows[0].last_message;
lastMessageTime = lastMessageResult.rows[0].last_message_time;
lastMessageSender = lastMessageResult.rows[0].last_message_sender;
}
// Return formatted result with contact's user_id, last message, last active time, and last message sender
return { return {
id: contact.contact_id, id: contact.contact_id,
user_id: receiverId, // Now using the contact's user_id instead of the initiator's user_id: receiverId,
username: contactUsername, username: contactUsername,
last_active: contact.last_active, last_active: contact.last_active,
conversation_id: contact.conversation_id, conversation_id: contact.conversation_id,
type: "direct", type: "direct",
read: contact.read, read: contact.read,
last_message: lastMessage || null, last_message_id: latestMessage.message_id,
last_message_time: lastMessageTime || null, last_message: latestMessage.last_message,
last_message_sender: lastMessageSender || null, last_message_time: latestMessage.last_message_time,
last_message_sender: latestMessage.last_message_sender,
}; };
} catch (error) { } catch (error) {
console.error("Failed to insert contact:", error); console.error("Failed to insert contact:", error);
@@ -675,6 +657,7 @@ async function insertContact(initiatorId, receiverId, contactUsername, read) {
async function getLatestMessage(conversation_id) { async function getLatestMessage(conversation_id) {
const query = ` const query = `
SELECT DISTINCT ON (m.conversation_id) SELECT DISTINCT ON (m.conversation_id)
m.message_id,
m.content AS last_message, m.content AS last_message,
m.sent_at AS last_message_time, m.sent_at AS last_message_time,
a.username AS last_message_sender a.username AS last_message_sender
@@ -689,13 +672,15 @@ async function getLatestMessage(conversation_id) {
const result = await client.query(query, [conversation_id]); const result = await client.query(query, [conversation_id]);
return { return {
last_message: result.rows[0].last_message || null, message_id: result.rows[0]?.message_id || null,
last_message_time: result.rows[0].last_message_time || null, last_message: result.rows[0]?.last_message || null,
last_message_sender: result.rows[0].last_message_sender || null, last_message_time: result.rows[0]?.last_message_time || null,
last_message_sender: result.rows[0]?.last_message_sender || null,
}; };
} catch (error) { } catch (error) {
console.error("Failed to get latest message:", error); console.error("Failed to get latest message:", error);
return { return {
message_id: null,
last_message: null, last_message: null,
last_message_time: null, last_message_time: null,
last_message_sender: null, last_message_sender: null,
@@ -764,11 +749,9 @@ async function getContacts(user_id) {
`; `;
try { try {
// Execute the query with the user_id parameter
const contactsResult = await client.query(contactsQuery, [user_id]); const contactsResult = await client.query(contactsQuery, [user_id]);
console.log(contactsResult.rows); // Debugging: log the results to verify
// Map the results to a more friendly format and fetch the latest message for each conversation // Map the results to include the latest message and message_id
const contacts = await Promise.all( const contacts = await Promise.all(
contactsResult.rows.map(async (row) => { contactsResult.rows.map(async (row) => {
const latestMessage = await getLatestMessage(row.conversation_id); const latestMessage = await getLatestMessage(row.conversation_id);
@@ -780,9 +763,10 @@ async function getContacts(user_id) {
conversation_id: row.conversation_id, conversation_id: row.conversation_id,
type: row.type, type: row.type,
read: row.read, read: row.read,
last_message: latestMessage.last_message || null, last_message_id: latestMessage.message_id,
last_message_time: latestMessage.last_message_time || null, last_message: latestMessage.last_message,
last_message_sender: latestMessage.last_message_sender || null, last_message_time: latestMessage.last_message_time,
last_message_sender: latestMessage.last_message_sender,
}; };
}), }),
); );