From 9b8e83d2e2c57fde0d61f28865862aabac5851fa Mon Sep 17 00:00:00 2001 From: Izan Gil <66965250+SrIzan10@users.noreply.github.com> Date: Wed, 2 Jul 2025 13:16:47 +0200 Subject: [PATCH] feat: add message history --- apps/chat/src/index.ts | 45 ++++++++++++++----- .../components/app/ChatPanel/ChatPanel.tsx | 33 +++++++------- .../src/components/app/ChatPanel/message.tsx | 18 ++++++++ apps/web/src/lib/auth/actions.ts | 2 +- apps/web/src/lib/auth/validate.ts | 2 +- .../src/lib/instrumentation/writeSessions.ts | 2 +- 6 files changed, 70 insertions(+), 32 deletions(-) create mode 100644 apps/web/src/components/app/ChatPanel/message.tsx diff --git a/apps/chat/src/index.ts b/apps/chat/src/index.ts index e42bb6d..6df82d7 100644 --- a/apps/chat/src/index.ts +++ b/apps/chat/src/index.ts @@ -5,8 +5,10 @@ import { readFile } from 'node:fs/promises'; import { lucia } from '@hctv/auth'; import { getCookie } from 'hono/cookie'; import { getPersonalChannel } from './utils/personalChannel.js'; -import { prisma } from '@hctv/db'; +import { getRedisConnection, prisma } from '@hctv/db'; +const MESSAGE_HISTORY_SIZE = 15; +const MESSAGE_TTL = 60 * 60 * 24; const threed = await readFile('./src/3d.txt', 'utf-8'); const app = new Hono(); @@ -54,6 +56,19 @@ app.get( ws.raw.personalChannel = personalChannel; } + const redis = getRedisConnection(); + const channelKey = `chat:history:${username}`; + const messages = await redis.zrange(channelKey, 0, MESSAGE_HISTORY_SIZE - 1); + + if (messages.length > 0) { + ws.send( + JSON.stringify({ + type: 'history', + messages: messages.map((msg) => JSON.parse(msg)), + }) + ); + } + await prisma.streamInfo.update({ where: { username, @@ -98,19 +113,27 @@ app.get( return; } if (msg.type === 'message') { + const msgObj = { + user: { + id: ws.user.id, + username: ws.personalChannel.name, + pfpUrl: ws.user.pfpUrl, + }, + message: msg.message, + }; + const msgStr = JSON.stringify(msgObj); + + const redis = getRedisConnection(); + const channelKey = `chat:history:${ws.targetUsername}`; + + redis.zadd(channelKey, Date.now(), msgStr); + redis.zremrangebyrank(channelKey, 0, -MESSAGE_HISTORY_SIZE - 1); + redis.expire(channelKey, MESSAGE_TTL); + ws.wss.clients.forEach((c) => { const client = c as ModifiedWebSocket; if (client.readyState === client.OPEN && client.targetUsername === ws.targetUsername) { - c.send( - JSON.stringify({ - user: { - id: ws.user.id, - username: ws.personalChannel.name, - pfpUrl: ws.user.pfpUrl, - }, - message: msg.message, - }) - ); + c.send(msgStr); } }); } diff --git a/apps/web/src/components/app/ChatPanel/ChatPanel.tsx b/apps/web/src/components/app/ChatPanel/ChatPanel.tsx index a819832..f88f7d5 100644 --- a/apps/web/src/components/app/ChatPanel/ChatPanel.tsx +++ b/apps/web/src/components/app/ChatPanel/ChatPanel.tsx @@ -5,6 +5,7 @@ import { Send } from 'lucide-react'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { useParams } from 'next/navigation'; +import { Message } from './message'; export default function ChatPanel() { const { username } = useParams(); @@ -28,7 +29,12 @@ export default function ChatPanel() { socket.onmessage = (event) => { try { const data = JSON.parse(event.data); - if (data.type === 'ping' || data.type === 'pong' || !data.user) return; + if (data.type === 'ping' || data.type === 'pong') return; + if (data.type === 'history') { + const messages = data.messages as ChatMessage[]; + setChatMessages((prev) => [...prev, ...messages, { message: 'Welcome to the chat!', type: 'systemMsg' }]); + return; + } setChatMessages((prev) => [...prev, data]); } catch (e) { console.log('Received message confirmation:', event.data); @@ -86,17 +92,7 @@ export default function ChatPanel() {
+ {user?.username} + {message} +
+