Sidebar UI, server stuff
This commit is contained in:
@@ -13,7 +13,7 @@ export async function GET(
|
|||||||
|
|
||||||
const { data: profile } = await supabase
|
const { data: profile } = await supabase
|
||||||
.from("servers")
|
.from("servers")
|
||||||
.select("id, created_at, owner, address")
|
.select("id, name, created_at, owner, address, icon_url")
|
||||||
.eq("id", id)
|
.eq("id", id)
|
||||||
.maybeSingle();
|
.maybeSingle();
|
||||||
|
|
||||||
|
|||||||
+32
-64
@@ -14,7 +14,6 @@ import {
|
|||||||
Dialog,
|
Dialog,
|
||||||
DialogClose,
|
DialogClose,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
@@ -37,6 +36,7 @@ import App from "@/types/app";
|
|||||||
import { ProfileSettings } from "@/types/settings";
|
import { ProfileSettings } from "@/types/settings";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { useIsMobile } from "@/hooks/is-mobile";
|
import { useIsMobile } from "@/hooks/is-mobile";
|
||||||
|
import { get } from "@/lib/request";
|
||||||
|
|
||||||
export default function AppSidebar({
|
export default function AppSidebar({
|
||||||
children,
|
children,
|
||||||
@@ -51,8 +51,7 @@ export default function AppSidebar({
|
|||||||
server?: Server | undefined;
|
server?: Server | undefined;
|
||||||
app: App;
|
app: App;
|
||||||
}>) {
|
}>) {
|
||||||
const [servers, setServers] = useState<[string, string][]>([]);
|
const [newServer, setNewServer] = useState("");
|
||||||
const [newServer, setNewServer] = useState({ name: "", ip: "" });
|
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
@@ -82,15 +81,7 @@ export default function AppSidebar({
|
|||||||
load();
|
load();
|
||||||
}, [dms, query]);
|
}, [dms, query]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {}, []);
|
||||||
const stored = Cookies.get("servers");
|
|
||||||
if (!stored) return;
|
|
||||||
const parsed = stored
|
|
||||||
.split(",")
|
|
||||||
.map((x) => x.trim().split("@"))
|
|
||||||
.filter(([ip, name]) => ip && name) as [string, string][];
|
|
||||||
setServers(parsed);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!app.profile?.node_address) return;
|
if (!app.profile?.node_address) return;
|
||||||
@@ -123,16 +114,20 @@ export default function AppSidebar({
|
|||||||
});
|
});
|
||||||
}, [chatWith, app.profile]);
|
}, [chatWith, app.profile]);
|
||||||
|
|
||||||
const handleAddServer = () => {
|
const handleAddServer = async () => {
|
||||||
if (!newServer.ip) return;
|
if (!newServer || Object.hasOwn(app.servers, newServer)) return;
|
||||||
const updatedServers = [
|
|
||||||
...servers,
|
|
||||||
[newServer.ip, newServer.name] as [string, string],
|
|
||||||
];
|
|
||||||
|
|
||||||
setServers(updatedServers);
|
try {
|
||||||
Cookies.set("servers", updatedServers.map((x) => x.join("@")).join(","));
|
const server = (await get(`/api/server/${newServer}`)).data as Server;
|
||||||
setNewServer({ name: "", ip: "" });
|
app.setServers((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[server.id]: server,
|
||||||
|
}));
|
||||||
|
Cookies.set("servers", Object.keys(app.servers).join(","));
|
||||||
|
setNewServer("");
|
||||||
|
} catch (e: any) {
|
||||||
|
toast.error(e);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -143,7 +138,7 @@ export default function AppSidebar({
|
|||||||
app.sidebarOpen && isMobile ? "w-5xl md:w-md" : "w-0 md:w-md"
|
app.sidebarOpen && isMobile ? "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">
|
<div className="w-16 flex flex-col items-center gap-4 py-4 border-r">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => router.push(`/chat`)}
|
onClick={() => router.push(`/chat`)}
|
||||||
className="bg-transparent text-accent-foreground hover:text-accent"
|
className="bg-transparent text-accent-foreground hover:text-accent"
|
||||||
@@ -151,16 +146,12 @@ export default function AppSidebar({
|
|||||||
<MessageCircle className="h-6 w-6" />
|
<MessageCircle className="h-6 w-6" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{servers.map(
|
{Object.values(app.servers).map(
|
||||||
([ip, name]) =>
|
(srv) =>
|
||||||
ip && (
|
srv && (
|
||||||
<Button
|
<Link href={`/server/${srv.id}`} key={srv.id} className="">
|
||||||
onClick={() => router.push(`/server/${ip}`)}
|
<ProfilePicture name={srv.name} url={srv.icon_url} />
|
||||||
key={ip}
|
</Link>
|
||||||
className="bg-transparent text-accent-foreground hover:text-accent"
|
|
||||||
>
|
|
||||||
<HashIcon className="h-6 w-6" />
|
|
||||||
</Button>
|
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -173,41 +164,18 @@ export default function AppSidebar({
|
|||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Add server</DialogTitle>
|
<DialogTitle>Join a server</DialogTitle>
|
||||||
<DialogDescription>
|
|
||||||
Add a new server to your server list.
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
<div className="grid gap-3">
|
<div className="grid gap-3">
|
||||||
<Label htmlFor="name-1">Name</Label>
|
<Label htmlFor="id-1">Server ID</Label>
|
||||||
<Input
|
<Input
|
||||||
id="name-1"
|
id="id-1"
|
||||||
name="name"
|
name="id"
|
||||||
value={newServer.name}
|
value={newServer}
|
||||||
onChange={(e) =>
|
onChange={(e) => setNewServer(e.target.value)}
|
||||||
setNewServer((prev) => ({
|
placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||||
...prev,
|
|
||||||
name: e.target.value,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
placeholder="Voxa Server"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="grid gap-3">
|
|
||||||
<Label htmlFor="ip-1">Server Ip</Label>
|
|
||||||
<Input
|
|
||||||
id="ip-1"
|
|
||||||
name="ip"
|
|
||||||
value={newServer.ip}
|
|
||||||
onChange={(e) =>
|
|
||||||
setNewServer((prev) => ({
|
|
||||||
...prev,
|
|
||||||
ip: e.target.value,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
placeholder="192.168.1.5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -218,7 +186,7 @@ export default function AppSidebar({
|
|||||||
</DialogClose>
|
</DialogClose>
|
||||||
<DialogClose asChild>
|
<DialogClose asChild>
|
||||||
<Button type="button" onClick={handleAddServer}>
|
<Button type="button" onClick={handleAddServer}>
|
||||||
Add server
|
Join server
|
||||||
</Button>
|
</Button>
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
@@ -231,7 +199,7 @@ export default function AppSidebar({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right column */}
|
{/* Right column */}
|
||||||
<div className="w-full flex flex-col bg-card">
|
<div className="w-full flex flex-col bg-card/50">
|
||||||
{server ? (
|
{server ? (
|
||||||
<div className="p-3 border-b flex items-center gap-2">
|
<div className="p-3 border-b flex items-center gap-2">
|
||||||
<h2>{server.name}</h2>
|
<h2>{server.name}</h2>
|
||||||
|
|||||||
+23
-1
@@ -1,10 +1,13 @@
|
|||||||
import App from "@/types/app";
|
import App from "@/types/app";
|
||||||
import { useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { useMessages } from "./use-messages";
|
import { useMessages } from "./use-messages";
|
||||||
import { StringMap } from "@/types/typeUtils";
|
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 Cookies from "js-cookie";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
export default function useApp(): App {
|
export default function useApp(): App {
|
||||||
const node = useRef<WebSocket | null>(null);
|
const node = useRef<WebSocket | null>(null);
|
||||||
@@ -14,8 +17,27 @@ export default function useApp(): App {
|
|||||||
const profile = useUser();
|
const profile = useUser();
|
||||||
const [clientSettings, setClientSettings] = useClientSettings();
|
const [clientSettings, setClientSettings] = useClientSettings();
|
||||||
const [dms, setDms] = useState<UserProfile[]>([]);
|
const [dms, setDms] = useState<UserProfile[]>([]);
|
||||||
|
const [servers, setServers] = useState<StringMap<Server>>({});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const stored = Cookies.get("servers");
|
||||||
|
if (!stored) return;
|
||||||
|
Promise.all(
|
||||||
|
stored
|
||||||
|
.split(",")
|
||||||
|
.map((x) => x.trim())
|
||||||
|
.filter((id) => id)
|
||||||
|
.map((id) => get(`/api/server/${id}`))
|
||||||
|
)
|
||||||
|
.then((o) =>
|
||||||
|
setServers(Object.fromEntries(o.map((i) => [i.data.id, i.data])))
|
||||||
|
)
|
||||||
|
.catch(toast.error);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
servers,
|
||||||
|
setServers,
|
||||||
dms,
|
dms,
|
||||||
setDms,
|
setDms,
|
||||||
node,
|
node,
|
||||||
|
|||||||
+4
-1
@@ -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 } from "./types";
|
import { 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>>;
|
||||||
@@ -13,6 +13,9 @@ export default interface App {
|
|||||||
clientSettings: ClientSettings;
|
clientSettings: ClientSettings;
|
||||||
setClientSettings: SetState<ClientSettings>;
|
setClientSettings: SetState<ClientSettings>;
|
||||||
|
|
||||||
|
servers: StringMap<Server>;
|
||||||
|
setServers: SetState<StringMap<Server>>;
|
||||||
|
|
||||||
profiles: StringMap<UserProfile | null>;
|
profiles: StringMap<UserProfile | null>;
|
||||||
setProfiles: SetState<StringMap<UserProfile | null>>;
|
setProfiles: SetState<StringMap<UserProfile | null>>;
|
||||||
|
|
||||||
|
|||||||
@@ -15,5 +15,6 @@ export interface Message {
|
|||||||
export interface Server {
|
export interface Server {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
icon_url: string;
|
||||||
channels: Channel[];
|
channels: Channel[];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user