tangled
alpha
login
or
join now
shi.gg
/
mellow-web
4
fork
atom
The weeb for the next gen discord boat - Wamellow
wamellow.com
bot
discord
4
fork
atom
overview
issues
pulls
pipelines
use cookies for devTools & reduceMotions
shi.gg
2 years ago
c25aacfc
dd4a83ef
+84
-94
11 changed files
expand all
collapse all
unified
split
app
(home)
faq.component.tsx
ai-gallery
[uploadId]
side.component.tsx
dashboard
[guildId]
layout.tsx
leaderboards
page.tsx
updating.component.tsx
page.tsx
layout.tsx
leaderboard
[guildId]
side.component.tsx
common
webstore.ts
components
header.tsx
image-reduce-motion.tsx
+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
5
+
import { useCookies } from "next-client-cookies";
5
6
import { HiCash, HiChat, HiExternalLink, HiLockClosed, HiUserAdd } from "react-icons/hi";
6
7
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
101
-
const web = webStore((w) => w);
101
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
110
-
disableAnimation={web.reduceMotions}
110
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
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
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
29
-
const web = webStore((w) => w);
29
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
79
-
disableAnimation={web.reduceMotions}
79
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
162
-
disableAnimation={web.reduceMotions}
162
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
197
-
{web.devToolsEnabled &&
197
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
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
69
-
{web.devToolsEnabled &&
68
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
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
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
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
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
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
27
-
const web = webStore((w) => w);
27
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
80
-
{leaderboard?.channelId && web.devToolsEnabled &&
80
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
125
-
subChildren={leaderboard && web.devToolsEnabled &&
125
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
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
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
19
-
const web = webStore((w) => w);
19
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
136
-
disableAnimation={web.reduceMotions}
136
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
97
-
<html
98
98
-
suppressHydrationWarning
99
99
-
data-theme="dark"
100
100
-
lang="en"
101
101
-
className="dark flex justify-center min-h-screen max-w-screen overflow-x-hidden"
102
102
-
>
103
103
-
104
104
-
<Script defer data-domain="wamellow.com" src="https://analytics.wamellow.com/js/script.js" />
105
105
-
106
106
-
<body className={cn("w-full max-w-7xl", outfit.className)}>
107
107
-
<div id="bg" className="absolute top-0 right-0 w-screen h-screen -z-10" />
97
97
+
<CookiesProvider>
98
98
+
<html
99
99
+
suppressHydrationWarning
100
100
+
data-theme="dark"
101
101
+
lang="en"
102
102
+
className="dark flex justify-center min-h-screen max-w-screen overflow-x-hidden"
103
103
+
>
108
104
109
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
110
-
<Link
111
111
-
aria-label="Go to Wamellow's homepage"
112
112
-
className={cn("font-semibold flex items-center mr-2", montserrat.className)}
113
113
-
href="/?utm_source=wamellow.com&utm_medium=header"
114
114
-
>
115
115
-
<Image src="/waya-v3-small.webp" width={64} height={64} alt="" className="rounded-full mr-2 w-8 h-8 shrink-0" />
116
116
-
<span className="text-xl dark:text-neutral-100 text-neutral-900 hidden sm:block">Wamellow</span>
117
117
-
</Link>
105
105
+
<Script defer data-domain="wamellow.com" src="https://analytics.wamellow.com/js/script.js" />
118
106
119
119
-
<Divider
120
120
-
className="h-10 rotate-6 mx-1"
121
121
-
orientation="vertical"
122
122
-
/>
107
107
+
<body className={cn("w-full max-w-7xl", outfit.className)}>
108
108
+
<div id="bg" className="absolute top-0 right-0 w-screen h-screen -z-10" />
123
109
124
124
-
<div className="flex gap-1">
110
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
126
-
href="https://lunish.nl/kofi"
127
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
112
+
aria-label="Go to Wamellow's homepage"
113
113
+
className={cn("font-semibold flex items-center mr-2", montserrat.className)}
114
114
+
href="/?utm_source=wamellow.com&utm_medium=header"
128
115
>
129
129
-
<SiKofi className="group-hover:text-[#ff6c6b] duration-200 mt-0.5" />
130
130
-
Donate
131
131
-
</Link>
132
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
133
-
<TopggIcon className="group-hover:text-[#ff3366] duration-200 h-5 w-5 mt-0.5" />
134
134
-
Vote
116
116
+
<Image src="/waya-v3-small.webp" width={64} height={64} alt="" className="rounded-full mr-2 w-8 h-8 shrink-0" />
117
117
+
<span className="text-xl dark:text-neutral-100 text-neutral-900 hidden sm:block">Wamellow</span>
135
118
</Link>
136
136
-
</div>
137
119
138
138
-
{cookieStore.get("hasSession")?.value === "true" ?
139
139
-
<Header className="ml-auto" />
140
140
-
:
141
141
-
<LoginButton />
142
142
-
}
143
143
-
</nav>
120
120
+
<Divider
121
121
+
className="h-10 rotate-6 mx-1"
122
122
+
orientation="vertical"
123
123
+
/>
144
124
145
145
-
<CookiesProvider>
125
125
+
<div className="flex gap-1">
126
126
+
<Link
127
127
+
href="https://lunish.nl/kofi"
128
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
129
+
>
130
130
+
<SiKofi className="group-hover:text-[#ff6c6b] duration-200 mt-0.5" />
131
131
+
Donate
132
132
+
</Link>
133
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
134
+
<TopggIcon className="group-hover:text-[#ff3366] duration-200 h-5 w-5 mt-0.5" />
135
135
+
Vote
136
136
+
</Link>
137
137
+
</div>
138
138
+
139
139
+
{cookieStore.get("hasSession")?.value === "true" ?
140
140
+
<Header className="ml-auto" />
141
141
+
:
142
142
+
<LoginButton />
143
143
+
}
144
144
+
</nav>
145
145
+
146
146
<Provider>
147
147
{children}
148
148
</Provider>
149
149
150
150
<StoreLastPage />
151
151
-
</CookiesProvider>
152
151
153
153
-
</body>
154
154
-
</html>
152
152
+
</body>
153
153
+
</html>
154
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
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
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
31
-
const web = webStore((w) => w);
31
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
89
-
disableAnimation={web.reduceMotions}
89
89
+
disableAnimation={cookies.get("reduceMotions") === "true"}
90
90
>
91
91
92
92
-
{guild && "id" in guild && web.devToolsEnabled ?
92
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
4
-
devToolsEnabled: boolean | undefined;
5
5
-
reduceMotions: boolean;
6
4
width: number;
7
5
}
8
6
9
7
export const webStore = create<Web>(() => ({
10
10
-
devToolsEnabled: undefined,
11
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
6
+
import { useRouter } from "next/navigation";
7
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
20
+
const cookies = useCookies();
21
21
+
const devTools = cookies.get("devTools") === "true";
22
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
23
-
const web = webStore((w) => w);
28
28
+
const router = useRouter();
24
29
25
30
useEffect(() => {
26
31
···
32
37
});
33
38
});
34
39
35
35
-
const devToolsEnabled = localStorage.getItem("devToolsEnabled");
36
36
-
const reduceMotions = localStorage.getItem("reduceMotions");
37
37
-
38
40
webStore.setState({
39
39
-
...web,
40
40
-
width: window?.innerWidth,
41
41
-
devToolsEnabled: !!devToolsEnabled,
42
42
-
reduceMotions: !!reduceMotions
41
41
+
width: window?.innerWidth
43
42
});
44
43
}, []);
45
44
···
80
79
{
81
80
name: "Reduce Motion",
82
81
icon: <HiEyeOff />,
83
83
-
value: web.reduceMotions,
82
82
+
value: reduceMotions,
84
83
onChange: () => {
85
85
-
if (!web.reduceMotions) localStorage.setItem("reduceMotions", "true");
86
86
-
else localStorage.removeItem("reduceMotions");
87
87
-
88
88
-
webStore.setState({ ...web, reduceMotions: !web.reduceMotions });
84
84
+
if (!reduceMotions) cookies.set("reduceMotions", "true", { expires: 365 });
85
85
+
else cookies.remove("reduceMotions");
86
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
102
-
value: web.devToolsEnabled,
100
100
+
value: devTools,
103
101
onChange: () => {
104
104
-
if (!web.devToolsEnabled) localStorage.setItem("devToolsEnabled", "true");
105
105
-
else localStorage.removeItem("devToolsEnabled");
106
106
-
107
107
-
webStore.setState({ ...web, devToolsEnabled: !web.devToolsEnabled });
102
102
+
if (!devTools) cookies.set("devTools", "true", { expires: 365 });
103
103
+
else cookies.remove("devTools");
104
104
+
router.refresh();
108
105
}
109
106
}
110
107
]
···
223
220
}
224
221
225
222
<MotionConfig
226
226
-
transition={web.reduceMotions ?
223
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
3
+
import { useCookies } from "next-client-cookies";
3
4
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
20
-
const web = webStore((w) => w);
20
20
+
const cookies = useCookies();
21
21
+
const reduceMotions = cookies.get("reduceMotions") === "true";
21
22
22
23
return (
23
24
<Image
24
25
itemProp="image"
25
25
-
src={!url?.includes("null") && !url?.includes("undefined") && url ? `${url}.${url.includes("a_") && !web.reduceMotions && !forceStatic ? "gif" : "webp"}?size=${size}` : "/discord.webp"}
26
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}