added working status indicator

This commit is contained in:
slawk0
2024-10-31 00:11:12 +01:00
parent 69abbcb7b8
commit efe7559265
6 changed files with 130 additions and 18 deletions

View File

@@ -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>
);
}

View File

@@ -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);

View File

@@ -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 ? (

View File

@@ -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;

View File

@@ -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,
};

View File

@@ -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, () => {