From dfb6639ecfb4dca7359b57da063967d4542df848 Mon Sep 17 00:00:00 2001 From: Lukian Date: Fri, 4 Apr 2025 21:31:29 +0200 Subject: [PATCH] add: changed homepage to display most active channels --- back/api/activechannels.js | 13 ++++++++ back/api/channels.js | 1 - back/api/searchchannels.js | 14 +++++++++ back/libs/mysql.js | 42 ++++++++++++++++++++++++++ front/src/main.tsx | 2 ++ front/src/pages/ChannelPage.tsx | 2 +- front/src/pages/ChannelsPage.tsx | 52 ++++++++++++++++++++++++++++++++ front/src/pages/Home.tsx | 52 ++++++++++++++++++++++++++------ front/src/pages/UserPage.tsx | 2 +- front/src/types.tsx | 13 +++++++- 10 files changed, 179 insertions(+), 14 deletions(-) create mode 100644 back/api/activechannels.js create mode 100644 back/api/searchchannels.js create mode 100644 front/src/pages/ChannelsPage.tsx diff --git a/back/api/activechannels.js b/back/api/activechannels.js new file mode 100644 index 0000000..567fea6 --- /dev/null +++ b/back/api/activechannels.js @@ -0,0 +1,13 @@ +const express = require('express'); +const { getConnection, getActiveChannels } = require('../libs/mysql'); + +const router = express.Router(); + +router.get('/', async (req, res) => { + const connection = await getConnection(); + const channels = await getActiveChannels(connection); + connection.end(); + res.send(channels); +}); + +module.exports = router; \ No newline at end of file diff --git a/back/api/channels.js b/back/api/channels.js index c91da4f..6fd865f 100644 --- a/back/api/channels.js +++ b/back/api/channels.js @@ -1,5 +1,4 @@ const express = require('express'); -const jwt = require('jsonwebtoken'); const { getConnection, getChannels, getChannel, addChannel, getMessages, getMessage, addMessage, deleteMessage, getLastMessages } = require('../libs/mysql'); const { checkAuth } = require('../libs/middlewares'); diff --git a/back/api/searchchannels.js b/back/api/searchchannels.js new file mode 100644 index 0000000..0be2545 --- /dev/null +++ b/back/api/searchchannels.js @@ -0,0 +1,14 @@ +const express = require('express'); +const { getConnection, searchChannels } = require('../libs/mysql'); + +const router = express.Router(); + +router.get('/', async (req, res) => { + const { search } = req.query; + const connection = await getConnection(); + const channels = await searchChannels(connection, search); + connection.end(); + res.send(channels); +}); + +module.exports = router; \ No newline at end of file diff --git a/back/libs/mysql.js b/back/libs/mysql.js index b6d67e6..fd6cb48 100644 --- a/back/libs/mysql.js +++ b/back/libs/mysql.js @@ -88,6 +88,46 @@ function getChannels(connection) { }); } +function getActiveChannels(connection) { + return new Promise((resolve, reject) => { + connection.query( + `SELECT channels.id, name, description, owner_id, username AS owner_username, count(*) AS message_count + FROM messages + JOIN channels ON messages.channel_id = channels.id + JOIN users ON messages.user_id = users.id + WHERE date > (SELECT max(date) FROM messages) - 7 * 24 * 60 * 60 + GROUP BY channel_id + ORDER BY count(*) DESC + LIMIT 5;`, + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + +function searchChannels(connection, search) { + 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 LIKE ? + LIMIT 5`, + [`%${search}%`], // Use parameterized query + (error, result) => { + if (error) { + reject(new Error(error)); + } + resolve(result); + } + ); + }); +} + function getChannel(connection, name) { return new Promise((resolve, reject) => { connection.query( @@ -215,6 +255,8 @@ module.exports = { addUser, getUserLastMessages, getChannels, + getActiveChannels, + searchChannels, getChannel, addChannel, getMessages, diff --git a/front/src/main.tsx b/front/src/main.tsx index a424e72..44555dd 100644 --- a/front/src/main.tsx +++ b/front/src/main.tsx @@ -8,6 +8,7 @@ import UserPage from './pages/UserPage' import Login from './pages/Login' import Register from './pages/Register' import CreateChannel from './pages/CreateChannel' +import ChannelsPage from './pages/ChannelsPage' createRoot(document.getElementById('root')!).render( @@ -18,6 +19,7 @@ createRoot(document.getElementById('root')!).render( } /> } /> } /> + } /> ) diff --git a/front/src/pages/ChannelPage.tsx b/front/src/pages/ChannelPage.tsx index 9c8f37b..886727e 100644 --- a/front/src/pages/ChannelPage.tsx +++ b/front/src/pages/ChannelPage.tsx @@ -70,7 +70,7 @@ export default function ChannelPage() { .get(`/api/channels/${name}/messages`).then((res) => { setMessages(res.data) }) - }, 5000) + }, 2000) return () => { clearInterval(id) } }, []) diff --git a/front/src/pages/ChannelsPage.tsx b/front/src/pages/ChannelsPage.tsx new file mode 100644 index 0000000..222a88a --- /dev/null +++ b/front/src/pages/ChannelsPage.tsx @@ -0,0 +1,52 @@ +import { useState, useEffect } from "react" +import { Link } from "react-router-dom" +import { Channels } from "../types" +import axios from "axios" + +export default function ChannelsPage() { + const [channels, setChannels] = useState(); + const [search, setSearch] = useState(""); + + useEffect(() => { + axios + .get("/api/channels") + .then((res) => { + setChannels(res.data) + }) + .catch((err) => { + console.error(err.response) + }) + }, []) + + useEffect(() => { + const id = setInterval(() => { + axios + .get("/api/channels").then((res) => { + setChannels(res.data) + } + ) + }, 2000) + + return () => { clearInterval(id) } + }, []) + + return ( +
+ Home +

Channels

+ setSearch(e.target.value)} + /> +
    + {channels?.filter((channel) => channel.name.toLowerCase().includes(search.toLowerCase())).map((channel) => ( +
  • + {channel.name} +
  • + ))} +
+
+ ) +} \ No newline at end of file diff --git a/front/src/pages/Home.tsx b/front/src/pages/Home.tsx index 899f0ec..2ae2b58 100644 --- a/front/src/pages/Home.tsx +++ b/front/src/pages/Home.tsx @@ -1,12 +1,14 @@ import { useState, useEffect } from "react" import { Link } from "react-router-dom" -import { Channels, User, Messages } from "../types" +import { Channels, RecentChannels, User, Messages } from "../types" import axios from "axios" export default function Home() { const [user, setUser] = useState(); - const [channels, setChannels] = useState(); + const [channels, setChannels] = useState(); const [messages , setMessages] = useState(); + const [searchedChannels, setSearchedChannels] = useState([]); + const [search, setSearch] = useState(""); useEffect(() => { const token = localStorage.getItem("token") @@ -24,7 +26,7 @@ export default function Home() { } axios - .get("/api/channels") + .get("/api/activechannels") .then((res) => { setChannels(res.data) }) @@ -45,22 +47,37 @@ export default function Home() { useEffect(() => { const id = setInterval(() => { + axios + .get("/api/activechannels").then((res) => { + setChannels(res.data) + } + ) + axios .get("/api/lastmessages").then((res) => { setMessages(res.data) } ) - axios - .get("/api/channels").then((res) => { - setChannels(res.data) - } - ) - }, 5000) + }, 2000) return () => { clearInterval(id) } }, []) + useEffect(() => { + if (!search) { + setSearchedChannels([]) + return + } + axios + .get(`/api/searchchannels?search=${search}`).then((res) => { + setSearchedChannels(res.data) + } + ).catch((err) => { + console.error(err.response) + }) + }, [search]) + return (

Home

@@ -79,7 +96,7 @@ export default function Home() { Register
)} -

Channels

+

Recent active channels

    {channels?.map((channel) => (
  • @@ -87,6 +104,21 @@ export default function Home() {
  • ))}
+

Search channels

+ setSearch(e.target.value)} + /> +
    + {searchedChannels?.map((channel) => ( +
  • + {channel.name} +
  • + ))} +
+ All channels

Last messages

    {messages?.map((message) => ( diff --git a/front/src/pages/UserPage.tsx b/front/src/pages/UserPage.tsx index 2d467ee..33aea66 100644 --- a/front/src/pages/UserPage.tsx +++ b/front/src/pages/UserPage.tsx @@ -24,7 +24,7 @@ export default function UserPage() { .get(`/api/users/${username}/lastmessages`).then((res) => { setMessages(res.data) }) - }, 5000) + }, 2000) return () => { clearInterval(id) } }, []) diff --git a/front/src/types.tsx b/front/src/types.tsx index dcbc2c6..bc772d3 100644 --- a/front/src/types.tsx +++ b/front/src/types.tsx @@ -1,4 +1,4 @@ -export type Channel ={ +export type Channel = { id: number, name: string description: string @@ -8,6 +8,17 @@ export type Channel ={ export type Channels = Channel[] +export type RecentChannel = { + id: number, + name: string + description: string + owner_id: number + owner_username: string + message_count: number +} + +export type RecentChannels = RecentChannel[] + export type Message = { id: number, user_id: number,