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.

delete user route

Luna 0478fa2e 392de923

-182
-11
app/user/[userId]/api.ts
··· 1 - import { defaultFetchOptions } from "@/lib/api"; 2 - import type { ApiError, ApiV1UsersGetResponse } from "@/typings"; 3 - 4 - export async function getUser(userId: string): Promise<ApiV1UsersGetResponse | ApiError | undefined> { 5 - const res = await fetch( 6 - `${process.env.NEXT_PUBLIC_API}/users/${userId}`, 7 - defaultFetchOptions 8 - ); 9 - 10 - return res.json(); 11 - }
-71
app/user/[userId]/layout.tsx
··· 1 - import { Image } from "@nextui-org/react"; 2 - 3 - import ImageReduceMotion from "@/components/image-reduce-motion"; 4 - import paintPic from "@/public/paint.webp"; 5 - 6 - import { getUser } from "./api"; 7 - import Side from "./side.component"; 8 - 9 - interface Props { 10 - params: Promise<{ userId: string; }>; 11 - children: React.ReactNode; 12 - } 13 - 14 - export default async function RootLayout({ params, children }: Props) { 15 - const { userId } = await params; 16 - 17 - const user = await getUser(userId); 18 - const userExists = user && "id" in user; 19 - 20 - return ( 21 - <div className="w-full"> 22 - 23 - <div className="relative mb-16 w-full"> 24 - <Image 25 - alt="" 26 - className="w-full object-cover" 27 - classNames={{ img: "h-36 md:h-64", blurredImg: "h-40 md:h-72 -top-5" }} 28 - isBlurred 29 - src={userExists && user.bannerUrl ? user.bannerUrl : paintPic.src} 30 - width={3840 / 2} 31 - height={2160 / 2} 32 - /> 33 - 34 - <div 35 - className="text-lg flex gap-5 items-center absolute top-[100px] md:top-[203px] left-[12px] md:left-8 py-4 px-5 rounded-3xl z-20 backdrop-blur-3xl backdrop-brightness-75 shadow-md" 36 - > 37 - <ImageReduceMotion 38 - alt="Server icon" 39 - className="rounded-full h-14 w-14 ring-offset-[var(--background-rgb)] ring-2 ring-offset-2 ring-violet-400/40" 40 - url={userExists ? `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}` : "/discord"} 41 - size={128} 42 - /> 43 - 44 - <div className="flex flex-col gap-1"> 45 - <div className="text-2xl dark:text-neutral-200 text-neutral-800 font-medium max-w-md truncate"> 46 - {userExists ? (user.globalName || user.username) : "Unknown User"} 47 - </div> 48 - <div className="text-sm font-semibold flex items-center gap-1"> 49 - @{userExists ? user.username : "Unknown User"} 50 - </div> 51 - </div> 52 - </div> 53 - </div> 54 - 55 - <div className="md:flex"> 56 - 57 - <div className="lg:w-3/4 md:w-2/3 w-full md:mr-6" > 58 - {children} 59 - </div> 60 - 61 - <div className="lg:w-1/4 md:w-1/3 mt-8 md:mt-0"> 62 - <Side 63 - user={user} 64 - /> 65 - </div> 66 - 67 - </div> 68 - 69 - </div > 70 - ); 71 - }
-9
app/user/[userId]/page.tsx
··· 1 - export const revalidate = 3600; 2 - 3 - export default function Home() { 4 - return ( 5 - <> 6 - <i>User has no bio yet</i> 7 - </> 8 - ); 9 - }
-91
app/user/[userId]/side.component.tsx
··· 1 - "use client"; 2 - 3 - import { Accordion, AccordionItem, Skeleton } from "@nextui-org/react"; 4 - import { useCookies } from "next-client-cookies"; 5 - import { HiAnnotation, HiLink, HiVolumeUp } from "react-icons/hi"; 6 - 7 - import { ClientCountUp } from "@/components/counter"; 8 - import type { ApiError, ApiV1UsersGetResponse } from "@/typings"; 9 - 10 - export default function Side({ 11 - user 12 - }: { 13 - user: ApiV1UsersGetResponse | ApiError | undefined; 14 - }) { 15 - const cookies = useCookies(); 16 - const userExists = user && "id" in user; 17 - 18 - function Counters() { 19 - return ( 20 - <div className="md:ml-auto grid items-center gap-5 mt-6 md:mt-0"> 21 - <div> 22 - <div className="flex items-center gap-1 text-sm font-medium"> 23 - <HiVolumeUp /> 24 - Voice 25 - </div> 26 - 27 - {!userExists || !user?.activity 28 - ? <Skeleton className="rounded-md mt-1.5 w-20 h-6 mb-1" /> 29 - : 30 - <span className="text-2xl dark:text-neutral-100 text-neutral-900 font-medium"> 31 - {user.activity?.formattedVoicetime} 32 - </span> 33 - } 34 - </div> 35 - <div> 36 - <div className="flex items-center gap-1 text-sm font-medium"> 37 - <HiAnnotation /> 38 - Messages 39 - </div> 40 - 41 - {!userExists || !user?.activity 42 - ? <Skeleton className="rounded-md mt-1.5 w-12 h-6 mb-1" /> 43 - : 44 - <ClientCountUp 45 - className="text-2xl dark:text-neutral-100 text-neutral-900 font-medium" 46 - end={user.activity.messages || 0} 47 - /> 48 - } 49 - </div> 50 - <div> 51 - <div className="flex items-center gap-1 text-sm font-medium"> 52 - <HiLink /> 53 - Invites 54 - </div> 55 - 56 - {!userExists || !user?.activity 57 - ? <Skeleton className="rounded-md mt-1.5 w-8 h-6 mb-1" /> 58 - : 59 - <ClientCountUp 60 - className="text-2xl dark:text-neutral-100 text-neutral-900 font-medium" 61 - end={user.activity.invites || 0} 62 - /> 63 - } 64 - </div> 65 - </div> 66 - ); 67 - } 68 - 69 - return ( 70 - <div> 71 - 72 - <Accordion 73 - variant="shadow" 74 - className="bg-wamellow" 75 - selectionMode="multiple" 76 - defaultExpandedKeys={["1"]} 77 - disableAnimation={cookies.get("reduceMotions") === "true"} 78 - > 79 - <AccordionItem 80 - key="1" 81 - aria-label="user's activity" 82 - title={`${userExists ? user.username : "Unknown"}'s Activity`} 83 - classNames={{ content: "mb-2 space-y-4" }} 84 - subtitle="Activity from all servers" 85 - > 86 - <Counters /> 87 - </AccordionItem> 88 - </Accordion> 89 - </div> 90 - ); 91 - }