nie wiem co, cos zepsulem z nieprzeczytanymi wiadomosci i nie wiem jak naprawic

This commit is contained in:
slawek
2025-01-11 00:02:06 +01:00
parent dc1e5c1748
commit e9763084f4
9 changed files with 98 additions and 31 deletions

View File

@@ -27,7 +27,6 @@ function MessagesArea() {
const previousMessagesLength = useRef(messages.length); const previousMessagesLength = useRef(messages.length);
const isFocused = useWindowFocus(); const isFocused = useWindowFocus();
const previousTitle = useRef(document.title); const previousTitle = useRef(document.title);
const scrollToBottom = () => { const scrollToBottom = () => {
const container = containerRef.current; const container = containerRef.current;
if (container && shouldScrollToBottom) { if (container && shouldScrollToBottom) {
@@ -132,6 +131,7 @@ function MessagesArea() {
last_message_id: msg.message_id, 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(),
last_read_message_id: prevContacts[0]?.last_message_id || null,
}, },
]; ];
} }
@@ -211,7 +211,7 @@ function MessagesArea() {
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
setContactsList((prevContacts) => { setContactsList((prevContacts) => {
return prevContacts.map((contact) => return prevContacts.map((contact) =>
contact.conversation_id === currentContact?.conversation_id contact?.conversation_id === currentContact?.conversation_id
? { ? {
...contact, ...contact,
read: true, read: true,
@@ -224,6 +224,19 @@ function MessagesArea() {
return () => clearTimeout(timeout); return () => clearTimeout(timeout);
}, [isFocused]); }, [isFocused]);
useEffect(() => {
if (!socket) return;
if (currentContact?.read == true) return;
socket.emit('message read', {
conversation_id: currentContact?.conversation_id,
message_id: messages[messages?.length - 1].message_id,
});
console.info('MESSAGE READ: ', {
conversation_id: currentContact?.conversation_id,
message_id: messages[messages?.length - 1].message_id,
});
}, [isFocused]);
useEffect(() => { useEffect(() => {
const hasNewMessages = messages.length > previousMessagesLength.current; const hasNewMessages = messages.length > previousMessagesLength.current;
previousMessagesLength.current = messages.length; previousMessagesLength.current = messages.length;
@@ -241,6 +254,8 @@ function MessagesArea() {
return ( return (
<div ref={containerRef} className="flex flex-col h-full overflow-y-auto"> <div ref={containerRef} className="flex flex-col h-full overflow-y-auto">
<p>READ STATUS: {currentContact?.read ? <p>true</p> : <p>false</p>}</p>
<p>ISFOCUSED: {isFocused ? <p>tak</p> : <p>nie</p>}</p>
<p className="text-center text-gray-400">{errorMessage}</p> <p className="text-center text-gray-400">{errorMessage}</p>
<ul className="flex-grow list-none"> <ul className="flex-grow list-none">
{isLoading ? <LoadingWheel /> : null} {isLoading ? <LoadingWheel /> : null}

View File

@@ -134,7 +134,7 @@ function AddGroupMember() {
return ( return (
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
<button className="p-2 hover:bg-zinc-800 rounded-lg"> <button className="p-2 hover:bg-zinc-800 rounded-lg" title="Add member">
<UserRoundPlus /> <UserRoundPlus />
</button> </button>
</DialogTrigger> </DialogTrigger>

View File

@@ -47,7 +47,10 @@ function CreateGroupButton() {
return ( return (
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
<button className="p-2 hover:bg-zinc-800 rounded-lg"> <button
className="p-2 hover:bg-zinc-800 rounded-lg"
title="Create group"
>
<Plus /> <Plus />
</button> </button>
</DialogTrigger> </DialogTrigger>

View File

@@ -48,6 +48,22 @@ function ContactsList() {
}; };
}, [socket]); }, [socket]);
useEffect(() => {
contactsList.forEach((contact) => {
if (contact.last_message_id && contact.last_read_message_id) {
if (contact.last_message_id > contact.last_read_message_id) {
setContactsList((prevContacts) =>
prevContacts.map((prevContact) =>
prevContact.id === contact.id
? { ...prevContact, read: false }
: prevContact,
),
);
}
}
});
}, [contactsList]);
const fetchContacts = async () => { const fetchContacts = async () => {
console.log('Fetching contacts list'); console.log('Fetching contacts list');
const response = await axiosClient.get(`/api/chat/contacts`); const response = await axiosClient.get(`/api/chat/contacts`);

View File

@@ -23,7 +23,7 @@ export const ChatProvider = ({ children }: { children: ReactNode }) => {
async function initializeContact(newContact: ContactsProps) { async function initializeContact(newContact: ContactsProps) {
setMessages([]); // Clear messages from previous contact setMessages([]); // Clear messages from previous contact
localStorage.setItem('contact', JSON.stringify(newContact)); // Set current contact in localstorage localStorage.setItem('contact', JSON.stringify(newContact)); // Set current contact in localstorage
setCurrentContact(newContact); setCurrentContact({ ...newContact, read: true });
console.log('Initialized contact: ', newContact); console.log('Initialized contact: ', newContact);
try { try {
const joinResult = await joinRoom(newContact.conversation_id); const joinResult = await joinRoom(newContact.conversation_id);

View File

@@ -26,6 +26,7 @@ export type ContactsProps = {
last_message_id: number | null; last_message_id: number | null;
last_message_time: string | null; last_message_time: string | null;
last_message_sender: string | null; last_message_sender: string | null;
last_read_message_id: number | null;
}; };
export type ParticipantsProps = { export type ParticipantsProps = {

View File

@@ -86,6 +86,7 @@ async function createTables() {
conversation_id UUID NOT NULL REFERENCES Conversations(conversation_id) ON DELETE CASCADE, conversation_id UUID NOT NULL REFERENCES Conversations(conversation_id) ON DELETE CASCADE,
user_id UUID REFERENCES Accounts(user_id) ON DELETE CASCADE, user_id UUID REFERENCES Accounts(user_id) ON DELETE CASCADE,
joined_at TIMESTAMPTZ DEFAULT NOW(), joined_at TIMESTAMPTZ DEFAULT NOW(),
last_read_message_id INT REFERENCES Messages(message_id) ON DELETE SET NULL,
PRIMARY KEY (conversation_id, user_id) PRIMARY KEY (conversation_id, user_id)
); );
CREATE INDEX IF NOT EXISTS idx_memberships_conversation_id ON Memberships (conversation_id); CREATE INDEX IF NOT EXISTS idx_memberships_conversation_id ON Memberships (conversation_id);
@@ -100,7 +101,6 @@ async function createTables() {
contact_id SERIAL PRIMARY KEY, contact_id SERIAL PRIMARY KEY,
user_id UUID REFERENCES Accounts(user_id) ON DELETE CASCADE, user_id UUID REFERENCES Accounts(user_id) ON DELETE CASCADE,
conversation_id UUID NOT NULL REFERENCES Conversations(conversation_id) ON DELETE CASCADE, conversation_id UUID NOT NULL REFERENCES Conversations(conversation_id) ON DELETE CASCADE,
read BOOLEAN NOT NULL DEFAULT FALSE,
last_active TIMESTAMPTZ DEFAULT NOW(), last_active TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT unique_contact UNIQUE (user_id, conversation_id) CONSTRAINT unique_contact UNIQUE (user_id, conversation_id)
); );
@@ -237,6 +237,8 @@ async function insertMessage(
attachmentUrls, attachmentUrls,
]); ]);
updateConversationLastActive(conversation_id, senderId); updateConversationLastActive(conversation_id, senderId);
updateContactStatus(senderId, conversation_id, result.rows[0].message_id);
return result.rows[0]; return result.rows[0];
} catch (e) { } catch (e) {
console.error("Failed to insert message ", e); console.error("Failed to insert message ", e);
@@ -473,10 +475,10 @@ async function changePassword(username, newPasswordHash) {
} }
} }
async function insertContactById(senderId, conversation_id, read) { async function insertContactById(senderId, conversation_id) {
// First check if contact already exists // First check if contact already exists
const checkQuery = ` const checkQuery = `
SELECT contact_id, conversation_id, user_id, read, last_active SELECT contact_id, conversation_id, user_id, last_active
FROM Contacts FROM Contacts
WHERE user_id = $1 AND conversation_id = $2 WHERE user_id = $1 AND conversation_id = $2
`; `;
@@ -494,16 +496,12 @@ async function insertContactById(senderId, conversation_id, read) {
// If contact doesn't exist, create new one // If contact doesn't exist, create new one
const insertQuery = ` const insertQuery = `
INSERT INTO Contacts (user_id, conversation_id, read) INSERT INTO Contacts (user_id, conversation_id)
VALUES($1, $2, $3) VALUES($1, $2)
RETURNING contact_id, conversation_id, user_id, read, last_active RETURNING contact_id, conversation_id, user_id, last_active
`; `;
const result = await client.query(insertQuery, [ const result = await client.query(insertQuery, [senderId, conversation_id]);
senderId,
conversation_id,
read,
]);
console.log("Insert contact by id:", senderId, conversation_id); console.log("Insert contact by id:", senderId, conversation_id);
return result.rows[0] || null; return result.rows[0] || null;
} catch (error) { } catch (error) {
@@ -512,7 +510,7 @@ async function insertContactById(senderId, conversation_id, read) {
} }
} }
async function insertContact(initiatorId, receiverId, contactUsername, read) { async function insertContact(initiatorId, receiverId, contactUsername) {
try { try {
let conversation_id; let conversation_id;
@@ -625,7 +623,7 @@ async function insertContact(initiatorId, receiverId, contactUsername, read) {
} }
// Insert the contact for the initiator // Insert the contact for the initiator
const contact = await insertContactById(initiatorId, conversation_id, read); const contact = await insertContactById(initiatorId, conversation_id);
if (!contact.contact_id) { if (!contact.contact_id) {
console.error("Failed to insert contact by id"); console.error("Failed to insert contact by id");
return null; return null;
@@ -642,7 +640,6 @@ async function insertContact(initiatorId, receiverId, contactUsername, read) {
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,
last_message_id: latestMessage.last_message_id, last_message_id: latestMessage.last_message_id,
last_message: latestMessage.last_message, last_message: latestMessage.last_message,
last_message_time: latestMessage.last_message_time, last_message_time: latestMessage.last_message_time,
@@ -717,10 +714,10 @@ async function getContacts(user_id) {
conv.last_active, conv.last_active,
c.conversation_id, c.conversation_id,
conv.conversation_type AS type, conv.conversation_type AS type,
c.read m.last_read_message_id
FROM Contacts c FROM Contacts c
JOIN Conversations conv ON c.conversation_id = conv.conversation_id JOIN Conversations conv ON c.conversation_id = conv.conversation_id
JOIN Memberships m ON m.conversation_id = c.conversation_id JOIN Memberships m ON m.conversation_id = conv.conversation_id
JOIN Accounts a ON a.user_id = m.user_id JOIN Accounts a ON a.user_id = m.user_id
WHERE c.user_id = $1 WHERE c.user_id = $1
AND conv.conversation_type = 'direct' AND conv.conversation_type = 'direct'
@@ -735,9 +732,10 @@ async function getContacts(user_id) {
conv.last_active, conv.last_active,
c.conversation_id, c.conversation_id,
conv.conversation_type AS type, conv.conversation_type AS type,
c.read m.last_read_message_id -- We need to add Memberships join here too
FROM Contacts c FROM Contacts c
JOIN Conversations conv ON c.conversation_id = conv.conversation_id JOIN Conversations conv ON c.conversation_id = conv.conversation_id
JOIN Memberships m ON m.conversation_id = conv.conversation_id -- Added this JOIN
WHERE c.user_id = $1 WHERE c.user_id = $1
AND conv.conversation_type = 'group' AND conv.conversation_type = 'group'
ORDER BY c.conversation_id, conv.last_active DESC ORDER BY c.conversation_id, conv.last_active DESC
@@ -762,15 +760,14 @@ async function getContacts(user_id) {
last_active: row.last_active, last_active: row.last_active,
conversation_id: row.conversation_id, conversation_id: row.conversation_id,
type: row.type, type: row.type,
read: row.read, last_read_message_id: row.last_read_message_id,
last_message_id: latestMessage.message_id, last_message_id: latestMessage.last_message_id,
last_message: latestMessage.last_message, last_message: latestMessage.last_message,
last_message_time: latestMessage.last_message_time, last_message_time: latestMessage.last_message_time,
last_message_sender: latestMessage.last_message_sender, last_message_sender: latestMessage.last_message_sender,
}; };
}), }),
); );
return contacts; return contacts;
} catch (error) { } catch (error) {
console.error("Failed to get contacts:", error); console.error("Failed to get contacts:", error);
@@ -864,23 +861,48 @@ async function deleteContact(user_id, conversation_id) {
} }
} }
async function updateContactStatus(user_id, conversation_id, read) { async function updateContactStatus(
user_id,
conversation_id,
last_read_message_id,
) {
const query = ` const query = `
UPDATE Contacts SET read = $1 UPDATE Memberships
SET last_read_message_id = $1
WHERE user_id = $2 WHERE user_id = $2
AND conversation_id = $3; AND conversation_id = $3;
`; `;
try { try {
await client.query(query, [read, user_id, conversation_id]); await client.query(query, [last_read_message_id, user_id, conversation_id]);
// await updateContactLastActive(user_id, conversation_id);
console.log( console.log(
`Successfully updated contact status, user_id: ${user_id}, conversation_id: ${conversation_id}, read: ${read}: `, `Successfully updated contact status, user_id: ${user_id}, conversation_id: ${conversation_id}, last_read_message_id: ${last_read_message_id}`,
); );
} catch (e) { } catch (e) {
console.error("Failed to update contact status ", e); console.error("Failed to update contact status ", e);
} }
} }
async function getLastReadMessageId(user_id, conversation_id) {
const query = `
SELECT last_read_message_id
FROM Memberships
WHERE user_id = $1
AND conversation_id = $2;
`;
try {
const result = await client.query(query, [user_id, conversation_id]);
if (result.rows.length > 0) {
return result.rows[0].last_read_message_id;
} else {
console.log("No read message found for user in conversation");
return null;
}
} catch (e) {
console.error("Failed to get last read message ID ", e);
return null;
}
}
async function updateContactLastActive(user_id, contact_id) { async function updateContactLastActive(user_id, contact_id) {
const query = ` const query = `
UPDATE Contacts UPDATE Contacts

View File

@@ -247,7 +247,7 @@ app.put(
} }
const read = req.body.status; const read = req.body.status;
await updateContactStatus(req.user.user_id, conversation_id, read); //await updateContactStatus(req.user.user_id, conversation_id, read);
return res return res
.status(200) .status(200)

View File

@@ -8,6 +8,7 @@ const {
isAdmin, isAdmin,
addAdministrator, addAdministrator,
removeAdministrator, removeAdministrator,
updateContactStatus,
} = require("../db/db"); } = require("../db/db");
const { isValidUsername } = require("../utils/filter"); const { isValidUsername } = require("../utils/filter");
const { verifyJwtToken } = require("../auth/jwt"); const { verifyJwtToken } = require("../auth/jwt");
@@ -342,6 +343,15 @@ function initializeSocket(io) {
}); });
}); });
socket.on("message read", async (msg) => {
const { conversation_id, message_id } = msg;
if (!conversation_id || !message_id) {
return;
}
console.error("MESSGE READ: ", conversation_id, message_id);
await updateContactStatus(socket.user_id, conversation_id, message_id);
});
socket.on("disconnect", (reason) => { socket.on("disconnect", (reason) => {
console.log("(socket)", socket.id, " disconnected due to: ", reason); console.log("(socket)", socket.id, " disconnected due to: ", reason);
}); });