Better ui and slight change for settings #3
This commit is contained in:
+3
-50
@@ -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
@@ -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>
|
||||||
|
|||||||
@@ -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
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user