Better ui and slight change for settings #3

This commit is contained in:
2025-10-02 13:14:30 +02:00
parent 0734dde01e
commit 10f6f3b736
7 changed files with 96 additions and 88 deletions
+3 -50
View File
@@ -1,28 +1,14 @@
"use client"; "use client";
import { Button } from "@/components/ui/button";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Card } from "@/components/ui/card";
import { useState } from "react";
import AppSidebar from "@/components/Sidebar"; import AppSidebar from "@/components/Sidebar";
import MessageBox from "@/components/MessageBox"; import MessageBox from "@/components/MessageBox";
import useUser from "@/hooks/get-user";
export default function HomePage() { export default function HomePage() {
const [messages, setMessages] = useState([ const { user, loading } = useUser();
{ id: 1, user: "Alice", text: "Hey, how are you?" },
{ id: 2, user: "You", text: "Doing good, just working on the chat app!" },
]);
const [input, setInput] = useState("");
function sendMessage() {
if (!input.trim()) return;
setMessages([...messages, { id: Date.now(), user: "You", text: input }]);
setInput("");
}
return ( return (
<AppSidebar> <AppSidebar user={user}>
<MessageBox <MessageBox
channelName="Alice" channelName="Alice"
messages={[]} messages={[]}
@@ -33,36 +19,3 @@ export default function HomePage() {
</AppSidebar> </AppSidebar>
); );
} }
function ServerButton({
label,
icon,
}: {
label: string;
icon: React.ReactNode;
}) {
return (
<Button
variant="ghost"
size="icon"
className="rounded-full h-12 w-12"
title={label}
>
{icon}
</Button>
);
}
function DMItem({ name, status }: { name: string; status: string }) {
return (
<Card className="p-2 flex items-center gap-2 cursor-pointer hover:bg-accent">
<Avatar className="h-8 w-8">
<AvatarFallback>{name[0]}</AvatarFallback>
</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>
);
}
+8 -2
View File
@@ -1,6 +1,8 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google"; import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css"; import "./globals.css";
import { cookies } from "next/headers";
import { parseClientSettings } from "@/lib/clientSettings";
const geistSans = Geist({ const geistSans = Geist({
variable: "--font-geist-sans", variable: "--font-geist-sans",
@@ -17,15 +19,19 @@ export const metadata: Metadata = {
description: "Generated by create next app", description: "Generated by create next app",
}; };
export default function RootLayout({ export default async function RootLayout({
children, children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
const cookieStore = await cookies();
const settings = parseClientSettings(cookieStore.get("settings")?.value);
return ( return (
<html lang="en"> <html lang="en">
<body <body
className={`${geistSans.variable} ${geistMono.variable} antialiased dark`} className={`${geistSans.variable} ${geistMono.variable} antialiased ${settings.theme}`}
> >
{children} {children}
</body> </body>
+1
View File
@@ -84,6 +84,7 @@ export default function Server() {
return ( return (
<AppSidebar <AppSidebar
user={user}
server={ server={
{ {
channels: [{ id: "general", name: "General", kind: "text" }], channels: [{ id: "general", name: "General", kind: "text" }],
+46 -30
View File
@@ -7,41 +7,57 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Card } from "@/components/ui/card"; import { Card } from "@/components/ui/card";
import { CardContent } from "@/components/ui/card"; import { CardContent } from "@/components/ui/card";
import { ACCOUNT_SCHEMA, CLIENT_SCHEMA } from "@/types/settings"; import { ACCOUNT_SCHEMA, CLIENT_SCHEMA } from "@/types/settings";
import { useRouter } from "next/navigation";
import { Button } from "@/components/ui/button";
import { ChevronLeftCircle } from "lucide-react";
export default function SettingsPage() { export default function SettingsPage() {
const [settings, setSettings] = useClientSettings(); const [settings, setSettings] = useClientSettings();
const [accountSettings, setAccountSettings] = useAccountSettings(); const [accountSettings, setAccountSettings] = useAccountSettings();
const router = useRouter();
return ( return (
<Tabs className="mx-auto" defaultValue="client"> <div>
<TabsList className="mx-auto"> <Button
<TabsTrigger value="client">Client Settings</TabsTrigger> variant="outline"
<TabsTrigger value="account">Account Settings</TabsTrigger> className="ml-2 mt-2"
</TabsList> onClick={() => router.back()}
<TabsContent value="client"> >
<Card className="w-xl mx-auto"> <ChevronLeftCircle />
<CardContent className="flex flex-col gap-3"> </Button>
<Settings <Tabs className="mx-auto mt-3" defaultValue="client">
settings={settings} <TabsList className="mx-auto">
setSettings={setSettings} <TabsTrigger value="client">Client Settings</TabsTrigger>
schema={CLIENT_SCHEMA} <TabsTrigger value="account">Account Settings</TabsTrigger>
onSave={() => setClientSettings(settings)} </TabsList>
/> <TabsContent value="client">
</CardContent> <Card className="w-xl mx-auto">
</Card> <CardContent className="flex flex-col gap-3">
</TabsContent> <Settings
<TabsContent value="account"> settings={settings}
<Card className="w-xl mx-auto"> setSettings={setSettings}
<CardContent className="flex flex-col gap-3"> schema={CLIENT_SCHEMA}
<Settings onSave={() => {
settings={accountSettings} setClientSettings(settings);
setSettings={setAccountSettings} router.refresh();
schema={ACCOUNT_SCHEMA} }}
onSave={() => {}} />
/> </CardContent>
</CardContent> </Card>
</Card> </TabsContent>
</TabsContent> <TabsContent value="account">
</Tabs> <Card className="w-xl mx-auto">
<CardContent className="flex flex-col gap-3">
<Settings
settings={accountSettings}
setSettings={setAccountSettings}
schema={ACCOUNT_SCHEMA}
onSave={() => {}}
/>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
); );
} }
+13 -1
View File
@@ -18,6 +18,7 @@ import remarkGfm from "remark-gfm";
import { NumberMap, StringMap } from "@/types/typeUtils"; import { NumberMap, StringMap } from "@/types/typeUtils";
import { User } from "@/hooks/get-user"; import { User } from "@/hooks/get-user";
import axios from "axios"; import axios from "axios";
import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar";
function MessageContainer({ function MessageContainer({
message, message,
@@ -40,7 +41,18 @@ function MessageContainer({
return ( return (
<div className="flex flex-col gap-3 rounded-lg p-4 hover:bg-accent"> <div className="flex flex-col gap-3 rounded-lg p-4 hover:bg-accent">
<div className="flex gap-2"> <div className="flex gap-2">
<div className="w-8 h-8 rounded-full bg-gray-400" /> {/* <div className="w-8 h-8 rounded-full bg-gray-400" /> */}
<Avatar className="h-8 w-8">
<AvatarImage
src="https://github.com/kleo-dev.png"
alt={message.from.toString()}
/>
<AvatarFallback>
{userList[message.from]
? userList[message.from].username
: message.from}
</AvatarFallback>
</Avatar>
<div className="flex flex-col flex-1"> <div className="flex flex-col flex-1">
<span className="font-bold flex gap-1 items-center"> <span className="font-bold flex gap-1 items-center">
{userList[message.from] {userList[message.from]
+18 -1
View File
@@ -5,6 +5,7 @@ import {
MessageCircle, MessageCircle,
Plus, Plus,
Search, Search,
SettingsIcon,
Volume2Icon, Volume2Icon,
} from "lucide-react"; } from "lucide-react";
import Server from "@/types/server"; import Server from "@/types/server";
@@ -24,16 +25,21 @@ import { Input } from "./ui/input";
import { Label } from "./ui/label"; import { Label } from "./ui/label";
import { Button } from "./ui/button"; import { Button } from "./ui/button";
import { Card } from "./ui/card"; import { Card } from "./ui/card";
import { Avatar, AvatarFallback } from "./ui/avatar"; import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar";
import { ScrollArea } from "./ui/scroll-area"; import { ScrollArea } from "./ui/scroll-area";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import Channel from "@/types/channel"; import Channel from "@/types/channel";
import Link from "next/link";
import { NumberMap } from "@/types/typeUtils";
import { User } from "@/hooks/get-user";
export default function AppSidebar({ export default function AppSidebar({
user,
server, server,
children, children,
}: Readonly<{ }: Readonly<{
server?: Server; server?: Server;
user: User | null;
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
const [servers, setServers] = useState<[string, string][]>([]); const [servers, setServers] = useState<[string, string][]>([]);
@@ -143,6 +149,12 @@ export default function AppSidebar({
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
<footer className="mt-auto">
<Link href="/settings">
<SettingsIcon />
</Link>
</footer>
</div> </div>
{/* Right column */} {/* Right column */}
@@ -177,6 +189,10 @@ export default function AppSidebar({
)} )}
</div> </div>
</ScrollArea> </ScrollArea>
<footer className="mt-auto pb-3 pl-3 pr-3">
<DMItem name={user?.username || "Loading.."} status="online" />
</footer>
</div> </div>
</div> </div>
@@ -189,6 +205,7 @@ function DMItem({ name, status }: { name: string; status: string }) {
return ( return (
<Card className="p-2 flex flex-row items-center gap-2 cursor-pointer hover:bg-accent"> <Card className="p-2 flex flex-row items-center gap-2 cursor-pointer hover:bg-accent">
<Avatar className="h-8 w-8"> <Avatar className="h-8 w-8">
<AvatarImage src="https://github.com/kleo-dev.png" alt={name} />
<AvatarFallback>{name[0]}</AvatarFallback> <AvatarFallback>{name[0]}</AvatarFallback>
</Avatar> </Avatar>
<div className="flex flex-col"> <div className="flex flex-col">
+7 -4
View File
@@ -5,13 +5,12 @@ export const defaultSettings: ClientSettings = {
theme: "dark", theme: "dark",
}; };
export function getClientSettings(): ClientSettings { export function parseClientSettings(s?: string): ClientSettings {
const s_cookie = Cookies.get("settings");
let parsed: Partial<ClientSettings> = {}; let parsed: Partial<ClientSettings> = {};
if (s_cookie) { if (s) {
try { try {
parsed = JSON.parse(s_cookie); parsed = JSON.parse(s);
} catch { } catch {
parsed = {}; parsed = {};
} }
@@ -23,6 +22,10 @@ export function getClientSettings(): ClientSettings {
}; };
} }
export function getClientSettings(): ClientSettings {
return parseClientSettings(Cookies.get("settings"));
}
export function setClientSettings(value?: ClientSettings) { export function setClientSettings(value?: ClientSettings) {
Cookies.set("settings", JSON.stringify(value || defaultSettings)); Cookies.set("settings", JSON.stringify(value || defaultSettings));
} }