Server functionality

This commit is contained in:
2025-10-31 15:22:14 +01:00
parent bee5b91fef
commit f123adb15f
5 changed files with 67 additions and 26 deletions
+28 -6
View File
@@ -1,7 +1,7 @@
"use client"; "use client";
import { useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useParams } from "next/navigation"; import { useParams, useSearchParams } from "next/navigation";
import MessageBox from "@/components/MessageBox"; import MessageBox from "@/components/MessageBox";
import AppLayout from "@/components/app/AppLayout"; import AppLayout from "@/components/app/AppLayout";
import { Server as ServerType } from "@/types/types"; import { Server as ServerType } from "@/types/types";
@@ -12,6 +12,7 @@ import { useEffectOnceWhenReady } from "@/hooks/use-once";
export default function Server() { export default function Server() {
const { ip } = useParams<{ ip: string }>(); const { ip } = useParams<{ ip: string }>();
const searchParams = useSearchParams();
const serverRef = useRef<WebSocket | null>(null); const serverRef = useRef<WebSocket | null>(null);
const [server, setServer] = useState<undefined | ServerType>(); const [server, setServer] = useState<undefined | ServerType>();
const app = useApp(); const app = useApp();
@@ -31,27 +32,48 @@ export default function Server() {
); );
}, },
[ip, messages], [ip, messages],
[undefined, (v) => v[ip]] [undefined, (v) => v]
); );
useEffect(() => {
const channelId = searchParams.get("ch");
if (channelId) {
app.setCurrentChannel(
server?.channels.find((v) => v.id === channelId) ||
server?.channels[0] ||
null
);
} else {
app.setCurrentChannel(server?.channels[0] || null);
}
}, [server]);
return ( return (
<AppLayout app={app} server={server}> <AppLayout app={app} server={server}>
{app.currentChannel ? (
<MessageBox <MessageBox
app={app} app={app}
channelName="General" channelName={app.currentChannel.name}
messages={messages[ip]?.filter((m) => m.channel_id === "general") || []} messages={
messages[ip]?.filter(
(m) => m.channel_id === app.currentChannel?.id
) || []
}
sendMessage={(message) => { sendMessage={(message) => {
serverRef.current?.send( serverRef.current?.send(
JSON.stringify({ JSON.stringify({
type: "send_message", type: "send_message",
params: { params: {
channel_id: "general", channel_id: app.currentChannel?.id,
contents: message, contents: message,
}, },
}) })
); );
}} }}
/> />
) : (
<div className="h-svh w-full max-h-svh flex flex-col pb-5 pl-5 gap-5"></div>
)}
</AppLayout> </AppLayout>
); );
} }
+1 -1
View File
@@ -78,7 +78,7 @@ export default function AppLayout({
<div className="flex-1 min-w-0 flex flex-col bg-sidebar"> <div className="flex-1 min-w-0 flex flex-col bg-sidebar">
<div className="flex-1 min-h-0"> <div className="flex-1 min-h-0">
{server ? ( {server ? (
<SidebarChannels server={server} /> <SidebarChannels server={server} app={app} />
) : ( ) : (
<SidebarDMs app={app} /> <SidebarDMs app={app} />
)} )}
+17 -4
View File
@@ -3,6 +3,7 @@ import { ScrollArea } from "../ui/scroll-area";
import { Server, Channel } from "@/types/types"; import { Server, Channel } from "@/types/types";
import { Card } from "../ui/card"; import { Card } from "../ui/card";
import { HashIcon, Volume2Icon } from "lucide-react"; import { HashIcon, Volume2Icon } from "lucide-react";
import App from "@/types/app";
export function ChannelIcon({ kind }: { kind: "text" | "voice" }) { export function ChannelIcon({ kind }: { kind: "text" | "voice" }) {
switch (kind) { switch (kind) {
@@ -15,16 +16,28 @@ export function ChannelIcon({ kind }: { kind: "text" | "voice" }) {
} }
} }
export function ChannelItem({ channel }: { channel: Channel }) { export function ChannelItem({ channel, app }: { channel: Channel; app: App }) {
return ( return (
<Card className="p-2 flex items-center gap-2 cursor-pointer hover:bg-accent"> <Card
className="p-2 flex flex-row px-3 items-center gap-2 cursor-pointer hover:bg-accent"
onClick={() => {
app.setCurrentChannel(channel);
app.setSidebarOpen(false);
}}
>
<ChannelIcon kind={channel.kind} /> <ChannelIcon kind={channel.kind} />
<span className="text-sm font-medium">{channel.name}</span> <span className="text-sm font-medium">{channel.name}</span>
</Card> </Card>
); );
} }
export default function SidebarChannels({ server }: { server: Server }) { export default function SidebarChannels({
server,
app,
}: {
server: Server;
app: App;
}) {
return ( return (
<div className="w-full flex flex-col bg-sidebar"> <div className="w-full flex flex-col bg-sidebar">
<div className="p-3 border-b flex items-center gap-2"> <div className="p-3 border-b flex items-center gap-2">
@@ -33,7 +46,7 @@ export default function SidebarChannels({ server }: { server: Server }) {
<ScrollArea className="flex-1"> <ScrollArea className="flex-1">
<div className="p-2 flex flex-col gap-2"> <div className="p-2 flex flex-col gap-2">
{server.channels.map((channel: Channel) => ( {server.channels.map((channel: Channel) => (
<ChannelItem key={channel.id} channel={channel} /> <ChannelItem key={channel.id} channel={channel} app={app} />
))} ))}
</div> </div>
</ScrollArea> </ScrollArea>
+4 -1
View File
@@ -5,7 +5,7 @@ import { StringMap } from "@/types/typeUtils";
import useUser, { UserProfile } from "./get-user"; import useUser, { UserProfile } from "./get-user";
import { useClientSettings } from "./use-settings"; import { useClientSettings } from "./use-settings";
import { get, Response } from "@/lib/request"; import { get, Response } from "@/lib/request";
import { Server } from "@/types/types"; import { Channel, Server } from "@/types/types";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { toast } from "sonner"; import { toast } from "sonner";
@@ -18,6 +18,7 @@ export default function useApp(): App {
const [clientSettings, setClientSettings] = useClientSettings(); const [clientSettings, setClientSettings] = useClientSettings();
const [dms, setDms] = useState<UserProfile[]>([]); const [dms, setDms] = useState<UserProfile[]>([]);
const [servers, setServers] = useState<StringMap<Server>>({}); const [servers, setServers] = useState<StringMap<Server>>({});
const [currentChannel, setCurrentChannel] = useState<Channel | null>(null);
useEffect(() => { useEffect(() => {
const stored = Cookies.get("servers"); const stored = Cookies.get("servers");
@@ -36,6 +37,8 @@ export default function useApp(): App {
}, []); }, []);
return { return {
currentChannel,
setCurrentChannel,
servers, servers,
setServers, setServers,
dms, dms,
+4 -1
View File
@@ -1,7 +1,7 @@
import { Dispatch, RefObject, SetStateAction } from "react"; import { Dispatch, RefObject, SetStateAction } from "react";
import { StringMap } from "./typeUtils"; import { StringMap } from "./typeUtils";
import { UserProfile } from "@/hooks/get-user"; import { UserProfile } from "@/hooks/get-user";
import { Message, Server } from "./types"; import { Channel, Message, Server } from "./types";
import { ClientSettings } from "./settings"; import { ClientSettings } from "./settings";
export type SetState<T> = Dispatch<SetStateAction<T>>; export type SetState<T> = Dispatch<SetStateAction<T>>;
@@ -29,4 +29,7 @@ export default interface App {
dms: UserProfile[]; dms: UserProfile[];
setDms: SetState<UserProfile[]>; setDms: SetState<UserProfile[]>;
currentChannel: Channel | null;
setCurrentChannel: SetState<Channel | null>;
} }