Added state structure #19
This commit is contained in:
+11
-30
@@ -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<WebSocket | null>(null);
|
||||
const messages = useMessages((s) => s.messages);
|
||||
const addMessage = useMessages((s) => s.addMessage);
|
||||
const [userList, setUserList] = useState<StringMap<UserProfile>>({});
|
||||
const [user, setUser] = useState<UserProfile | undefined>();
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
const app = useApp();
|
||||
|
||||
return (
|
||||
<AppSidebar
|
||||
open={sidebarOpen}
|
||||
setOpen={setSidebarOpen}
|
||||
addMessage={addMessage}
|
||||
wsRef={wsRef}
|
||||
userList={userList}
|
||||
setUserList={setUserList}
|
||||
chatWith={id}
|
||||
setUser={setUser}
|
||||
messages={messages}
|
||||
>
|
||||
<AppSidebar app={app}>
|
||||
<MessageBox
|
||||
toggleSidebar={() => 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: {
|
||||
|
||||
+4
-43
@@ -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<WebSocket | null>(null);
|
||||
const [userList, setUserList] = useState<StringMap<UserProfile>>({});
|
||||
const messages = useMessages((s) => s.messages);
|
||||
const addMessage = useMessages((s) => s.addMessage);
|
||||
const [sidebarOpen, setSidebarOpen] = useState(true);
|
||||
const app = useApp();
|
||||
|
||||
return (
|
||||
<AppSidebar
|
||||
open={sidebarOpen}
|
||||
setOpen={setSidebarOpen}
|
||||
wsRef={wsRef}
|
||||
userList={userList}
|
||||
setUserList={setUserList}
|
||||
addMessage={addMessage}
|
||||
messages={messages}
|
||||
>
|
||||
<AppSidebar app={app}>
|
||||
<Card className="bg-background w-full">
|
||||
<CardHeader>
|
||||
<CardTitle>Direct Messages</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{/* <DMItem name="Alice" status="online" avatar="" /> */}
|
||||
</CardContent>
|
||||
<CardContent></CardContent>
|
||||
</Card>
|
||||
</AppSidebar>
|
||||
);
|
||||
}
|
||||
|
||||
function DMItem({
|
||||
name,
|
||||
avatar,
|
||||
status,
|
||||
}: {
|
||||
name: string;
|
||||
avatar: string;
|
||||
status: string;
|
||||
}) {
|
||||
return (
|
||||
<Card className="p-2 flex flex-row items-center gap-2 cursor-pointer hover:bg-accent">
|
||||
<ProfilePicture name={name} url={avatar} />
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium">{name}</span>
|
||||
<span className="text-xs text-muted-foreground">{status}</span>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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<WebSocket | null>(null);
|
||||
const wsNodeRef = useRef<WebSocket | null>(null);
|
||||
const serverRef = useRef<WebSocket | null>(null);
|
||||
const [server, setServer] = useState<undefined | ServerType>();
|
||||
const [userList, setUserList] = useState<StringMap<UserProfile>>({});
|
||||
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 (
|
||||
<AppSidebar
|
||||
open={sidebarOpen}
|
||||
setOpen={setSidebarOpen}
|
||||
wsRef={wsNodeRef}
|
||||
addMessage={() => {}}
|
||||
userList={userList}
|
||||
setUserList={setUserList}
|
||||
server={server}
|
||||
>
|
||||
<AppSidebar app={app} server={server}>
|
||||
<MessageBox
|
||||
toggleSidebar={() => 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: {
|
||||
|
||||
@@ -156,7 +156,7 @@ export default function MessageBox({
|
||||
const [text, setText] = useState("");
|
||||
|
||||
return (
|
||||
<div className="h-full w-full flex flex-col pb-5 pl-5 gap-5">
|
||||
<div className="h-full w-full max-h-svh flex flex-col pb-5 pl-5 gap-5">
|
||||
{channelName && (
|
||||
<header className="h-12 py-4 flex items-center border-b text-sm font-semibold">
|
||||
<span onClick={toggleSidebar} className="cursor-pointer flex">
|
||||
@@ -165,7 +165,7 @@ export default function MessageBox({
|
||||
</span>
|
||||
</header>
|
||||
)}
|
||||
<div className="w-full h-max flex-1 overflow-y-scroll flex gap-2 flex-col-reverse pr-5">
|
||||
<div className="w-full h-full flex-1 overflow-y-scroll flex gap-2 flex-col-reverse pr-5">
|
||||
{messages.map((msg) => (
|
||||
<MessageContainer
|
||||
key={msg.id}
|
||||
|
||||
+26
-39
@@ -26,53 +26,39 @@ import { Button } from "./ui/button";
|
||||
import { Card } from "./ui/card";
|
||||
import { ScrollArea } from "./ui/scroll-area";
|
||||
import { useRouter } from "next/navigation";
|
||||
import useUser, { UserProfile } from "@/hooks/get-user";
|
||||
import { UserProfile } from "@/hooks/get-user";
|
||||
import ProfilePicture from "./ProfilePicture";
|
||||
import auth from "@/lib/auth";
|
||||
import { toast } from "sonner";
|
||||
import { StringMap } from "@/types/typeUtils";
|
||||
import SettingsDialog from "./settings/SettingsDialog";
|
||||
import Link from "next/link";
|
||||
import axios from "axios";
|
||||
import { get, Response } from "@/lib/request";
|
||||
import { cn } from "@/lib/utils";
|
||||
import App from "@/types/app";
|
||||
|
||||
export default function AppSidebar({
|
||||
children,
|
||||
chatWith,
|
||||
wsRef,
|
||||
onNewMessage,
|
||||
setUser,
|
||||
server,
|
||||
messages,
|
||||
addMessage,
|
||||
open,
|
||||
setOpen,
|
||||
app,
|
||||
}: Readonly<{
|
||||
wsRef: React.RefObject<WebSocket | null>;
|
||||
addMessage: (msg: Message) => void;
|
||||
messages?: Message[];
|
||||
onNewMessage?: (msg: Message) => void;
|
||||
userList: StringMap<UserProfile>;
|
||||
setUserList: React.Dispatch<React.SetStateAction<StringMap<UserProfile>>>;
|
||||
chatWith?: string;
|
||||
children?: React.ReactNode;
|
||||
setUser?: React.Dispatch<React.SetStateAction<UserProfile | undefined>>;
|
||||
server?: Server | undefined;
|
||||
open: boolean;
|
||||
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
app: App;
|
||||
}>) {
|
||||
const [servers, setServers] = useState<[string, string][]>([]);
|
||||
const [newServer, setNewServer] = useState({ name: "", ip: "" });
|
||||
const router = useRouter();
|
||||
const user = useUser();
|
||||
const [userList, setUserList] = useState<StringMap<UserProfile>>({});
|
||||
|
||||
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({
|
||||
<div
|
||||
className={cn(
|
||||
"border-r transition-all duration-300 ease-in-out overflow-hidden flex",
|
||||
open ? "w-5xl md:w-md" : "w-0 md:w-md"
|
||||
app.sidebarOpen ? "w-5xl md:w-md" : "w-0 md:w-md"
|
||||
)}
|
||||
>
|
||||
<div className="w-16 flex flex-col items-center gap-4 py-4 bg-muted border-r">
|
||||
@@ -250,12 +235,14 @@ export default function AppSidebar({
|
||||
{dms.map((id) => (
|
||||
<DMItem
|
||||
name={
|
||||
getUser(id, userList, setUserList)?.display_name || id
|
||||
getUser(id, app.profiles, app.setProfiles)
|
||||
?.display_name || id
|
||||
}
|
||||
id={id}
|
||||
status="online"
|
||||
avatar={
|
||||
getUser(id, userList, setUserList)?.avatar_url || ""
|
||||
getUser(id, app.profiles, app.setProfiles)
|
||||
?.avatar_url || ""
|
||||
}
|
||||
key={id}
|
||||
/>
|
||||
@@ -267,19 +254,19 @@ export default function AppSidebar({
|
||||
|
||||
<footer className="mt-auto pb-3 pl-3 pr-3">
|
||||
<DMItem
|
||||
name={user?.display_name || "Loading.."}
|
||||
id={user?.id || "me"}
|
||||
avatar={user?.avatar_url || ""}
|
||||
name={app.profile?.display_name || "Loading.."}
|
||||
id={app.profile?.id || "me"}
|
||||
avatar={app.profile?.avatar_url || ""}
|
||||
status="online"
|
||||
settings
|
||||
/>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
{open ? (
|
||||
{app.sidebarOpen ? (
|
||||
<div
|
||||
className="w-screen h-full overflow-x-hidden"
|
||||
onClick={() => setOpen(false)}
|
||||
onClick={() => app.setSidebarOpen(false)}
|
||||
>
|
||||
<div className="overflow-x-hidden w-screen min-w-max flex-shrink-0">
|
||||
{children}
|
||||
|
||||
@@ -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<WebSocket | null>(null);
|
||||
const [profiles, setProfiles] = useState<StringMap<UserProfile>>({});
|
||||
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,
|
||||
};
|
||||
}
|
||||
@@ -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<T> = Dispatch<SetStateAction<T>>;
|
||||
|
||||
export default interface App {
|
||||
node: RefObject<WebSocket | null>;
|
||||
profile: UserProfile | null;
|
||||
|
||||
profiles: StringMap<UserProfile>;
|
||||
setProfiles: SetState<StringMap<UserProfile>>;
|
||||
|
||||
messages: Message[];
|
||||
addMessage: (v: Message) => void;
|
||||
|
||||
sidebarOpen: boolean;
|
||||
setSidebarOpen: SetState<boolean>;
|
||||
}
|
||||
Reference in New Issue
Block a user