The weeb for the next gen discord boat - Wamellow wamellow.com
bot discord
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

create metrics component

Luna 89427d48 493f1042

+89 -32
+22 -32
app/profile/layout.tsx
··· 1 1 "use client"; 2 2 3 - import { Chip, Skeleton } from "@nextui-org/react"; 3 + import { Chip } from "@nextui-org/react"; 4 4 import Image from "next/image"; 5 5 import Link from "next/link"; 6 6 import { redirect } from "next/navigation"; ··· 13 13 import { userStore } from "@/common/user"; 14 14 import ImageReduceMotion from "@/components/image-reduce-motion"; 15 15 import { ListTab } from "@/components/list"; 16 + import { MetricCard, Metrics } from "@/components/metric-card"; 16 17 import { HomeButton, ScreenMessage, SupportButton } from "@/components/screen-message"; 18 + import { Skeleton } from "@/components/ui/skeleton"; 17 19 import { cacheOptions, getData } from "@/lib/api"; 18 20 import SadWumpusPic from "@/public/sad-wumpus.gif"; 19 21 import { ApiV1UsersMeGetResponse } from "@/typings"; ··· 71 73 <div className="text-lg flex flex-col md:flex-row md:items-center"> 72 74 <div className="flex gap-5"> 73 75 <Skeleton 74 - isLoaded={!!user?.id} 76 + isLoading={!user?.id} 75 77 className="rounded-full h-14 w-14 ring-offset-[var(--background-rgb)] ring-2 ring-offset-2 ring-violet-400/40 shrink-0 relative top-1" 76 78 > 77 79 <ImageReduceMotion ··· 89 91 </div> 90 92 : 91 93 <div className="flex flex-col gap-1"> 92 - <div className="text-2xl dark:text-neutral-200 text-neutral-800 font-medium">@{user?.username || "Unknown User"}</div> 94 + <div className="text-2xl dark:text-neutral-200 text-neutral-800 font-medium"> 95 + {user.globalName || user.username} 96 + </div> 93 97 <Chip 94 98 as={Link} 95 99 href="/vote" ··· 99 103 variant="flat" 100 104 radius="sm" 101 105 > 102 - <span className="font-bold uppercase"> {user.extended?.voteCount} votes </span> 106 + <span className="font-bold uppercase"> 107 + {user.extended?.voteCount} votes 108 + </span> 103 109 </Chip> 104 110 </div> 105 111 } 106 112 </div> 107 113 108 - <div className="md:ml-auto flex items-center gap-5 mt-6 md:mt-0"> 109 - <div> 110 - <div className="text-sm font-medium">Messages</div> 111 - {!user?.extended?.activity 112 - ? <Skeleton className="rounded-md mt-1.5 w-12 h-6 mb-1" /> 113 - : 114 - <CountUp className="text-2xl dark:text-neutral-100 text-neutral-900 font-medium" duration={4} end={user?.extended?.activity?.messages || 0} /> 115 - } 116 - </div> 117 - <div> 118 - <div className="text-sm font-medium">Invites</div> 119 - {!user?.extended?.activity 120 - ? <Skeleton className="rounded-md mt-1.5 w-8 h-6 mb-1" /> 121 - : 122 - <CountUp className="text-2xl dark:text-neutral-100 text-neutral-900 font-medium" duration={4} end={user?.extended?.activity?.invites || 0} /> 123 - } 124 - </div> 125 - <div> 126 - <div className="text-sm font-medium">Voice</div> 127 - {!user?.extended?.activity 128 - ? <Skeleton className="rounded-md mt-1.5 w-20 h-6 mb-1" /> 129 - : 130 - <span className="text-2xl dark:text-neutral-100 text-neutral-900 font-medium">{user?.extended?.activity?.formattedVoicetime}</span> 131 - } 132 - </div> 133 - </div> 114 + <Metrics> 115 + <MetricCard name="Messages" isLoading={!user?.extended}> 116 + <CountUp className="text-2xl dark:text-neutral-100 text-neutral-900 font-medium" duration={4} end={user?.extended?.activity?.messages || 0} /> 117 + </MetricCard> 118 + <MetricCard name="Invites" isLoading={!user?.extended}> 119 + <CountUp className="text-2xl dark:text-neutral-100 text-neutral-900 font-medium" duration={4} end={user?.extended?.activity?.invites || 0} /> 120 + </MetricCard> 121 + <MetricCard name="Voice" isLoading={!user?.extended}> 122 + <span className="text-2xl dark:text-neutral-100 text-neutral-900 font-medium">{user?.extended?.activity?.formattedVoicetime}</span> 123 + </MetricCard> 124 + </Metrics> 134 125 </div> 135 126 </div> 136 127 ··· 169 160 [] 170 161 ) 171 162 ]} 172 - url={"/profile"} 163 + url="/profile" 173 164 disabled={!user?.id} 174 165 /> 175 166 </Suspense> 176 167 177 168 {user?.id ? children : <></>} 178 - 179 169 </div> 180 170 ); 181 171 }
+37
components/metric-card.tsx
··· 1 + import { Skeleton } from "./ui/skeleton"; 2 + 3 + export function Metrics({ 4 + children 5 + }: { 6 + children: JSX.Element[]; 7 + }) { 8 + return ( 9 + <div className="md:ml-auto flex items-center gap-5 mt-6 md:mt-0"> 10 + {children} 11 + </div> 12 + ); 13 + } 14 + 15 + export function MetricCard({ 16 + children, 17 + name, 18 + isLoading 19 + }: { 20 + children: React.ReactNode; 21 + name: string; 22 + isLoading: boolean; 23 + }) { 24 + return ( 25 + <div> 26 + <div className="text-sm font-medium"> 27 + {name} 28 + </div> 29 + {isLoading 30 + ? <Skeleton className="rounded-md mt-1.5 w-20 h-6 mb-1" /> 31 + : <span className="text-2xl dark:text-neutral-100 text-neutral-900 font-medium"> 32 + {children} 33 + </span> 34 + } 35 + </div> 36 + ); 37 + }
+30
components/ui/skeleton.tsx
··· 1 + import { cn } from "@/utils/cn"; 2 + 3 + export interface SkeletonProps 4 + extends React.HTMLAttributes<HTMLDivElement> { 5 + isLoading?: boolean; 6 + } 7 + 8 + function Skeleton({ 9 + className, 10 + children, 11 + isLoading, 12 + ...props 13 + }: SkeletonProps) { 14 + if (!isLoading) { 15 + return ( 16 + <div className={className} {...props}> 17 + {children} 18 + </div> 19 + ); 20 + } 21 + 22 + return ( 23 + <div 24 + className={cn("animate-pulse rounded-md bg-primary/10", className)} 25 + {...props} 26 + /> 27 + ); 28 + } 29 + 30 + export { Skeleton };