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({ weight: "600", 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: 0.85
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 data-api="/api/event"
112 src="/static/analytics.js"
113 />
114
115 <Script
116 id="reviews"
117 type="application/ld+json"
118 >
119 {JSON.stringify(reviews)}
120 </Script>
121
122 {process.env.NODE_ENV === "development" && (
123 <Script src="https://unpkg.com/react-scan/dist/auto.global.js" />
124 )}
125
126 <body
127 className={cn(
128 "relative top-0 w-full flex justify-center overflow-x-hidden xl:overflow-visible!",
129 outfit.variable,
130 notosansJP.variable
131 )}
132 >
133 <div id="bg" className="absolute top-0 right-0 w-screen h-screen -z-50" />
134 <Noise />
135
136 <div className="w-full max-w-7xl">
137 <NavBar className="w-full" />
138 <Provider className="w-full">{children}</Provider>
139 </div>
140 </body>
141 </html>
142 </CookiesProvider>
143 );
144}
145
146function Noise() {
147 return (
148 <svg
149 className="absolute top-0 left-0 w-screen h-screen -z-40 blur-[1px] saturate-0"
150 viewBox='0 0 142 158'
151 xmlns='http://www.w3.org/2000/svg'
152 >
153 <filter id='noiseFilter'>
154 <feTurbulence
155 type="fractalNoise"
156 baseFrequency="9"
157 numOctaves="1"
158 stitchTiles="stitch"
159 result="turbulence"
160
161 />
162 <feComponentTransfer>
163 <feFuncR type="table" tableValues="-1 0.2" />
164 <feFuncG type="table" tableValues="-1 0.2" />
165 <feFuncB type="table" tableValues="-1 0.2" />
166 </feComponentTransfer>
167 </filter>
168
169 <rect
170 className="w-screen h-screen"
171 filter='url(#noiseFilter)'
172 />
173 </svg>
174 );
175}
176
177async function NavBar({ className }: { className?: string; }) {
178 const jar = await cookies();
179
180 return (
181 <nav className={cn("p-4 flex items-center gap-2 text-base text-neutral-300 select-none h-20 relative", className)}>
182 <Link
183 aria-label="Go to Wamellow's homepage"
184 className={cn("font-semibold flex items-center shrink-0 group", lexend.className)}
185 href="/"
186 >
187 <Image src={Icon} alt="wamellow icon" className="size-8 shrink-0 mr-4 pt-1 group-hover:rotate-45 duration-200" />
188 <span className="text-xl dark:text-neutral-100 text-neutral-900 hidden sm:block">Wamellow</span>
189 </Link>
190
191 <Separator
192 className="h-10 rotate-6 ml-3"
193 orientation="vertical"
194 />
195
196 <div className="flex shrink-0">
197 <Button
198 asChild
199 size="sm"
200 variant="ghost"
201 >
202 <Link href="/docs/index">
203 Documentation
204 </Link>
205 </Button>
206 <Button
207 asChild
208 className="hidden sm:flex"
209 size="sm"
210 variant="ghost"
211 >
212 <Link href="/premium">
213 Premium
214 </Link>
215 </Button>
216 </div>
217
218 {jar.get("session")?.value
219 ? <Header />
220 : <LoginButton className="ml-auto" />
221 }
222 </nav>
223 );
224}