From f92449c493763c50334f8406e1ae43faebdc2ac5 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Mon, 22 Dec 2025 19:55:05 +0100 Subject: [PATCH] Message chunk loading test --- src/app/auth/Login.tsx | 3 +- src/app/chat/[id]/page.tsx | 2 +- src/app/chat/page.tsx | 2 +- src/app/server/[ip]/page.tsx | 18 ++-- src/components/app/AppLayout.tsx | 2 +- src/components/app/SidebarDMs.tsx | 4 +- src/components/channel/ChannelView.tsx | 8 +- src/components/channel/PluginChannel.tsx | 2 +- src/hooks/use-app.ts | 9 +- src/hooks/use-messages.ts | 120 +++++------------------ src/lib/auth.ts | 32 +++--- src/types/app.ts | 6 +- 12 files changed, 61 insertions(+), 147 deletions(-) diff --git a/src/app/auth/Login.tsx b/src/app/auth/Login.tsx index b5a1f18..b5419d9 100644 --- a/src/app/auth/Login.tsx +++ b/src/app/auth/Login.tsx @@ -33,8 +33,7 @@ export default function Login() { message: "Login successful! Redirecting...", }); - // small delay to show feedback before redirect - setTimeout(() => router.push("/chat"), 800); + router.push("/chat"); } catch (err: any) { setFeedback({ kind: "error", diff --git a/src/app/chat/[id]/page.tsx b/src/app/chat/[id]/page.tsx index 84d1259..db6891e 100644 --- a/src/app/chat/[id]/page.tsx +++ b/src/app/chat/[id]/page.tsx @@ -37,7 +37,7 @@ export default function DMs() { app={app} channelName={target?.display_name || id} channelIcon={target?.avatar_url || "_"} - messages={app.messages.filter( + messages={app.privateMessages.filter( (m) => (m.channel_id === id && m.from === app.profile?.id) || (m.from === id && m.channel_id === app.profile?.id) diff --git a/src/app/chat/page.tsx b/src/app/chat/page.tsx index f6a639b..18b5c7c 100644 --- a/src/app/chat/page.tsx +++ b/src/app/chat/page.tsx @@ -52,7 +52,7 @@ export default function ChatHub() { channels: [], }, ]; - const recentActivity = app.messages.slice(0, 3); + const recentActivity = app.privateMessages.slice(0, 3); return ( diff --git a/src/app/server/[ip]/page.tsx b/src/app/server/[ip]/page.tsx index e10152d..fba3c96 100644 --- a/src/app/server/[ip]/page.tsx +++ b/src/app/server/[ip]/page.tsx @@ -6,7 +6,7 @@ import MessageBox from "@/components/channel/MessageBox"; import AppLayout from "@/components/app/AppLayout"; import { Server as ServerType } from "@/types/types"; import auth from "@/lib/auth"; -import { useServerMessages } from "@/hooks/use-messages"; +import useMessages from "@/hooks/use-messages"; import useApp from "@/hooks/use-app"; import { useEffectOnceWhenReady } from "@/hooks/use-once"; import ChannelView from "@/components/channel/ChannelView"; @@ -16,25 +16,19 @@ export default function Server() { const searchParams = useSearchParams(); const serverRef = useRef(null); const app = useApp(); - const { messages, addMessage, editMessage, deleteMessage } = - useServerMessages(); + const messageStore = useMessages(); useEffectOnceWhenReady( () => { if (!ip) return; - const msgs = messages[ip]; auth({ id: ip, wsRef: serverRef, setServer: app.setServer, - lastMessage: msgs ? msgs[msgs.length - 1]?.id : undefined, - - addMessage: (m) => addMessage(ip, m), - editMessage: (msg_id, contents) => editMessage(ip, msg_id, contents), - deleteMessage: (msg_id) => deleteMessage(ip, msg_id), + messageStore: messageStore, }); }, - [ip, messages], + [ip, messageStore.messages], [undefined, (v) => v] ); @@ -55,7 +49,9 @@ export default function Server() { m.channel_id === app.currentChannel?.id + )} ip={ip} serverRef={serverRef} /> diff --git a/src/components/app/AppLayout.tsx b/src/components/app/AppLayout.tsx index 4a2d628..e5edecd 100644 --- a/src/components/app/AppLayout.tsx +++ b/src/components/app/AppLayout.tsx @@ -50,7 +50,7 @@ export default function AppLayout({ if (onNewMessage) onNewMessage(m); }, - lastMessage: app.messages?.[app.messages.length - 1]?.id, + lastMessage: app.privateMessages?.[app.privateMessages.length - 1]?.id, addMessage: app.addMessage, deleteMessage: app.deleteMessage, diff --git a/src/components/app/SidebarDMs.tsx b/src/components/app/SidebarDMs.tsx index 148fe8e..90a41ab 100644 --- a/src/components/app/SidebarDMs.tsx +++ b/src/components/app/SidebarDMs.tsx @@ -24,7 +24,7 @@ export default function SidebarDMs({ app }: { app: App }) { await Promise.all( [ ...new Set( - app.messages + app.privateMessages.messages .sort((a, b) => a.timestamp - b.timestamp) .flatMap((m) => [m.from, m.channel_id]) .filter((id) => id !== app.profile?.id) @@ -44,7 +44,7 @@ export default function SidebarDMs({ app }: { app: App }) { ); }; load(); - }, [query, app.messages, app.profile]); + }, [query, app.privateMessages, app.profile]); return (
diff --git a/src/components/channel/ChannelView.tsx b/src/components/channel/ChannelView.tsx index 68d563c..ba3cc18 100644 --- a/src/components/channel/ChannelView.tsx +++ b/src/components/channel/ChannelView.tsx @@ -11,7 +11,7 @@ export default function ChannelView({ serverRef, }: { app: App; - messages: Record; + messages: Message[]; ip: string; serverRef: React.RefObject; }) { @@ -25,11 +25,7 @@ export default function ChannelView({ m.channel_id === app.currentChannel?.id - ) || [] - } + messages={messages} sendRequest={(req) => serverRef.current?.send(JSON.stringify(req))} /> ); diff --git a/src/components/channel/PluginChannel.tsx b/src/components/channel/PluginChannel.tsx index 62bb1b8..a1fead5 100644 --- a/src/components/channel/PluginChannel.tsx +++ b/src/components/channel/PluginChannel.tsx @@ -10,7 +10,7 @@ export default function PluginChannel({ serverRef, }: { app: App; - messages: Record; + messages: Message[]; ip: string; serverRef: React.RefObject; }) { diff --git a/src/hooks/use-app.ts b/src/hooks/use-app.ts index 840cafd..213de1d 100644 --- a/src/hooks/use-app.ts +++ b/src/hooks/use-app.ts @@ -1,6 +1,6 @@ import App from "@/types/app"; import { useEffect, useRef, useState } from "react"; -import { useMessages } from "./use-messages"; +import useMessages from "./use-messages"; import { StringMap } from "@/types/typeUtils"; import useUser, { UserProfile } from "./get-user"; import { useClientSettings } from "./use-settings"; @@ -12,7 +12,6 @@ import { toast } from "sonner"; export default function useApp(): App { const node = useRef(null); const [profiles, setProfiles] = useState>({}); - const { messages, addMessage, deleteMessage, editMessage } = useMessages(); const [sidebarOpen, setSidebarOpen] = useState(false); const profile = useUser(); const [clientSettings, setClientSettings] = useClientSettings(); @@ -20,6 +19,7 @@ export default function useApp(): App { const [servers, setServers] = useState>({}); const [currentChannel, setCurrentChannel] = useState(null); const [server, setServer] = useState(); + const privateMessages = useMessages(); useEffect(() => { const stored = Cookies.get("servers"); @@ -38,8 +38,7 @@ export default function useApp(): App { }, []); return { - editMessage, - deleteMessage, + privateMessages, currentChannel, setCurrentChannel, server, @@ -54,8 +53,6 @@ export default function useApp(): App { profile, profiles, setProfiles, - messages, - addMessage, sidebarOpen, setSidebarOpen, getUserById: async (id: string) => { diff --git a/src/hooks/use-messages.ts b/src/hooks/use-messages.ts index 7cf0712..883d84a 100644 --- a/src/hooks/use-messages.ts +++ b/src/hooks/use-messages.ts @@ -1,108 +1,32 @@ import { Message } from "@/types/types"; -import { create } from "zustand"; -import { persist } from "zustand/middleware"; +import { useState } from "react"; -interface MessageStore { +export interface MessageStore { messages: Message[]; setMessages: (msgs: Message[]) => void; addMessage: (msg: Message) => void; deleteMessage: (id: number) => void; editMessage: (id: number, contents: string) => void; clearMessages: () => void; + insertMessages: (msgs: Message[]) => void; } +export default function useMessages(): MessageStore { + const [messages, setMessages] = useState([]); -type ServerMessageStore = { - messages: Record; // server_id → messages - setMessages: (serverId: string, msgs: Message[]) => void; - addMessage: (serverId: string, msg: Message) => void; - deleteMessage: (serverId: string, id: number) => void; - editMessage: (serverId: string, id: number, contents: string) => void; - clearMessages: (serverId?: string) => void; -}; - -export const useMessages = create()( - persist( - (set) => ({ - messages: [], - setMessages: (msgs) => set({ messages: msgs }), - addMessage: (msg) => - set((state) => - state.messages.some((m) => m.id === msg.id) - ? state - : { messages: [...state.messages, msg] } - ), - deleteMessage: (id: number) => - set((state) => ({ - messages: state.messages.filter((m) => m.id !== id), - })), - editMessage: (id: number, contents) => - set((state) => ({ - messages: state.messages.map((m) => - m.id === id ? { ...m, contents } : m - ), - })), - clearMessages: () => set({ messages: [] }), - }), - { name: "chat-messages" } - ) -); - -export const useServerMessages = create()( - persist( - (set) => ({ - messages: {}, - - setMessages: (serverId, msgs) => - set((state) => ({ - messages: { ...state.messages, [serverId]: msgs }, - })), - - addMessage: (serverId, msg) => - set((state) => { - const serverMsgs = state.messages[serverId] || []; - - if (serverMsgs.some((m) => m.id === msg.id)) return state; - - return { - messages: { - ...state.messages, - [serverId]: [...serverMsgs, msg], - }, - }; - }), - - deleteMessage: (serverId, id) => - set((state) => ({ - messages: { - ...state.messages, - [serverId]: (state.messages[serverId] || []).filter( - (m) => m.id !== id - ), - }, - })), - - editMessage: (serverId, id, contents) => - set((state) => ({ - messages: { - ...state.messages, - [serverId]: (state.messages[serverId] || []).map((m) => - m.id === id ? { ...m, contents } : m - ), - }, - })), - - clearMessages: (serverId) => - set((state) => - serverId - ? { - messages: { - ...state.messages, - [serverId]: [], - }, - } - : { messages: {} } - ), - }), - { name: "server-messages" } - ) -); + return { + messages: messages, + setMessages: (msgs) => setMessages(msgs), + addMessage: (msg) => + setMessages((messages) => + messages.some((m) => m.id === msg.id) ? messages : [...messages, msg] + ), + deleteMessage: (id: number) => + setMessages((messages) => messages.filter((m) => m.id !== id)), + editMessage: (id: number, contents) => + setMessages((messages) => + messages.map((m) => (m.id === id ? { ...m, contents } : m)) + ), + clearMessages: () => setMessages([]), + insertMessages: (msgs) => setMessages((m) => [...msgs, ...m]), + }; +} diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 1dd0bc8..4cc6351 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -3,28 +3,22 @@ import axios from "axios"; import { Dispatch, RefObject, SetStateAction } from "react"; import { get } from "./request"; import { toast } from "sonner"; +import { MessageStore } from "@/hooks/use-messages"; type AuthParams = { id: string; wsRef: RefObject; setServer?: Dispatch>; onNewMessage?: (m: Message) => void; - lastMessage?: number; - - addMessage: (m: Message) => void; - deleteMessage: (id: number) => void; - editMessage: (id: number, contents: string) => void; + messageStore: MessageStore; }; export default async function auth({ id, wsRef, setServer, - addMessage, onNewMessage, - lastMessage, - deleteMessage, - editMessage, + messageStore, }: AuthParams) { console.log("Authenticating with server id:", id); @@ -64,7 +58,6 @@ export default async function auth({ JSON.stringify({ version: "0.0.1", auth_token: server_auth, - last_message: lastMessage || 0, }) ); name = data.name; @@ -73,20 +66,31 @@ export default async function auth({ switch (data.type) { case "authenticated": - data.params.messages.forEach(addMessage); + ws.send( + JSON.stringify({ + type: "load_chunk", + params: { + channel_id: "general", + chunk_id: 0, + }, + }) + ); break; case "message_create": if (onNewMessage) onNewMessage(data.params as Message); - addMessage(data.params as Message); + messageStore.addMessage(data.params as Message); break; case "message_delete": - deleteMessage(data.params.message_id); + messageStore.deleteMessage(data.params.message_id); break; case "message_update": - editMessage(data.params.message_id, data.params.contents); + messageStore.editMessage(data.params.message_id, data.params.contents); + break; + case "chunk": + messageStore.insertMessages(data.params); break; } }; diff --git a/src/types/app.ts b/src/types/app.ts index 87784da..fd685bd 100644 --- a/src/types/app.ts +++ b/src/types/app.ts @@ -3,6 +3,7 @@ import { StringMap } from "./typeUtils"; import { UserProfile } from "@/hooks/get-user"; import { Channel, Message, Server } from "./types"; import { ClientSettings } from "./settings"; +import { MessageStore } from "@/hooks/use-messages"; export type SetState = Dispatch>; @@ -22,10 +23,7 @@ export default interface App { profiles: StringMap; setProfiles: SetState>; - messages: Message[]; - addMessage: (v: Message) => void; - deleteMessage: (id: number) => void; - editMessage: (id: number, v: string) => void; + privateMessages: MessageStore; sidebarOpen: boolean; setSidebarOpen: SetState;