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

use cookies for devTools & reduceMotions

+84 -94
+3 -3
app/(home)/faq.component.tsx
··· 2 2 3 3 import { Accordion, AccordionItem, Code } from "@nextui-org/react"; 4 4 import Link from "next/link"; 5 + import { useCookies } from "next-client-cookies"; 5 6 import { HiCash, HiChat, HiExternalLink, HiLockClosed, HiUserAdd } from "react-icons/hi"; 6 7 7 - import { webStore } from "@/common/webstore"; 8 8 import cn from "@/utils/cn"; 9 9 10 10 const data = [ ··· 98 98 ]; 99 99 100 100 export default function Faq() { 101 - const web = webStore((w) => w); 101 + const cookies = useCookies(); 102 102 103 103 return ( 104 104 <div className="my-4 w-full"> ··· 107 107 className="rounded-lg overflow-hidden" 108 108 variant="splitted" 109 109 defaultExpandedKeys={["0"]} 110 - disableAnimation={web.reduceMotions} 110 + disableAnimation={cookies.get("reduceMotions") === "true"} 111 111 > 112 112 {data.map((item, index) => ( 113 113 <AccordionItem
+4 -4
app/ai-gallery/[uploadId]/side.component.tsx
··· 2 2 3 3 import { Accordion, AccordionItem, Button, Chip, Tooltip } from "@nextui-org/react"; 4 4 import Link from "next/link"; 5 + import { useCookies } from "next-client-cookies"; 5 6 import { FaReddit, FaTwitter } from "react-icons/fa"; 6 7 import { HiHand, HiShare, HiUserGroup } from "react-icons/hi"; 7 8 8 - import { webStore } from "@/common/webstore"; 9 9 import Ad from "@/components/ad"; 10 10 import { CopyToClipboardButton } from "@/components/copy-to-clipboard"; 11 11 import ImageReduceMotion from "@/components/image-reduce-motion"; ··· 26 26 guild: ApiV1GuildsGetResponse | ApiError | undefined; 27 27 analytics: { results: AnalyticsResponse[] } | AnalyticsError | undefined; 28 28 }) { 29 - const web = webStore((w) => w); 29 + const cookies = useCookies(); 30 30 31 31 const prompt = "prompt" in upload 32 32 ? truncate(upload.prompt.split(" ").map((str) => str.replace(/^\w/, (char) => char.toUpperCase())).join(" "), 32) ··· 76 76 className="bg-wamellow" 77 77 selectionMode="multiple" 78 78 defaultExpandedKeys={["1"]} 79 - disableAnimation={web.reduceMotions} 79 + disableAnimation={cookies.get("reduceMotions") === "true"} 80 80 > 81 81 <AccordionItem 82 82 key="1" ··· 159 159 variant="shadow" 160 160 className="bg-wamellow" 161 161 selectionMode="multiple" 162 - disableAnimation={web.reduceMotions} 162 + disableAnimation={cookies.get("reduceMotions") === "true"} 163 163 > 164 164 <AccordionItem 165 165 key="1"
+1 -1
app/dashboard/[guildId]/layout.tsx
··· 194 194 </div> 195 195 196 196 <div className="md:ml-auto mt-6 md:mt-0"> 197 - {web.devToolsEnabled && 197 + {cookies.get("devTools") && 198 198 <CopyToClipboardButton 199 199 needsWait 200 200 text={getCanonicalUrl("leaderboard", params.guildId.toString())}
+1 -5
app/dashboard/[guildId]/leaderboards/page.tsx
··· 22 22 const cookies = useCookies(); 23 23 24 24 const guild = guildStore((g) => g); 25 - const web = webStore((w) => w); 26 25 const params = useParams(); 27 26 28 27 const url = `/guilds/${params.guildId}/modules/leaderboard` as const; ··· 66 65 icon={<HiChartBar />} 67 66 /> 68 67 69 - {web.devToolsEnabled && 68 + {cookies.get("devTools") && 70 69 <div className={"flex gap-4 border-2 border-violet-400 p-4 mb-4 rounded-lg"}> 71 70 72 71 <div className="lg:w-1/2 flex gap-2 w-full"> ··· 80 79 type="color" 81 80 defaultState={data.textColor ?? 0xe5e5e5} 82 81 resetState={0xe5e5e5} 83 - disabled={!web.devToolsEnabled} 84 82 /> 85 83 </div> 86 84 ··· 93 91 type="color" 94 92 defaultState={data.accentColor ?? 0x8b5cf6} 95 93 resetState={0x8b5cf6} 96 - disabled={!web.devToolsEnabled} 97 94 /> 98 95 </div> 99 96 ··· 108 105 type="color" 109 106 defaultState={data.backgroundColor ?? 0x0d0f11} 110 107 resetState={0x0d0f11} 111 - disabled={!web.devToolsEnabled} 112 108 /> 113 109 </div> 114 110
+4 -4
app/dashboard/[guildId]/leaderboards/updating.component.tsx
··· 2 2 import { Tab, Tabs } from "@nextui-org/react"; 3 3 import Image from "next/image"; 4 4 import Link from "next/link"; 5 + import { useCookies } from "next-client-cookies"; 5 6 import { FunctionComponent, useState } from "react"; 6 7 import { HiExternalLink, HiPencil, HiTrash } from "react-icons/hi"; 7 8 8 9 import { Guild } from "@/common/guilds"; 9 - import { webStore } from "@/common/webstore"; 10 10 import SelectInput from "@/components/inputs/SelectMenu"; 11 11 import Switch from "@/components/inputs/Switch"; 12 12 import Modal from "@/components/modal"; ··· 24 24 } 25 25 26 26 const UpdatingLeaderboardCard: FunctionComponent<Props> = ({ guild, lb, type }) => { 27 - const web = webStore((w) => w); 27 + const cookies = useCookies(); 28 28 29 29 const [leaderboard, setLeaderboard] = useState(lb); 30 30 const [modal, setModal] = useState<ModalType | undefined>(undefined); ··· 77 77 <span className="ml-1">{leaderboard?.channelId ? "Edit" : "Create"} leaderboard</span> 78 78 </button> 79 79 80 - {leaderboard?.channelId && web.devToolsEnabled && 80 + {leaderboard?.channelId && cookies.get("devTools") && 81 81 <button 82 82 onClick={() => setModal(ModalType.Delete)} 83 83 className="flex dark:text-red-400/60 dark:hover:text-red-400/90 text-red-600/60 hover:text-red-600/90 duration-200" ··· 122 122 styles 123 123 }); 124 124 }} 125 - subChildren={leaderboard && web.devToolsEnabled && 125 + subChildren={leaderboard && cookies.get("devTools") && 126 126 <div className="text-xs flex flex-col"> 127 127 {leaderboard.createdAt && 128 128 <span>
+3 -3
app/dashboard/[guildId]/page.tsx
··· 4 4 import Image from "next/image"; 5 5 import Link from "next/link"; 6 6 import { useParams } from "next/navigation"; 7 + import { useCookies } from "next-client-cookies"; 7 8 import { useState } from "react"; 8 9 import { HiChartBar, HiMail } from "react-icons/hi"; 9 10 10 11 import { guildStore } from "@/common/guilds"; 11 - import { webStore } from "@/common/webstore"; 12 12 import SelectMenu from "@/components/inputs/SelectMenu"; 13 13 import Switch from "@/components/inputs/Switch"; 14 14 import Modal from "@/components/modal"; ··· 16 16 import OverviewLinkComponent from "../../../components/OverviewLinkComponent"; 17 17 18 18 export default function Home() { 19 - const web = webStore((w) => w); 19 + const cookies = useCookies(); 20 20 const guild = guildStore((g) => g); 21 21 22 22 const [modal, setModal] = useState(false); ··· 133 133 <Accordion 134 134 className="lg:w-1/2" 135 135 defaultExpandedKeys={["1"]} 136 - disableAnimation={web.reduceMotions} 136 + disableAnimation={cookies.get("reduceMotions") === "true"} 137 137 > 138 138 <AccordionItem 139 139 key="1"
+44 -44
app/layout.tsx
··· 94 94 const cookieStore = cookies(); 95 95 96 96 return ( 97 - <html 98 - suppressHydrationWarning 99 - data-theme="dark" 100 - lang="en" 101 - className="dark flex justify-center min-h-screen max-w-screen overflow-x-hidden" 102 - > 103 - 104 - <Script defer data-domain="wamellow.com" src="https://analytics.wamellow.com/js/script.js" /> 105 - 106 - <body className={cn("w-full max-w-7xl", outfit.className)}> 107 - <div id="bg" className="absolute top-0 right-0 w-screen h-screen -z-10" /> 97 + <CookiesProvider> 98 + <html 99 + suppressHydrationWarning 100 + data-theme="dark" 101 + lang="en" 102 + className="dark flex justify-center min-h-screen max-w-screen overflow-x-hidden" 103 + > 108 104 109 - <nav className="p-4 flex items-center gap-2 text-base font-medium dark:text-neutral-300 text-neutral-700 select-none h-20"> 110 - <Link 111 - aria-label="Go to Wamellow's homepage" 112 - className={cn("font-semibold flex items-center mr-2", montserrat.className)} 113 - href="/?utm_source=wamellow.com&utm_medium=header" 114 - > 115 - <Image src="/waya-v3-small.webp" width={64} height={64} alt="" className="rounded-full mr-2 w-8 h-8 shrink-0" /> 116 - <span className="text-xl dark:text-neutral-100 text-neutral-900 hidden sm:block">Wamellow</span> 117 - </Link> 105 + <Script defer data-domain="wamellow.com" src="https://analytics.wamellow.com/js/script.js" /> 118 106 119 - <Divider 120 - className="h-10 rotate-6 mx-1" 121 - orientation="vertical" 122 - /> 107 + <body className={cn("w-full max-w-7xl", outfit.className)}> 108 + <div id="bg" className="absolute top-0 right-0 w-screen h-screen -z-10" /> 123 109 124 - <div className="flex gap-1"> 110 + <nav className="p-4 flex items-center gap-2 text-base font-medium dark:text-neutral-300 text-neutral-700 select-none h-20"> 125 111 <Link 126 - href="https://lunish.nl/kofi" 127 - className="dark:hover:bg-wamellow-alpha hover:bg-wamellow-100-alpha py-1 px-3 rounded-md duration-200 hidden sm:flex items-center gap-2 group" 112 + aria-label="Go to Wamellow's homepage" 113 + className={cn("font-semibold flex items-center mr-2", montserrat.className)} 114 + href="/?utm_source=wamellow.com&utm_medium=header" 128 115 > 129 - <SiKofi className="group-hover:text-[#ff6c6b] duration-200 mt-0.5" /> 130 - Donate 131 - </Link> 132 - <Link href="/vote" className="dark:hover:bg-wamellow-alpha hover:bg-wamellow-100-alpha py-1 px-3 rounded-md duration-200 flex items-center gap-2 group"> 133 - <TopggIcon className="group-hover:text-[#ff3366] duration-200 h-5 w-5 mt-0.5" /> 134 - Vote 116 + <Image src="/waya-v3-small.webp" width={64} height={64} alt="" className="rounded-full mr-2 w-8 h-8 shrink-0" /> 117 + <span className="text-xl dark:text-neutral-100 text-neutral-900 hidden sm:block">Wamellow</span> 135 118 </Link> 136 - </div> 137 119 138 - {cookieStore.get("hasSession")?.value === "true" ? 139 - <Header className="ml-auto" /> 140 - : 141 - <LoginButton /> 142 - } 143 - </nav> 120 + <Divider 121 + className="h-10 rotate-6 mx-1" 122 + orientation="vertical" 123 + /> 144 124 145 - <CookiesProvider> 125 + <div className="flex gap-1"> 126 + <Link 127 + href="https://lunish.nl/kofi" 128 + className="dark:hover:bg-wamellow-alpha hover:bg-wamellow-100-alpha py-1 px-3 rounded-md duration-200 hidden sm:flex items-center gap-2 group" 129 + > 130 + <SiKofi className="group-hover:text-[#ff6c6b] duration-200 mt-0.5" /> 131 + Donate 132 + </Link> 133 + <Link href="/vote" className="dark:hover:bg-wamellow-alpha hover:bg-wamellow-100-alpha py-1 px-3 rounded-md duration-200 flex items-center gap-2 group"> 134 + <TopggIcon className="group-hover:text-[#ff3366] duration-200 h-5 w-5 mt-0.5" /> 135 + Vote 136 + </Link> 137 + </div> 138 + 139 + {cookieStore.get("hasSession")?.value === "true" ? 140 + <Header className="ml-auto" /> 141 + : 142 + <LoginButton /> 143 + } 144 + </nav> 145 + 146 146 <Provider> 147 147 {children} 148 148 </Provider> 149 149 150 150 <StoreLastPage /> 151 - </CookiesProvider> 152 151 153 - </body> 154 - </html> 152 + </body> 153 + </html> 154 + </CookiesProvider> 155 155 ); 156 156 }
+4 -4
app/leaderboard/[guildId]/side.component.tsx
··· 3 3 import { Accordion, AccordionItem, Button, Code, Tooltip } from "@nextui-org/react"; 4 4 import Link from "next/link"; 5 5 import { useRouter } from "next/navigation"; 6 + import { useCookies } from "next-client-cookies"; 6 7 import { useState } from "react"; 7 8 import { BsDiscord } from "react-icons/bs"; 8 9 import { FaReddit, FaTwitter } from "react-icons/fa"; 9 10 import { HiAnnotation, HiLink, HiShare, HiTrash, HiViewGridAdd, HiVolumeUp } from "react-icons/hi"; 10 11 11 - import { webStore } from "@/common/webstore"; 12 12 import Ad from "@/components/ad"; 13 13 import { CopyToClipboardButton } from "@/components/copy-to-clipboard"; 14 14 import Modal from "@/components/modal"; ··· 28 28 pagination: ApiV1GuildsTopmembersPaginationGetResponse | ApiError | undefined; 29 29 currentCircular: "next" | "server" | undefined; 30 30 }) { 31 - const web = webStore((w) => w); 31 + const cookies = useCookies(); 32 32 const router = useRouter(); 33 33 34 34 const [modal, setModal] = useState(false); ··· 86 86 <Accordion 87 87 selectionMode="multiple" 88 88 defaultExpandedKeys={["1", "2", "3"]} 89 - disableAnimation={web.reduceMotions} 89 + disableAnimation={cookies.get("reduceMotions") === "true"} 90 90 > 91 91 92 - {guild && "id" in guild && web.devToolsEnabled ? 92 + {guild && "id" in guild && cookies.get("devTools") ? 93 93 <AccordionItem 94 94 key="1" 95 95 aria-label="admin tools"
-4
common/webstore.ts
··· 1 1 import { create } from "zustand"; 2 2 3 3 export interface Web { 4 - devToolsEnabled: boolean | undefined; 5 - reduceMotions: boolean; 6 4 width: number; 7 5 } 8 6 9 7 export const webStore = create<Web>(() => ({ 10 - devToolsEnabled: undefined, 11 - reduceMotions: false, 12 8 width: Infinity 13 9 }));
+16 -19
components/header.tsx
··· 3 3 import { Button, Chip, Skeleton, Switch, Tooltip } from "@nextui-org/react"; 4 4 import { AnimatePresence, motion, MotionConfig } from "framer-motion"; 5 5 import Link from "next/link"; 6 + import { useRouter } from "next/navigation"; 7 + import { useCookies } from "next-client-cookies"; 6 8 import React, { useEffect, useState } from "react"; 7 9 import { HiBadgeCheck, HiBeaker, HiChartPie, HiChevronDown, HiEyeOff, HiIdentification, HiLogout, HiViewGridAdd } from "react-icons/hi"; 8 10 ··· 15 17 import ImageReduceMotion from "./image-reduce-motion"; 16 18 17 19 export default function Header(props: React.ComponentProps<"div">) { 20 + const cookies = useCookies(); 21 + const devTools = cookies.get("devTools") === "true"; 22 + const reduceMotions = cookies.get("reduceMotions") === "true"; 18 23 19 24 const [menu, setMenu] = useState(false); 20 25 const [loginstate, setLoginstate] = useState<"LOADING" | "ERRORED" | undefined>("LOADING"); 21 26 22 27 const user = userStore((s) => s); 23 - const web = webStore((w) => w); 28 + const router = useRouter(); 24 29 25 30 useEffect(() => { 26 31 ··· 32 37 }); 33 38 }); 34 39 35 - const devToolsEnabled = localStorage.getItem("devToolsEnabled"); 36 - const reduceMotions = localStorage.getItem("reduceMotions"); 37 - 38 40 webStore.setState({ 39 - ...web, 40 - width: window?.innerWidth, 41 - devToolsEnabled: !!devToolsEnabled, 42 - reduceMotions: !!reduceMotions 41 + width: window?.innerWidth 43 42 }); 44 43 }, []); 45 44 ··· 80 79 { 81 80 name: "Reduce Motion", 82 81 icon: <HiEyeOff />, 83 - value: web.reduceMotions, 82 + value: reduceMotions, 84 83 onChange: () => { 85 - if (!web.reduceMotions) localStorage.setItem("reduceMotions", "true"); 86 - else localStorage.removeItem("reduceMotions"); 87 - 88 - webStore.setState({ ...web, reduceMotions: !web.reduceMotions }); 84 + if (!reduceMotions) cookies.set("reduceMotions", "true", { expires: 365 }); 85 + else cookies.remove("reduceMotions"); 86 + router.refresh(); 89 87 } 90 88 }, 91 89 ...(user?.HELLO_AND_WELCOME_TO_THE_DEV_TOOLS__PLEASE_GO_AWAY ? ··· 99 97 { 100 98 name: "Lunar Tools", 101 99 icon: <HiBeaker />, 102 - value: web.devToolsEnabled, 100 + value: devTools, 103 101 onChange: () => { 104 - if (!web.devToolsEnabled) localStorage.setItem("devToolsEnabled", "true"); 105 - else localStorage.removeItem("devToolsEnabled"); 106 - 107 - webStore.setState({ ...web, devToolsEnabled: !web.devToolsEnabled }); 102 + if (!devTools) cookies.set("devTools", "true", { expires: 365 }); 103 + else cookies.remove("devTools"); 104 + router.refresh(); 108 105 } 109 106 } 110 107 ] ··· 223 220 } 224 221 225 222 <MotionConfig 226 - transition={web.reduceMotions ? 223 + transition={reduceMotions ? 227 224 { duration: 0 } 228 225 : 229 226 { type: "spring", bounce: 0.4, duration: menu ? 0.7 : 0.4 }
+4 -3
components/image-reduce-motion.tsx
··· 1 1 "use client"; 2 2 import Image from "next/image"; 3 + import { useCookies } from "next-client-cookies"; 3 4 4 - import { webStore } from "@/common/webstore"; 5 5 6 6 interface Props { 7 7 url: string | null | undefined; ··· 17 17 className, 18 18 forceStatic 19 19 }: Props) { 20 - const web = webStore((w) => w); 20 + const cookies = useCookies(); 21 + const reduceMotions = cookies.get("reduceMotions") === "true"; 21 22 22 23 return ( 23 24 <Image 24 25 itemProp="image" 25 - src={!url?.includes("null") && !url?.includes("undefined") && url ? `${url}.${url.includes("a_") && !web.reduceMotions && !forceStatic ? "gif" : "webp"}?size=${size}` : "/discord.webp"} 26 + src={!url?.includes("null") && !url?.includes("undefined") && url ? `${url}.${url.includes("a_") && !reduceMotions && !forceStatic ? "gif" : "webp"}?size=${size}` : "/discord.webp"} 26 27 width={size} 27 28 height={size} 28 29 alt={alt}