From cc4812ef3cbd04bdd8367c7b03f984520a279fa5 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 15 Oct 2025 21:01:04 +0200 Subject: [PATCH 1/4] Added state structure #19 --- src/app/chat/[id]/page.tsx | 41 ++++++---------------- src/app/chat/page.tsx | 47 +++---------------------- src/app/server/[ip]/page.tsx | 31 ++++++----------- src/components/MessageBox.tsx | 4 +-- src/components/Sidebar.tsx | 65 ++++++++++++++--------------------- src/hooks/use-app.ts | 25 ++++++++++++++ src/types/app.ts | 20 +++++++++++ 7 files changed, 98 insertions(+), 135 deletions(-) create mode 100644 src/hooks/use-app.ts create mode 100644 src/types/app.ts diff --git a/src/app/chat/[id]/page.tsx b/src/app/chat/[id]/page.tsx index 8af22d6..6ad5435 100644 --- a/src/app/chat/[id]/page.tsx +++ b/src/app/chat/[id]/page.tsx @@ -1,49 +1,30 @@ "use client"; -import { useRef, useState } from "react"; import MessageBox from "@/components/MessageBox"; import AppSidebar from "@/components/Sidebar"; -import { Message } from "@/types/types"; -import { UserProfile } from "@/hooks/get-user"; -import { StringMap } from "@/types/typeUtils"; import { useParams } from "next/navigation"; -import { useMessages } from "@/hooks/use-messages"; +import useApp from "@/hooks/use-app"; export default function DMs() { const { id } = useParams<{ id: string }>(); - const wsRef = useRef(null); - const messages = useMessages((s) => s.messages); - const addMessage = useMessages((s) => s.addMessage); - const [userList, setUserList] = useState>({}); - const [user, setUser] = useState(); - const [sidebarOpen, setSidebarOpen] = useState(false); + const app = useApp(); return ( - + setSidebarOpen(true)} - channelName={userList[id]?.display_name || id} - userList={userList} - setUserList={setUserList} - messages={messages + toggleSidebar={() => app.setSidebarOpen(true)} + channelName={app.profiles[id]?.display_name || id} + userList={app.profiles} + setUserList={app.setProfiles} + messages={app.messages .filter( (m) => - (m.channel_id === id && m.from === user?.id) || - (m.from === id && m.channel_id === user?.id) + (m.channel_id === id && m.from === app.profile?.id) || + (m.from === id && m.channel_id === app.profile?.id) ) .toReversed()} sendMessage={(message) => { - wsRef.current?.send( + app.node.current?.send( JSON.stringify({ type: "send_message", params: { diff --git a/src/app/chat/page.tsx b/src/app/chat/page.tsx index 31af79b..546180b 100644 --- a/src/app/chat/page.tsx +++ b/src/app/chat/page.tsx @@ -1,59 +1,20 @@ "use client"; -import { useRef, useState } from "react"; import AppSidebar from "@/components/Sidebar"; -import { UserProfile } from "@/hooks/get-user"; -import { StringMap } from "@/types/typeUtils"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import ProfilePicture from "@/components/ProfilePicture"; -import { Message } from "@/types/types"; -import { useMessages } from "@/hooks/use-messages"; +import useApp from "@/hooks/use-app"; export default function Chat() { - const wsRef = useRef(null); - const [userList, setUserList] = useState>({}); - const messages = useMessages((s) => s.messages); - const addMessage = useMessages((s) => s.addMessage); - const [sidebarOpen, setSidebarOpen] = useState(true); + const app = useApp(); return ( - + Direct Messages - - {/* */} - + ); } - -function DMItem({ - name, - avatar, - status, -}: { - name: string; - avatar: string; - status: string; -}) { - return ( - - -
- {name} - {status} -
-
- ); -} diff --git a/src/app/server/[ip]/page.tsx b/src/app/server/[ip]/page.tsx index d438230..037f0ea 100644 --- a/src/app/server/[ip]/page.tsx +++ b/src/app/server/[ip]/page.tsx @@ -9,42 +9,31 @@ import { UserProfile } from "@/hooks/get-user"; import { StringMap } from "@/types/typeUtils"; import auth from "@/lib/auth"; import { useMessages } from "@/hooks/use-messages"; +import useApp from "@/hooks/use-app"; export default function Server() { const { ip } = useParams<{ ip: string }>(); - const wsServerRef = useRef(null); - const wsNodeRef = useRef(null); + const serverRef = useRef(null); const [server, setServer] = useState(); - const [userList, setUserList] = useState>({}); - const messages = useMessages((s) => s.messages); - const addMessage = useMessages((s) => s.addMessage); - const [sidebarOpen, setSidebarOpen] = useState(false); + const app = useApp(); useEffect(() => { if (!ip) return; - auth(ip, wsServerRef, setServer, addMessage); + auth(ip, serverRef, setServer, app.addMessage); }, [ip]); return ( - {}} - userList={userList} - setUserList={setUserList} - server={server} - > + setSidebarOpen(true)} + toggleSidebar={() => app.setSidebarOpen(true)} channelName="General" - userList={userList} - setUserList={setUserList} - messages={messages + userList={app.profiles} + setUserList={app.setProfiles} + messages={app.messages .filter((m) => m.channel_id === "general") .toReversed()} sendMessage={(message) => { - wsServerRef.current?.send( + serverRef.current?.send( JSON.stringify({ type: "send_message", params: { diff --git a/src/components/MessageBox.tsx b/src/components/MessageBox.tsx index 20ad876..0178fd1 100644 --- a/src/components/MessageBox.tsx +++ b/src/components/MessageBox.tsx @@ -156,7 +156,7 @@ export default function MessageBox({ const [text, setText] = useState(""); return ( -
+
{channelName && (
@@ -165,7 +165,7 @@ export default function MessageBox({
)} -
+
{messages.map((msg) => ( ; - addMessage: (msg: Message) => void; - messages?: Message[]; onNewMessage?: (msg: Message) => void; - userList: StringMap; - setUserList: React.Dispatch>>; chatWith?: string; children?: React.ReactNode; - setUser?: React.Dispatch>; server?: Server | undefined; - open: boolean; - setOpen: React.Dispatch>; + app: App; }>) { const [servers, setServers] = useState<[string, string][]>([]); const [newServer, setNewServer] = useState({ name: "", ip: "" }); const router = useRouter(); - const user = useUser(); - const [userList, setUserList] = useState>({}); const dms = Array.from( new Set( - messages + app.messages ?.flatMap((item) => [item.from, item.channel_id]) - .filter((item) => item !== user?.id) || [] + .filter((item) => item !== app.profile?.id) || [] ) ); @@ -82,17 +68,16 @@ export default function AppSidebar({ }, []); useEffect(() => { - if (!user?.node_address) return; - if (setUser) setUser(user); + if (!app.profile?.node_address) return; auth( - user?.node_address, - wsRef, + app.profile?.node_address, + app.node, () => {}, - addMessage, + app.addMessage, (m) => { - if (m.from !== chatWith && m.from !== user.id) + if (m.from !== chatWith && m.from !== app.profile?.id) toast( - `New message from ${userList[m.from]?.display_name || m.from}`, + `New message from ${app.profiles[m.from]?.display_name || m.from}`, { description: m.contents.slice(0, 80), action: { @@ -105,13 +90,13 @@ export default function AppSidebar({ if (onNewMessage) onNewMessage(m); }, - messages ? messages[messages.length - 1]?.id : undefined + app.messages ? app.messages[app.messages.length - 1]?.id : undefined ); - setUserList((prev) => { - prev[user.id] = user; + app.setProfiles((prev) => { + if (app.profile) prev[app.profile.id] = app.profile; return prev; }); - }, [chatWith, user]); + }, [chatWith, app.profile]); const handleAddServer = () => { if (!newServer.ip) return; @@ -132,7 +117,7 @@ export default function AppSidebar({
@@ -250,12 +235,14 @@ export default function AppSidebar({ {dms.map((id) => ( @@ -267,19 +254,19 @@ export default function AppSidebar({
- {open ? ( + {app.sidebarOpen ? (
setOpen(false)} + onClick={() => app.setSidebarOpen(false)} >
{children} diff --git a/src/hooks/use-app.ts b/src/hooks/use-app.ts new file mode 100644 index 0000000..801203e --- /dev/null +++ b/src/hooks/use-app.ts @@ -0,0 +1,25 @@ +import App from "@/types/app"; +import { useRef, useState } from "react"; +import { useMessages } from "./use-messages"; +import { StringMap } from "@/types/typeUtils"; +import useUser, { UserProfile } from "./get-user"; + +export default function useApp(): App { + const node = useRef(null); + const [profiles, setProfiles] = useState>({}); + const messages = useMessages((s) => s.messages); + const addMessage = useMessages((s) => s.addMessage); + const [sidebarOpen, setSidebarOpen] = useState(true); + const profile = useUser(); + + return { + node, + profile, + profiles, + setProfiles, + messages, + addMessage, + sidebarOpen, + setSidebarOpen, + }; +} diff --git a/src/types/app.ts b/src/types/app.ts new file mode 100644 index 0000000..3ddf70b --- /dev/null +++ b/src/types/app.ts @@ -0,0 +1,20 @@ +import { Dispatch, RefObject, SetStateAction } from "react"; +import { StringMap } from "./typeUtils"; +import { UserProfile } from "@/hooks/get-user"; +import { Message } from "./types"; + +export type SetState = Dispatch>; + +export default interface App { + node: RefObject; + profile: UserProfile | null; + + profiles: StringMap; + setProfiles: SetState>; + + messages: Message[]; + addMessage: (v: Message) => void; + + sidebarOpen: boolean; + setSidebarOpen: SetState; +} From e7293aa6251e4501f2d9a5525a4b75adda6b2cec Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 15 Oct 2025 21:20:15 +0200 Subject: [PATCH 2/4] Implemented state structure #19 --- src/app/chat/[id]/page.tsx | 4 +-- src/app/server/[ip]/page.tsx | 4 +-- src/components/MessageBox.tsx | 59 ++++++++++++++--------------------- src/components/Sidebar.tsx | 4 +-- 4 files changed, 27 insertions(+), 44 deletions(-) diff --git a/src/app/chat/[id]/page.tsx b/src/app/chat/[id]/page.tsx index 6ad5435..8ed4e7b 100644 --- a/src/app/chat/[id]/page.tsx +++ b/src/app/chat/[id]/page.tsx @@ -12,10 +12,8 @@ export default function DMs() { return ( app.setSidebarOpen(true)} + app={app} channelName={app.profiles[id]?.display_name || id} - userList={app.profiles} - setUserList={app.setProfiles} messages={app.messages .filter( (m) => diff --git a/src/app/server/[ip]/page.tsx b/src/app/server/[ip]/page.tsx index 037f0ea..82a99b2 100644 --- a/src/app/server/[ip]/page.tsx +++ b/src/app/server/[ip]/page.tsx @@ -25,10 +25,8 @@ export default function Server() { return ( app.setSidebarOpen(true)} + app={app} channelName="General" - userList={app.profiles} - setUserList={app.setProfiles} messages={app.messages .filter((m) => m.channel_id === "general") .toReversed()} diff --git a/src/components/MessageBox.tsx b/src/components/MessageBox.tsx index 0178fd1..e95a8e8 100644 --- a/src/components/MessageBox.tsx +++ b/src/components/MessageBox.tsx @@ -10,53 +10,46 @@ import { DropdownMenuContent, DropdownMenuTrigger, } from "./ui/dropdown-menu"; -import { ChevronLeft, ChevronLeftIcon, SmilePlusIcon } from "lucide-react"; -import { useEffect, useRef, useState } from "react"; +import { ChevronLeftIcon, SmilePlusIcon } from "lucide-react"; +import { useEffect, useState } from "react"; import { format, isToday, isYesterday } from "date-fns"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; -import { NumberMap, StringMap } from "@/types/typeUtils"; import { UserProfile } from "@/hooks/get-user"; import axios from "axios"; import ProfilePicture from "./ProfilePicture"; +import { ScrollArea } from "./ui/scroll-area"; +import App from "@/types/app"; -function MessageContainer({ - message, - userList, - setUserList, -}: { - message: Message; - userList: StringMap; - setUserList: React.Dispatch>>; -}) { +function MessageContainer({ message, app }: { message: Message; app: App }) { useEffect(() => { axios .get("/api/profile/", { params: { id: message.from } }) .then((res) => - setUserList((prev) => ({ + app.setProfiles((prev) => ({ ...prev, [message.from]: res.data as UserProfile, })) ) .catch(() => {}); - }, [message.from, setUserList]); + }, [message.from]); return (
{/*
*/}
- {userList[message.from] - ? userList[message.from].display_name + {app.profiles[message.from] + ? app.profiles[message.from].display_name : message.from}

@@ -139,19 +132,15 @@ function MessageContainer({ } export default function MessageBox({ - messages, - userList, - setUserList, + app, sendMessage, channelName, - toggleSidebar, + messages, }: { - messages: Message[]; - userList: NumberMap; - setUserList: React.Dispatch>>; + app: App; sendMessage: (m: string) => void; channelName?: string; - toggleSidebar: () => void; + messages: Message[]; }) { const [text, setText] = useState(""); @@ -159,22 +148,20 @@ export default function MessageBox({

{channelName && (
- + app.setSidebarOpen(true)} + className="cursor-pointer flex" + > {channelName}
)} -
+ {messages.map((msg) => ( - + ))} -
+