The weeb for the next gen discord boat - Wamellow
wamellow.com
bot
discord
1import "./globals.css";
2import { Header } from "@/components/header";
3import { LoginButton } from "@/components/login-button";
4import { Button } from "@/components/ui/button";
5import { Separator } from "@/components/ui/separator";
6import Icon from "@/public/icon.svg";
7import { cn } from "@/utils/cn";
8import { getBaseUrl } from "@/utils/urls";
9import type { Metadata, Viewport } from "next";
10import { Lexend, Noto_Sans_JP, Outfit } from "next/font/google";
11import { cookies } from "next/headers";
12import Image from "next/image";
13import Link from "next/link";
14import Script from "next/script";
15import { CookiesProvider } from "next-client-cookies/server";
16
17import { Provider } from "./provider";
18
19const outfit = Outfit({ subsets: ["latin", "latin-ext"], variable: "--font-outfit" });
20const notosansJP = Noto_Sans_JP({ subsets: ["cyrillic", "vietnamese"], variable: "--font-noto-sans-jp" });
21
22const lexend = Lexend({ subsets: ["latin"] });
23
24// TODO: get automatically from top.gg
25const reviews = {
26 "@context": "https://schema.org",
27 "@type": "Product",
28 name: "wamellow",
29 aggregateRating: {
30 "@type": "AggregateRating",
31 ratingValue: "5",
32 reviewCount: "110",
33 bestRating: "5"
34 }
35};
36
37export const viewport: Viewport = {
38 themeColor: "#8957ff",
39 initialScale: 1
40};
41
42export const generateMetadata = (): Metadata => {
43
44 const title = "Wamellow: Next-gen of Discord Bots & Apps";
45 const description = "Accessibility where it's needed the most: Discord Voice Chats. Social notifications to stay connected and up to date with anyone, anywhere. Simple, customizable, free, and built in public.";
46
47 return {
48 metadataBase: new URL(getBaseUrl()),
49
50 manifest: "/manifest.json",
51 appleWebApp: {
52 capable: true,
53 title: "Wamellow",
54 startupImage: "/waya-v3.webp",
55 statusBarStyle: "black-translucent"
56 },
57
58 title: {
59 default: title,
60 template: "%s"
61 },
62
63 description,
64
65 alternates: {
66 canonical: getBaseUrl()
67 },
68
69 openGraph: {
70 title: {
71 default: title,
72 template: "%s on Wamellow"
73 },
74 description,
75 type: "website",
76 url: getBaseUrl(),
77 images: `${getBaseUrl()}/waya-v3.webp?v=3`
78 },
79
80 twitter: {
81 card: "summary",
82 site: "wamellow.com",
83 title,
84 description,
85 images: `${getBaseUrl()}/waya-v3.webp?v=3`
86 },
87
88 other: {
89 google: "notranslate"
90 },
91
92 creator: "Luna (shi.gg)",
93 publisher: "Luna (shi.gg)",
94
95 robots: "index, follow"
96 };
97};
98
99export default function RootLayout({ children }: { children: React.ReactNode; }) {
100 return (
101 <CookiesProvider>
102 <html
103 suppressHydrationWarning
104 data-theme="dark"
105 lang="en"
106 className="dark max-w-screen overflow-x-hidden"
107 >
108 <Script
109 defer
110 data-domain="wamellow.com"
111 src="https://analytics.wamellow.com/js/script.outbound-links.js"
112 />
113
114 <Script
115 id="reviews"
116 type="application/ld+json"
117 >
118 {JSON.stringify(reviews)}
119 </Script>
120
121 {process.env.NODE_ENV === "development" && (
122 <Script src="https://unpkg.com/react-scan/dist/auto.global.js" />
123 )}
124
125 <body
126 className={cn(
127 "relative top-0 w-full flex justify-center overflow-x-hidden xl:overflow-visible!",
128 outfit.variable,
129 notosansJP.variable
130 )}
131 >
132 <div id="bg" className="absolute top-0 right-0 w-screen h-screen -z-50" />
133 <Noise />
134
135 <div className="w-full max-w-7xl">
136 <NavBar className="w-full" />
137 <Provider className="w-full">{children}</Provider>
138 </div>
139 </body>
140 </html>
141 </CookiesProvider>
142 );
143}
144
145function Noise() {
146 return (
147 <svg
148 className="absolute top-0 left-0 w-screen h-screen -z-40 blur-[1px] saturate-0"
149 viewBox='0 0 142 158'
150 xmlns='http://www.w3.org/2000/svg'
151 >
152 <filter id='noiseFilter'>
153 <feTurbulence
154 type="fractalNoise"
155 baseFrequency="9"
156 numOctaves="1"
157 stitchTiles="stitch"
158 result="turbulence"
159
160 />
161 <feComponentTransfer>
162 <feFuncR type="table" tableValues="-1 0.2" />
163 <feFuncG type="table" tableValues="-1 0.2" />
164 <feFuncB type="table" tableValues="-1 0.2" />
165 </feComponentTransfer>
166 </filter>
167
168 <rect
169 className="w-screen h-screen"
170 filter='url(#noiseFilter)'
171 />
172 </svg>
173 );
174}
175
176async function NavBar({ className }: { className?: string; }) {
177 const jar = await cookies();
178
179 return (
180 <nav className={cn("p-4 flex items-center gap-2 text-base text-neutral-300 select-none h-20 relative", className)}>
181 <Link
182 aria-label="Go to Wamellow's homepage"
183 className={cn("font-semibold flex items-center shrink-0 group", lexend.className)}
184 href="/"
185 >
186 <Image src={Icon} alt="wamellow icon" className="size-8 shrink-0 mr-4 pt-1 group-hover:rotate-45 duration-200" />
187 <span className="text-xl dark:text-neutral-100 text-neutral-900 hidden sm:block">Wamellow</span>
188 </Link>
189
190 <Separator
191 className="h-10 rotate-6 ml-3"
192 orientation="vertical"
193 />
194
195 <div className="flex shrink-0">
196 <Button
197 asChild
198 size="sm"
199 variant="ghost"
200 >
201 <Link href="/docs/index">
202 Documentation
203 </Link>
204 </Button>
205 <Button
206 asChild
207 className="hidden sm:flex"
208 size="sm"
209 variant="ghost"
210 >
211 <Link href="/premium">
212 Premium
213 </Link>
214 </Button>
215 </div>
216
217 {jar.get("session")?.value
218 ? <Header />
219 : <LoginButton className="ml-auto" />
220 }
221 </nav>
222 );
223}