improved ContactsList.tsx UI, return last message id for
This commit is contained in:
@@ -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,
|
||||||
|
);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user