From f438d0339967d81796f9355420bace928df496ee Mon Sep 17 00:00:00 2001
From: dvelo <52332868+DeveloLongScript@users.noreply.github.com>
Date: Sat, 15 Feb 2025 17:18:58 -0600
Subject: [PATCH] feat: add topbar and fix versioning
---
apps/www/src/app/(main)/layout.tsx | 78 ++++
apps/www/src/app/(main)/page.tsx | 16 +
apps/www/src/app/layout.tsx | 51 +++
apps/www/src/app/not-found.tsx | 12 +
.../www/src/components/feat/navbar/navbar.tsx | 24 ++
.../feat/server-list/server-list.tsx | 4 +
apps/www/src/components/icon.tsx | 357 ++++++++++++++++++
apps/www/src/components/ui/github.tsx | 20 +
apps/www/src/components/util/is-script.tsx | 11 +
apps/www/src/components/util/link.tsx | 109 ++++++
apps/www/src/components/util/not-found.tsx | 20 +
apps/www/src/config/version.tsx | 44 ++-
12 files changed, 723 insertions(+), 23 deletions(-)
create mode 100644 apps/www/src/app/(main)/layout.tsx
create mode 100644 apps/www/src/app/(main)/page.tsx
create mode 100644 apps/www/src/app/layout.tsx
create mode 100644 apps/www/src/app/not-found.tsx
create mode 100644 apps/www/src/components/feat/navbar/navbar.tsx
create mode 100644 apps/www/src/components/feat/server-list/server-list.tsx
create mode 100644 apps/www/src/components/icon.tsx
create mode 100644 apps/www/src/components/ui/github.tsx
create mode 100644 apps/www/src/components/util/is-script.tsx
create mode 100644 apps/www/src/components/util/link.tsx
create mode 100644 apps/www/src/components/util/not-found.tsx
diff --git a/apps/www/src/app/(main)/layout.tsx b/apps/www/src/app/(main)/layout.tsx
new file mode 100644
index 0000000..cd23220
--- /dev/null
+++ b/apps/www/src/app/(main)/layout.tsx
@@ -0,0 +1,78 @@
+/*
+ * 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 "../globals.css";
+import { useSearchParams } from "next/navigation";
+import { Inter } from "next/font/google";
+import { Placeholder } from "@/components/ui/placeholder";
+import { X } from "lucide-react";
+import { IsScript } from "@/components/util/is-script";
+import { Button } from "@/components/ui/button";
+import { ClerkProvider } from "@clerk/nextjs";
+import Link from "next/link";
+import { NavBar } from "@/components/feat/navbar/navbar";
+
+const inter = Inter({ subsets: ["latin"] });
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ const searchParams = useSearchParams();
+ const search = searchParams?.get("theme") || "light";
+
+ return (
+
+
+
+
+
+
+
{children}
+
+
+
+
+ );
+}
diff --git a/apps/www/src/app/(main)/page.tsx b/apps/www/src/app/(main)/page.tsx
new file mode 100644
index 0000000..9c50722
--- /dev/null
+++ b/apps/www/src/app/(main)/page.tsx
@@ -0,0 +1,16 @@
+import { ServerList } from "@/components/feat/server-list/server-list";
+import type { Metadata } from "next";
+
+export const metadata: Metadata = {
+ applicationName: "MHSF",
+ title: "Server list · MHSF",
+ description: "View all servers on Minehut using the MHSF server list.",
+};
+
+export default function ServerListPage() {
+ return (
+
+
+
+ );
+}
diff --git a/apps/www/src/app/layout.tsx b/apps/www/src/app/layout.tsx
new file mode 100644
index 0000000..0cb2762
--- /dev/null
+++ b/apps/www/src/app/layout.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 "./globals.css";
+import { useSearchParams } from "next/navigation";
+import { Inter } from "next/font/google";
+
+const inter = Inter({ subsets: ["latin"] });
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ const searchParams = useSearchParams();
+ const search = searchParams?.get("theme") || "light";
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/apps/www/src/app/not-found.tsx b/apps/www/src/app/not-found.tsx
new file mode 100644
index 0000000..7ad6a74
--- /dev/null
+++ b/apps/www/src/app/not-found.tsx
@@ -0,0 +1,12 @@
+import { NotFoundComponent } from "@/components/util/not-found";
+import type { Metadata } from "next";
+
+export const metadata: Metadata = {
+ applicationName: "MHSF",
+ title: "Page not found · MHSF",
+ description: "Couldn't find the page that was requested.",
+};
+
+export default function NotFoundPage() {
+ return ;
+}
diff --git a/apps/www/src/components/feat/navbar/navbar.tsx b/apps/www/src/components/feat/navbar/navbar.tsx
new file mode 100644
index 0000000..46faed7
--- /dev/null
+++ b/apps/www/src/components/feat/navbar/navbar.tsx
@@ -0,0 +1,24 @@
+"use client";
+
+import { BrandingColorfulIcon } from "@/components/Icon";
+import { version } from "@/config/version";
+import { cn } from "@/lib/utils";
+
+export function NavBar() {
+ return (
+
+
+
+
+ MHSF
+ v{version}
+
+
+
+ );
+}
diff --git a/apps/www/src/components/feat/server-list/server-list.tsx b/apps/www/src/components/feat/server-list/server-list.tsx
new file mode 100644
index 0000000..e2bbb25
--- /dev/null
+++ b/apps/www/src/components/feat/server-list/server-list.tsx
@@ -0,0 +1,4 @@
+"use client";
+export function ServerList() {
+ return Server List ;
+}
diff --git a/apps/www/src/components/icon.tsx b/apps/www/src/components/icon.tsx
new file mode 100644
index 0000000..fc93891
--- /dev/null
+++ b/apps/www/src/components/icon.tsx
@@ -0,0 +1,357 @@
+/*
+ * 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
+ *
+ * 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 { useTheme } from "next-themes";
+import type { SVGProps } from "react";
+
+/**
+ * Returns a colorful version of the branding icon.
+ *
+ * The stored SVG file is at `/public/svg/icon-cf.svg`
+ *
+ * @param props The props for the SVG element.
+ * @returns A JSX element representing the colorful branding icon.
+ */
+export function BrandingColorfulIcon(props: SVGProps) {
+ return (
+
+ );
+}
+/**
+ * Returns the optional Pride icon
+ *
+ * The stored SVG file is at `/public/svg/icon-p.svg`
+ *
+ * @param {SVGProps} props The props for the SVG element.
+ * @returns A JSX element representing the branding icon.
+ */
+export function BrandingPrideIcon(props: SVGProps) {
+ return (
+
+ );
+}
+
+/**
+ * Returns the branding icon based on the current theme.
+ *
+ * If the theme is dark, the branding icon is a dark version of the logo.
+ * If the theme is light, the branding icon is a light version of the logo.
+ *
+ * The stored SVG file is at `/public/svg/icon-gl.svg` or `/public/svg/icon-gd.svg`
+ *
+ * @param {SVGProps} props The props for the SVG element.
+ *
+ * @returns A JSX element representing the branding icon.
+ */
+export function BrandingGenericIcon(props: SVGProps) {
+ const { resolvedTheme } = useTheme();
+
+ if (resolvedTheme == "dark") {
+ return (
+
+ );
+ } else {
+ return (
+
+ );
+ }
+}
+
+export function BadgeOfAffiliation(props: SVGProps) {
+ return (
+
+ );
+}
+
+export const Discord = (props: SVGProps) => (
+
+);
diff --git a/apps/www/src/components/ui/github.tsx b/apps/www/src/components/ui/github.tsx
new file mode 100644
index 0000000..8dca926
--- /dev/null
+++ b/apps/www/src/components/ui/github.tsx
@@ -0,0 +1,20 @@
+import { useTheme } from "next-themes";
+import type { SVGProps } from "react";
+const Github = (props: SVGProps) => {
+ const { resolvedTheme } = useTheme();
+
+ return (
+
+ );
+};
+export default Github;
diff --git a/apps/www/src/components/util/is-script.tsx b/apps/www/src/components/util/is-script.tsx
new file mode 100644
index 0000000..1ac7636
--- /dev/null
+++ b/apps/www/src/components/util/is-script.tsx
@@ -0,0 +1,11 @@
+"use client";
+
+import { useEffect, useState } from "react";
+
+export function IsScript({ children }: { children: React.ReactNode }) {
+ const [mounted, setMounted] = useState(false);
+
+ useEffect(() => setMounted(true));
+
+ return mounted && children;
+}
diff --git a/apps/www/src/components/util/link.tsx b/apps/www/src/components/util/link.tsx
new file mode 100644
index 0000000..8b869d3
--- /dev/null
+++ b/apps/www/src/components/util/link.tsx
@@ -0,0 +1,109 @@
+/*
+ * 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
+ *
+ * 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 type { ReactNode } from "react";
+import { default as NextLink } from "next/link";
+import { Book, ExternalLink, NotebookText } from "lucide-react";
+
+export default function A({
+ children,
+ alt,
+}: {
+ children: string;
+ alt: string | ReactNode;
+}) {
+ return (
+
+ {(children || "").startsWith("Docs:") && (
+
+ )}
+ {(children || "").startsWith("Wiki:") && (
+
+ )}
+ {alt}
+ {(children || "").startsWith("https") && (
+
+ )}
+
+ );
+}
+
+export function ALegacy({
+ children,
+ href,
+}: {
+ children?: string | ReactNode;
+ href?: string;
+}) {
+ return (
+
+ {(href || "").startsWith("Docs:") && (
+
+ )}
+ {(href || "").startsWith("Wiki:") && (
+
+ )}
+ {children}
+ {(href || "").startsWith("https") && (
+
+ )}
+
+ );
+}
+
+export const pageFind = (text: string) => {
+ if (text.startsWith("Docs:")) {
+ return "/docs/" + text.substring(5).toLowerCase();
+ }
+ if (text === "Special:Root") return "/";
+ if (text === "Special:Preferences") return "/account/settings";
+ if (text === "Special:AccountOptions") return "/account/settings/options";
+ if (text.startsWith("Server:") && text.endsWith("/Customization"))
+ return "/server/" + text.substring(7, text.length - 14) + "/customization";
+ if (text === "Special:ClerkConvertionPage") return process.env.NEXT_PUBLIC_CLERK_SWITCH_DOMAIN;
+ if (text.startsWith("Server:")) return "/server/" + text.substring(7);
+ if (text.startsWith("Wiki:"))
+ return "https://minehut.wiki.gg/wiki/" + text.substring(5);
+ if (text.startsWith("GitHub:"))
+ return "https://github.com/" + text.substring(7);
+ if (text === "Special:GitHub")
+ return "https://github.com/DeveloLongScript/MHSF";
+ if (text.startsWith("Special:GitHub/"))
+ return "https://github.com/DeveloLongScript/MHSF/" + text.substring(15);
+ return text;
+};
diff --git a/apps/www/src/components/util/not-found.tsx b/apps/www/src/components/util/not-found.tsx
new file mode 100644
index 0000000..eb3921c
--- /dev/null
+++ b/apps/www/src/components/util/not-found.tsx
@@ -0,0 +1,20 @@
+import { X } from "lucide-react";
+import { Placeholder } from "../ui/placeholder";
+import Link from "next/link";
+import { Button } from "../ui/button";
+
+export function NotFoundComponent() {
+ return (
+
+ }
+ title="We couldn't find that page"
+ description="The page you were trying to go to either doesn't exist ever or has been removed previously."
+ >
+
+
+
+
+