fixed contacts status
This commit is contained in:
@@ -13,10 +13,10 @@ export async function getContactsList(): Promise<ContactsProps[]> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function setContactStatus(contactId: number, read: boolean) {
|
||||
export async function setContactStatus(conversation_id: number, read: boolean) {
|
||||
try {
|
||||
const response = await axiosClient.put(
|
||||
`/api/chat/contacts/${contactId}`,
|
||||
`/api/chat/contacts/${conversation_id}`,
|
||||
{ status: read },
|
||||
{ withCredentials: true },
|
||||
);
|
||||
|
||||
@@ -42,6 +42,7 @@ function ContactsList({
|
||||
if (!socket) return;
|
||||
|
||||
socket?.on('added to group', () => {
|
||||
console.log('added to group, fetching contacts');
|
||||
fetchContacts();
|
||||
});
|
||||
|
||||
@@ -55,7 +56,7 @@ function ContactsList({
|
||||
console.log('Fetching contacts list');
|
||||
setContactsList((prevContacts) => {
|
||||
const newContacts = contacts.filter(
|
||||
(contact) => !prevContacts.some((c) => c.username === contact.username),
|
||||
(contact) => !prevContacts.some((c) => c.user_id === contact.user_id),
|
||||
);
|
||||
return [...prevContacts, ...newContacts];
|
||||
});
|
||||
@@ -98,7 +99,9 @@ function ContactsList({
|
||||
updateContactStatus(contact, true);
|
||||
}}
|
||||
>
|
||||
<span className="flex-shrink-0">{contact.username}</span>
|
||||
<span className="flex-shrink-0">
|
||||
{contact.user_id} {contact.username}
|
||||
</span>
|
||||
{contact.type === 'group' ? (
|
||||
<img src={GroupIcon} alt="Group icon" className="ml-2 invert w-5" />
|
||||
) : null}
|
||||
|
||||
@@ -159,6 +159,12 @@ const MessageForm = ({ contact }: MessageFormProps) => {
|
||||
console.log('Response: ', response);
|
||||
},
|
||||
);
|
||||
console.log('sent: ', {
|
||||
message: data.message.trim(),
|
||||
recipient: contact.conversation_id,
|
||||
attachment_urls: attachmentUrls,
|
||||
recipient_id: contact.user_id,
|
||||
});
|
||||
};
|
||||
|
||||
const handleKeyPress: KeyboardEventHandler<HTMLTextAreaElement> = (e) => {
|
||||
|
||||
@@ -98,7 +98,7 @@ function MessagesArea({
|
||||
socket.on('chat message', (msg: ChatMessages) => {
|
||||
console.log('Received message: ', msg);
|
||||
if (
|
||||
msg.conversation_id !== currentContact?.conversation_id ||
|
||||
msg.conversation_id !== currentContact?.conversation_id &&
|
||||
msg.sender !== username
|
||||
) {
|
||||
setContactsList((prevContacts) => {
|
||||
@@ -152,6 +152,7 @@ function MessagesArea({
|
||||
} else {
|
||||
if (msg.conversation_id == currentContact?.conversation_id) {
|
||||
messageHandler(msg);
|
||||
|
||||
setContactsList((prevContacts) => {
|
||||
// Find if contact already exists
|
||||
const existingContact = prevContacts.find(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { axiosClient } from '@/App.tsx';
|
||||
import { ContactsProps } from '@/pages/Chat.tsx';
|
||||
import { socket } from '@/socket/socket.tsx';
|
||||
|
||||
type ParticipantsProps = {
|
||||
user_id: number;
|
||||
@@ -17,7 +18,12 @@ function ParticipantsBar({ contact }: { contact: ContactsProps | null }) {
|
||||
const response = await axiosClient.get(
|
||||
`/api/chat/groups/getMembers/${contact.conversation_id}`,
|
||||
);
|
||||
console.log('getParticipants for: ', contact.conversation_id);
|
||||
console.log(
|
||||
'getParticipants for: ',
|
||||
contact.conversation_id,
|
||||
'response: ',
|
||||
response.data,
|
||||
);
|
||||
setParticipants(response.data);
|
||||
} catch (e) {
|
||||
console.error('Failed to get chat participants: ', e);
|
||||
@@ -27,6 +33,37 @@ function ParticipantsBar({ contact }: { contact: ContactsProps | null }) {
|
||||
}
|
||||
}, [contact]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!socket || !contact) return;
|
||||
|
||||
socket.on(
|
||||
'added to group',
|
||||
(msg: { username: string; user_id: number; group_id: number }) => {
|
||||
const { group_id } = msg;
|
||||
console.log('Added to group: ', msg);
|
||||
console.log('Current participants: ', participants);
|
||||
|
||||
if (group_id === contact.conversation_id) {
|
||||
setParticipants((prevMembers) => {
|
||||
// Check if member already exists
|
||||
const existingMember = prevMembers.some(
|
||||
(m) => m.user_id === msg.user_id,
|
||||
);
|
||||
if (existingMember) {
|
||||
return prevMembers;
|
||||
}
|
||||
return [...prevMembers, msg];
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Cleanup function to remove event listener
|
||||
return () => {
|
||||
socket?.off('added to group');
|
||||
};
|
||||
}, [contact, participants]); // Add participants to dependency array
|
||||
|
||||
const ParticipantsList = participants?.map(
|
||||
(participant: ParticipantsProps) => (
|
||||
<li
|
||||
@@ -43,7 +80,7 @@ function ParticipantsBar({ contact }: { contact: ContactsProps | null }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-[#1E1E1E] h-full w-full flex flex-col ml-auto">
|
||||
<div className="bg-zinc-900 h-full w-full flex flex-col ml-auto">
|
||||
<ul className="m-2 rounded">{ParticipantsList}</ul>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -166,7 +166,7 @@ function Chat() {
|
||||
prevContacts.map((contact) => {
|
||||
if (contact.conversation_id === contactObj.conversation_id) {
|
||||
if (!contactObj.read) {
|
||||
setContactStatus(contactObj.id, read);
|
||||
setContactStatus(contactObj.conversation_id, read);
|
||||
}
|
||||
return { ...contact, read: read };
|
||||
} else {
|
||||
|
||||
244
server/db/db.js
244
server/db/db.js
@@ -194,6 +194,7 @@ async function createGroup(user_id, groupName) {
|
||||
const group_id = result.rows[0].group_id;
|
||||
|
||||
const contact_user_id = await addMemberToGroup(group_id, user_id);
|
||||
insertContactById(user_id, group_id, true);
|
||||
return { group_id, contact_user_id };
|
||||
} catch (e) {
|
||||
console.error("Failed to create conversation ", e);
|
||||
@@ -246,6 +247,8 @@ async function addMemberToGroupByUsername(conversation_id, username) {
|
||||
console.log(
|
||||
`Added user with username ${username} to conversation_id ${conversation_id}`,
|
||||
);
|
||||
const added_user_id = result.rows[0].added_user_id;
|
||||
insertContactById(added_user_id, conversation_id, true);
|
||||
return result.rows[0].added_user_id;
|
||||
} else {
|
||||
console.log(
|
||||
@@ -376,90 +379,133 @@ async function changePassword(username, newPasswordHash) {
|
||||
}
|
||||
}
|
||||
|
||||
async function insertContact(userUsername, receiverUsername, read) {
|
||||
async function insertContactById(senderId, conversation_id, read) {
|
||||
const query = `
|
||||
WITH user_ids AS (
|
||||
SELECT
|
||||
(SELECT user_id FROM Accounts WHERE LOWER(username) = LOWER($1)) AS sender_id,
|
||||
(SELECT user_id FROM Accounts WHERE LOWER(username) = LOWER($2)) AS receiver_id
|
||||
),
|
||||
existing_conversation AS (
|
||||
SELECT c.conversation_id
|
||||
FROM Conversations c
|
||||
JOIN Memberships m1 ON c.conversation_id = m1.conversation_id
|
||||
JOIN Memberships m2 ON c.conversation_id = m2.conversation_id
|
||||
JOIN user_ids ON m1.user_id = user_ids.sender_id AND m2.user_id = user_ids.receiver_id
|
||||
WHERE c.conversation_type = 'direct'
|
||||
LIMIT 1
|
||||
),
|
||||
new_conversation AS (
|
||||
INSERT INTO Conversations (conversation_type)
|
||||
SELECT 'direct'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM existing_conversation)
|
||||
RETURNING conversation_id
|
||||
),
|
||||
conversation AS (
|
||||
SELECT conversation_id FROM existing_conversation
|
||||
UNION ALL
|
||||
SELECT conversation_id FROM new_conversation
|
||||
),
|
||||
new_memberships AS (
|
||||
INSERT INTO Memberships (conversation_id, user_id)
|
||||
SELECT conv.conversation_id, u.user_id
|
||||
FROM conversation conv
|
||||
CROSS JOIN (
|
||||
SELECT sender_id AS user_id FROM user_ids
|
||||
UNION ALL
|
||||
SELECT receiver_id FROM user_ids
|
||||
) u
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM Memberships m
|
||||
WHERE m.conversation_id = conv.conversation_id
|
||||
AND m.user_id = u.user_id
|
||||
)
|
||||
),
|
||||
contact_insert AS (
|
||||
INSERT INTO Contacts (user_id, conversation_id, read)
|
||||
SELECT sender_id, conversation_id, $3
|
||||
FROM user_ids, conversation
|
||||
ON CONFLICT (user_id, conversation_id)
|
||||
DO UPDATE SET last_active = CURRENT_TIMESTAMP, read = $3
|
||||
RETURNING contact_id, conversation_id, last_active, user_id, read
|
||||
)
|
||||
SELECT
|
||||
ci.contact_id AS id,
|
||||
ci.conversation_id,
|
||||
CASE
|
||||
WHEN ci.user_id = (SELECT sender_id FROM user_ids) THEN (SELECT username FROM Accounts WHERE user_id = (SELECT receiver_id FROM user_ids))
|
||||
ELSE (SELECT username FROM Accounts WHERE user_id = (SELECT sender_id FROM user_ids))
|
||||
END AS username,
|
||||
c.last_active,
|
||||
ci.read,
|
||||
'direct' AS type
|
||||
FROM contact_insert ci
|
||||
JOIN Conversations c ON ci.conversation_id = c.conversation_id;
|
||||
INSERT INTO Contacts (user_id, conversation_id, read)
|
||||
VALUES( $1, $2, $3)
|
||||
`;
|
||||
|
||||
try {
|
||||
const result = await client.query(query, [
|
||||
userUsername,
|
||||
receiverUsername,
|
||||
read,
|
||||
]);
|
||||
const result = await client.query(query, [senderId, conversation_id, read]);
|
||||
console.log(
|
||||
"Insertcontactbyid: ",
|
||||
result.rows[0],
|
||||
senderId,
|
||||
conversation_id,
|
||||
);
|
||||
return result.rows[0] || null;
|
||||
} catch (error) {
|
||||
console.error("Failed to insert contact:", error);
|
||||
console.error("Failed to insert contact by IDs:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function insertContact(userUsername, receiverUsername, read) {
|
||||
try {
|
||||
// 1. Get user IDs
|
||||
const getUsersQuery = `
|
||||
SELECT
|
||||
u1.user_id AS initiator_id,
|
||||
u1.username AS initiator_username,
|
||||
u2.user_id AS contact_id,
|
||||
u2.username AS contact_username
|
||||
FROM Accounts u1
|
||||
CROSS JOIN Accounts u2
|
||||
WHERE LOWER(u1.username) = LOWER($1)
|
||||
AND LOWER(u2.username) = LOWER($2)
|
||||
`;
|
||||
const usersResult = await client.query(getUsersQuery, [
|
||||
userUsername,
|
||||
receiverUsername,
|
||||
]);
|
||||
if (!usersResult.rows[0]) {
|
||||
throw new Error("Users not found");
|
||||
}
|
||||
const { initiator_id, contact_id, contact_username } = usersResult.rows[0];
|
||||
|
||||
// 2. Find existing conversation
|
||||
const findConversationQuery = `
|
||||
SELECT c.conversation_id
|
||||
FROM Conversations c
|
||||
JOIN Memberships m1 ON c.conversation_id = m1.conversation_id
|
||||
JOIN Memberships m2 ON c.conversation_id = m2.conversation_id
|
||||
WHERE c.conversation_type = 'direct'
|
||||
AND m1.user_id = $1
|
||||
AND m2.user_id = $2
|
||||
LIMIT 1
|
||||
`;
|
||||
const conversationResult = await client.query(findConversationQuery, [
|
||||
initiator_id,
|
||||
contact_id,
|
||||
]);
|
||||
let conversation_id = conversationResult.rows[0]?.conversation_id;
|
||||
|
||||
// 3. Create new conversation if none exists
|
||||
if (!conversation_id) {
|
||||
const createConversationQuery = `
|
||||
INSERT INTO Conversations (conversation_type)
|
||||
VALUES ('direct')
|
||||
RETURNING conversation_id
|
||||
`;
|
||||
const newConversationResult = await client.query(createConversationQuery);
|
||||
conversation_id = newConversationResult.rows[0].conversation_id;
|
||||
|
||||
// 4. Create memberships for both users
|
||||
const createMembershipsQuery = `
|
||||
INSERT INTO Memberships (conversation_id, user_id)
|
||||
VALUES ($1, $2), ($1, $3)
|
||||
ON CONFLICT (conversation_id, user_id) DO NOTHING
|
||||
`;
|
||||
await client.query(createMembershipsQuery, [
|
||||
conversation_id,
|
||||
initiator_id,
|
||||
contact_id,
|
||||
]);
|
||||
}
|
||||
|
||||
// 5. Create or update contact
|
||||
const upsertContactQuery = `
|
||||
INSERT INTO Contacts (user_id, conversation_id, read)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (user_id, conversation_id)
|
||||
DO UPDATE SET last_active = CURRENT_TIMESTAMP, read = $3
|
||||
RETURNING contact_id, conversation_id, user_id, read, last_active
|
||||
`;
|
||||
const contactResult = await client.query(upsertContactQuery, [
|
||||
initiator_id,
|
||||
conversation_id,
|
||||
read,
|
||||
]);
|
||||
const contact = contactResult.rows[0];
|
||||
|
||||
// 6. Return formatted result with contact's user_id
|
||||
return {
|
||||
id: contact.contact_id,
|
||||
user_id: contact_id, // Now using the contact's user_id instead of the initiator's
|
||||
username: contact_username,
|
||||
last_active: contact.last_active,
|
||||
conversation_id: contact.conversation_id,
|
||||
type: "direct",
|
||||
read: contact.read,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Failed to insert contact:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function getContacts(user_id) {
|
||||
const contactsQuery = `
|
||||
SELECT
|
||||
SELECT DISTINCT ON (c.conversation_id)
|
||||
c.contact_id AS id,
|
||||
a2.user_id AS user_id,
|
||||
a2.username AS username,
|
||||
CASE
|
||||
WHEN conv.conversation_type = 'group' THEN NULL
|
||||
ELSE a2.user_id
|
||||
END AS user_id,
|
||||
CASE
|
||||
WHEN conv.conversation_type = 'group' THEN conv.name
|
||||
ELSE a2.username
|
||||
END AS username,
|
||||
conv.last_active,
|
||||
c.conversation_id,
|
||||
conv.conversation_type AS type,
|
||||
@@ -468,40 +514,30 @@ async function getContacts(user_id) {
|
||||
JOIN Conversations conv ON c.conversation_id = conv.conversation_id
|
||||
JOIN Memberships m ON m.conversation_id = c.conversation_id
|
||||
JOIN Accounts a2 ON a2.user_id = m.user_id
|
||||
WHERE c.user_id = $1
|
||||
AND conv.conversation_type = 'direct'
|
||||
AND a2.user_id != $1
|
||||
ORDER BY conv.last_active DESC;
|
||||
`;
|
||||
|
||||
const groupsQuery = `
|
||||
SELECT
|
||||
c.conversation_id AS id,
|
||||
c.name AS username,
|
||||
c.last_active,
|
||||
c.conversation_type AS type
|
||||
FROM Memberships m
|
||||
JOIN Conversations c ON m.conversation_id = c.conversation_id
|
||||
WHERE m.user_id = $1 AND c.conversation_type = 'group'
|
||||
ORDER BY c.last_active DESC;
|
||||
WHERE c.user_id = $1
|
||||
AND (
|
||||
(conv.conversation_type = 'direct' AND a2.user_id != $1)
|
||||
OR
|
||||
(conv.conversation_type = 'group')
|
||||
)
|
||||
ORDER BY c.conversation_id, conv.last_active DESC;
|
||||
`;
|
||||
|
||||
try {
|
||||
const contactsResult = await client.query(contactsQuery, [user_id]);
|
||||
const groupsResult = await client.query(groupsQuery, [user_id]);
|
||||
|
||||
const contacts = contactsResult.rows;
|
||||
const groups = groupsResult.rows.map((group) => ({
|
||||
username: group.username,
|
||||
last_active: group.last_active,
|
||||
type: group.type,
|
||||
conversation_id: group.id,
|
||||
const contacts = contactsResult.rows.map((row) => ({
|
||||
id: row.id,
|
||||
user_id: row.user_id,
|
||||
username: row.username,
|
||||
last_active: row.last_active,
|
||||
conversation_id: row.conversation_id,
|
||||
type: row.type,
|
||||
read: row.read,
|
||||
}));
|
||||
|
||||
// Combine contacts and groups
|
||||
return [...contacts, ...groups];
|
||||
console.log("Get contacts: ", contacts);
|
||||
return contacts;
|
||||
} catch (e) {
|
||||
console.error("Failed to get contacts and groups:", e);
|
||||
console.error("Failed to get contacts:", e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -561,17 +597,17 @@ async function deleteContact(user_id, contact_id, conversation_id) {
|
||||
}
|
||||
}
|
||||
|
||||
async function updateContactStatus(user_id, contact_id, read) {
|
||||
async function updateContactStatus(user_id, conversation_id, read) {
|
||||
const query = `
|
||||
UPDATE Contacts SET read = $1
|
||||
WHERE user_id = $2
|
||||
AND contact_id = $3;
|
||||
AND conversation_id = $3;
|
||||
`;
|
||||
try {
|
||||
await client.query(query, [read, user_id, contact_id]);
|
||||
await updateContactLastActive(user_id, contact_id);
|
||||
await client.query(query, [read, user_id, conversation_id]);
|
||||
await updateContactLastActive(user_id, conversation_id);
|
||||
console.log(
|
||||
`Successfully updated contact status, user_id: ${user_id}, contact_id: ${contact_id}, read: ${read}: `,
|
||||
`Successfully updated contact status, user_id: ${user_id}, conversation_id: ${conversation_id}, read: ${read}: `,
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("Failed to update contact status ", e);
|
||||
|
||||
@@ -223,19 +223,25 @@ app.delete(
|
||||
},
|
||||
);
|
||||
|
||||
app.put("/api/chat/contacts/:contact_id", authorizeUser, async (req, res) => {
|
||||
const contact_id = req.params.contact_id;
|
||||
if (!contact_id) {
|
||||
return res.status(400).json({ message: "Missing contact id parameter" });
|
||||
}
|
||||
app.put(
|
||||
"/api/chat/contacts/:conversation_id",
|
||||
authorizeUser,
|
||||
async (req, res) => {
|
||||
const conversation_id = req.params.conversation_id;
|
||||
if (!conversation_id) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ message: "Missing conversation id parameter" });
|
||||
}
|
||||
|
||||
const read = req.body.status;
|
||||
await updateContactStatus(req.user.user_id, contact_id, read);
|
||||
const read = req.body.status;
|
||||
await updateContactStatus(req.user.user_id, conversation_id, read);
|
||||
|
||||
return res
|
||||
.status(200)
|
||||
.json({ message: `Successfully updated contact status to: ${read}` });
|
||||
});
|
||||
return res
|
||||
.status(200)
|
||||
.json({ message: `Successfully updated contact status to: ${read}` });
|
||||
},
|
||||
);
|
||||
|
||||
app.post("/api/chat/contact/:contact", authorizeUser, async (req, res) => {
|
||||
if (!req.params.contact) {
|
||||
@@ -249,7 +255,6 @@ app.post("/api/chat/contact/:contact", authorizeUser, async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
console.log("contact post request: ", usernameContact);
|
||||
const result = await insertContact(
|
||||
req.user.username,
|
||||
usernameContact,
|
||||
@@ -258,6 +263,9 @@ app.post("/api/chat/contact/:contact", authorizeUser, async (req, res) => {
|
||||
if (result === null) {
|
||||
return res.status(500).json({ message: "Failed to create conversation" });
|
||||
}
|
||||
if (!result.user_id) {
|
||||
return res.status(500).json({ message: "Something went wrong" });
|
||||
}
|
||||
console.log("sent contact post response: ", result);
|
||||
return res.status(200).json(result);
|
||||
} catch (e) {
|
||||
@@ -347,7 +355,9 @@ app.post("/api/chat/groups/create", authorizeUser, async (req, res) => {
|
||||
if (!group_id) {
|
||||
return res.status(500).json({ message: "Failed to create group" });
|
||||
}
|
||||
io.to(contact_user_id).emit("added to group");
|
||||
console.log("Successfully created group: ", groupname, "id: ", group_id);
|
||||
console.log("io.to: ", contact_user_id, "added to group");
|
||||
io.to(contact_user_id).emit("added to group", { group_id });
|
||||
return res.status(200).json({
|
||||
message: `Successfully created group: ${groupname}`,
|
||||
group_id: group_id,
|
||||
@@ -365,7 +375,11 @@ app.post("/api/chat/groups/addMember", async (req, res) => {
|
||||
}
|
||||
const result = await addMemberToGroupByUsername(group_id, username);
|
||||
if (result !== null) {
|
||||
io.to(result).emit("added to group");
|
||||
io.to(result).to(group_id).emit("added to group", {
|
||||
username,
|
||||
user_id: result,
|
||||
group_id,
|
||||
});
|
||||
console.log("added to group: ", result);
|
||||
return res.status(200).json({ message: "Successfully added member" });
|
||||
}
|
||||
|
||||
@@ -112,6 +112,7 @@ function initializeSocket(io) {
|
||||
insertedMessage;
|
||||
console.log("(socket) received from chat message", msg);
|
||||
|
||||
console.log(`io to ${username}, ${recipient}, ${recipient_id}`);
|
||||
io.to(username).to(recipient).to(recipient_id).emit("chat message", {
|
||||
sender,
|
||||
message: content,
|
||||
|
||||
Reference in New Issue
Block a user