added working status indicator
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import { sendRequestContactsList, socket } from '../../socket/socket.tsx';
|
||||
import { useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
type ContactsProps = {
|
||||
usernamecontact: string;
|
||||
@@ -13,6 +12,7 @@ type ContactsListProps = {
|
||||
setContactsList: React.Dispatch<React.SetStateAction<ContactsProps[]>>;
|
||||
contactsList: ContactsProps[];
|
||||
setCurrentContact: React.Dispatch<React.SetStateAction<string | null>>;
|
||||
updateStatus: (contactObj: ContactsProps, read: true) => void;
|
||||
};
|
||||
|
||||
function ContactsList({
|
||||
@@ -20,6 +20,7 @@ function ContactsList({
|
||||
contactsList,
|
||||
setContactsList,
|
||||
setCurrentContact,
|
||||
updateStatus,
|
||||
}: ContactsListProps) {
|
||||
function contactHandler(contactsList: ContactsProps[]) {
|
||||
setContactsList((prevContacts) => [...prevContacts, ...contactsList]);
|
||||
@@ -67,13 +68,24 @@ function ContactsList({
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const addContactsList = contactsList.map((contact: ContactsProps, index) => (
|
||||
<li
|
||||
className="flex hover:bg-green-700 p-2 rounded cursor-pointer"
|
||||
onClick={() => InitializeContact(contact.usernamecontact)}
|
||||
className="flex hover:bg-green-700 p-2 rounded cursor-pointer justify-between items-center min-h-[40px]" // Added min-h-[40px]
|
||||
onClick={() => {
|
||||
InitializeContact(contact.usernamecontact);
|
||||
updateStatus(contact, true);
|
||||
}}
|
||||
key={index}
|
||||
>
|
||||
{contact.usernamecontact}{' '}
|
||||
<span className="flex-shrink-0">{contact.usernamecontact}</span>
|
||||
|
||||
<div className="w-4 h-4 mx-2 flex items-center justify-center mr-auto">
|
||||
<p className="text-center text-2xl text-red-200 leading-none">
|
||||
{contact.read ? '' : '•'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
@@ -81,16 +93,17 @@ function ContactsList({
|
||||
setCurrentContact(null);
|
||||
localStorage.removeItem('contact');
|
||||
}}
|
||||
className="ml-auto mr-2 text-gray-400 w-6 h-6 rounded-full hover:text-red-500 flex items-center justify-center"
|
||||
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>
|
||||
));
|
||||
|
||||
return (
|
||||
<div className="flex-grow overflow-y-auto w-64">
|
||||
<ul className="m-2 flex-grow-1">{addContactsList}</ul>
|
||||
<ul className="items-center text-center ml-1 mt-2 flex-grow-1">
|
||||
{addContactsList}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { socket } from '../../socket/socket.tsx';
|
||||
import { useOutletContext } from 'react-router-dom';
|
||||
import { UsernameType } from '../../utils/ProtectedRoutes.tsx';
|
||||
type ChatMessages = {
|
||||
sender: string;
|
||||
message: string;
|
||||
@@ -7,14 +9,29 @@ type ChatMessages = {
|
||||
message_id: number;
|
||||
timestamp: string;
|
||||
};
|
||||
|
||||
type ContactProps = {
|
||||
usernamecontact: string;
|
||||
read: boolean;
|
||||
};
|
||||
|
||||
type MessagesAreaProps = {
|
||||
messages: ChatMessages[];
|
||||
setMessages: React.Dispatch<React.SetStateAction<ChatMessages[]>>;
|
||||
currentContact: string | null;
|
||||
setStatus: (contact: string, read: boolean) => void;
|
||||
updateStatus: (contact: ContactProps, read: boolean) => void;
|
||||
};
|
||||
|
||||
function MessagesArea({ messages, setMessages }: MessagesAreaProps) {
|
||||
function MessagesArea({
|
||||
messages,
|
||||
setMessages,
|
||||
currentContact,
|
||||
setStatus,
|
||||
updateStatus,
|
||||
}: MessagesAreaProps) {
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { username }: UsernameType = useOutletContext();
|
||||
useEffect(() => {
|
||||
if (!socket) {
|
||||
console.error('Socket not initialized');
|
||||
@@ -33,7 +50,14 @@ function MessagesArea({ messages, setMessages }: MessagesAreaProps) {
|
||||
|
||||
socket.on('chat message', (msg: ChatMessages) => {
|
||||
console.log('Received message: ', msg);
|
||||
messageHandler(msg);
|
||||
if (msg.sender !== currentContact && msg.sender !== username) {
|
||||
setStatus(msg.sender, false);
|
||||
updateStatus({ usernamecontact: msg.sender, read: false }, false);
|
||||
console.log('changed status to false');
|
||||
}
|
||||
if (msg.sender == currentContact || msg.sender == username) {
|
||||
messageHandler(msg);
|
||||
}
|
||||
});
|
||||
socket.on('historical messages', (msg: ChatMessages[]) => {
|
||||
console.log('Received historical messages: ', msg);
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
sendRequestHistoricalMessages,
|
||||
} from '../socket/socket.tsx';
|
||||
import Cookies from 'js-cookie';
|
||||
import axios from 'axios';
|
||||
|
||||
type ChatMessages = {
|
||||
sender: string;
|
||||
@@ -24,6 +25,11 @@ type ContactsProps = {
|
||||
read: boolean;
|
||||
};
|
||||
|
||||
type ContactObjProps = {
|
||||
usernamecontact: string;
|
||||
read: boolean;
|
||||
};
|
||||
|
||||
function Chat() {
|
||||
const [contactsList, setContactsList] = useState<ContactsProps[]>([]);
|
||||
const [currentContact, setCurrentContact] = useState<string | null>('');
|
||||
@@ -36,32 +42,61 @@ function Chat() {
|
||||
}
|
||||
}, []);
|
||||
|
||||
function setStatus(contact: string, read: boolean) {
|
||||
axios
|
||||
.put(
|
||||
`http://localhost:5173/api/chat/contacts/${contact}`,
|
||||
{ status: read },
|
||||
{ withCredentials: true },
|
||||
)
|
||||
.then((res) => {
|
||||
console.log(res.data.message);
|
||||
setContactsList((prevContacts) =>
|
||||
prevContacts.map((c) =>
|
||||
c.usernamecontact === contact ? { ...c, read } : c,
|
||||
),
|
||||
);
|
||||
})
|
||||
.catch((e) => console.log(e.response.data.message));
|
||||
}
|
||||
|
||||
function InitializeContact(newContact: string) {
|
||||
setMessages([]);
|
||||
sendRequestHistoricalMessages(newContact);
|
||||
setMessages([]); // Clear messages from previous contact
|
||||
sendRequestHistoricalMessages(newContact); // Request historical messages for new contact
|
||||
localStorage.setItem('contact', newContact);
|
||||
setCurrentContact(newContact);
|
||||
sendContact({ contact: newContact, read: true });
|
||||
sendContact({ contact: newContact, read: true }); // TODO do api instead of sending contact to server via socket
|
||||
setStatus(newContact, true);
|
||||
console.log('Contact submitted:', newContact);
|
||||
}
|
||||
|
||||
function updateStatus(contactObj: ContactObjProps, read: boolean) {
|
||||
setContactsList((prevContacts) =>
|
||||
prevContacts.map((contact) => {
|
||||
if (contact.usernamecontact === contactObj.usernamecontact) {
|
||||
return { ...contact, read: read };
|
||||
} else {
|
||||
return contact;
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="text-white flex h-screen">
|
||||
{/*Sidebar*/}
|
||||
<div className="h-screen bg-[#1E1E1E] flex flex-col">
|
||||
{/*Contact input*/}
|
||||
<ContactForm
|
||||
contactsList={contactsList}
|
||||
setContactsList={setContactsList}
|
||||
InitializeContact={InitializeContact}
|
||||
/>
|
||||
{/*Contact list*/}
|
||||
<ContactsList
|
||||
InitializeContact={InitializeContact}
|
||||
contactsList={contactsList}
|
||||
setContactsList={setContactsList}
|
||||
setCurrentContact={setCurrentContact}
|
||||
updateStatus={updateStatus}
|
||||
/>
|
||||
<hr />
|
||||
<UserProfile />
|
||||
@@ -73,7 +108,13 @@ function Chat() {
|
||||
</div>
|
||||
<hr className="flex-shrink-0" />
|
||||
<div className="flex-grow overflow-x-hidden overflow-y-auto m-2">
|
||||
<MessagesArea messages={messages} setMessages={setMessages} />
|
||||
<MessagesArea
|
||||
messages={messages}
|
||||
setMessages={setMessages}
|
||||
currentContact={currentContact}
|
||||
setStatus={setStatus}
|
||||
updateStatus={updateStatus}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-shrink-0 mb-2 mt-0">
|
||||
{currentContact && currentContact.length >= 4 ? (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const jwt = require("jsonwebtoken");
|
||||
const jwtSecret = process.env.JWT_SECRET;
|
||||
|
||||
const filter = require("../utils/filter");
|
||||
function generateJwtToken(username, user_id) {
|
||||
try {
|
||||
return jwt.sign({ username: username, user_id: user_id }, jwtSecret, {
|
||||
@@ -20,7 +20,8 @@ function verifyJwtToken(token) {
|
||||
throw new Error("Token verification failed - missing user_id");
|
||||
}
|
||||
console.log("verifyJwtToken decoded: ", decoded);
|
||||
return { username: decoded.username, user_id: decoded.user_id };
|
||||
const username = filter(decoded.username);
|
||||
return { username: username, user_id: decoded.user_id };
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
throw e;
|
||||
|
||||
@@ -222,6 +222,18 @@ async function deleteContact(username, usernamecontact) {
|
||||
console.error("Failed to remove contact ", e);
|
||||
}
|
||||
}
|
||||
|
||||
async function updateContactStatus(username, usernamecontact, read) {
|
||||
const query = `
|
||||
UPDATE contacts SET read = $1 WHERE username = $2 AND usernamecontact = $3
|
||||
`;
|
||||
try {
|
||||
await client.query(query, [read, username, usernamecontact]);
|
||||
console.log("Successfully updated contact status");
|
||||
} catch (e) {
|
||||
console.error("Failed to update contact status ", e);
|
||||
}
|
||||
}
|
||||
module.exports = {
|
||||
client,
|
||||
insertUser,
|
||||
@@ -234,4 +246,5 @@ module.exports = {
|
||||
getMessages,
|
||||
getUserId,
|
||||
getContacts,
|
||||
updateContactStatus,
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@ const {
|
||||
getPassword,
|
||||
getUserId,
|
||||
deleteContact,
|
||||
updateContactStatus,
|
||||
} = require("./db/db.js");
|
||||
const filter = require("./utils/filter");
|
||||
const { generateJwtToken, verifyJwtToken } = require("./auth/jwt");
|
||||
@@ -152,6 +153,25 @@ app.delete("/api/chat/contacts/:contact", async (req, res) => {
|
||||
return res.status(200).json({ message: "Successfully deleted contact" });
|
||||
});
|
||||
|
||||
app.put("/api/chat/contacts/:contact", async (req, res) => {
|
||||
const token = req.cookies.token;
|
||||
|
||||
if (!req.params.contact) {
|
||||
return res.status(400).json({ message: "Missing usernamecontact" });
|
||||
}
|
||||
const usernamecontact = filter(req.params.contact);
|
||||
const read = req.body.status;
|
||||
if (!token) {
|
||||
return res.status(401).json({ message: "Unauthorized" });
|
||||
}
|
||||
const { username } = verifyJwtToken(token);
|
||||
if (!username) {
|
||||
return res.status(401).json({ message: "Unauthorized" });
|
||||
}
|
||||
|
||||
await updateContactStatus(username, usernamecontact, read);
|
||||
});
|
||||
|
||||
initializeSocket(io);
|
||||
|
||||
server.listen(PORT, () => {
|
||||
|
||||
Reference in New Issue
Block a user