fixed something, added AddGroupMember.tsx

This commit is contained in:
slawk0
2024-11-29 19:27:53 +01:00
parent 7e362de7e6
commit d2ffc4b08d
7 changed files with 219 additions and 56 deletions

View File

@@ -0,0 +1,98 @@
import LoadingWheel from './LoadingWheel.tsx';
import { useRef, useState } from 'react';
import { SubmitHandler, useForm } from 'react-hook-form';
import { axiosClient } from '../../App.tsx';
type Inputs = {
username: string;
};
function AddGroupMember() {
const [isLoading, setIsLoading] = useState<boolean>(false);
const modalRef = useRef<HTMLDialogElement | null>(null);
const {
register,
handleSubmit,
formState: { errors },
} = useForm<Inputs>();
const onSubmit: SubmitHandler<Inputs> = async (data) => {
try {
setIsLoading(true);
const response = await axiosClient.post(`/api/chat/groups/add/`, data);
console.log(response.data);
if (response.data.ok) {
setIsLoading(false);
if (modalRef.current) {
modalRef.current.close();
}
}
} catch (e) {
console.error('Failed to create group: ', e);
setIsLoading(false);
}
};
return (
<>
<div>
<button
className="m-2 border p-1 rounded-md"
onClick={() =>
(
document.getElementById('my_modal_1') as HTMLDialogElement
).showModal()
}
>
Add member
</button>
<dialog id="my_modal_1" className="modal" ref={modalRef}>
<div className="modal-box bg-gray-800 text-center relative p-1">
<div className="absolute right-2 top-2">
<form method="dialog">
<button className="btn btn-sm btn-circle btn-ghost"></button>
</form>
</div>
<div className="flex flex-col items-center justify-center pb-1 mt-6">
<h3 className="text-lg mb-4">Enter username</h3>
<form
onSubmit={handleSubmit(onSubmit)}
className="w-full max-w-xs relative"
>
<input
className="input input-bordered bg-green-50 w-full text-black rounded-md text-center"
{...register('username', {
required: true,
minLength: 4,
maxLength: 20,
})}
aria-invalid={errors.username ? 'true' : 'false'}
/>
{errors.username?.type === 'minLength' && (
<p className="text-gray-300">Invalid username</p>
)}
{errors.username?.type === 'maxLength' && (
<p className="text-gray-300">Invalid username</p>
)}
</form>
</div>
<div className="mt-4 flex justify-center">
<button
type="submit"
onClick={handleSubmit(onSubmit)}
className="btn btn-sm bg-green-500 text-black hover:bg-green-600"
>
{isLoading ? <LoadingWheel /> : 'Add'}
</button>
</div>
</div>
</dialog>
</div>
</>
);
}
export default AddGroupMember;

View File

@@ -76,7 +76,10 @@ function ContactsList({
}}
key={contact.conversation_id}
>
<span className="flex-shrink-0">{contact.username}</span>
<span className="flex-shrink-0">
{/*user_id: {contact.user_id}, contact id: {contact.id}, username:*/}
{contact.username}
</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">

View File

@@ -99,13 +99,14 @@ const MessageForm = ({ contact, setMessages }: MessageFormProps) => {
const tempMessage: ChatMessages = {
sender: username,
content: data.message.trim(),
message: data.message.trim(),
recipient: contact,
message_id: 0, // Set to 0 because of key={msg.message_id || msg.tempId} in messages list
pending: true,
tempId: tempId,
attachment_url: attachmentUrl || null,
sender_id: 0,
conversation_id: 0,
};
setMessages((prevMessages) => [...prevMessages, tempMessage]); // Display as gray

View File

@@ -129,7 +129,7 @@ function MessagesArea({
}`}
key={msg.message_id || msg.tempId}
>
{msg.message_id} {msg.sender}: {msg.content}
{msg.message_id || msg.tempId} {msg.sender}: {msg.message}
{msg.attachment_url ? (
<div className="mt-2">
{msg.attachment_url.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/i) ? (

View File

@@ -11,7 +11,7 @@ import { getMessages, setContactStatus } from '../api/contactsApi.tsx';
export type ChatMessages = {
sender: string;
content: string;
message: string;
recipient: string;
message_id: number;
tempId: string;

View File

@@ -297,7 +297,7 @@ async function getMessages(user_id, receiverUsername, limit = 50, cursor = 0) {
)
SELECT
m.message_id,
m.content,
m.content AS message,
m.sent_at,
m.attachment_url,
a.username AS sender,
@@ -341,7 +341,7 @@ async function getMessages(user_id, receiverUsername, limit = 50, cursor = 0) {
)
SELECT
m.message_id,
m.content,
m.content AS message,
m.sent_at,
m.attachment_url,
a.username AS sender,
@@ -453,19 +453,91 @@ async function changePassword(username, newPasswordHash) {
}
async function insertContact(userUsername, receiverUsername, read) {
const query = `
// Step 1: Get user IDs for the sender and receiver
const senderUserId = await getUserId(userUsername);
const receiverUserId = await getUserId(receiverUsername);
if (!senderUserId || !receiverUserId) {
console.error("One or both users do not exist.");
return null;
}
// Step 2: Check if a conversation already exists between the two users
let conversationId = await getConversationId(userUsername, receiverUsername);
// Step 3: If no conversation exists, create a new one
if (!conversationId) {
const query = `
INSERT INTO Conversations (conversation_type)
VALUES ('direct')
RETURNING conversation_id;
`;
try {
const result = await client.query(query);
conversationId = result.rows[0].conversation_id;
// Add both users to the new conversation
await addMemberToGroup(conversationId, senderUserId);
await addMemberToGroup(conversationId, receiverUserId);
} catch (e) {
console.error("Failed to create conversation ", e);
return null;
}
}
// Step 4: Insert the contact
const insertContactQuery = `
INSERT INTO Contacts (user_id, contact_user_id, read, last_active)
VALUES
((SELECT user_id FROM Accounts WHERE username = $1),
(SELECT user_id FROM Accounts WHERE username = $2),
$3,
CURRENT_TIMESTAMP)
ON CONFLICT DO NOTHING;
VALUES ($1, $2, $3, CURRENT_TIMESTAMP)
ON CONFLICT DO NOTHING
RETURNING contact_id AS id, contact_user_id AS user_id;
`;
try {
await client.query(query, [userUsername, receiverUsername, read]);
const result = await client.query(insertContactQuery, [
senderUserId,
receiverUserId,
read,
]);
if (result.rows.length > 0) {
return result.rows[0];
}
return null;
} catch (e) {
console.error("Failed to insert contact:", e);
return null;
}
}
// Helper function to get conversation ID between two users
async function getConversationId(senderUsername, receiverUsername) {
const query = `
SELECT conversation_id FROM Conversations
WHERE conversation_type = 'direct'
AND EXISTS (
SELECT 1 FROM Memberships WHERE conversation_id = Conversations.conversation_id AND user_id = (SELECT user_id FROM Accounts WHERE username = $1)
)
AND EXISTS (
SELECT 1 FROM Memberships WHERE conversation_id = Conversations.conversation_id AND user_id = (SELECT user_id FROM Accounts WHERE username = $2)
)
LIMIT 1;
`;
try {
const result = await client.query(query, [
senderUsername,
receiverUsername,
]);
if (result.rowCount > 0) {
return result.rows[0].conversation_id;
} else {
console.log("No conversation found between these users.");
return null;
}
} catch (e) {
console.error("Failed to get conversation id ", e);
return null;
}
}
@@ -473,38 +545,22 @@ async function getContacts(user_id) {
const contactsQuery = `
SELECT
c.contact_id AS id,
c.contact_user_id AS contact_user_id,
c.contact_user_id AS user_id,
a.username AS username,
c.last_active,
'direct' AS type,
COALESCE(m.conversation_id, NULL) AS conversation_id
'direct' AS type
FROM Contacts c
JOIN Accounts a ON a.user_id = c.contact_user_id
LEFT JOIN Memberships m ON m.user_id = c.contact_user_id AND m.conversation_id = (
SELECT conv.conversation_id
FROM Conversations conv
JOIN Memberships mem ON conv.conversation_id = mem.conversation_id
WHERE mem.user_id = $1
AND conv.conversation_type = 'direct'
AND EXISTS (
SELECT 1
FROM Memberships mem2
WHERE mem2.conversation_id = mem.conversation_id
AND mem2.user_id = c.contact_user_id
)
LIMIT 1
)
WHERE c.user_id = $1
ORDER BY c.last_active DESC;
`;
const groupsQuery = `
SELECT
m.conversation_id AS user_id,
c.conversation_id AS id,
c.name AS username,
c.created_at AS last_active,
c.conversation_type AS type,
c.conversation_id AS conversation_id
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'
@@ -517,8 +573,10 @@ async function getContacts(user_id) {
const contacts = contactsResult.rows;
const groups = groupsResult.rows.map((group) => ({
...group,
contact_id: group.conversation_id, // Ensure user_id is correctly mapped
group_id: group.id,
username: group.username,
last_active: group.last_active,
type: group.type,
}));
// Combine contacts and groups

View File

@@ -282,7 +282,7 @@ app.get("/api/chat/messages/:contact", authorizeUser, async (req, res) => {
return res.status(404).json({ message: "No more messages found" });
}
console.log("Sent messages for: ", req.user.user_id, "messages: ", messages);
console.log("Sent messages for: ", req.user.user_id);
return res.status(200).json({ messages });
});
@@ -303,25 +303,28 @@ app.post(
},
);
app.post(
"/api/chat/creategroup/:groupname",
authorizeUser,
async (req, res) => {
const user_id = req.user.user_id;
const groupname = req.params.groupname;
if (!groupname) {
res.status(400).json({ message: "Missing groupname parameter" });
}
const group_id = await createGroup(user_id, groupname);
if (!group_id) {
return res.status(500).json({ message: "Failed to create group" });
}
return res.status(200).json({
message: `Successfully created group: ${groupname}`,
group_id: group_id,
});
},
);
app.post("/api/chat/groups/create", authorizeUser, async (req, res) => {
const user_id = req.user.user_id;
const groupname = req.body.groupname;
if (!groupname) {
res.status(400).json({ message: "Groupname not provided" });
}
const group_id = await createGroup(user_id, groupname);
if (!group_id) {
return res.status(500).json({ message: "Failed to create group" });
}
return res.status(200).json({
message: `Successfully created group: ${groupname}`,
group_id: group_id,
});
});
app.post("/api/chat/groups/add", async (req, res) => {
const username = req.body.username;
if (!username) {
return res.status(400).json({ message: "Username not provided" });
}
});
initializeSocket(io);