diff --git a/src/app/chat/[id]/page.tsx b/src/app/chat/[id]/page.tsx index 344334e..1fc0bb8 100644 --- a/src/app/chat/[id]/page.tsx +++ b/src/app/chat/[id]/page.tsx @@ -5,7 +5,7 @@ import MessageBox from "@/components/MessageBox"; import AppSidebar from "@/components/Sidebar"; import { Message } from "@/types/types"; import useUser, { User } from "@/hooks/get-user"; -import { NumberMap } from "@/types/typeUtils"; +import { NumberMap, StringMap } from "@/types/typeUtils"; import auth, { makeAddress } from "@/lib/auth"; import { useParams } from "next/navigation"; @@ -14,36 +14,37 @@ import { useParams } from "next/navigation"; export default function DMs() { const { id } = useParams<{ id: string }>(); - const numericId = id ? Number(id) : undefined; const wsRef = useRef(null); const [messages, setMessages] = useState([]); - const { user, loading } = useUser(); - const [userList, setUserList] = useState>({}); - - useEffect(() => { - if (!numericId || !user || loading) return; - auth(makeAddress("localhost", 7090), wsRef, () => {}, setMessages); - setUserList((prev) => { - prev[user.id] = user; - return prev; - }); - }, [id, user, loading]); + const [userList, setUserList] = useState>({}); + const [user, setUser] = useState(); return ( - + m.channel_id === numericId || m.from === numericId) + .filter( + (m) => + (m.channel_id === id && m.from === user?.id) || + (m.from === id && m.channel_id === user?.id) + ) .toReversed()} sendMessage={(message) => { wsRef.current?.send( JSON.stringify({ type: "send_message", params: { - channel_id: numericId, + channel_id: id, contents: message, }, }) diff --git a/src/app/chat/page.tsx b/src/app/chat/page.tsx index 7704c0b..1c8f0d6 100644 --- a/src/app/chat/page.tsx +++ b/src/app/chat/page.tsx @@ -1,60 +1,47 @@ "use client"; -import { useEffect, useRef, useState } from "react"; -import MessageBox from "@/components/MessageBox"; +import { useRef, useState } from "react"; import AppSidebar from "@/components/Sidebar"; -import { Message } from "@/types/types"; -import useUser, { User } from "@/hooks/get-user"; -import { NumberMap } from "@/types/typeUtils"; -import auth, { makeAddress } from "@/lib/auth"; +import { User } from "@/hooks/get-user"; +import { StringMap } from "@/types/typeUtils"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { toast } from "sonner"; import ProfilePicture from "@/components/ProfilePicture"; export default function Chat() { - const ip = "localhost"; const wsRef = useRef(null); - const { user, loading } = useUser(); - const [userList, setUserList] = useState>({}); - - useEffect(() => { - if (!ip || !user || loading) return; - auth( - makeAddress(ip, 7090), - wsRef, - () => {}, - () => {}, - (m) => { - toast(`New message from ${m.from}`, { - description: m.contents.slice(0, 80), - }); - } - ); - setUserList((prev) => { - prev[user.id] = user; - return prev; - }); - }, [ip, user, loading]); + const [userList, setUserList] = useState>({}); return ( - + {}} + > Direct Messages - + ); } -function DMItem({ name, status }: { name: string; status: string }) { +function DMItem({ + name, + avatar, + status, +}: { + name: string; + avatar: string; + status: string; +}) { return ( - +
{name} {status} diff --git a/src/app/server/[id]/page.tsx b/src/app/server/[ip]/page.tsx similarity index 59% rename from src/app/server/[id]/page.tsx rename to src/app/server/[ip]/page.tsx index d700cf0..cf518f0 100644 --- a/src/app/server/[id]/page.tsx +++ b/src/app/server/[ip]/page.tsx @@ -5,36 +5,30 @@ import { useParams } from "next/navigation"; import MessageBox from "@/components/MessageBox"; import AppSidebar from "@/components/Sidebar"; import { Message, Server as ServerType } from "@/types/types"; -import useUser, { User } from "@/hooks/get-user"; -import { NumberMap } from "@/types/typeUtils"; +import { User } from "@/hooks/get-user"; +import { StringMap } from "@/types/typeUtils"; import auth, { makeAddress } from "@/lib/auth"; export default function Server() { - const { id: ip } = useParams<{ id: string }>(); + const { ip } = useParams<{ ip: string }>(); + const wsServerRef = useRef(null); + const wsNodeRef = useRef(null); const [server, setServer] = useState(); - const wsRef = useRef(null); const [messages, setMessages] = useState([]); - const { user, loading } = useUser(); - const [userList, setUserList] = useState>({}); + const [userList, setUserList] = useState>({}); useEffect(() => { - if (!ip || !user || loading) return; - auth(makeAddress(ip), wsRef, setServer, setMessages); - setUserList((prev) => { - prev[user.id] = user; - return prev; - }); - }, [ip, user, loading]); + if (!ip) return; + auth(makeAddress(ip, 7080), wsServerRef, setServer, setMessages); + }, [ip]); return ( m.channel_id === "general") .toReversed()} sendMessage={(message) => { - wsRef.current?.send( + wsServerRef.current?.send( JSON.stringify({ type: "send_message", params: { diff --git a/src/app/settings/page.tsx b/src/app/settings/page.tsx deleted file mode 100644 index b559245..0000000 --- a/src/app/settings/page.tsx +++ /dev/null @@ -1,66 +0,0 @@ -"use client"; - -import { useProfileSettings, useClientSettings } from "@/hooks/use-settings"; -import { setClientSettings } from "@/lib/clientSettings"; -import Settings from "./settings"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { Card } from "@/components/ui/card"; -import { CardContent } from "@/components/ui/card"; -import { PROFILE_SCHEMA, CLIENT_SCHEMA } from "@/types/settings"; -import { useRouter } from "next/navigation"; -import { Button } from "@/components/ui/button"; -import { ChevronLeftCircle } from "lucide-react"; -import axios from "axios"; - -export default function SettingsPage() { - const [settings, setSettings] = useClientSettings(); - const [profileSettings, setProfileSettings] = useProfileSettings(); - const router = useRouter(); - - return ( -
- - - - Client Settings - Profile Settings - - - - - { - setClientSettings(settings); - router.refresh(); - }} - /> - - - - - - - { - await axios.post("/api/profile", profileSettings); - }} - /> - - - - -
- ); -} diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 7c8c7db..7bd011c 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -8,8 +8,8 @@ import { SettingsIcon, Volume2Icon, } from "lucide-react"; -import { Server, Channel } from "@/types/types"; -import { useEffect, useState } from "react"; +import { Server, Channel, Message } from "@/types/types"; +import React, { useEffect, useRef, useState } from "react"; import Cookies from "js-cookie"; import { Dialog, @@ -25,32 +25,77 @@ import { Input } from "./ui/input"; import { Label } from "./ui/label"; import { Button } from "./ui/button"; import { Card } from "./ui/card"; -import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar"; import { ScrollArea } from "./ui/scroll-area"; import { useRouter } from "next/navigation"; import Link from "next/link"; -import { NumberMap } from "@/types/typeUtils"; -import { User } from "@/hooks/get-user"; +import useUser, { User } from "@/hooks/get-user"; import ProfilePicture from "./ProfilePicture"; +import auth, { makeAddress } from "@/lib/auth"; +import { toast } from "sonner"; +import { StringMap } from "@/types/typeUtils"; +import SettingsDialog from "./settings/SettingsDialog"; export default function AppSidebar({ - user, - server, children, + chatWith, + wsRef, + setMessages, + onNewMessage, + setUser, + server, }: Readonly<{ - server?: Server; - user: User | null; + wsRef: React.RefObject; + setMessages: React.Dispatch>; + onNewMessage?: (msg: Message) => void; + userList: StringMap; + setUserList: React.Dispatch>>; + chatWith?: string; children?: React.ReactNode; + setUser?: React.Dispatch>; + server?: Server | undefined; }>) { const [servers, setServers] = useState<[string, string][]>([]); const [newServer, setNewServer] = useState({ name: "", ip: "" }); const router = useRouter(); + const user = useUser(); + const [userList, setUserList] = useState>({}); + const ip = "192.168.100.3"; useEffect(() => { const s = Cookies.get("servers")?.split(","); if (s) setServers(s.map((x) => x.trim().split("@") as [string, string])); }, []); + useEffect(() => { + if (!ip || !user) return; + if (setUser) setUser(user); + auth( + makeAddress(ip, 7090), + wsRef, + () => {}, + setMessages, + (m) => { + if (m.from !== chatWith && m.from !== user.id) + toast( + `New message from ${userList[m.from]?.display_name || m.from}`, + { + description: m.contents.slice(0, 80), + action: { + label: "View", + onClick: () => router.push(`/chat/${m.from}`), + }, + } + ); + + if (onNewMessage) onNewMessage(m); + } + ); + setUserList((prev) => { + prev[user.id] = user; + return prev; + }); + }, [chatWith, user]); + const handleAddServer = () => { if (!newServer.ip) return; const updatedServers = [ @@ -151,9 +196,7 @@ export default function AppSidebar({
- - - +
@@ -182,16 +225,21 @@ export default function AppSidebar({ ) : ( <> - - - + + + )}
- +
@@ -201,14 +249,32 @@ export default function AppSidebar({ ); } -function DMItem({ name, status }: { name: string; status: string }) { +function DMItem({ + name, + avatar, + status, + settings, +}: { + name: string; + avatar: string; + status: string; + settings?: boolean; +}) { + const router = useRouter(); + return ( - +
{name} {status}
+ {settings && ( + + )}
); } diff --git a/src/app/settings/settings.tsx b/src/components/settings/SettingSchema.tsx similarity index 93% rename from src/app/settings/settings.tsx rename to src/components/settings/SettingSchema.tsx index 01f2c59..840c1dc 100644 --- a/src/app/settings/settings.tsx +++ b/src/components/settings/SettingSchema.tsx @@ -25,7 +25,7 @@ export type SettingsSchema = StringMap< "boolean" | "string" | "number" | string[] | RegexSetting >; -export default function Settings({ +export default function SettingSchema({ settings, setSettings, schema, @@ -70,10 +70,10 @@ export default function Settings({ if (Array.isArray(type)) { return ( handleChange(key, e.target.value)} /> ); @@ -104,7 +104,7 @@ export default function Settings({ return ( handleChange(key, Number(e.target.value))} /> ); diff --git a/src/components/settings/Settings.tsx b/src/components/settings/Settings.tsx new file mode 100644 index 0000000..6842abf --- /dev/null +++ b/src/components/settings/Settings.tsx @@ -0,0 +1,66 @@ +"use client"; + +import { useClientSettings } from "@/hooks/use-settings"; +import { setClientSettings } from "@/lib/clientSettings"; +import SettingSchema from "./SettingSchema"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Card } from "@/components/ui/card"; +import { CardContent } from "@/components/ui/card"; +import { + PROFILE_SCHEMA, + CLIENT_SCHEMA, + ProfileSettings, +} from "@/types/settings"; +import { useParams, useRouter, useSearchParams } from "next/navigation"; +import axios from "axios"; + +export default function Settings({ + tab, + profileSettings, + setProfileSettings, +}: { + tab?: string; + profileSettings: ProfileSettings; + setProfileSettings: React.Dispatch>; +}) { + const [settings, setSettings] = useClientSettings(); + const router = useRouter(); + + return ( + + + Client Settings + Profile Settings + + + + + { + setClientSettings(settings); + router.refresh(); + }} + /> + + + + + + + { + await axios.post("/api/profile", profileSettings); + }} + /> + + + + + ); +} diff --git a/src/components/settings/SettingsDialog.tsx b/src/components/settings/SettingsDialog.tsx new file mode 100644 index 0000000..4d98415 --- /dev/null +++ b/src/components/settings/SettingsDialog.tsx @@ -0,0 +1,53 @@ +import { SettingsIcon } from "lucide-react"; +import { Button } from "../ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "../ui/dialog"; +import { cn } from "@/lib/utils"; +import Settings from "./Settings"; +import { ProfileSettings } from "@/types/settings"; +import { useProfileSettings } from "@/hooks/use-settings"; + +export default function SettingsDialog({ + className, + tab, +}: { + className?: string; + tab?: string; + profile?: ProfileSettings; +}) { + const [profileSettings, setProfileSettings] = useProfileSettings(); + + return ( + + + + + + + Settings + + +
+ +
+
+
+ ); +} diff --git a/src/hooks/get-user.ts b/src/hooks/get-user.ts index b3613e4..f1fdb35 100644 --- a/src/hooks/get-user.ts +++ b/src/hooks/get-user.ts @@ -14,7 +14,6 @@ export interface User { export default function useUser() { const [user, setUser] = useState(null); - const [loading, setLoading] = useState(true); const router = useRouter(); useEffect(() => { @@ -31,13 +30,11 @@ export default function useUser() { setUser(res); } catch { router.push("/login"); - } finally { - setLoading(false); } } fetchUser(); }, [router]); - return { user, loading }; + return user; } diff --git a/src/lib/auth.ts b/src/lib/auth.ts index f964cd3..1124987 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -13,6 +13,7 @@ export default async function auth( setMessages: Dispatch>, onNewMessage?: (m: Message) => void ) { + console.log("Authenticating with server at:", ip); const server_auth = ( (await axios.post("/api/auth", { server_ip: ip })).data as any ).token; @@ -37,7 +38,7 @@ export default async function auth( console.log("Message received:", data); if (data.version) { - setServer(data); + setServer({ channels: [], ...data }); return; } diff --git a/src/types/settings.ts b/src/types/settings.ts index 37d4374..132b3b0 100644 --- a/src/types/settings.ts +++ b/src/types/settings.ts @@ -1,4 +1,7 @@ -import { RegexSetting, SettingsSchema } from "@/app/settings/settings"; +import { + RegexSetting, + SettingsSchema, +} from "@/components/settings/SettingSchema"; export interface ClientSettings { theme: "light" | "dark"; diff --git a/src/types/types.ts b/src/types/types.ts index a3ed9b0..9bb3c32 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -6,8 +6,8 @@ export interface Channel { export interface Message { id: number; - channel_id: string | number; - from: number; + channel_id: string; + from: string; contents: string; timestamp: number; }