add: added replies to messages

This commit is contained in:
Lukian 2025-05-11 21:10:46 +02:00
parent 05dd75fa74
commit b10a472ed7
10 changed files with 607 additions and 306 deletions

View file

@ -4,17 +4,18 @@ const {
getChannels,
getChannel,
addChannel,
getMessages,
getMessage,
getMessages,
addMessage,
deleteMessage,
addMention,
getMentions,
getUserByUsername,
deleMentions,
deleteChannelMentions,
deleteChannelMessages,
deleteChannel
deleteChannel,
setHasReplies,
addReply,
getMessageReplies
} = require('../libs/mysql');
const rateLimit = require("express-rate-limit");
const slowDown = require("express-slow-down");
@ -70,7 +71,6 @@ router.post('/:name/purge', async (req, res) => {
return res.status(401).send({ error: 'Unauthorized' });
}
await deleteChannelMentions(connection, channel[0].id);
await deleteChannelMessages(connection, channel[0].id);
connection.end();
@ -111,6 +111,23 @@ router.post('/:name/delete', async (req, res) => {
res.send({ message: 'Channel deleted' });
});
async function getReplies(message, connection) {
const replies = await getMessageReplies(connection, message.id);
for (const reply of replies) {
if (reply.has_replies) {
const subReplies = await getReplies(reply, connection);
reply.replies = subReplies;
}
if (reply.content.includes('@')) {
const mentions = await getMentions(connection, reply.id);
reply.mentions = mentions;
} else {
reply.mentions = [];
}
}
return replies;
}
router.get('/:name/messages', async (req, res) => {
const name = req.params.name;
const connection = await getConnection();
@ -128,6 +145,14 @@ router.get('/:name/messages', async (req, res) => {
} else {
message.mentions = [];
}
if (message.has_replies) {
const replies = await getReplies(message, connection);
message.replies = replies;
}
else {
message.replies = [];
}
}
connection.end();
@ -175,46 +200,6 @@ router.post('/:name/messages/send', speedLimiter, limiter, checkAuth, async (req
res.send({ message: 'Message sent' });
});
router.post('/:name/messages/delete', checkAuth, async (req, res) => {
const { message_id } = req.body;
const name = req.params.name;
const user = req.user;
if (!message_id) {
return res.status(400).send({ error: 'Missing message_id' });
}
const connection = await getConnection();
const message = await getMessage(connection, message_id);
if (!message[0]) {
connection.end();
return res.status(400).send({ error: 'No message found' });
}
const channel = await getChannel(connection, name);
if (!channel[0]) {
connection.end();
return res.status(400).send({ error: 'No channel found' });
}
if (user.id !== channel[0].owner_id && user.id !== message[0].user_id && user.admin !== 1) {
connection.end();
return res.status(401).send({ error: 'Unauthorized' });
}
await deleteMessage(connection, message_id);
connection.end();
req.sockets.emit({
type: 'delete_message',
channel_id: channel[0].id,
user_id: user.id,
});
res.send({ message: 'Message deleted' });
});
router.post('/add', speedLimiter, limiter, checkAuth, async (req, res) => {
const { name, description } = req.body;
const user = req.user;

View file

@ -14,6 +14,7 @@ router.get('/', async (req, res) => {
} else {
message.mentions = [];
}
message.replies = [];
}
connection.end();

156
back/api/messages.js Normal file
View file

@ -0,0 +1,156 @@
const express = require('express');
const {
getConnection,
getChannel,
getMessage,
deleteMessage,
addMention,
getMentions,
getUserByUsername,
setHasReplies,
addReply,
getMessageReplies
} = require('../libs/mysql');
const rateLimit = require("express-rate-limit");
const slowDown = require("express-slow-down");
const { checkAuth } = require('../libs/middlewares');
const limiter = rateLimit({
windowMs: 1 * 1000,
max: 2,
});
const speedLimiter = slowDown({
windowMs: 1 * 1000,
delayAfter: 2,
delayMs: () => 5000,
});
const router = express.Router();
async function getReplies(message, connection) {
const replies = await getMessageReplies(connection, message.id);
for (const reply of replies) {
if (reply.has_replies) {
const subReplies = await getReplies(reply, connection);
reply.replies = subReplies;
}
if (reply.content.includes('@')) {
const mentions = await getMentions(connection, reply.id);
reply.mentions = mentions;
} else {
reply.mentions = [];
}
}
return replies;
}
router.get('/:message_id', async (req, res) => {
const message_id = req.params.message_id;
const connection = await getConnection();
const message = await getMessage(connection, message_id);
if (!message[0]) {
connection.end();
return res.send({ error: 'No message found' });
}
if (message[0].content.includes('@')) {
const mentions = await getMentions(connection, message.id);
message[0].mentions = mentions;
} else {
message[0].mentions = [];
}
if (message[0].has_replies) {
const replies = await getReplies(message[0], connection);
message[0].replies = replies;
} else {
message[0].replies = [];
}
connection.end();
res.send(message[0]);
});
router.post('/:message_id/reply', speedLimiter, limiter, checkAuth, async (req, res) => {
const { message } = req.body;
const { message_id } = req.params;
const user = req.user;
if (!message) {
return res.status(400).send({ error: 'Missing parameters' });
}
const connection = await getConnection();
const originalMessage = await getMessage(connection, message_id);
if (!originalMessage[0]) {
connection.end();
return res.status(400).send({ error: 'No message found' });
}
const channel = await getChannel(connection, originalMessage[0].channel_name);
const sent_message = await addReply(connection, channel[0].id, user.id, message.replace("\"", "'"), message_id);
await setHasReplies(connection, message_id, true);
const new_message_id = sent_message.insertId;
for (const word of message.split(' ')) {
if (word.startsWith('@')) {
const username = word.substring(1);
const mentionedUser = await getUserByUsername(connection, username);
if (mentionedUser[0]) {
await addMention(connection, new_message_id, mentionedUser[0].id);
}
}
}
connection.end();
req.sockets.emit({
type: 'new_message',
channel_id: channel[0].id,
user_id: user.id,
});
res.send({ message: 'Reply sent' });
});
router.post('/:message_id/delete', checkAuth, async (req, res) => {
const { message_id } = req.params;
const user = req.user;
const connection = await getConnection();
const message = await getMessage(connection, message_id);
if (!message[0]) {
connection.end();
return res.status(400).send({ error: 'No message found' });
}
const channel = await getChannel(connection, message[0].channel_name);
if (user.id !== channel[0].owner_id && user.id !== message[0].user_id && user.admin !== 1) {
connection.end();
return res.status(401).send({ error: 'Unauthorized' });
}
if (message[0].reply_to_id) {
const replies = await getMessageReplies(connection, message[0].reply_to_id);
if (replies.length === 1) {
await setHasReplies(connection, replyToId, false);
}
}
await deleteMessage(connection, message_id);
connection.end();
req.sockets.emit({
type: 'delete_message',
channel_id: channel[0].id,
user_id: user.id,
});
res.send({ message: 'Message deleted' });
});
module.exports = router;

View file

@ -37,6 +37,7 @@ router.get('/:username/lastmessages', async (req, res) => {
} else {
message.mentions = [];
}
message.replies = [];
}
connection.end();

View file

@ -9,6 +9,41 @@ function getConnection() {
});
}
// +---------------------------+
// | Users |
// +---------------------------+
function addUser(connection, username, password) {
return new Promise((resolve, reject) => {
connection.query(
`INSERT INTO users (username, password) VALUES (?, ?)`,
[username, password],
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function deleteUser(connection, id) {
return new Promise((resolve, reject) => {
connection.query(
`DELETE FROM users WHERE id = ?`,
[id],
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function getUsers(connection) {
return new Promise((resolve, reject) => {
connection.query(
@ -27,22 +62,7 @@ function getUser(connection, id) {
return new Promise((resolve, reject) => {
connection.query(
`SELECT * FROM users WHERE id = ?`,
[id], // Use parameterized query
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function searchUser(connection, search) {
return new Promise((resolve, reject) => {
connection.query(
`SELECT * FROM users WHERE username LIKE ? LIMIT 5`,
[`%${search}%`], // Use parameterized query
[id],
(error, result) => {
if (error) {
reject(new Error(error));
@ -57,7 +77,7 @@ function getUserByUsername(connection, username) {
return new Promise((resolve, reject) => {
connection.query(
`SELECT * FROM users WHERE username = ?`,
[username], // Use parameterized query
[username],
(error, result) => {
if (error) {
reject(new Error(error));
@ -68,11 +88,11 @@ function getUserByUsername(connection, username) {
});
}
function addUser(connection, username, password) {
function searchUser(connection, search) {
return new Promise((resolve, reject) => {
connection.query(
`INSERT INTO users (username, password) VALUES (?, ?)`,
[username, password], // Use parameterized query
`SELECT * FROM users WHERE username LIKE ? LIMIT 5`,
[`%${search}%`],
(error, result) => {
if (error) {
reject(new Error(error));
@ -83,46 +103,11 @@ function addUser(connection, username, password) {
});
}
function getUserLastMessages(connection, username) {
return new Promise((resolve, reject) => {
connection.query(
`SELECT messages.id, user_id, username, content, date, channels.name AS channel_name
FROM messages
JOIN users ON messages.user_id = users.id
JOIN channels ON messages.channel_id = channels.id
WHERE username = ?
ORDER BY date DESC LIMIT 5`,
[username], // Use parameterized query
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function deleteUser(connection, id) {
return new Promise((resolve, reject) => {
connection.query(
`DELETE FROM users WHERE id = ?`,
[id], // Use parameterized query
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
};
function setUserPfp(connection, id, pfp) {
return new Promise((resolve, reject) => {
connection.query(
`UPDATE users SET pfp = ? WHERE id = ?`,
[pfp, id], // Use parameterized query
[pfp, id],
(error, result) => {
if (error) {
reject(new Error(error));
@ -137,7 +122,7 @@ function setUserUsername(connection, id, username) {
return new Promise((resolve, reject) => {
connection.query(
`UPDATE users SET username = ? WHERE id = ?`,
[username, id], // Use parameterized query
[username, id],
(error, result) => {
if (error) {
reject(new Error(error));
@ -152,7 +137,41 @@ function setUserPassword(connection, id, password) {
return new Promise((resolve, reject) => {
connection.query(
`UPDATE users SET password = ? WHERE id = ?`,
[password, id], // Use parameterized query
[password, id],
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
// +---------------------------+
// | Channels |
// +---------------------------+
function addChannel(connection, name, description, owner_id) {
return new Promise((resolve, reject) => {
connection.query(
`INSERT INTO channels (name, description, owner_id) VALUES (?, ?, ?)`,
[name, description, owner_id],
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function deleteChannel(connection, channel_id) {
return new Promise((resolve, reject) => {
connection.query(
`DELETE FROM channels WHERE id = ?`,
[channel_id],
(error, result) => {
if (error) {
reject(new Error(error));
@ -177,6 +196,24 @@ function getChannels(connection) {
});
}
function getChannel(connection, name) {
return new Promise((resolve, reject) => {
connection.query(
`SELECT channels.id, name, description, owner_id, username AS owner_username
FROM channels
JOIN users ON channels.owner_id = users.id
WHERE name = ?`,
[name],
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function getActiveChannels(connection) {
return new Promise((resolve, reject) => {
connection.query(
@ -223,7 +260,7 @@ function searchChannels(connection, search) {
JOIN users ON channels.owner_id = users.id
WHERE name LIKE ?
LIMIT 5`,
[`%${search}%`], // Use parameterized query
[`%${search}%`],
(error, result) => {
if (error) {
reject(new Error(error));
@ -234,116 +271,30 @@ function searchChannels(connection, search) {
});
}
function getChannel(connection, name) {
return new Promise((resolve, reject) => {
connection.query(
`SELECT channels.id, name, description, owner_id, username AS owner_username
FROM channels
JOIN users ON channels.owner_id = users.id
WHERE name = ?`,
[name], // Use parameterized query
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function addChannel(connection, name, description, owner_id) {
return new Promise((resolve, reject) => {
connection.query(
`INSERT INTO channels (name, description, owner_id) VALUES (?, ?, ?)`,
[name, description, owner_id], // Use parameterized query
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function deleteChannel(connection, channel_id) {
return new Promise((resolve, reject) => {
connection.query(
`DELETE FROM channels WHERE id = ?`,
[channel_id], // Use parameterized query
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function getMessages(connection, channel_id) {
return new Promise((resolve, reject) => {
connection.query(
`SELECT messages.id, user_id, username, content, date, channels.name AS channel_name
FROM messages
JOIN users ON messages.user_id = users.id
JOIN channels ON messages.channel_id = channels.id
WHERE channel_id = ?
ORDER BY date DESC`,
[channel_id], // Use parameterized query
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function getLastMessages(connection) {
return new Promise((resolve, reject) => {
connection.query(
`SELECT messages.id, user_id, username, content, date, channels.name AS channel_name
FROM messages
JOIN users ON messages.user_id = users.id
JOIN channels ON messages.channel_id = channels.id
ORDER BY date DESC LIMIT 5`,
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function getMessage(connection, message_id) {
return new Promise((resolve, reject) => {
connection.query(
`SELECT messages.id, user_id, username, content, date, channels.name AS channel_name
FROM messages
JOIN users ON messages.user_id = users.id
JOIN channels ON messages.channel_id = channels.id
WHERE messages.id = ?`,
[message_id], // Use parameterized query
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
// +---------------------------+
// | Messages |
// +---------------------------+
function addMessage(connection, channel_id, user_id, message) {
return new Promise((resolve, reject) => {
connection.query(
`INSERT INTO messages (channel_id, user_id, content, date) VALUES (?, ?, ?, ?)`,
[channel_id, user_id, message, Math.floor(Date.now() / 1000)], // Use parameterized query
[channel_id, user_id, message, Math.floor(Date.now() / 1000)],
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function addReply(connection, channel_id, user_id, message, message_id) {
return new Promise((resolve, reject) => {
connection.query(
`INSERT INTO messages (channel_id, user_id, content, date, reply_to_id) VALUES (?, ?, ?, ?, ?)`,
[channel_id, user_id, message, Math.floor(Date.now() / 1000), message_id],
(error, result) => {
if (error) {
reject(new Error(error));
@ -358,7 +309,7 @@ function deleteMessage(connection, message_id) {
return new Promise((resolve, reject) => {
connection.query(
`DELETE FROM messages WHERE id = ?`,
[message_id], // Use parameterized query
[message_id],
(error, result) => {
if (error) {
reject(new Error(error));
@ -373,7 +324,7 @@ function deleteChannelMessages(connection, channel_id) {
return new Promise((resolve, reject) => {
connection.query(
`DELETE FROM messages WHERE channel_id = ?`,
[channel_id], // Use parameterized query
[channel_id],
(error, result) => {
if (error) {
reject(new Error(error));
@ -384,11 +335,16 @@ function deleteChannelMessages(connection, channel_id) {
});
}
function deleteUserMessages(connection, user_id) {
function getMessages(connection, channel_id) {
return new Promise((resolve, reject) => {
connection.query(
`DELETE FROM messages WHERE user_id = ?`,
[user_id], // Use parameterized query
`SELECT messages.id, user_id, username, content, date, channels.name AS channel_name, has_replies
FROM messages
JOIN users ON messages.user_id = users.id
JOIN channels ON messages.channel_id = channels.id
WHERE channel_id = ? AND reply_to_id IS NULL
ORDER BY date DESC`,
[channel_id],
(error, result) => {
if (error) {
reject(new Error(error));
@ -399,11 +355,106 @@ function deleteUserMessages(connection, user_id) {
});
}
function getMessage(connection, message_id) {
return new Promise((resolve, reject) => {
connection.query(
`SELECT messages.id, user_id, username, content, date, channels.name AS channel_name, has_replies
FROM messages
JOIN users ON messages.user_id = users.id
JOIN channels ON messages.channel_id = channels.id
WHERE messages.id = ?`,
[message_id],
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function getLastMessages(connection) {
return new Promise((resolve, reject) => {
connection.query(
`SELECT messages.id, user_id, username, content, date, channels.name AS channel_name, has_replies
FROM messages
JOIN users ON messages.user_id = users.id
JOIN channels ON messages.channel_id = channels.id
ORDER BY date DESC LIMIT 5`,
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function getUserLastMessages(connection, username) {
return new Promise((resolve, reject) => {
connection.query(
`SELECT messages.id, user_id, username, content, date, channels.name AS channel_name, has_replies
FROM messages
JOIN users ON messages.user_id = users.id
JOIN channels ON messages.channel_id = channels.id
WHERE username = ?
ORDER BY date DESC LIMIT 5`,
[username],
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function getMessageReplies(connection, message_id) {
return new Promise((resolve, reject) => {
connection.query(
`SELECT messages.id, user_id, username, content, date, channels.name AS channel_name, has_replies
FROM messages
JOIN users ON messages.user_id = users.id
JOIN channels ON messages.channel_id = channels.id
WHERE reply_to_id = ?`,
[message_id],
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function setHasReplies(connection, message_id, has_replies) {
return new Promise((resolve, reject) => {
connection.query(
`UPDATE messages SET has_replies = ? WHERE id = ?`,
[has_replies, message_id],
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
// +---------------------------+
// | Mentions |
// +---------------------------+
function addMention(connection, message_id, user_id) {
return new Promise((resolve, reject) => {
connection.query(
`INSERT INTO mentions (message_id, user_id) VALUES (?, ?)`,
[message_id, user_id], // Use parameterized query
[message_id, user_id],
(error, result) => {
if (error) {
reject(new Error(error));
@ -420,7 +471,7 @@ function getMentions(connection, message_id) {
`SELECT users.username FROM mentions
JOIN users ON mentions.user_id = users.id
WHERE message_id = ?`,
[message_id], // Use parameterized query
[message_id],
(error, result) => {
if (error) {
reject(new Error(error));
@ -431,11 +482,15 @@ function getMentions(connection, message_id) {
});
}
function deleMentions(connection, message_id) {
// +---------------------------+
// | Emojis |
// +---------------------------+
function addEmoji(connection, name, file) {
return new Promise((resolve, reject) => {
connection.query(
`DELETE FROM mentions WHERE message_id = ?`,
[message_id], // Use parameterized query
`INSERT INTO emojis (name, file) VALUES (?, ?)`,
[name, file],
(error, result) => {
if (error) {
reject(new Error(error));
@ -446,27 +501,11 @@ function deleMentions(connection, message_id) {
});
}
function deleteChannelMentions(connection, channel_id) {
function deleteEmoji(connection, id) {
return new Promise((resolve, reject) => {
connection.query(
`DELETE FROM mentions WHERE message_id IN
(SELECT id FROM messages WHERE channel_id = ?)`,
[channel_id], // Use parameterized query
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function deleteUserMentions(connection, user_id) {
return new Promise((resolve, reject) => {
connection.query(
`DELETE FROM mentions WHERE user_id = ?`,
[user_id], // Use parameterized query
`DELETE FROM emojis WHERE id = ?`,
[id],
(error, result) => {
if (error) {
reject(new Error(error));
@ -491,41 +530,11 @@ function getEmojis(connection) {
});
}
function addEmoji(connection, name, file) {
return new Promise((resolve, reject) => {
connection.query(
`INSERT INTO emojis (name, file) VALUES (?, ?)`,
[name, file], // Use parameterized query
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function deleteEmoji(connection, id) {
return new Promise((resolve, reject) => {
connection.query(
`DELETE FROM emojis WHERE id = ?`,
[id], // Use parameterized query
(error, result) => {
if (error) {
reject(new Error(error));
}
resolve(result);
}
);
});
}
function getEmoji(connection, id) {
return new Promise((resolve, reject) => {
connection.query(
`SELECT * FROM emojis WHERE id = ?`,
[id], // Use parameterized query
[id],
(error, result) => {
if (error) {
reject(new Error(error));
@ -540,7 +549,7 @@ function getEmojiByName(connection, name) {
return new Promise((resolve, reject) => {
connection.query(
`SELECT * FROM emojis WHERE name = ?`,
[name], // Use parameterized query
[name],
(error, result) => {
if (error) {
reject(new Error(error));
@ -555,7 +564,7 @@ function searchEmojis(connection, search) {
return new Promise((resolve, reject) => {
connection.query(
`SELECT * FROM emojis WHERE name LIKE ? LIMIT 5`,
[`%${search}%`], // Use parameterized query
[`%${search}%`],
(error, result) => {
if (error) {
reject(new Error(error));
@ -567,39 +576,43 @@ function searchEmojis(connection, search) {
}
module.exports = {
getUsers,
getConnection,
addUser,
deleteUser,
getUsers,
getUser,
searchUser,
getUserByUsername,
addUser,
deleteUser,
setUserPfp,
setUserUsername,
setUserPassword,
getUserLastMessages,
addChannel,
deleteChannel,
getChannels,
getChannel,
getActiveChannels,
getNewChannels,
searchChannels,
getChannel,
addChannel,
deleteChannel,
getMessages,
getLastMessages,
getMessage,
addMessage,
addReply,
deleteMessage,
deleteChannelMessages,
deleteUserMessages,
getMessages,
getMessage,
getUserLastMessages,
getLastMessages,
getMessageReplies,
setHasReplies,
addMention,
getMentions,
deleMentions,
deleteUserMentions,
deleteChannelMentions,
getEmojis,
addEmoji,
deleteEmoji,
getEmojis,
getEmoji,
getEmojiByName,
searchEmojis,

View file

@ -1,14 +1,21 @@
import { Link } from "react-router-dom";
import { Message, User, Channel } from "../types";
import { Message, User, Channel, Users, Emojis } from "../types";
import { useState, createRef, useEffect } from "react";
import axios from "axios";
import "../styles/MessageComponent.css";
export default function MessageComponent({ message, user, channel, deleteMessage }: {
export default function MessageComponent({ message, user, channel }: {
message: Message;
user: User | undefined;
channel: Channel | undefined;
deleteMessage: ((messageId: number) => void) | undefined;
}) {
const [reply, setReply] = useState(false);
const token = localStorage.getItem("token");
const [replyContent, setReplyContent] = useState("");
const [searchedUsers, setSearchedUsers] = useState<Users>([]);
const [searchedEmojis, setSearchedEmojis] = useState<Emojis>([]);
const ref = createRef<HTMLInputElement>();
function getMessageArray(message: Message) {
const array = []
@ -38,6 +45,70 @@ export default function MessageComponent({ message, user, channel, deleteMessage
array.push(string)
return array.filter((word) => word !== '');
}
function handleReply(e: React.FormEvent<HTMLFormElement>, messageId: number) {
e.preventDefault();
axios
.post(`/api/messages/${messageId}/reply`, {
token: token,
message: replyContent,
})
.then(() => {
setReplyContent("");
setReply(false);
setSearchedUsers([]);
setSearchedEmojis([]);
})
.catch((err) => {
console.error(err.response.data);
});
}
function handleDelete() {
if (!window.confirm("Are you sure you want to delete this message?")) {
return;
}
axios
.post(`/api/messages/${message.id}/delete`, {
token: token,
})
.catch((err) => {
console.error(err.response.data);
});
}
useEffect(() => {
const words = replyContent.toString().split(" ");
const lastWord = words[words.length - 1];
if (lastWord && lastWord.startsWith("@")) {
const username = lastWord.slice(1);
axios
.get(`/api/searchuser?search=${username}`)
.then((res) => {
setSearchedUsers(res.data);
})
.catch((err) => {
console.error(err.response.data);
});
} else {
setSearchedUsers([]);
}
if (lastWord && lastWord.startsWith(":")) {
const emojiName = lastWord.slice(1);
axios
.get(`/api/searchemojis?search=${emojiName}`)
.then((res) => {
setSearchedEmojis(res.data);
})
.catch((err) => {
console.error(err.response.data);
});
} else {
setSearchedEmojis([]);
}
}, [replyContent]);
return (
<div key={message.id} className="message">
@ -70,13 +141,81 @@ export default function MessageComponent({ message, user, channel, deleteMessage
<img src="/le_poisson_steve.png" alt="Le poisson steve" className="fish" />
)}
</div>
{channel && deleteMessage ? (
{channel?.owner_id == user?.id || user?.admin == 1 || user?.username == message.username ? (
user?.id === message.user_id || user?.id === channel?.owner_id || user?.admin === 1) && (
<button onClick={() => {deleteMessage(message.id)}}>Delete</button>
) : (
<p>In <Link to={`/c/${message.channel_name}`}>{message.channel_name}</Link></p>
)
}
<button onClick={handleDelete}>Delete</button>
) : (
<p>In <Link to={`/c/${message.channel_name}`}>{message.channel_name}</Link></p>
)}
{channel && user && !reply && (
<button onClick={() => setReply(true)}>Reply</button>
)}
{reply && (
<form onSubmit={(e) => handleReply(e, message.id)} className="message-form">
<input
type="text"
placeholder="Message"
value={replyContent}
onChange={(e) => setReplyContent(e.target.value)}
ref={ref}
/>
<button type="submit">Send</button>
{searchedUsers.length > 0 && (
<div className="mentions">
{searchedUsers.map((user) => (
<div key={user.id} className="mention">
<Link to={`/u/${user.username}`}>{user.username}</Link>
<button
type="button"
onClick={() => {
setReplyContent(
replyContent.split(" ").slice(0, -1).join(" ") + " @" + user.username + " "
);
setSearchedUsers([]);
ref.current?.focus();
}}
>
Mention
</button>
</div>
))}
</div>
)}
{searchedEmojis.length > 0 && (
<div className="emojis">
{searchedEmojis.map((emoji) => (
<div key={emoji.id} className="search-emoji">
<img src={`/api/emojis/${emoji.name}`} alt={emoji.name} className="emojis-emoji" />
<span>:{emoji.name}:</span>
<button
type="button"
onClick={() => {
setReplyContent(
replyContent.split(" ").slice(0, -1).join(" ") + " :" + emoji.name + ": "
);
setSearchedEmojis([]);
ref.current?.focus();
}}
>
Add Emoji
</button>
</div>
))}
</div>
)}
<button onClick={() => setReply(false)}>Cancel</button>
</form>
)}
{message.has_replies == true && (
<div className="message-replies">
{message.replies.map((reply) => (
<MessageComponent message={reply} user={user} channel={channel} key={reply.id} />
))}
</div>
)}
{!channel && (
<p>in: <Link to={`/c/${message.channel_name}`}>{message.channel_name}</Link></p>
)}
</div>
);
}

View file

@ -34,14 +34,6 @@ export default function ChannelPage({socket}: {socket: WebSocket}) {
});
}
function deleteMessage(message_id: number) {
axios
.post(`/api/channels/${name}/messages/delete`, { token, message_id })
.catch((err) => {
console.error(err.response);
});
}
function purgeChannel() {
if (!window.confirm(`Are you sure you want to purge ${channel?.name}?`)) {
return;
@ -261,7 +253,6 @@ export default function ChannelPage({socket}: {socket: WebSocket}) {
message={message}
user={user}
channel={channel}
deleteMessage={deleteMessage}
/>
))}
{messages.length > maxMessageToShown && (

View file

@ -165,7 +165,6 @@ export default function Home({socket}: {socket: WebSocket}) {
message={message}
user={user}
channel={undefined}
deleteMessage={undefined}
/>
))}
</div>

View file

@ -1,7 +1,12 @@
.message {
width: 95%;
width: 97%;
border: 1px solid #270722;
padding: 10px;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: start;
gap: 5px;
}
.message-user-pfp {
@ -37,6 +42,15 @@
max-height: 1em;
}
.message-replies {
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
gap: 10px;
}
@media (prefers-color-scheme: dark) {
.message {
border: 1px solid #a678af;

View file

@ -34,6 +34,8 @@ export type Message = {
channel_id: number
channel_name: string
mentions: Mentions
replies: Messages
has_replies: boolean
}
export type Messages = Message[]