diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..e2a1032 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +nodejs 23.3.0 diff --git a/package.json b/package.json index 36afd69..bd4c935 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@emotion/is-prop-valid": "^1.3.0", "@linear/sdk": "^31.0.0", "@monaco-editor/react": "^4.6.0", + "@radix-ui/react-aspect-ratio": "^1.1.1", "@radix-ui/react-avatar": "^1.1.1", "@radix-ui/react-collapsible": "^1.1.1", "@radix-ui/react-hover-card": "^1.1.1", diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx index 7a72fec..d484c5e 100644 --- a/src/app/(main)/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -32,27 +32,17 @@ import { Analytics } from "@vercel/analytics/react"; import { SpeedInsights } from "@vercel/speed-insights/next"; import { GeistSans } from "geist/font/sans"; import "../globals.css"; -import ClientFadeIn from "@/components/ClientFadeIn"; import { CommandBarer } from "@/components/CommandBar"; -import { BrandingGenericIcon } from "@/components/Icon"; -import TextFromPathname from "@/components/TextFromPathname"; import { ThemeProvider } from "@/components/ThemeProvider"; import { ClerkThemeProvider } from "@/components/clerk/ClerkThemeProvider"; -import TopBar from "@/components/clerk/Topbar"; import NewDomainDialog from "@/components/misc/NewDomainDialog"; import ThemedToaster from "@/components/misc/ThemedToaster"; import UnofficalDialog from "@/components/misc/UnofficalDialog"; -import { - Breadcrumb, - BreadcrumbList, - BreadcrumbPage, -} from "@/components/ui/breadcrumb"; import { TooltipProvider } from "@/components/ui/tooltip"; -import { banner } from "@/config/banner"; -import NextTopLoader from "@/lib/top-loader"; import type { Metadata, Viewport } from "next"; import { Inter as interFont } from "next/font/google"; -import Link from "next/link"; +import LayoutPart from "@/components/feat/LayoutPart"; +import AllBanners from "@/components/feat/AllBanners"; export const extraMetadata = { twitter: { @@ -86,35 +76,8 @@ export default async function RootLayout({ - {banner.isBanner && ( -
- {banner.bannerText} -
- )} -
-
- - - - - - - - - - -
- -
-
- - {children} -
{" "} + + {children} diff --git a/src/app/(main)/server/[server]/page.tsx b/src/app/(main)/server/[server]/page.tsx index 8d7b44d..0625a71 100644 --- a/src/app/(main)/server/[server]/page.tsx +++ b/src/app/(main)/server/[server]/page.tsx @@ -34,7 +34,6 @@ import ColorProvider from "@/components/ColorProvider"; import ServerView from "@/components/ServerView"; import StickyTopbar from "@/components/misc/StickyTopbar"; import TabServer from "@/components/misc/TabServer"; -import { Separator } from "@/components/ui/separator"; import type { Metadata, ResolvingMetadata } from "next"; type Props = { @@ -129,17 +128,17 @@ export async function generateMetadata( export default function ServerPage({ params }: { params: { server: string } }) { return ( -
+
-
+
+
+ +
+ -
- -
-
diff --git a/src/app/(main)/server/[server]/statistics/page.tsx b/src/app/(main)/server/[server]/statistics/page.tsx index f7090a3..a009481 100644 --- a/src/app/(main)/server/[server]/statistics/page.tsx +++ b/src/app/(main)/server/[server]/statistics/page.tsx @@ -35,6 +35,7 @@ import ServerView from "@/components/ServerView"; import TabServer from "@/components/misc/TabServer"; import { Separator } from "@/components/ui/separator"; import type { Metadata, ResolvingMetadata } from "next"; +import StickyTopbar from "@/components/misc/StickyTopbar"; type Props = { params: { server: string }; @@ -96,18 +97,21 @@ export async function generateMetadata( export default function ServerPage({ params }: { params: { server: string } }) { return ( -
+
-
+
- -
+
- -
-
- -
+
+ + + + + +
+
+
diff --git a/src/components/AfterServerView.tsx b/src/components/AfterServerView.tsx index e05185e..4021a23 100644 --- a/src/components/AfterServerView.tsx +++ b/src/components/AfterServerView.tsx @@ -117,7 +117,7 @@ export default function AfterServerView({ server }: { server: string }) { - +
{(description != "" || discord != "") && ( diff --git a/src/components/Banner.tsx b/src/components/Banner.tsx index 0f803bc..d5aad92 100644 --- a/src/components/Banner.tsx +++ b/src/components/Banner.tsx @@ -31,44 +31,54 @@ "use client"; import { getCustomization } from "@/lib/api"; import { useEffect, useState } from "react"; +import useTotalBannerSize from "@/lib/hooks/use-total-banner-size"; export default function Banner({ server }: { server: string }) { - const [bannerURL, setBannerURL] = useState(""); - const [loading, setLoading] = useState(true); + const [bannerURL, setBannerURL] = useState(""); + const [loading, setLoading] = useState(true); + const { bannerSize } = useTotalBannerSize(); - useEffect(() => { - getCustomization(server).then((c) => { - if (c != null) { - setLoading(false); - setBannerURL(c.banner == undefined ? "" : c.banner); - } else { - setLoading(false); - } - }); - }, [server]); + useEffect(() => { + getCustomization(server).then((c) => { + if (c != null) { + setLoading(false); + setBannerURL(c.banner == undefined ? "" : c.banner); + } else { + setLoading(false); + } + }); + }, [server]); - if (loading) { - return ( - <> -
- - ); - } + if (loading) { + return ( + <> +
+ + ); + } - return ( - <> - {bannerURL != "" && ( - User-provided banner for this server. - )} -
- - ); + return ( + <> + {bannerURL != "" && ( + User-provided banner for this server. + )} +
+ + ); } diff --git a/src/components/CustomizeRoot.tsx b/src/components/CustomizeRoot.tsx index f5364e2..5392f24 100644 --- a/src/components/CustomizeRoot.tsx +++ b/src/components/CustomizeRoot.tsx @@ -30,7 +30,6 @@ "use client"; import { useState } from "react"; -import Banner from "./Banner"; import ServerCustomize from "./ServerCustomize"; import TabServer from "./misc/TabServer"; @@ -42,7 +41,6 @@ export default function CustomizeRoot({ const [color, setColor] = useState(""); return (
-
diff --git a/src/components/ThemeProvider.tsx b/src/components/ThemeProvider.tsx index 847935c..fa7a23b 100644 --- a/src/components/ThemeProvider.tsx +++ b/src/components/ThemeProvider.tsx @@ -33,6 +33,7 @@ import * as React from "react"; import { ThemeProvider as NextThemesProvider, useTheme } from "next-themes"; import { type ThemeProviderProps } from "next-themes"; +import { usePathname } from "next/navigation"; declare global { interface Document { @@ -48,6 +49,7 @@ declare global { export function ThemeProvider({ children, ...props }: ThemeProviderProps) { const [mounted, setMounted] = React.useState(false); + const pathname = usePathname(); React.useEffect(() => { setMounted(true); @@ -55,7 +57,14 @@ export function ThemeProvider({ children, ...props }: ThemeProviderProps) { if (!mounted) return null; - return {children}; + return ( + + {children} + + ); } interface UseThemeTransitionResult { diff --git a/src/components/ThemeSwitcher.tsx b/src/components/ThemeSwitcher.tsx index e2758ed..298aa2d 100644 --- a/src/components/ThemeSwitcher.tsx +++ b/src/components/ThemeSwitcher.tsx @@ -39,9 +39,11 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { useThemeTransition } from "./ThemeProvider"; +import { usePathname } from "next/navigation"; export function ModeToggle() { const { changeTheme } = useThemeTransition(); + const pathname = usePathname(); return ( @@ -53,13 +55,28 @@ export function ModeToggle() { - changeTheme("light")}> + {pathname?.startsWith("/server") && ( +
+ For compatibility reasons,
server pages are forced to dark + mode +
+ )} + changeTheme("light")} + disabled={pathname?.startsWith("/server")} + > Light - changeTheme("dark")}> + changeTheme("dark")} + disabled={pathname?.startsWith("/server")} + > Dark - changeTheme("system")}> + changeTheme("system")} + disabled={pathname?.startsWith("/server")} + > System
diff --git a/src/components/effects/gradient-banner.tsx b/src/components/effects/gradient-banner.tsx new file mode 100644 index 0000000..d96bba8 --- /dev/null +++ b/src/components/effects/gradient-banner.tsx @@ -0,0 +1,73 @@ +/* + * MHSF, Minehut Server List + * All external content is rather licensed under the ECA Agreement + * located here: https://mhsf.app/docs/legal/external-content-agreement + * + * All code under MHSF is licensed under the MIT License + * by open source contributors + * + * Copyright (c) 2024 dvelo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +"use client"; + +import React, { useEffect, useState } from "react"; +import { Gradient } from "stripe-gradient"; + +export default function GradientBanner({ + children, +}: { + children?: React.ReactNode; +}) { + const [gradientId, setGradientId] = useState("gradient-banner"); + + useEffect(() => { + setGradientId("gradient-banner"); + const gradient = new Gradient(); + gradient.initGradient("#" + gradientId); + }, [gradientId]); + + return ( +
+ {" "} +
+ {children} +
+
+ ); +} diff --git a/src/config/banner.tsx b/src/components/feat/AllBanners.tsx similarity index 64% rename from src/config/banner.tsx rename to src/components/feat/AllBanners.tsx index 1fcd32a..0c1ab01 100644 --- a/src/config/banner.tsx +++ b/src/components/feat/AllBanners.tsx @@ -1,7 +1,7 @@ /* * MHSF, Minehut Server List * All external content is rather licensed under the ECA Agreement - * located here: https://list.mlnehut.com/docs/legal/external-content-agreement + * located here: https://mhsf.app/docs/legal/external-content-agreement * * All code under MHSF is licensed under the MIT License * by open source contributors @@ -28,26 +28,16 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -import { Button } from "@/components/ui/button"; -import Link from "next/link"; +"use client"; -/** used when there is a outage */ -export const banner = { - isBanner: - process.env.NEXT_PUBLIC_VERCEL_ENV !== "production" - ? true - : /** Set this to true when outage --->*/ false, - bannerText: - process.env.NEXT_PUBLIC_VERCEL_ENV !== "production" ? ( - <> - Your not in production!{" "} - - - - - ) : ( - <>{/** Set this to an explanation! */} - ), -}; +import { useBanners } from "@/lib/hooks/use-banners"; + +export default function AllBanners() { + const { banners } = useBanners(); + + return ( +
+ {banners.map((banner) => banner.bannerContent)} +
+ ); +} diff --git a/src/components/feat/BannerContainer.tsx b/src/components/feat/BannerContainer.tsx new file mode 100644 index 0000000..11e3739 --- /dev/null +++ b/src/components/feat/BannerContainer.tsx @@ -0,0 +1,51 @@ +/* + * MHSF, Minehut Server List + * All external content is rather licensed under the ECA Agreement + * located here: https://mhsf.app/docs/legal/external-content-agreement + * + * All code under MHSF is licensed under the MIT License + * by open source contributors + * + * Copyright (c) 2024 dvelo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +"use client"; + +import useTotalBannerSize from "@/lib/hooks/use-total-banner-size"; + +export default function BannerContainer({ + children, + className, + style, +}: { + children: React.ReactNode; + className?: string; + style: (size: number) => React.CSSProperties; +}) { + const { bannerSize } = useTotalBannerSize(); + + return ( +
+ {children} +
+ ); +} diff --git a/src/components/feat/LayoutPart.tsx b/src/components/feat/LayoutPart.tsx new file mode 100644 index 0000000..1556782 --- /dev/null +++ b/src/components/feat/LayoutPart.tsx @@ -0,0 +1,55 @@ +"use client"; + +import ClientFadeIn from "@/components/ClientFadeIn"; +import { BrandingGenericIcon } from "@/components/Icon"; +import TextFromPathname from "@/components/TextFromPathname"; +import TopBar from "@/components/clerk/Topbar"; +import { + Breadcrumb, + BreadcrumbList, + BreadcrumbPage, +} from "@/components/ui/breadcrumb"; +import NextTopLoader from "@/lib/top-loader"; +import Link from "next/link"; +import BannerContainer from "@/components/feat/BannerContainer"; +import { Inter } from "next/font/google"; + +const inter = Inter({ variable: "--font-inter", subsets: ["latin"] }); +export default function LayoutPart({ + children, +}: { + children: React.ReactNode; +}) { + return ( + <> + ({ + marginTop: `${2 * size}rem`, + })} + > +
+ + + + + + + + + + +
+ +
+ ({ + paddingTop: `${2 * size}rem`, + })} + > + + {children} + + + ); +} diff --git a/src/components/feat/MainBanner.tsx b/src/components/feat/MainBanner.tsx new file mode 100644 index 0000000..a2197eb --- /dev/null +++ b/src/components/feat/MainBanner.tsx @@ -0,0 +1,50 @@ +/* + * MHSF, Minehut Server List + * All external content is rather licensed under the ECA Agreement + * located here: https://mhsf.app/docs/legal/external-content-agreement + * + * All code under MHSF is licensed under the MIT License + * by open source contributors + * + * Copyright (c) 2024 dvelo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +export default function MainBanner({ + className, + size = 1, + children, +}: { + className?: string; + size?: number; + children?: React.ReactNode; +}) { + return ( +
+ {children} +
+ ); +} diff --git a/src/components/misc/AffilatePopup.tsx b/src/components/misc/AffilatePopup.tsx new file mode 100644 index 0000000..c54e8c9 --- /dev/null +++ b/src/components/misc/AffilatePopup.tsx @@ -0,0 +1,29 @@ +/* + * MHSF, Minehut Server List + * All external content is rather licensed under the ECA Agreement + * located here: https://mhsf.app/docs/legal/external-content-agreement + * + * All code under MHSF is licensed under the MIT License + * by open source contributors + * + * Copyright (c) 2024 dvelo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ diff --git a/src/components/misc/StickyTopbar.tsx b/src/components/misc/StickyTopbar.tsx index 31234dc..b3e788f 100644 --- a/src/components/misc/StickyTopbar.tsx +++ b/src/components/misc/StickyTopbar.tsx @@ -29,8 +29,7 @@ */ "use client"; - -import { banner } from "@/config/banner"; +import useTotalBannerSize from "@/lib/hooks/use-total-banner-size"; import { useEffect, useState, ReactNode } from "react"; export default function StickyTopbar({ @@ -43,6 +42,7 @@ export default function StickyTopbar({ className?: string; }) { const [isSticky, setIsSticky] = useState(false); + const { bannerSize } = useTotalBannerSize(); const handleScroll = () => { if (window.scrollY > scrollElevation) { @@ -61,7 +61,10 @@ export default function StickyTopbar({ return (
{children}
diff --git a/src/components/ui/aspect-ratio.tsx b/src/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000..d6a5226 --- /dev/null +++ b/src/components/ui/aspect-ratio.tsx @@ -0,0 +1,7 @@ +"use client" + +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" + +const AspectRatio = AspectRatioPrimitive.Root + +export { AspectRatio } diff --git a/src/components/ui/discord.tsx b/src/components/ui/discord.tsx new file mode 100644 index 0000000..5e039c0 --- /dev/null +++ b/src/components/ui/discord.tsx @@ -0,0 +1,17 @@ +import type { SVGProps } from "react"; +const Discord = (props: SVGProps) => ( + + + +); +export default Discord; diff --git a/src/components/ui/github.tsx b/src/components/ui/github.tsx new file mode 100644 index 0000000..704f108 --- /dev/null +++ b/src/components/ui/github.tsx @@ -0,0 +1,15 @@ +import type { SVGProps } from "react"; +const Github = (props: SVGProps) => ( + + + +); +export default Github; diff --git a/src/config/banners.tsx b/src/config/banners.tsx new file mode 100644 index 0000000..a096f10 --- /dev/null +++ b/src/config/banners.tsx @@ -0,0 +1,41 @@ +export const defaultBanners: { + bannerSpace: number; + bannerContent: React.ReactNode; +}[] = [ + // The sponsor banner ALWAYS has to be first. + // { + // bannerSpace: 2, + // bannerContent: ( + // + // {" "} + // + // ???an official affiliate of MHSF{" "} + //
+ // Lorem ipsum odor amet, consectetuer adipiscing elit. — check it out + //
+ //
+ // ), + // }, +]; + +export const bannerHooks: (() => + | { bannerSpace: number; bannerContent: React.ReactNode } + | undefined)[] = [ + () => { + // if (process.env.NEXT_PUBLIC_VERCEL_ENV !== "production") + // return { + // bannerSpace: 1, + // bannerContent: ( + // + // Your not in production!{" "} + // + // + // + // + // ), + // }; + return undefined; + }, +]; diff --git a/src/config/docs.ts b/src/config/docs.ts index 12f3137..5d20630 100644 --- a/src/config/docs.ts +++ b/src/config/docs.ts @@ -29,61 +29,60 @@ */ export const allFolders: DocsFolder[] = [ - { - name: "General", - docs: [ - { - title: "Getting Started", - url: "/docs/getting-started", - }, - { - title: "Reading", - url: "/docs/reading", - }, - ], - }, - { - name: "Guides", - docs: [ - { - title: "Linking", - url: "/docs/guides/linking", - }, - { - title: "Owning a Server", - url: "/docs/guides/owning-a-server", - }, - { - title: "Server Customization", - url: "/docs/guides/customization", - }, - { title: "Reporting a server", url: "/docs/guides/reporting-server" }, - ], - }, - { - name: "Advanced", - docs: [ - { title: "Tech Stack", url: "/docs/advanced/tech-stack" }, - { title: "Using the Command-bar", url: "/docs/advanced/command-bar" }, - { title: "Tips with external servers", url: "/docs/advanced/external" }, - { title: "Achievements", url: "/docs/advanced/achievements" }, - ], - }, - { - name: "Legal", - docs: [ - { title: "ECA Agreement", url: "/docs/legal/external-content-agreement" }, - { title: "Email List", url: "/docs/legal/email-list" }, - ], - }, + { + name: "General", + docs: [ + { + title: "Getting Started", + url: "/docs/getting-started", + }, + { + title: "Reading", + url: "/docs/reading", + }, + ], + }, + { + name: "Guides", + docs: [ + { + title: "Linking", + url: "/docs/guides/linking", + }, + { + title: "Owning a Server", + url: "/docs/guides/owning-a-server", + }, + { + title: "Server Customization", + url: "/docs/guides/customization", + }, + { title: "Reporting a server", url: "/docs/guides/reporting-server" }, + ], + }, + { + name: "Advanced", + docs: [ + { title: "Tech Stack", url: "/docs/advanced/tech-stack" }, + { title: "Using the Command-bar", url: "/docs/advanced/command-bar" }, + { title: "Tips with external servers", url: "/docs/advanced/external" }, + { title: "Achievements", url: "/docs/advanced/achievements" }, + ], + }, + { + name: "Legal", + docs: [ + { title: "ECA Agreement", url: "/docs/legal/external-content-agreement" }, + ], + }, ]; export type Docs = { - title: string; - url: string; + title: string; + url: string; }; export type DocsFolder = { - name: string; - docs: Array; + name: string; + docs: Array; }; diff --git a/src/config/version.tsx b/src/config/version.tsx index 77238eb..1ea92bc 100644 --- a/src/config/version.tsx +++ b/src/config/version.tsx @@ -65,7 +65,7 @@ export const changelog: { name: string; id: string; changelog: ReactNode }[] = [ features={[ "New MOTD engine that is over 3,000% faster, runs client-side, and doesn't need any requests to run.", "Fixed issue where GitHub link was broken if you were signed-out", - "", + "Adding snowfall finally (better late then ever)", ]} title={ diff --git a/src/lib/api.ts b/src/lib/api.ts index 7130a9d..49f4e8f 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -62,13 +62,17 @@ async function apiConstructor( export async function getMOTDFromServer( list: Array<{ server: string; motd: string }>, ): Promise> { - const result = await fetch(connector("/motd", { version: 1 }), { - body: JSON.stringify({ motd: list }), - method: "POST", - headers: { - "Content-Type": "application/json", + const result = await fetch( + process.env.NEXT_PUBLIC_ALTERNATE_MOTD_ENDPOINT ?? + connector("/motd", { version: 1 }), + { + body: JSON.stringify({ motd: list }), + method: "POST", + headers: { + "Content-Type": "application/json", + }, }, - }); + ); let json = await result.json(); return json.result; diff --git a/src/lib/hooks/use-banners.tsx b/src/lib/hooks/use-banners.tsx new file mode 100644 index 0000000..936ffc2 --- /dev/null +++ b/src/lib/hooks/use-banners.tsx @@ -0,0 +1,57 @@ +/* + * MHSF, Minehut Server List + * All external content is rather licensed under the ECA Agreement + * located here: https://mhsf.app/docs/legal/external-content-agreement + * + * All code under MHSF is licensed under the MIT License + * by open source contributors + * + * Copyright (c) 2024 dvelo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +import { bannerHooks, defaultBanners } from "@/config/banners"; +import { useEffect, useState } from "react"; +import { useIsMobile } from "./use-mobile"; + +export function useBanners() { + const [banners, setBanners] = useState< + { + bannerSpace: number; + bannerContent: React.ReactNode; + }[] + >(defaultBanners); + const isOnMobile = useIsMobile(); + + useEffect(() => { + if (isOnMobile) { + setBanners([]); + return; + } + setBanners(defaultBanners); + bannerHooks.forEach((hook) => { + const run = hook(); + if (run !== undefined) setBanners((oldBanners) => [...oldBanners, run]); + }); + }, [isOnMobile]); + + return { banners }; +} diff --git a/src/lib/hooks/use-total-banner-size.tsx b/src/lib/hooks/use-total-banner-size.tsx new file mode 100644 index 0000000..5ab3173 --- /dev/null +++ b/src/lib/hooks/use-total-banner-size.tsx @@ -0,0 +1,61 @@ +/* + * MHSF, Minehut Server List + * All external content is rather licensed under the ECA Agreement + * located here: https://mhsf.app/docs/legal/external-content-agreement + * + * All code under MHSF is licensed under the MIT License + * by open source contributors + * + * Copyright (c) 2024 dvelo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +import { bannerHooks, defaultBanners } from "@/config/banners"; +import { useEffect, useState } from "react"; +import { useIsMobile } from "./use-mobile"; + +export default function useTotalBannerSize() { + const [bannerSize, setBannerSize] = useState(0); + const isOnMobile = useIsMobile(); + + useEffect(() => { + setBannerSize(0); + if (isOnMobile) return; + const allBanners = []; + + // First push the default banners + allBanners.push(...defaultBanners); + + // Then push the banner hooks + bannerHooks.forEach((hook) => { + allBanners.push(hook()); + }); + + setBannerSize( + allBanners.reduce( + (acc, banner) => acc + (banner ?? { bannerSpace: 0 }).bannerSpace, + 0 + ) ?? 0 + ); + }, [isOnMobile]); + + return { bannerSize }; +} diff --git a/src/lib/types/stripe-gradient.d.ts b/src/lib/types/stripe-gradient.d.ts new file mode 100644 index 0000000..fcadc08 --- /dev/null +++ b/src/lib/types/stripe-gradient.d.ts @@ -0,0 +1,5 @@ +declare module "stripe-gradient" { + declare class Gradient { + initGradient(id: string): void; + }; +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 5e7f4c8..fbc1388 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1178,6 +1178,13 @@ dependencies: "@radix-ui/react-primitive" "2.0.0" +"@radix-ui/react-aspect-ratio@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.1.tgz#95d7692e61bab5eb7fec91f241ea993899593313" + integrity sha512-kNU4FIpcFMBLkOUcgeIteH06/8JLBcYY6Le1iKenDGCYNYFX3TQqCZjzkOsz37h7r94/99GTb7YhEr98ZBJibw== + dependencies: + "@radix-ui/react-primitive" "2.0.1" + "@radix-ui/react-avatar@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-avatar/-/react-avatar-1.1.1.tgz#5848d2ed5f34d18b36fc7e2d227c41fca8600ea1"