mirror of
https://github.com/SrIzan10/hctv.git
synced 2026-06-06 00:56:56 +00:00
feat: add message history
This commit is contained in:
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
<div ref={scrollRef} className="flex-1 p-4 overflow-y-auto flex flex-col">
|
||||
<div className="space-y-4 flex-1">
|
||||
{chatMessages.map((msg, i) => (
|
||||
<div key={i} className="flex space-x-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="font-bold shrink-0">{msg.user.username}</div>
|
||||
</div>
|
||||
<div
|
||||
lang="en"
|
||||
className="max-w-[calc(100%-4rem)] break-all whitespace-pre-wrap hyphens-auto"
|
||||
>
|
||||
{msg.message}
|
||||
</div>
|
||||
</div>
|
||||
<Message key={i} user={msg.user} message={msg.message} type={msg.type} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
@@ -122,13 +118,14 @@ export default function ChatPanel() {
|
||||
);
|
||||
}
|
||||
|
||||
interface User {
|
||||
export interface ChatMessage {
|
||||
user?: User;
|
||||
message: string;
|
||||
type: 'message' | 'systemMsg';
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id: string;
|
||||
username: string;
|
||||
pfpUrl: string;
|
||||
}
|
||||
|
||||
interface ChatMessage {
|
||||
user: User;
|
||||
message: string;
|
||||
}
|
||||
|
||||
18
apps/web/src/components/app/ChatPanel/message.tsx
Normal file
18
apps/web/src/components/app/ChatPanel/message.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ChatMessage } from "./ChatPanel";
|
||||
|
||||
export function Message({ user, message, type }: ChatMessage) {
|
||||
return (
|
||||
<div className="flex">
|
||||
<div
|
||||
lang="en"
|
||||
className={cn("max-w-full break-all whitespace-pre-wrap hyphens-auto", type === "systemMsg" ? "text-muted-foreground" : "")}
|
||||
>
|
||||
<p>
|
||||
<span className="font-bold mr-2">{user?.username}</span>
|
||||
<span>{message}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import { cookies } from 'next/headers';
|
||||
import { lucia } from '@hctv/auth';
|
||||
import { validateRequest } from '@/lib/auth/validate';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { getRedisConnection } from '../services/redis';
|
||||
import { getRedisConnection } from '@hctv/db';
|
||||
|
||||
export async function logout() {
|
||||
const { session } = await validateRequest();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { cookies } from "next/headers";
|
||||
import { cache } from "react";
|
||||
import { lucia } from '@hctv/auth';
|
||||
import { getRedisConnection } from "../services/redis";
|
||||
import { getRedisConnection } from "@hctv/db";
|
||||
|
||||
export const validateRequest = cache(async () => {
|
||||
const sessionId = (await cookies()).get(lucia.sessionCookieName)?.value ?? null;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { prisma } from "@hctv/db";
|
||||
import { getRedisConnection } from "../services/redis";
|
||||
import { getRedisConnection } from "@hctv/db";
|
||||
|
||||
export default async function writeSessions() {
|
||||
const sessions = await prisma.session.findMany();
|
||||
|
||||
Reference in New Issue
Block a user