diff --git a/package.json b/package.json index a136dda..ec257d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mh-stats", - "version": "0.1.0", + "version": "0.10.0", "private": true, "packageManager": "yarn@1.22.22", "scripts": { @@ -16,6 +16,8 @@ "@babel/parser": "^7.24.7", "@clerk/nextjs": "^5.1.3", "@monaco-editor/react": "^4.6.0", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-menubar": "^1.1.1", "@unocss/eslint-plugin": "^0.61.5", "@unocss/postcss": "^0.61.5", "@unocss/transformer-directives": "^0.61.5", @@ -43,41 +45,8 @@ "unplugin-tailwindcss-mangle": "^3.0.1" }, "devDependencies": { - "geist": "^1.3.0", - "@hookform/resolvers": "^3.9.0", - "@types/canvas-confetti": "^1.6.4", - "@types/nprogress": "^0.2.3", - "@types/react-twemoji": "^0.4.3", "@clerk/themes": "^2.1.19", - "@tailwindcss/typography": "^0.5.13", - "@types/node": "^20", - "@types/react": "^18", - "@types/react-dom": "^18", - "@unocss/eslint-config": "^0.61.5", - "@unocss/preset-uno": "^0.61.5", - "@unocss/transformer-compile-class": "^0.61.5", - "eslint": "^8", - "eslint-config-next": "14.2.3", - "mangle-css-class-webpack-plugin": "^5.1.0", - "postcss": "^8", - "tailwindcss": "^3.4.1", - "typescript": "^5", - "vaul": "^0.9.1", - "zod": "^3.23.8", - "react-hook-form": "^7.52.2", - "react-hot-toast": "^2.4.1", - "react-hotkeys-hook": "^4.5.0", - "react-infinite-scroll-component": "^6.1.0", - "react-markdown": "^9.0.1", - "react-resizable-panels": "^2.0.23", - "recharts": "^2.12.7", - "@vercel/analytics": "^1.3.1", - "@vercel/speed-insights": "^1.0.12", - "canvas-confetti": "^1.9.3", - "class-variance-authority": "^0.7.0", - "clsx": "^2.1.1", - "cmdk": "^1.0.0", - "framer-motion": "^11.3.8", + "@hookform/resolvers": "^3.9.0", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-context-menu": "^2.1.5", "@radix-ui/react-dialog": "^1.1.1", @@ -90,6 +59,39 @@ "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-tabs": "^1.1.0", - "@radix-ui/react-tooltip": "^1.0.7" + "@radix-ui/react-tooltip": "^1.0.7", + "@tailwindcss/typography": "^0.5.13", + "@types/canvas-confetti": "^1.6.4", + "@types/node": "^20", + "@types/nprogress": "^0.2.3", + "@types/react": "^18", + "@types/react-dom": "^18", + "@types/react-twemoji": "^0.4.3", + "@unocss/eslint-config": "^0.61.5", + "@unocss/preset-uno": "^0.61.5", + "@unocss/transformer-compile-class": "^0.61.5", + "@vercel/analytics": "^1.3.1", + "@vercel/speed-insights": "^1.0.12", + "canvas-confetti": "^1.9.3", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "cmdk": "^1.0.0", + "eslint": "^8", + "eslint-config-next": "14.2.3", + "framer-motion": "^11.3.8", + "geist": "^1.3.0", + "mangle-css-class-webpack-plugin": "^5.1.0", + "postcss": "^8", + "react-hook-form": "^7.52.2", + "react-hot-toast": "^2.4.1", + "react-hotkeys-hook": "^4.5.0", + "react-infinite-scroll-component": "^6.1.0", + "react-markdown": "^9.0.1", + "react-resizable-panels": "^2.0.23", + "recharts": "^2.12.7", + "tailwindcss": "^3.4.1", + "typescript": "^5", + "vaul": "^0.9.1", + "zod": "^3.23.8" } } diff --git a/src/app/globals.css b/src/app/globals.css index a99c7ac..18aefe9 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -96,26 +96,23 @@ } .backdrop-blur { - -webkit-backdrop-filter: blur(8px)!important; - backdrop-filter: blur(8px)!important; + -webkit-backdrop-filter: blur(8px) !important; + backdrop-filter: blur(8px) !important; } -/* width */ +/** Cool scrollbar */ ::-webkit-scrollbar { width: 10px; } -/* Track */ ::-webkit-scrollbar-track { background: transparent; } -/* Handle */ ::-webkit-scrollbar-thumb { background: #888; } -/* Handle on hover */ ::-webkit-scrollbar-thumb:hover { background: #555; } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index c883086..7fcc4fc 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -19,6 +19,7 @@ import TextFromPathname from "@/components/TextFromPathname"; import { Inter as interFont } from "next/font/google"; import { CommandBarer } from "@/components/CommandBar"; import ThemedToaster from "@/components/misc/ThemedToaster"; +import UnofficalDialog from "@/components/misc/UnofficalDialog"; const inter = interFont({ variable: "--font-inter", subsets: ["latin"] }); export default async function RootLayout({ @@ -68,6 +69,7 @@ export default async function RootLayout({ + diff --git a/src/components/ServerList.tsx b/src/components/ServerList.tsx index a8dabc9..04af60d 100644 --- a/src/components/ServerList.tsx +++ b/src/components/ServerList.tsx @@ -23,22 +23,29 @@ import toast from "react-hot-toast"; import { allTags, allCategories } from "@/allTags"; import IconDisplay from "./IconDisplay"; import InfiniteScroll from "react-infinite-scroll-component"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; -import { Checkbox } from "@/components/ui/checkbox"; import { Spinner } from "./ui/spinner"; import { CommandIcon } from "lucide-react"; import { OnlineServer } from "@/lib/types/mh-server"; -import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { useEffectOnce } from "@/lib/useEffectOnce"; import ServerCard from "./ServerCard"; import events from "@/lib/commandEvent"; import { BorderBeam } from "@/components/effects/border-beam"; -import { Label } from "./ui/label"; import { useRouter } from "@/lib/useRouter"; +import { + Menubar, + MenubarCheckboxItem, + MenubarContent, + MenubarItem, + MenubarMenu, + MenubarRadioGroup, + MenubarRadioItem, + MenubarSeparator, + MenubarShortcut, + MenubarSub, + MenubarSubContent, + MenubarSubTrigger, + MenubarTrigger, +} from "@/components/ui/menubar"; export default function ServerList() { const [loading, setLoading]: any = useState(true); @@ -62,6 +69,7 @@ export default function ServerList() { const [inErrState, setErrState] = useState(false); const [servers, setServers] = useState>([]); const router = useRouter(); + const [ipr, setIPR] = useState("4"); const [filters, setFilters] = useState< Array<(server: OnlineServer) => Promise> >([]); @@ -200,27 +208,77 @@ export default function ServerList() {
-
- - - - - - - + + Servers + + events.emit("search-request-event")}> + Search Servers + + + +Shift+K + + + { + setRandomData(serverList.getRandomServer()); + setRandom(true); + }} + > + Pick Random Server + + + { + toast.promise( + new Promise((s, e) => { + setLoading(true); + serverList + .fetchDataAndFilter() + .then(() => { + serverList.moveListDown(); + + let stringList: Array<{ + server: string; + motd: string; + }> = []; + let obj: any = {}; + + serverList.currentServers.forEach((b) => { + stringList.push({ motd: b.motd, server: b.name }); + }); + + serverList.getMOTDs(stringList).then((c) => { + var updatedSL = motdList; + c.forEach((b: { server: string; motd: string }) => { + updatedSL[b.server] = b.motd; + }); + setMotdList(updatedSL); + setServers(serverList.currentServers); + setLoading(false); + s(false); + }); + }) + .catch(() => { + e(); + }); + }), + { + success: "Succesfully refreshed servers", + loading: "Refreshing...", + error: "Error while refreshing", + } + ); + }} + > + Refresh + + + + + Filter + + { toast.promise( new Promise((g, b) => { @@ -344,7 +402,7 @@ export default function ServerList() { } ); }} - defaultValue={(() => { + value={(() => { if (nameFilters["smaller-tf"]) { return "smaller"; } else if (nameFilters["bigger-tf"]) { @@ -354,219 +412,112 @@ export default function ServerList() { } })()} > -
- -
- -

- Server have 15-7 players, cannot be{" "} - Always Online -

+ +
+ Only allow smaller servers +
+ + Only allow servers that have the player range 7-15, and + cannot
+ be Always Online. +
-
-
- -
- -

- Server has 16 players or more -

+ + +
+ Only allow bigger servers +
+ + Only allow servers with more than 15 players. +
-
-
- -
- -
-
- -
-
- Tags -
- + + + No/custom requirements + + + + + Tags + {allTags.map((tag) => (
{tag.docsName && tag.__filter == undefined && ( -
- { - return nameFilters["t-" + tag.docsName]; - })()} - onCheckedChange={async (b) => { - var filt = nameFilters; - filt["t-" + tag.docsName] = b; - setNameFilters(filt); - if (b) { - var filt2 = filters; - filt2.push(tag.condition); - setFilters(filt2); - } else { - var filt2 = filters; - filt2.splice(filt2.indexOf(tag.condition), 1); - setFilters(filt2); - } - serverList.editFilters(filters); - serverList.fetchDataAndFilter().then(() => { - serverList.moveListDown(); + { + return nameFilters["t-" + tag.docsName]; + })()} + onCheckedChange={async (b) => { + var filt = nameFilters; + filt["t-" + tag.docsName] = b; + setNameFilters(filt); + if (b) { + var filt2 = filters; + filt2.push(tag.condition); + setFilters(filt2); + } else { + var filt2 = filters; + filt2.splice(filt2.indexOf(tag.condition), 1); + setFilters(filt2); + } + serverList.editFilters(filters); + serverList.fetchDataAndFilter().then(() => { + serverList.moveListDown(); - let stringList: Array<{ - server: string; - motd: string; - }> = []; - let obj: any = {}; + let stringList: Array<{ + server: string; + motd: string; + }> = []; + let obj: any = {}; - serverList.currentServers.forEach((b) => { - stringList.push({ motd: b.motd, server: b.name }); - }); - - serverList.getMOTDs(stringList).then((c) => { - var updatedSL = motdList; - c.forEach((b: { server: string; motd: string }) => { - updatedSL[b.server] = b.motd; - }); - setMotdList(updatedSL); - setServers(serverList.currentServers); - }); + serverList.currentServers.forEach((b) => { + stringList.push({ motd: b.motd, server: b.name }); }); - }} - /> -
- -
-
+ + serverList.getMOTDs(stringList).then((c) => { + var updatedSL = motdList; + c.forEach((b: { server: string; motd: string }) => { + updatedSL[b.server] = b.motd; + }); + setMotdList(updatedSL); + setServers(serverList.currentServers); + }); + }); + }} + > + + {tag.docsName} + + )}
))} -
- Categories -
+ + + + Categories + + {allCategories.map((categorie) => ( -
- { - var filt = nameFilters; - filt["c-" + categorie.name] = b; - setNameFilters(filt); - if (b) { - var filt2 = filters; - filt2.push(categorie.condition); - setFilters(filt2); - } else { - var filt2 = filters; - filt2.splice(filt2.indexOf(categorie.condition), 1); - setFilters(filt2); - } - serverList.editFilters(filters); - serverList.fetchDataAndFilter().then(() => { - serverList.moveListDown(); - - let stringList: Array<{ server: string; motd: string }> = - []; - let obj: any = {}; - - serverList.currentServers.forEach((b) => { - stringList.push({ motd: b.motd, server: b.name }); - }); - - serverList.getMOTDs(stringList).then((c) => { - var updatedSL = motdList; - c.forEach((b: { server: string; motd: string }) => { - updatedSL[b.server] = b.motd; - }); - setMotdList(updatedSL); - setServers(serverList.currentServers); - }); - }); - }} - checked={(() => { - return nameFilters["c-" + categorie.name]; - })()} - /> -
- -
-
- ))} - - - - - - - - router.push("/sort/favorites")} - > -
- - -
-
- - -
-
-
-
- - - - - {randomData == undefined && <>No data to randomize} - {randomData != undefined && ( - - - {randomData.name} - {randomData.author != undefined ? ( -
- by {randomData.author} -
- ) : ( -
- )} - -
- - - {randomData.playerData.playerCount == 0 ? ( -
- ) : ( -
- )} + }} + checked={(() => { + return nameFilters["c-" + categorie.name]; + })()} + > + + {categorie.name} + + + ))} + + + + View + + + Grid + + + 4 items per row + 5 items per row + 6 items per row + + + + + Sort + + + c == "favorites" && router.push("/sort/favorites") + } + > + + Players Online + + + Favorites + + + + + + + - - {randomData.playerData.playerCount}{" "} - {randomData.playerData.playerCount == 1 - ? "player" - : "players"}{" "} - currently online - - + + + {randomData == undefined && <>No data to randomize} + {randomData != undefined && ( + + + {randomData.name} + {randomData.author != undefined ? ( +
+ by {randomData.author} +
+ ) : (
- Server IP -
-
- - {randomData.name}.minehut.gg{" "} - - - -
- )} -
-
-
+ /> + ) : ( +
+ )} + + + {randomData.playerData.playerCount}{" "} + {randomData.playerData.playerCount == 1 + ? "player" + : "players"}{" "} + currently online + + +
+ Server IP +
+
+ + {randomData.name}.minehut.gg{" "} + + + + + )} + +
+
-
+
{servers.map((b: any) => ( <> diff --git a/src/components/misc/UnofficalDialog.tsx b/src/components/misc/UnofficalDialog.tsx new file mode 100644 index 0000000..48692cf --- /dev/null +++ b/src/components/misc/UnofficalDialog.tsx @@ -0,0 +1,47 @@ +"use client"; +import { useEffect, useState } from "react"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "../ui/dialog"; + +export default function UnofficalDialog() { + const [isOpen, setOpen] = useState(false); + + useEffect(() => { + const dialog = localStorage.getItem("unoffical-dialog-open"); + + if (dialog == null) { + setOpen(true); + } + }, []); + + const onChangeVal = (state: boolean) => { + if (state == false) { + setOpen(false); + localStorage.setItem("unoffical-dialog-open", "true"); + } else setOpen(state); + }; + + return ( + + + + Welcome to the Minehut Server List (MHSF) + + + MHSF is a Minehut server-list using the Minehut API to serve you + servers with filters, sorts and other helpful information when picking + a server to play.{" "} + + Keep in mind that MHSF is not endorsed by Minehut or GamerSafer in + any way, as MHSF is a open-source unofficial project. + + + + + ); +} diff --git a/src/components/ui/menubar.tsx b/src/components/ui/menubar.tsx new file mode 100644 index 0000000..010145c --- /dev/null +++ b/src/components/ui/menubar.tsx @@ -0,0 +1,240 @@ +"use client" + +import * as React from "react" +import { + CheckIcon, + ChevronRightIcon, + DotFilledIcon, +} from "@radix-ui/react-icons" +import * as MenubarPrimitive from "@radix-ui/react-menubar" + +import { cn } from "@/lib/utils" + +const MenubarMenu = MenubarPrimitive.Menu + +const MenubarGroup = MenubarPrimitive.Group + +const MenubarPortal = MenubarPrimitive.Portal + +const MenubarSub = MenubarPrimitive.Sub + +const MenubarRadioGroup = MenubarPrimitive.RadioGroup + +const Menubar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Menubar.displayName = MenubarPrimitive.Root.displayName + +const MenubarTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName + +const MenubarSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)) +MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName + +const MenubarSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName + +const MenubarContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>( + ( + { className, align = "start", alignOffset = -4, sideOffset = 8, ...props }, + ref + ) => ( + + + + ) +) +MenubarContent.displayName = MenubarPrimitive.Content.displayName + +const MenubarItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +MenubarItem.displayName = MenubarPrimitive.Item.displayName + +const MenubarCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)) +MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName + +const MenubarRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName + +const MenubarLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +MenubarLabel.displayName = MenubarPrimitive.Label.displayName + +const MenubarSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName + +const MenubarShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +MenubarShortcut.displayname = "MenubarShortcut" + +export { + Menubar, + MenubarMenu, + MenubarTrigger, + MenubarContent, + MenubarItem, + MenubarSeparator, + MenubarLabel, + MenubarCheckboxItem, + MenubarRadioGroup, + MenubarRadioItem, + MenubarPortal, + MenubarSubContent, + MenubarSubTrigger, + MenubarGroup, + MenubarSub, + MenubarShortcut, +} diff --git a/src/lib/motdEngine.ts b/src/lib/motdEngine.ts index b8295fd..d8ee07d 100644 --- a/src/lib/motdEngine.ts +++ b/src/lib/motdEngine.ts @@ -115,7 +115,10 @@ export default function parseToHTML(m: string, tw?: boolean): Promise { }); } -function objToHTML(i: Element): string { +function objToHTML(i: Element | string): string { + if (typeof i == "string") { + return i; + } var curClass = ""; var contents = ""; if (i.extra != undefined) { @@ -152,7 +155,7 @@ function createHTML( ) { if (className == undefined) className = ""; if (contents == undefined) contents = ""; - if (contents == "undefined") contents = "
"; + if (contents == "\n") contents = "
"; if (tw == false || tw == undefined) { return ( @@ -189,6 +192,9 @@ function colorConvert(className: string) { if (classUnique.startsWith("text-[")) { result += "color: " + classUnique.substring(6, classUnique.length - 1) + "; "; + } else { + result += + "color: " + classUnique.substring(5, classUnique.length) + "; "; } } if (classUnique.startsWith("font-bold")) { diff --git a/src/version.tsx b/src/version.tsx index 33d53bb..5f5560a 100644 --- a/src/version.tsx +++ b/src/version.tsx @@ -1,7 +1,7 @@ import Image from "next/image"; import Link from "next/link"; -export const version = "b-0.9.0"; +export const version = "b-0.10.0"; const User = ({ user }: { user: string }) => ( @@ -44,6 +44,28 @@ export const Changelog = () => (

+
+ + Version b-0.10.0 (August 17th 2024) + +
    +
  • • Revamped server list button list
  • +
  • • Added welcome dialog when first launching
  • +
  • + • Fixed an issue where servers were still able to be favorited + client-side when logged out +
  • +
  • • Improved MOTD engine
  • +
+
+ 👀 + {/** Ensure Tailwind pre-renders all grid column types */} + + + +
+
+
Version b-0.9.0 (August 15th 2024) diff --git a/yarn.lock b/yarn.lock index 3fe8bc1..feaa35e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -675,6 +675,11 @@ "@radix-ui/react-primitive" "2.0.0" "@radix-ui/react-use-callback-ref" "1.1.0" +"@radix-ui/react-icons@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-icons/-/react-icons-1.3.0.tgz#c61af8f323d87682c5ca76b856d60c2312dbcb69" + integrity sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw== + "@radix-ui/react-id@1.0.1": version "1.0.1" resolved "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz" @@ -746,6 +751,22 @@ aria-hidden "^1.1.1" react-remove-scroll "2.5.7" +"@radix-ui/react-menubar@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-menubar/-/react-menubar-1.1.1.tgz#e126514cb1c46e0a4f9fba7d016e578cc4e41f22" + integrity sha512-V05Hryq/BE2m+rs8d5eLfrS0jmSWSDHEbG7jEyLA5D5J9jTvWj/o3v3xDN9YsOlH6QIkJgiaNDaP+S4T1rdykw== + dependencies: + "@radix-ui/primitive" "1.1.0" + "@radix-ui/react-collection" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.0" + "@radix-ui/react-direction" "1.1.0" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-menu" "2.1.1" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-roving-focus" "1.1.0" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-navigation-menu@^1.1.4": version "1.1.4" resolved "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.1.4.tgz"