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.

export homepage components, add faq

Luna f43508da 9077192f

+226 -92
+34
app/(home)/ai/pagination.component.tsx
··· 1 + "use client"; 2 + 3 + import { Pagination as UiPagination } from "@nextui-org/react"; 4 + import { useRouter } from "next/navigation"; 5 + 6 + import { getCanonicalUrl } from "@/utils/urls"; 7 + 8 + export default function Pagination( 9 + { 10 + searchParams, 11 + pages 12 + }: { 13 + searchParams: { page: string, model: string }; 14 + pages: number; 15 + } 16 + ) { 17 + const router = useRouter(); 18 + 19 + return ( 20 + <UiPagination 21 + className="w-full" 22 + classNames={{ prev: "bg-wamellow", item: "bg-wamellow", next: "bg-wamellow" }} 23 + color="secondary" 24 + showControls 25 + total={pages} 26 + size="lg" 27 + page={parseInt(searchParams.page || "0")} 28 + onChange={(now) => { 29 + router.push(getCanonicalUrl("ai", `?page=${now}${searchParams.model ? `&model=${searchParams.model}` : ""}`)); 30 + }} 31 + /> 32 + ); 33 + 34 + }
+69
app/(home)/commands.component.tsx
··· 1 + import { Chip } from "@nextui-org/react"; 2 + import { HiFire, HiInformationCircle } from "react-icons/hi"; 3 + 4 + import Box from "@/components/box"; 5 + import { defaultFetchOptions } from "@/lib/api"; 6 + import { intl } from "@/utils/numbers"; 7 + 8 + interface Commands { 9 + name: string; 10 + description: string; 11 + uses: number; 12 + } 13 + 14 + export default async function Commands() { 15 + 16 + const commands = await fetch(`${process.env.NEXT_PUBLIC_API}/commands`, defaultFetchOptions) 17 + .then((res) => res.json()) 18 + .catch(() => null) as Commands[] | null; 19 + 20 + return ( 21 + <Box 22 + none 23 + className="p-5 pb-3 dark:bg-wamellow bg-wamellow-100 rounded-lg my-4 w-full" 24 + > 25 + <div className="flex"> 26 + <Chip 27 + color="secondary" 28 + variant="flat" 29 + size="sm" 30 + startContent={<HiFire className="ml-1" />} 31 + > 32 + <span className="font-semibold">Popular Slash Commands</span> 33 + </Chip> 34 + <div className="ml-auto flex items-center gap-1 opacity-80"> 35 + <span className="text-xs">Since 7th December</span> 36 + <HiInformationCircle /> 37 + </div> 38 + </div> 39 + 40 + {commands && Array.isArray(commands) ? 41 + <div className="divide-y divide-wamellow"> 42 + {commands 43 + .sort((a, b) => b.uses - a.uses) 44 + .slice(0, 4) 45 + .map((command) => ( 46 + <div 47 + key={command.name} 48 + className="text-base py-4 flex flex-col md:flex-row gap-4 md:items-center" 49 + > 50 + <div className="-mb-2 md:mb-0 flex items-center h-min"> 51 + <span className="dark:text-neutral-100 text-neutral-900 text-xl font-semibold md:font-medium">/{command.name}</span> 52 + <span className="ml-auto italic text-sm md:hidden opacity-80">{intl.format(command.uses)} uses</span> 53 + </div> 54 + <span>{command.description}</span> 55 + <span className="ml-auto italic text-sm hidden md:block">{intl.format(command.uses)} uses</span> 56 + </div> 57 + )) 58 + } 59 + </div> 60 + : 61 + <div className="flex flex-col items-center my-10"> 62 + <div className="text-3xl dark:text-neutral-100 text-neutral-900 font-semibold mb-4">Something went wrong...</div> 63 + <div className="text-md dark:text-neutral-400 text-neutral-600 font-semibold">The commands list could not be loaded at this time</div> 64 + </div> 65 + } 66 + 67 + </Box> 68 + ); 69 + }
+58
app/(home)/faq.component.tsx
··· 1 + "use client"; 2 + 3 + import { Accordion, AccordionItem, Code } from "@nextui-org/react"; 4 + import Link from "next/link"; 5 + 6 + import { webStore } from "@/common/webstore"; 7 + 8 + const data = [ 9 + { 10 + title: "How do I invite Wamellow to my server?", 11 + subtitle: "Invite Wamellow to your server to get started!", 12 + content: ( 13 + <ol className="list-decimal list-inside marker:text-neutral-500"> 14 + <li> 15 + Be sure to have the <Code color="secondary">Manage Server</Code> permission on the server you want to <Link href="/login?invite=true" target="_blanket" className="text-violet-400">invite Wamellow</Link> to. 16 + </li> 17 + <li> 18 + Open Discord{"'"}s add-bot flow at <Link href="/login?invite=true" target="_blanket" className="text-violet-400">wamellow.com/invite</Link>. 19 + </li> 20 + <li> 21 + Select a server and click on {'"'}Continue{'"'} 22 + </li> 23 + <li> 24 + Do <span className="font-semibold">not uncheck</span> any permissions and click on {'"'}Authorize{'"'} 25 + </li> 26 + <li> 27 + <span className="font-semibold">Done!</span> 🎉 Wamellow should now find yourself on the Dashboard for your server! 28 + </li> 29 + </ol> 30 + ) 31 + } 32 + ]; 33 + 34 + export default function Faq() { 35 + const web = webStore((w) => w); 36 + 37 + return ( 38 + <Accordion 39 + className="bg-wamellow border-none rounded-lg overflow-hidden" 40 + variant="shadow" 41 + selectionMode="multiple" 42 + defaultExpandedKeys={["1"]} 43 + disableAnimation={web.reduceMotions} 44 + > 45 + {data.map((item, index) => ( 46 + <AccordionItem 47 + key={index} 48 + aria-label={item.title} 49 + title={item.title} 50 + classNames={{ content: "mb-2 space-y-4" }} 51 + subtitle={item.subtitle} 52 + > 53 + {item.content} 54 + </AccordionItem> 55 + ))} 56 + </Accordion> 57 + ); 58 + }
+16 -82
app/(home)/page.tsx
··· 3 3 import Image from "next/image"; 4 4 import Link from "next/link"; 5 5 import { BsDiscord, BsYoutube } from "react-icons/bs"; 6 - import { HiArrowNarrowRight, HiArrowRight, HiCash, HiChevronRight, HiFire, HiInformationCircle, HiLockOpen, HiUserAdd } from "react-icons/hi"; 6 + import { HiArrowNarrowRight, HiArrowRight, HiCash, HiFire, HiLockOpen, HiUserAdd } from "react-icons/hi"; 7 7 8 8 import Box from "@/components/box"; 9 - import { StatsBar } from "@/components/counter"; 9 + import Comment from "@/components/comment"; 10 10 import DiscordChannelCategory from "@/components/discord/channel-category"; 11 11 import DiscordChannelVoice from "@/components/discord/channel-voice"; 12 12 import Highlight from "@/components/discord/markdown"; ··· 22 22 import SpacePic from "@/public/space.webp"; 23 23 import WaifuPic from "@/public/waifu.webp"; 24 24 import WelcomePic from "@/public/welcome.webp"; 25 - import { ApiV1StatisticsGetResponse, ApiV1TopguildsGetResponse } from "@/typings"; 25 + import { ApiV1TopguildsGetResponse } from "@/typings"; 26 26 import cn from "@/utils/cn"; 27 27 import { toFixedArrayLength } from "@/utils/fixed-array-length"; 28 - import { convertMonthToName } from "@/utils/time"; 29 28 import { actor } from "@/utils/tts"; 30 29 import { getCanonicalUrl } from "@/utils/urls"; 31 30 31 + import Commands from "./commands.component"; 32 + import Faq from "./faq.component"; 33 + import Stats from "./stats.component"; 34 + 32 35 const montserrat = Montserrat({ subsets: ["latin"] }); 33 36 const handwritten = Patrick_Hand({ subsets: ["latin"], weight: "400" }); 34 37 ··· 36 39 37 40 const fetchOptions = { headers: { Authorization: process.env.API_SECRET as string }, next: { revalidate: 60 * 60 } }; 38 41 39 - interface Commands { 40 - name: string; 41 - description: string; 42 - uses: number; 43 - } 44 - 45 42 export default async function Home() { 46 - const topGuilds = await fetch(`${process.env.NEXT_PUBLIC_API}/top-guilds`, fetchOptions).then((res) => res.json()).catch(() => null) as ApiV1TopguildsGetResponse[] | null; 47 - const stats = await fetch(`${process.env.NEXT_PUBLIC_API}/statistics`, fetchOptions).then((res) => res.json()).catch(() => null) as ApiV1StatisticsGetResponse | null; 48 - const commands = await fetch(`${process.env.NEXT_PUBLIC_API}/commands`, fetchOptions).then((res) => res.json()).catch(() => ([])) as Commands[]; 49 - 50 - const intl = new Intl.NumberFormat("en", { notation: "standard" }); 43 + const topGuilds = await fetch(`${process.env.NEXT_PUBLIC_API}/top-guilds`, fetchOptions) 44 + .then((res) => res.json()) 45 + .catch(() => null) as ApiV1TopguildsGetResponse[] | null; 51 46 52 47 const styles = { 53 48 h2: cn(montserrat.className, "lg:text-5xl text-4xl bg-gradient-to-b bg-clip-text text-transparent from-neutral-200 from-40% to-violet-300 font-bold underline decoration-violet-400"), ··· 187 182 > 188 183 <ImageReduceMotion 189 184 alt="server" 190 - className="rounded-xl" 185 + className="rounded-xl bg-wamellow" 191 186 url={(guild.icon || "/discord.webp")?.split(".").slice(0, -1).join(".")} 192 187 size={128} 193 188 /> ··· 716 711 717 712 </article> 718 713 714 + <Faq /> 719 715 720 - <Box none className="p-5 pb-3 dark:bg-wamellow bg-wamellow-100 rounded-lg mt-4 w-full"> 721 - <div className="flex"> 722 - <Chip 723 - color="secondary" 724 - variant="flat" 725 - size="sm" 726 - startContent={<HiFire className="ml-1" />} 727 - > 728 - <span className="font-semibold">Popular Slash Commands</span> 729 - </Chip> 730 - <div className="ml-auto flex items-center gap-1 opacity-80"> 731 - <span className="text-xs">Since 7th December</span> 732 - <HiInformationCircle /> 733 - </div> 734 - </div> 735 - <div className="divide-y divide-wamellow"> 736 - {Array.isArray(commands) && commands 737 - .sort((a, b) => b.uses - a.uses) 738 - .slice(0, 4) 739 - .map((command) => ( 740 - <div key={command.name} className="text-base py-4 flex flex-col md:flex-row gap-4 md:items-center"> 741 - <div className="-mb-2 md:mb-0 flex items-center h-min"> 742 - <span className="dark:text-neutral-100 text-neutral-900 text-xl font-semibold md:font-medium">/{command.name}</span> 743 - <span className="ml-auto italic text-sm md:hidden opacity-80">{intl.format(command.uses)} uses</span> 744 - </div> 745 - <span>{command.description}</span> 746 - <span className="ml-auto italic text-sm hidden md:block">{intl.format(command.uses)} uses</span> 747 - </div> 748 - )) 749 - } 750 - </div> 751 - {(!commands || !Array.isArray(commands)) && 752 - <div className="flex flex-col items-center my-10"> 753 - <div className="text-3xl dark:text-neutral-100 text-neutral-900 font-semibold mb-4">Something went wrong...</div> 754 - <div className="text-md dark:text-neutral-400 text-neutral-600 font-semibold">The commands list could not be loaded at this time</div> 755 - </div> 756 - } 757 - </Box> 758 - 759 - <div className="h-6" /> 760 - 761 - <StatsBar 762 - items={[ 763 - { 764 - name: "Guilds using us", 765 - number: stats?.approximateGuildCount || 0, 766 - gained: stats?.guildsGained 767 - }, 768 - { 769 - name: "Users using us", 770 - number: stats?.approximateUserCount || 0, 771 - gained: stats?.usersGained || 0 772 - }, 773 - { 774 - name: "Votes for us", 775 - number: stats?.approximateVoteCount || 0, 776 - gained: stats?.votesGained || 0, 777 - append: `in ${convertMonthToName(new Date().getMonth())}` 778 - }, 779 - { 780 - name: "Our experience with", 781 - number: stats?.globalGuilds || 0, 782 - gained: "guilds, 5 bots", 783 - info: "https://discordlist.gg/user/821472922140803112" 784 - } 785 - ]} 786 716 <Comment 787 717 username="Luna’s Grandpa <3" 788 718 bio="likes feta and wine" 789 719 avatar={SpacePic} 790 720 content="FUCK EVERYTHING! EXCEPT LUNA, LUNA MUST BE PROTECTED AT ALL COSTS" 791 721 /> 722 + 723 + <Commands /> 724 + 725 + <Stats /> 792 726 793 727 </div > 794 728 );
+49
app/(home)/stats.component.tsx
··· 1 + import { StatsBar } from "@/components/counter"; 2 + import { defaultFetchOptions } from "@/lib/api"; 3 + import { convertMonthToName } from "@/utils/time"; 4 + 5 + export interface Statistics { 6 + approximateGuildCount: number; 7 + guildsGained: number; 8 + approximateUserCount: number; 9 + usersGained: number; 10 + approximateVoteCount: number; 11 + votesGained: number; 12 + globalGuilds: number; 13 + } 14 + 15 + export default async function Stats() { 16 + const stats = await fetch(`${process.env.NEXT_PUBLIC_API}/statistics`, defaultFetchOptions) 17 + .then((res) => res.json()) 18 + .catch(() => null) as Statistics | null; 19 + 20 + return ( 21 + <StatsBar 22 + items={[ 23 + { 24 + name: "Guilds using us", 25 + number: stats?.approximateGuildCount || 0, 26 + gained: stats?.guildsGained 27 + }, 28 + { 29 + name: "Users using us", 30 + number: stats?.approximateUserCount || 0, 31 + gained: stats?.usersGained || 0 32 + }, 33 + { 34 + name: "Votes for us", 35 + number: stats?.approximateVoteCount || 0, 36 + gained: stats?.votesGained || 0, 37 + append: `in ${convertMonthToName(new Date().getMonth())}` 38 + }, 39 + { 40 + name: "Our experience with", 41 + number: stats?.globalGuilds || 0, 42 + gained: "guilds, 5 bots", 43 + info: "https://discordlist.gg/user/821472922140803112" 44 + } 45 + ]} 46 + /> 47 + ); 48 + 49 + }
-10
typings.ts
··· 15 15 partnered: boolean; 16 16 } 17 17 18 - export interface ApiV1StatisticsGetResponse { 19 - approximateGuildCount: number; 20 - guildsGained: number; 21 - approximateUserCount: number; 22 - usersGained: number; 23 - approximateVoteCount: number; 24 - votesGained: number; 25 - globalGuilds: number; 26 - } 27 - 28 18 export interface UserGuild { 29 19 id: string; 30 20 name: string;