Chat hub page
This commit is contained in:
+132
-9
@@ -1,24 +1,147 @@
|
||||
"use client";
|
||||
|
||||
import ProfilePicture from "@/components/ProfilePicture";
|
||||
import SettingsDialog from "@/components/settings/SettingsDialog";
|
||||
import AppSidebar from "@/components/Sidebar";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { useIsMobile } from "@/hooks/is-mobile";
|
||||
import useApp from "@/hooks/use-app";
|
||||
import { ProfileSettings } from "@/types/settings";
|
||||
import { Server } from "@/types/types";
|
||||
import { motion } from "framer-motion";
|
||||
import {
|
||||
ChevronLeft,
|
||||
MessageCircle,
|
||||
Users,
|
||||
PlusCircle,
|
||||
Settings,
|
||||
ChevronRight,
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function Chat() {
|
||||
export default function ChatHub() {
|
||||
const app = useApp();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const recentDMs = app.dms?.slice(0, 5) || [];
|
||||
// const activeServers = app.servers?.slice(0, 5) || [];
|
||||
const activeServers: Server[] = [];
|
||||
|
||||
return (
|
||||
<AppSidebar app={app}>
|
||||
<Card className="bg-background w-full">
|
||||
<CardHeader>
|
||||
<CardTitle onClick={() => isMobile && app.setSidebarOpen(true)}>
|
||||
Direct Messages
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent></CardContent>
|
||||
</Card>
|
||||
<div className="flex-1 p-6 overflow-y-auto">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="space-y-8"
|
||||
>
|
||||
{/* Greeting */}
|
||||
<div onClick={() => isMobile && app.setSidebarOpen(true)}>
|
||||
<h1 className="text-3xl font-bold flex cursor-pointer gap-2">
|
||||
<ChevronLeft className="my-auto w-5" />
|
||||
Hey {app.profile?.display_name || "there"}
|
||||
</h1>
|
||||
<p className="text-muted-foreground">
|
||||
Here's what's happening in your network today.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* Recent DMs */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<MessageCircle className="w-5 h-5" />
|
||||
Recent DMs
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
{recentDMs.length ? (
|
||||
recentDMs.map((dm) => (
|
||||
<Link
|
||||
href={`/chat/${dm.id}`}
|
||||
key={dm.id}
|
||||
className="flex justify-between items-center hover:bg-accent hover:text-accent-foreground rounded p-1"
|
||||
>
|
||||
<span className="flex gap-2">
|
||||
<ProfilePicture
|
||||
name={dm.display_name}
|
||||
url={dm.avatar_url}
|
||||
/>
|
||||
<span className="truncate my-auto">
|
||||
{dm.display_name || dm.id}
|
||||
</span>
|
||||
</span>
|
||||
<ChevronRight />
|
||||
</Link>
|
||||
))
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
No recent DMs yet.
|
||||
</p>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Active Servers */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Users className="w-5 h-5" />
|
||||
Active Servers
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
{activeServers.length ? (
|
||||
activeServers.map((srv) => (
|
||||
<Link
|
||||
href={`/server/${srv.id}`}
|
||||
key={srv.id}
|
||||
className="flex justify-between items-center hover:bg-accent hover:text-accent-foreground rounded p-1"
|
||||
>
|
||||
<span className="flex gap-2">
|
||||
<ProfilePicture name={srv.name} url="" />
|
||||
<span className="truncate my-auto">{srv.name}</span>
|
||||
</span>
|
||||
<ChevronRight />
|
||||
</Link>
|
||||
))
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
No servers joined yet.
|
||||
</p>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
{/* Quick Actions */}
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold mb-4">Quick Actions</h2>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Button variant="outline" className="gap-2">
|
||||
<PlusCircle className="w-4 h-4" />
|
||||
Start New DM
|
||||
</Button>
|
||||
<Button variant="outline" className="gap-2">
|
||||
<Users className="w-4 h-4" />
|
||||
Create Server
|
||||
</Button>
|
||||
<SettingsDialog profile={app.profile as ProfileSettings}>
|
||||
<Button variant="outline" className="gap-2">
|
||||
<Settings className="w-4 h-4" />
|
||||
Settings
|
||||
</Button>
|
||||
</SettingsDialog>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</AppSidebar>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,6 @@ export default function AppSidebar({
|
||||
const [newServer, setNewServer] = useState({ name: "", ip: "" });
|
||||
const [query, setQuery] = useState("");
|
||||
const router = useRouter();
|
||||
const [filteredDms, setFilteredDms] = useState<UserProfile[]>([]);
|
||||
|
||||
const dms = useMemo(() => {
|
||||
return Array.from(
|
||||
@@ -69,7 +68,7 @@ export default function AppSidebar({
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
const users = await Promise.all(dms.map((id) => app.getUserById(id)));
|
||||
setFilteredDms(
|
||||
app.setDms(
|
||||
users.filter(
|
||||
(user) =>
|
||||
user !== null &&
|
||||
@@ -258,7 +257,7 @@ export default function AppSidebar({
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{filteredDms.map((user) => (
|
||||
{app.dms.map((user) => (
|
||||
<DMItem
|
||||
name={user.display_name}
|
||||
id={user.id}
|
||||
@@ -268,7 +267,7 @@ export default function AppSidebar({
|
||||
app={app}
|
||||
/>
|
||||
))}
|
||||
{filteredDms.length === 0 && query.trim() !== "" ? (
|
||||
{app.dms.length === 0 && query.trim() !== "" ? (
|
||||
<div className="px-3 py-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
||||
@@ -10,28 +10,34 @@ import {
|
||||
import { cn } from "@/lib/utils";
|
||||
import Settings from "./Settings";
|
||||
import { ProfileSettings } from "@/types/settings";
|
||||
import { useState } from "react";
|
||||
import { ReactNode, useState } from "react";
|
||||
|
||||
export default function SettingsDialog({
|
||||
className,
|
||||
tab,
|
||||
profile,
|
||||
children,
|
||||
}: {
|
||||
className?: string;
|
||||
tab?: string;
|
||||
profile: ProfileSettings;
|
||||
children?: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
className={cn(
|
||||
"bg-transparent text-accent-foreground hover:text-accent",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<SettingsIcon />
|
||||
</Button>
|
||||
{children ? (
|
||||
children
|
||||
) : (
|
||||
<Button
|
||||
className={cn(
|
||||
"bg-transparent text-accent-foreground hover:text-accent",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<SettingsIcon />
|
||||
</Button>
|
||||
)}
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
|
||||
@@ -13,8 +13,11 @@ export default function useApp(): App {
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
const profile = useUser();
|
||||
const [clientSettings, setClientSettings] = useClientSettings();
|
||||
const [dms, setDms] = useState<UserProfile[]>([]);
|
||||
|
||||
return {
|
||||
dms,
|
||||
setDms,
|
||||
node,
|
||||
clientSettings,
|
||||
setClientSettings,
|
||||
|
||||
@@ -23,4 +23,7 @@ export default interface App {
|
||||
setSidebarOpen: SetState<boolean>;
|
||||
|
||||
getUserById: (userId: string) => Promise<UserProfile | null>;
|
||||
|
||||
dms: UserProfile[];
|
||||
setDms: SetState<UserProfile[]>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user