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.

add premium item tooltip

Luna 765a9edb af6d5cb1

+118 -44
+114 -39
app/(home)/premium/page.tsx
··· 1 1 import type { Metadata } from "next"; 2 - import { Montserrat } from "next/font/google"; 2 + import { Montserrat, Patrick_Hand } from "next/font/google"; 3 + import Image from "next/image"; 3 4 import Link from "next/link"; 4 5 import { BsQuestionLg } from "react-icons/bs"; 5 - import { HiLightningBolt, HiOutlineCheck, HiX } from "react-icons/hi"; 6 + import { HiArrowRight, HiLightningBolt, HiOutlineCheck, HiOutlineInformationCircle, HiUser, HiUserGroup, HiX } from "react-icons/hi"; 6 7 import { IoMdInfinite } from "react-icons/io"; 7 8 8 9 import Comment from "@/components/comment"; 9 10 import ImageGrid from "@/components/image-grid"; 10 11 import { OverviewLink } from "@/components/overview-link"; 12 + import { UserAvatar } from "@/components/ui/avatar"; 11 13 import { Badge } from "@/components/ui/badge"; 14 + import { InputBaseAdornment, InputBaseAdornmentButton } from "@/components/ui/input-base"; 15 + import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; 12 16 import { defaultFetchOptions } from "@/lib/api"; 17 + import type User from "@/lib/discord/user"; 18 + import { getUser } from "@/lib/discord/user"; 19 + import ArrowPic from "@/public/icons/arroww.webp"; 13 20 import type { ApiV1TopguildsGetResponse } from "@/typings"; 14 21 import { cn } from "@/utils/cn"; 15 22 import { getBaseUrl, getCanonicalUrl } from "@/utils/urls"; ··· 17 24 import { Subscribe } from "./subscribe.component"; 18 25 19 26 const montserrat = Montserrat({ subsets: ["latin"] }); 27 + const handwritten = Patrick_Hand({ subsets: ["latin"], weight: "400" }); 28 + 29 + const bots = ["1125449347451068437", "985213199248924722", "1097907896987160666"].map((userId) => getUser(userId)); 20 30 const maybe = null; 21 31 22 32 const items = [ 23 33 { title: "Price", free: "€0 /forever", premium: "€4 /month" }, 24 - { title: "TTS Translations", free: 10_000, premium: 100_000, unit: "chars" }, 25 - { title: "Custom commands", free: 30, premium: Infinity }, 34 + 35 + { title: "Your Benefits", icon: <HiUser /> }, 36 + { title: "TTS Translations", free: 10_000, premium: 100_000, unit: "chars /month" }, 37 + { title: "Bypass voting", free: false, premium: true, tooltip: <OtherBotsTooltip /> }, 38 + { title: "Bypass passport", free: false, premium: true }, 39 + { title: "Premium role", free: false, premium: true, url: "/support" }, 40 + { title: "Spotify control", free: maybe, premium: true, url: "/profile/connections" }, 41 + { title: "Fast support", free: true, premium: true }, 42 + 43 + { title: "For Your Server", icon: <HiUserGroup /> }, 26 44 { title: "Social notifications", free: 30, premium: Infinity }, 45 + { title: "Custom commands", free: 30, premium: Infinity }, 27 46 { title: "Dailyposts", free: 4, premium: 20 }, 47 + { title: "Welcome pings", free: 5, premium: 15 }, 28 48 { title: "Welcome roles", free: 5, premium: 10 }, 29 - { title: "Welcome pings", free: 5, premium: 15 }, 30 - { title: "Spotify control", free: maybe, premium: true, url: "/profile/connections" }, 31 - { title: "Bypass passport", free: false, premium: true }, 32 - { title: "Bypass Voting", free: false, premium: true }, 33 - { title: "Crosspost social notifications", free: false, premium: true }, 34 - { title: "Fast Support", free: true, premium: true } 49 + { title: "Crosspost notifications", free: false, premium: true } 35 50 ]; 36 51 37 52 export const revalidate = 3600; ··· 83 98 84 99 {topGuilds && ( 85 100 <ImageGrid 86 - images={topGuilds 87 - .sort((a, b) => b.memberCount - a.memberCount) 88 - .map((guild) => ({ 89 - id: guild.id, 90 - url: (guild.icon || "/discord.webp").replace("gif", "webp"), 91 - link: getCanonicalUrl("leaderboard", guild.id) 92 - }))} 101 + images={ 102 + topGuilds 103 + .sort((a, b) => b.memberCount - a.memberCount) 104 + .map((guild) => ({ 105 + id: guild.id, 106 + url: (guild.icon || "/discord.webp").replace("gif", "webp"), 107 + link: getCanonicalUrl("leaderboard", guild.id) 108 + })) 109 + } 93 110 /> 94 111 ) } 95 112 96 - <div className="dark:bg-wamellow bg-wamellow-100 dark:text-neutral-300 text-neutral-700 mt-10 w-full p-4 rounded-xl text-xl divide-y divide-wamellow"> 113 + <div className="dark:bg-wamellow bg-wamellow-100 dark:text-neutral-300 text-neutral-700 mt-10 w-full rounded-xl overflow-hidden"> 97 114 98 - <div className="flex items-center pb-4 text-2xl font-semibold"> 115 + <div className="flex items-center pb-4 text-2xl p-4 font-semibold bg-wamellow"> 99 116 <span className="dark:text-neutral-100 text-neutral-900 w-2/4 block md:hidden">Features</span> 100 117 <span className="dark:text-neutral-100 text-neutral-900 w-2/4 hidden md:block">Pricing and Features</span> 101 118 <span className="bg-gradient-to-r from-red-400 to-pink-400 bg-clip-text text-transparent w-1/4 ">Free</span> 102 119 <span className="bg-gradient-to-r from-indigo-400 to-pink-400 bg-clip-text text-transparent w-1/4">Premium</span> 103 120 </div> 104 121 105 - {items.map((item) => ( 106 - <div key={item.title} className="flex items-center py-4"> 107 - <span className="md:text-base text-sm font-medium w-2/4 md:pr-0 pr-4">{item.title}</span> 108 - <span className="dark:text-neutral-200 text-neutral-700 font-medium w-1/4"> 109 - {displayState(item.free, item.unit)} 110 - </span> 111 - <span className="dark:text-neutral-200 text-neutral-700 font-medium w-1/4 flex"> 112 - {displayState(item.premium, item.unit)} 113 - {item.url && <Link href={item.url} target="_blank" className="ml-auto mr-3 hover:underline italic text-sm text-neutral-500 hidden md:block relative top-0.5">Take me there {"->"}</Link>} 114 - </span> 115 - </div> 116 - ))} 122 + <div className="p-4 pt-0"> 123 + {items.map((item) => ( 124 + (item.free !== undefined && item.premium !== undefined) 125 + ? <div key={item.title} className="flex items-center py-4 border-b border-wamellow"> 126 + <div className="md:text-base text-sm font-medium w-2/4 md:pr-0 pr-4 flex gap-1"> 127 + {item.title} 128 + {item.tooltip && ( 129 + <Tooltip> 130 + <InputBaseAdornment> 131 + <InputBaseAdornmentButton asChild> 132 + <TooltipTrigger> 133 + <HiOutlineInformationCircle /> 134 + </TooltipTrigger> 135 + </InputBaseAdornmentButton> 136 + </InputBaseAdornment> 137 + <TooltipContent> 138 + {item.tooltip} 139 + </TooltipContent> 140 + </Tooltip> 141 + )} 142 + </div> 143 + <span className="text-xl dark:text-neutral-200 text-neutral-700 font-medium w-1/4 flex items-end"> 144 + {displayState(item.free, item.unit)} 145 + </span> 146 + <span className="text-xl dark:text-neutral-200 text-neutral-700 font-medium w-1/4 flex items-end"> 147 + {displayState(item.premium, item.unit)} 148 + {item.url && ( 149 + <Link 150 + href={item.url} 151 + target="_blank" 152 + className="ml-auto mr-3 hover:underline italic text-sm text-muted-foreground hidden md:block relative bottom-0.5" 153 + > 154 + Take me there <HiArrowRight className="inline ml-1 mb-px" /> 155 + </Link> 156 + )} 157 + </span> 158 + </div> 159 + : <div key={item.title}> 160 + <Badge className="flex items-center gap-2 md:text-muted-foreground text-xs font-semibold pb-1 rounded-t-none"> 161 + {item.icon} 162 + {item.title} 163 + </Badge> 164 + </div> 165 + ))} 117 166 118 - <div className="hidden md:flex items-center pt-4"> 119 - <div className="w-1/2 text-sm text-neutral-400 flex justify-between pr-4"> 120 - <div /> 121 - extra monthly donation 122 - </div> 123 - <div className="flex w-1/2 gap-4"> 124 - <Subscribe /> 167 + <div className="hidden md:flex items-center pt-4"> 168 + <div className="w-1/2 text-sm text-neutral-400 flex justify-between pr-4"> 169 + <div /> 170 + <div className={cn("text-medium text-neutral-500 font-medium rotate-2 flex items-center gap-1 mt-4", handwritten.className)}> 171 + extra monthly donation 172 + <Image src={ArrowPic} width={24} height={24} alt="arrow up" className="size-5 -scale-x-100 mb-1.5 rotate-6" draggable={false} /> 173 + </div> 174 + </div> 175 + <div className="flex w-1/2 gap-4"> 176 + <Subscribe /> 177 + </div> 125 178 </div> 126 179 </div> 127 180 ··· 189 242 } 190 243 191 244 if (is === Infinity) return <IoMdInfinite className="w-7 h-7" title="Infinite" />; 192 - if (typeof is === "number") return <>{is.toLocaleString()} {unit}</>; 245 + if (typeof is === "number") return <>{is.toLocaleString()} <span className="text-sm text-muted-foreground">{unit}</span></>; 193 246 return is; 247 + } 248 + 249 + async function OtherBotsTooltip() { 250 + const resolved = await Promise.all(bots) as User[]; 251 + 252 + return ( 253 + <div className="font-normal text-muted-foreground flex items-center"> 254 + Applies at 255 + {" "} 256 + {resolved.map((user) => ( 257 + <div key={user.id} className="font-semibold text-neutral-300 flex items-center"> 258 + <UserAvatar 259 + key={"avt-" + user.id} 260 + alt={user.username} 261 + className="ml-1.5 mr-0.5 size-4" 262 + src={user.avatarUrl || "/discord.webp"} 263 + /> 264 + {user.username} 265 + </div> 266 + ))} 267 + </div> 268 + ); 194 269 }
+1 -1
components/ui/avatar.tsx
··· 12 12 <AvatarPrimitive.Root 13 13 ref={ref} 14 14 className={cn( 15 - "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", 15 + "bg-muted relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", 16 16 className 17 17 )} 18 18 {...props}
+1 -1
components/ui/badge.tsx
··· 8 8 { 9 9 variants: { 10 10 variant: { 11 - default: "border-transparent bg-primary/10 text-primary-foreground", 11 + default: "border-transparent bg-wamellow text-primary-foreground", 12 12 secondary: "border-transparent bg-secondary text-secondary-foreground", 13 13 flat: "border-transparent bg-flat/40 text-flat-foreground", 14 14 destructive: "border-transparent bg-destructive text-destructive-foreground",
+2 -3
components/ui/tooltip.tsx
··· 9 9 10 10 const Tooltip = ({ delayDuration, ...props }: React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Root>) => ( 11 11 <TooltipPrimitive.Root 12 - delayDuration={delayDuration ?? 0} 12 + delayDuration={delayDuration ?? 4} 13 13 {...props} 14 14 /> 15 15 ); ··· 20 20 const TooltipContent = React.forwardRef< 21 21 React.ElementRef<typeof TooltipPrimitive.Content>, 22 22 React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> 23 - >(({ className, sideOffset = 4, ...props }, ref) => ( 23 + >(({ className, ...props }, ref) => ( 24 24 <TooltipPrimitive.Content 25 25 ref={ref} 26 - sideOffset={sideOffset} 27 26 className={cn( 28 27 "z-50 overflow-hidden rounded-md bg-popover/30 backdrop-blur-md px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 max-w-md data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", 29 28 className