chore: move dev branch commits -> main

chore: move dev branch commits -> main
This commit is contained in:
dvelo 2024-11-23 18:01:43 -06:00 committed by GitHub
commit cbdcc74ce8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 2609 additions and 2358 deletions

@ -44,7 +44,7 @@
"next": "14.2.10", "next": "14.2.10",
"next-contentlayer": "^0.3.4", "next-contentlayer": "^0.3.4",
"next-css-obfuscator": "^2.2.16", "next-css-obfuscator": "^2.2.16",
"next-themes": "^0.3.0", "next-themes": "^0.4.3",
"nextjs-toploader": "^1.6.12", "nextjs-toploader": "^1.6.12",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"postcss-obfuscator": "^1.6.1", "postcss-obfuscator": "^1.6.1",
@ -53,9 +53,11 @@
"react-dom": "^18", "react-dom": "^18",
"react-fade-in": "^2.0.1", "react-fade-in": "^2.0.1",
"react-fast-marquee": "^1.6.5", "react-fast-marquee": "^1.6.5",
"react-hot-toast": "^2.4.1",
"react-qr-code": "^2.0.15", "react-qr-code": "^2.0.15",
"rehype-slug": "^6.0.0", "rehype-slug": "^6.0.0",
"remark-gfm": "^4.0.0", "remark-gfm": "^4.0.0",
"sonner": "^1.7.0",
"tailwind-merge": "^2.3.0", "tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"tailwindcss-patch": "^4.0.0", "tailwindcss-patch": "^4.0.0",
@ -101,7 +103,6 @@
"mangle-css-class-webpack-plugin": "^5.1.0", "mangle-css-class-webpack-plugin": "^5.1.0",
"postcss": "^8", "postcss": "^8",
"react-hook-form": "^7.52.2", "react-hook-form": "^7.52.2",
"react-hot-toast": "^2.4.1",
"react-hotkeys-hook": "^4.5.0", "react-hotkeys-hook": "^4.5.0",
"react-infinite-scroll-component": "^6.1.0", "react-infinite-scroll-component": "^6.1.0",
"react-markdown": "^9.0.1", "react-markdown": "^9.0.1",

@ -31,7 +31,7 @@
"use client"; "use client";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useClerk, useUser } from "@clerk/nextjs"; import { useClerk, useUser } from "@clerk/nextjs";
import toast from "react-hot-toast"; import { toast } from "sonner";
import { unlinkMCAccount } from "@/lib/api"; import { unlinkMCAccount } from "@/lib/api";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Dialog } from "@/components/ui/dialog"; import { Dialog } from "@/components/ui/dialog";

@ -32,6 +32,7 @@ import AfterServerView from "@/components/AfterServerView";
import Banner from "@/components/Banner"; import Banner from "@/components/Banner";
import ColorProvider from "@/components/ColorProvider"; import ColorProvider from "@/components/ColorProvider";
import ServerView from "@/components/ServerView"; import ServerView from "@/components/ServerView";
import StickyTopbar from "@/components/misc/StickyTopbar";
import TabServer from "@/components/misc/TabServer"; import TabServer from "@/components/misc/TabServer";
import { Separator } from "@/components/ui/separator"; import { Separator } from "@/components/ui/separator";
import type { Metadata, ResolvingMetadata } from "next"; import type { Metadata, ResolvingMetadata } from "next";
@ -132,7 +133,9 @@ export default function ServerPage({ params }: { params: { server: string } }) {
<ColorProvider server={params.server}> <ColorProvider server={params.server}>
<div className={"pt-16 xl:px-[100px]"}> <div className={"pt-16 xl:px-[100px]"}>
<Banner server={params.server} /> <Banner server={params.server} />
<StickyTopbar scrollElevation={100} className="pt-4">
<TabServer server={params.server} tabDef="general" /> <TabServer server={params.server} tabDef="general" />
</StickyTopbar>
<div className="pt-8"> <div className="pt-8">
<ServerView server={params.server} /> <ServerView server={params.server} />
</div> </div>

@ -41,7 +41,8 @@ import { Copy, Info, QrCode, Share2 } from "lucide-react";
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import FadeIn from "react-fade-in/lib/FadeIn"; import FadeIn from "react-fade-in/lib/FadeIn";
import toast, { CheckmarkIcon } from "react-hot-toast"; import { CheckmarkIcon } from "react-hot-toast";
import { toast } from "sonner";
import Markdown from "react-markdown"; import Markdown from "react-markdown";
import IconDisplay from "./IconDisplay"; import IconDisplay from "./IconDisplay";
import AchievementList from "./feat/AchievementList"; import AchievementList from "./feat/AchievementList";

@ -68,7 +68,7 @@ import {
} from "@/lib/api"; } from "@/lib/api";
import IconDisplay from "./IconDisplay"; import IconDisplay from "./IconDisplay";
import ServerSingle from "@/lib/single"; import ServerSingle from "@/lib/single";
import toast from "react-hot-toast"; import { toast } from "sonner";
import { ServerResponse, OnlineServer } from "@/lib/types/mh-server"; import { ServerResponse, OnlineServer } from "@/lib/types/mh-server";
import { import {
Dialog, Dialog,
@ -80,6 +80,7 @@ import {
import { TagShower } from "./ServerList"; import { TagShower } from "./ServerList";
import { Button } from "./ui/button"; import { Button } from "./ui/button";
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import { CheckmarkIcon } from "react-hot-toast";
export function SearchCommandBar() { export function SearchCommandBar() {
const [serverList, setServerList] = useState<OnlineServer[]>([]); const [serverList, setServerList] = useState<OnlineServer[]>([]);
@ -278,7 +279,7 @@ export function OfflineServerCB() {
{customized && ( {customized && (
<h2 className="flex items-center text-muted-foreground"> <h2 className="flex items-center text-muted-foreground">
<CheckIcon /> <CheckmarkIcon />
<span className="pl-1.5 text-[16px]"> <span className="pl-1.5 text-[16px]">
Is customized by a MHSF User Is customized by a MHSF User
</span> </span>

@ -30,13 +30,12 @@
"use client"; "use client";
import { useState } from "react"; import { useState } from "react";
import { Spinner } from "./ui/spinner";
import { Card, CardHeader, CardTitle } from "./ui/card"; import { Card, CardHeader, CardTitle } from "./ui/card";
import type { ServerResponse } from "@/lib/types/mh-server"; import type { ServerResponse } from "@/lib/types/mh-server";
import { useEffectOnce } from "@/lib/useEffectOnce"; import { useEffectOnce } from "@/lib/useEffectOnce";
import { Button } from "./ui/button"; import { Button } from "./ui/button";
import { Copy, Layers, XIcon } from "lucide-react"; import { Copy, Layers, XIcon } from "lucide-react";
import toast from "react-hot-toast"; import { toast } from "sonner";
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
import { getAccountFavorites } from "@/lib/api"; import { getAccountFavorites } from "@/lib/api";
import { useRouter } from "@/lib/useRouter"; import { useRouter } from "@/lib/useRouter";

@ -304,3 +304,19 @@ export function BrandingGenericIcon(props: SVGProps<SVGSVGElement>) {
); );
} }
} }
export const Discord = (props: SVGProps<SVGSVGElement>) => (
<svg
viewBox="0 0 256 199"
width="1em"
height="1em"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid"
{...props}
>
<path
d="M216.856 16.597A208.502 208.502 0 0 0 164.042 0c-2.275 4.113-4.933 9.645-6.766 14.046-19.692-2.961-39.203-2.961-58.533 0-1.832-4.4-4.55-9.933-6.846-14.046a207.809 207.809 0 0 0-52.855 16.638C5.618 67.147-3.443 116.4 1.087 164.956c22.169 16.555 43.653 26.612 64.775 33.193A161.094 161.094 0 0 0 79.735 175.3a136.413 136.413 0 0 1-21.846-10.632 108.636 108.636 0 0 0 5.356-4.237c42.122 19.702 87.89 19.702 129.51 0a131.66 131.66 0 0 0 5.355 4.237 136.07 136.07 0 0 1-21.886 10.653c4.006 8.02 8.638 15.67 13.873 22.848 21.142-6.58 42.646-16.637 64.815-33.213 5.316-56.288-9.08-105.09-38.056-148.36ZM85.474 135.095c-12.645 0-23.015-11.805-23.015-26.18s10.149-26.2 23.015-26.2c12.867 0 23.236 11.804 23.015 26.2.02 14.375-10.148 26.18-23.015 26.18Zm85.051 0c-12.645 0-23.014-11.805-23.014-26.18s10.148-26.2 23.014-26.2c12.867 0 23.236 11.804 23.015 26.2 0 14.375-10.148 26.18-23.015 26.18Z"
fill="#5865F2"
/>
</svg>
);

@ -45,7 +45,7 @@ import { Checkbox } from "@/components/ui/checkbox";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Switch } from "./ui/switch"; import { Switch } from "./ui/switch";
import { setAccountSL } from "@/lib/api"; import { setAccountSL } from "@/lib/api";
import toast from "react-hot-toast"; import { toast } from "sonner";
import { useUser } from "@clerk/nextjs"; import { useUser } from "@clerk/nextjs";
export function SLCustomize() { export function SLCustomize() {

@ -1,34 +1,3 @@
/*
* 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 { NewChart } from "@/components/NewChart";
import { MiniJoinsChart } from "@/components/misc/MiniJoinsChart"; import { MiniJoinsChart } from "@/components/misc/MiniJoinsChart";
import { import {
ContextMenu, ContextMenu,
@ -66,7 +35,8 @@ import {
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import Link from "next/link"; import Link from "next/link";
import { useState } from "react"; import { useState } from "react";
import toast, { LoaderIcon } from "react-hot-toast"; import { LoaderIcon } from "react-hot-toast";
import { toast } from "sonner";
import IconDisplay from "./IconDisplay"; import IconDisplay from "./IconDisplay";
import { TagShower } from "./ServerList"; import { TagShower } from "./ServerList";
import { Button } from "./ui/button"; import { Button } from "./ui/button";

@ -45,7 +45,7 @@ import { CheckIcon, X } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { Dispatch, SetStateAction, useEffect, useState } from "react"; import { Dispatch, SetStateAction, useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import toast from "react-hot-toast"; import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
import { Button } from "./ui/button"; import { Button } from "./ui/button";
import { import {
@ -82,7 +82,7 @@ import {
DialogTrigger, DialogTrigger,
} from "./ui/dialog"; } from "./ui/dialog";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
import { Spinner } from "./ui/spinner"; import { LoadingSpinner } from "./ui/loading-spinner";
const formSchema = z.object({ const formSchema = z.object({
description: z description: z
@ -150,7 +150,7 @@ export default function ServerCustomize({
if (loading) { if (loading) {
return ( return (
<> <>
<Spinner className="flex items-center" /> <LoadingSpinner className="flex items-center" />
<br /> <br />
</> </>
); );
@ -233,7 +233,7 @@ export default function ServerCustomize({
success: "Report sent!", success: "Report sent!",
loading: "Sending report...", loading: "Sending report...",
error: "Error while sending report", error: "Error while sending report",
}, }
); );
}} }}
> >
@ -261,7 +261,7 @@ export default function ServerCustomize({
success: "Owned server!", success: "Owned server!",
loading: "Owning server...", loading: "Owning server...",
error: "Error while owning server", error: "Error while owning server",
}, }
); );
setMinehutOwned(true); setMinehutOwned(true);

@ -85,7 +85,7 @@ import { CommandIcon } from "lucide-react";
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import Link from "next/link"; import Link from "next/link";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import toast from "react-hot-toast"; import { toast } from "sonner";
import InfiniteScroll from "react-infinite-scroll-component"; import InfiniteScroll from "react-infinite-scroll-component";
import ClientFadeIn from "./ClientFadeIn"; import ClientFadeIn from "./ClientFadeIn";
import IconDisplay from "./IconDisplay"; import IconDisplay from "./IconDisplay";
@ -100,7 +100,9 @@ import { pageFind } from "./misc/Link";
import { Badge } from "./ui/badge"; import { Badge } from "./ui/badge";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
import { Skeleton } from "./ui/skeleton"; import { Skeleton } from "./ui/skeleton";
import { Spinner } from "./ui/spinner"; import { affiliates } from "@/config/affiliates";
import { LoadingSpinner } from "./ui/loading-spinner";
import StickyTopbar from "./misc/StickyTopbar";
const features = [ const features = [
{ {
@ -167,7 +169,7 @@ export default function ServerList() {
Array<(server: OnlineServer) => Promise<boolean>> Array<(server: OnlineServer) => Promise<boolean>>
>([]); >([]);
const [randomData, setRandomData] = useState<OnlineServer | undefined>( const [randomData, setRandomData] = useState<OnlineServer | undefined>(
undefined, undefined
); );
const { resolvedTheme } = useTheme(); const { resolvedTheme } = useTheme();
const [color, setColor] = useState("#ffffff"); const [color, setColor] = useState("#ffffff");
@ -349,7 +351,7 @@ export default function ServerList() {
className={cn( className={cn(
"relative w-64 cursor-pointer overflow-hidden rounded-xl border no-underline " + "relative w-64 cursor-pointer overflow-hidden rounded-xl border no-underline " +
"border-gray-950/[.1] bg-gray-950/[.01] hover:bg-gray-950/[.05] " + "border-gray-950/[.1] bg-gray-950/[.01] hover:bg-gray-950/[.05] " +
"dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15]", "dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15]"
)} )}
onClick={() => onClick={() =>
router.push(pageFind(`Server:${server.name}`)) router.push(pageFind(`Server:${server.name}`))
@ -383,7 +385,7 @@ export default function ServerList() {
className={cn( className={cn(
"relative w-64 cursor-pointer overflow-hidden rounded-xl border no-underline " + "relative w-64 cursor-pointer overflow-hidden rounded-xl border no-underline " +
"border-gray-950/[.1] bg-gray-950/[.01] hover:bg-gray-950/[.05] " + "border-gray-950/[.1] bg-gray-950/[.01] hover:bg-gray-950/[.05] " +
"dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15]", "dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15]"
)} )}
onClick={() => router.push(`/server/${server.name}`)} onClick={() => router.push(`/server/${server.name}`)}
> >
@ -519,7 +521,8 @@ export default function ServerList() {
<br id="serverlist" className="pb-14" /> <br id="serverlist" className="pb-14" />
<Separator /> <Separator />
<ClientFadeIn delay={100}> <ClientFadeIn delay={100}>
<Menubar className="mt-3 ml-2 border rounded p-2 shadow"> <StickyTopbar scrollElevation={250} className="p-2">
<Menubar className="mt-3 border rounded shadow">
<MenubarMenu> <MenubarMenu>
<MenubarTrigger>Servers</MenubarTrigger> <MenubarTrigger>Servers</MenubarTrigger>
<MenubarContent> <MenubarContent>
@ -558,7 +561,10 @@ export default function ServerList() {
let obj: any = {}; let obj: any = {};
serverList.currentServers.forEach((b) => { serverList.currentServers.forEach((b) => {
stringList.push({ motd: b.motd, server: b.name }); stringList.push({
motd: b.motd,
server: b.name,
});
}); });
serverList.getMOTDs(stringList).then((c) => { serverList.getMOTDs(stringList).then((c) => {
@ -566,7 +572,7 @@ export default function ServerList() {
c.forEach( c.forEach(
(b: { server: string; motd: string }) => { (b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd; updatedSL[b.server] = b.motd;
}, }
); );
setMotdList(updatedSL); setMotdList(updatedSL);
setServers(serverList.currentServers); setServers(serverList.currentServers);
@ -582,7 +588,7 @@ export default function ServerList() {
success: "Succesfully refreshed servers", success: "Succesfully refreshed servers",
loading: "Refreshing...", loading: "Refreshing...",
error: "Error while refreshing", error: "Error while refreshing",
}, }
); );
}} }}
> >
@ -621,7 +627,10 @@ export default function ServerList() {
let obj: any = {}; let obj: any = {};
serverList.currentServers.forEach((b) => { serverList.currentServers.forEach((b) => {
stringList.push({ motd: b.motd, server: b.name }); stringList.push({
motd: b.motd,
server: b.name,
});
}); });
serverList.getMOTDs(stringList).then((c) => { serverList.getMOTDs(stringList).then((c) => {
@ -629,7 +638,7 @@ export default function ServerList() {
c.forEach( c.forEach(
(b: { server: string; motd: string }) => { (b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd; updatedSL[b.server] = b.motd;
}, }
); );
setMotdList(updatedSL); setMotdList(updatedSL);
setServers(serverList.currentServers); setServers(serverList.currentServers);
@ -660,7 +669,10 @@ export default function ServerList() {
let obj: any = {}; let obj: any = {};
serverList.currentServers.forEach((b) => { serverList.currentServers.forEach((b) => {
stringList.push({ motd: b.motd, server: b.name }); stringList.push({
motd: b.motd,
server: b.name,
});
}); });
serverList.getMOTDs(stringList).then((c) => { serverList.getMOTDs(stringList).then((c) => {
@ -668,7 +680,7 @@ export default function ServerList() {
c.forEach( c.forEach(
(b: { server: string; motd: string }) => { (b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd; updatedSL[b.server] = b.motd;
}, }
); );
setMotdList(updatedSL); setMotdList(updatedSL);
setServers(serverList.currentServers); setServers(serverList.currentServers);
@ -699,7 +711,10 @@ export default function ServerList() {
let obj: any = {}; let obj: any = {};
serverList.currentServers.forEach((b) => { serverList.currentServers.forEach((b) => {
stringList.push({ motd: b.motd, server: b.name }); stringList.push({
motd: b.motd,
server: b.name,
});
}); });
serverList.getMOTDs(stringList).then((c) => { serverList.getMOTDs(stringList).then((c) => {
@ -707,7 +722,7 @@ export default function ServerList() {
c.forEach( c.forEach(
(b: { server: string; motd: string }) => { (b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd; updatedSL[b.server] = b.motd;
}, }
); );
setMotdList(updatedSL); setMotdList(updatedSL);
setServers(serverList.currentServers); setServers(serverList.currentServers);
@ -720,7 +735,7 @@ export default function ServerList() {
error: "Error while changing filters", error: "Error while changing filters",
loading: "Changing filters...", loading: "Changing filters...",
success: "Changed filters!", success: "Changed filters!",
}, }
); );
}} }}
value={(() => { value={(() => {
@ -738,8 +753,8 @@ export default function ServerList() {
Only allow smaller servers Only allow smaller servers
<br /> <br />
<span className="text-sm text-muted-foreground"> <span className="text-sm text-muted-foreground">
Only allow servers that have the player range 7-15, and Only allow servers that have the player range 7-15,
cannot <br /> and cannot <br />
be Always Online. be Always Online.
</span> </span>
</div> </div>
@ -796,7 +811,10 @@ export default function ServerList() {
let obj: any = {}; let obj: any = {};
serverList.currentServers.forEach((b) => { serverList.currentServers.forEach((b) => {
stringList.push({ motd: b.motd, server: b.name }); stringList.push({
motd: b.motd,
server: b.name,
});
}); });
serverList.getMOTDs(stringList).then((c) => { serverList.getMOTDs(stringList).then((c) => {
@ -804,7 +822,7 @@ export default function ServerList() {
c.forEach( c.forEach(
(b: { server: string; motd: string }) => { (b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd; updatedSL[b.server] = b.motd;
}, }
); );
setMotdList(updatedSL); setMotdList(updatedSL);
setServers(serverList.currentServers); setServers(serverList.currentServers);
@ -887,21 +905,15 @@ export default function ServerList() {
value={ipr} value={ipr}
onValueChange={(v) => { onValueChange={(v) => {
if (am) if (am)
toast( toast.warning(
<span> "These settings will not change over reloads because you have account specific options enabled",
These settings will not change over reloads {
because you have account specific options enabled action: {
<Button label: "Check settings",
variant="link" onClick: () =>
className="p-0 m-0" router.push("/account/settings/options"),
onClick={() => },
router.push("/account/settings/options")
} }
>
Change your preferences
</Button>
</span>,
{ icon: "!" },
); );
setIPR(v); setIPR(v);
}} }}
@ -925,21 +937,15 @@ export default function ServerList() {
value={padding.toString()} value={padding.toString()}
onValueChange={(v) => { onValueChange={(v) => {
if (am) if (am)
toast( toast.warning(
<span> "These settings will not change over reloads because you have account specific options enabled",
These settings will not change over reloads {
because you have account specific options enabled action: {
<Button label: "Check settings",
variant="link" onClick: () =>
className="p-0 m-0" router.push("/account/settings/options"),
onClick={() => },
router.push("/account/settings/options")
} }
>
Change your preferences
</Button>
</span>,
{ icon: "!" },
); );
setPadding(v); setPadding(v);
}} }}
@ -954,7 +960,10 @@ export default function ServerList() {
<MenubarRadioItem value="200">200px</MenubarRadioItem> <MenubarRadioItem value="200">200px</MenubarRadioItem>
</MenubarRadioGroup> </MenubarRadioGroup>
<MenubarSeparator /> <MenubarSeparator />
<MenubarCheckboxItem checked={pOS} onCheckedChange={setpOS}> <MenubarCheckboxItem
checked={pOS}
onCheckedChange={setpOS}
>
Only use padding on servers Only use padding on servers
</MenubarCheckboxItem> </MenubarCheckboxItem>
</MenubarSubContent> </MenubarSubContent>
@ -979,7 +988,10 @@ export default function ServerList() {
</MenubarSub> </MenubarSub>
<MenubarSeparator /> <MenubarSeparator />
<SignedIn> <SignedIn>
<MenubarCheckboxItem checked={hero} onCheckedChange={setHero}> <MenubarCheckboxItem
checked={hero}
onCheckedChange={setHero}
>
Show Hero Show Hero
</MenubarCheckboxItem> </MenubarCheckboxItem>
</SignedIn> </SignedIn>
@ -1002,6 +1014,7 @@ export default function ServerList() {
</MenubarContent> </MenubarContent>
</MenubarMenu> </MenubarMenu>
</Menubar> </Menubar>
</StickyTopbar>
</ClientFadeIn> </ClientFadeIn>
<Dialog open={random} onOpenChange={setRandom}> <Dialog open={random} onOpenChange={setRandom}>
@ -1063,7 +1076,7 @@ export default function ServerList() {
onClick={() => { onClick={() => {
setTextCopied(true); setTextCopied(true);
clipboard.writeText( clipboard.writeText(
randomData.name + ".mshf.minehut.gg", randomData.name + ".mshf.minehut.gg"
); );
toast.success("Copied!"); toast.success("Copied!");
setTimeout(() => setTextCopied(false), 1000); setTimeout(() => setTextCopied(false), 1000);
@ -1103,7 +1116,7 @@ export default function ServerList() {
setLoading(false); setLoading(false);
}); });
}} }}
loader={<Spinner className="flex items-center" />} loader={<LoadingSpinner className="flex items-center" />}
endMessage={ endMessage={
<p <p
style={{ textAlign: "center" }} style={{ textAlign: "center" }}
@ -1134,8 +1147,45 @@ export default function ServerList() {
" gap-4 sm:grid-cols-2" " gap-4 sm:grid-cols-2"
} }
> >
{servers.map((b: any) => ( {servers.map((b: any, i: number) => (
<> <>
{i === Number(ipr) && affiliates.length != 0 && (
<div
className="border rounded h-[450px] shadow p-10 max-w-full dark:prose-invert prose grid grid-cols-3 max-xl:hidden"
style={{
gridColumn: `span ${ipr} / span ${ipr}`,
}}
>
<div className="dark:text-white text-black max-w-[300px] overflow-auto">
<h2 className="bg-gradient-to-bl from-yellow-300 via-yellow-500 to-yellow-100 bg-clip-text text-transparent">
Affiliates
</h2>
<p>
We have been able to partner with some servers that we
think are high-effort servers that need to be
recognized.
</p>
<p>
Please take some interest in a server you find
interesting and give them some much needed support.
</p>
<small>
These servers have absolutely no financial affiliation
with MHSF.
</small>
</div>
{affiliates
.filter((a) => a.mode.includes("server-list"))
.map((a) => (
<div
className="border rounded p-4 col-span-2"
key={a.name}
>
{a.name}
</div>
))}
</div>
)}
<ServerCard b={b} motd={motdList[b.name]} /> <ServerCard b={b} motd={motdList[b.name]} />
</> </>
))} ))}

@ -59,6 +59,7 @@ import { useClerk, useUser } from "@clerk/nextjs";
import { LoaderIcon } from "react-hot-toast"; import { LoaderIcon } from "react-hot-toast";
import { Separator } from "./ui/separator"; import { Separator } from "./ui/separator";
import { convert } from "@/components/NewChart"; import { convert } from "@/components/NewChart";
import { LoadingSpinner } from "./ui/loading-spinner";
export default function ServerView(props: { server: string }) { export default function ServerView(props: { server: string }) {
const [single, setSingle] = useState(new ServerSingle(props.server)); const [single, setSingle] = useState(new ServerSingle(props.server));
@ -210,7 +211,9 @@ export default function ServerView(props: { server: string }) {
}} }}
disabled={loadingFavorite} disabled={loadingFavorite}
> >
{loadingFavorite && <LoaderIcon className="mr-2" />} {loadingFavorite && (
<LoadingSpinner className="mr-2 h-4 w-4" />
)}
{!favorited && !loadingFavorite && ( {!favorited && !loadingFavorite && (
<motion.div <motion.div
animate={{ opacity: 1, scale: 1 }} animate={{ opacity: 1, scale: 1 }}

@ -31,7 +31,6 @@
"use client"; "use client";
import IconDisplay from "@/components/IconDisplay"; import IconDisplay from "@/components/IconDisplay";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Spinner } from "@/components/ui/spinner";
import type { ServerResponse } from "@/lib/types/mh-server"; import type { ServerResponse } from "@/lib/types/mh-server";
import { Copy, ExternalLink, ServerCrash } from "lucide-react"; import { Copy, ExternalLink, ServerCrash } from "lucide-react";
import { notFound, useSearchParams } from "next/navigation"; import { notFound, useSearchParams } from "next/navigation";
@ -40,6 +39,7 @@ import { Button } from "../ui/button";
import { CheckmarkIcon } from "react-hot-toast"; import { CheckmarkIcon } from "react-hot-toast";
import useClipboard from "@/lib/useClipboard"; import useClipboard from "@/lib/useClipboard";
import { Tooltip, TooltipTrigger, TooltipContent } from "../ui/tooltip"; import { Tooltip, TooltipTrigger, TooltipContent } from "../ui/tooltip";
import { LoadingSpinner } from "../ui/loading-spinner";
export default function Embed({ params }: { params: { server: string } }) { export default function Embed({ params }: { params: { server: string } }) {
const [serverFound, setServerFound] = useState(true); const [serverFound, setServerFound] = useState(true);
@ -65,7 +65,7 @@ export default function Embed({ params }: { params: { server: string } }) {
}, [params]); }, [params]);
if (loading) { if (loading) {
return <Spinner />; return <LoadingSpinner />;
} }
if (!serverFound) { if (!serverFound) {

@ -39,7 +39,7 @@ import { codeToHtml } from "shiki";
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import { Asterisk, Copy } from "lucide-react"; import { Asterisk, Copy } from "lucide-react";
import useClipboard from "@/lib/useClipboard"; import useClipboard from "@/lib/useClipboard";
import toast from "react-hot-toast"; import { toast } from "sonner";
import { Checkbox } from "../ui/checkbox"; import { Checkbox } from "../ui/checkbox";
import { import {
Select, Select,

@ -1,5 +1,4 @@
"use client"; "use client";;
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { z } from "zod"; import { z } from "zod";
@ -15,8 +14,7 @@ import {
} from "@/components/ui/form"; } from "@/components/ui/form";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { setCustomization } from "@/lib/api"; import { setCustomization } from "@/lib/api";
import { useEffect } from "react"; import { toast } from "sonner";
import toast from "react-hot-toast";
import ColorProvider from "../ColorProvider"; import ColorProvider from "../ColorProvider";
const FormSchema = z.object({ const FormSchema = z.object({

@ -47,7 +47,7 @@ import { Input } from "@/components/ui/input";
import { setCustomization } from "@/lib/api"; import { setCustomization } from "@/lib/api";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import ColorProvider from "../ColorProvider"; import ColorProvider from "../ColorProvider";
import toast from "react-hot-toast"; import { toast } from "sonner";
const FormSchema = z.object({ const FormSchema = z.object({
id: z.string().min(2, { id: z.string().min(2, {

@ -49,7 +49,7 @@ import {
InputOTPSlot, InputOTPSlot,
} from "@/components/ui/input-otp"; } from "@/components/ui/input-otp";
import { linkMCAccount } from "@/lib/api"; import { linkMCAccount } from "@/lib/api";
import toast from "react-hot-toast"; import { toast } from "sonner";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,

@ -0,0 +1,68 @@
/*
* 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 { useEffect, useState, ReactNode } from "react";
export default function StickyTopbar({
children,
scrollElevation,
className,
}: {
children: ReactNode;
scrollElevation: number;
className?: string;
}) {
const [isSticky, setIsSticky] = useState(false);
const handleScroll = () => {
if (window.scrollY > scrollElevation) {
setIsSticky(true);
} else {
setIsSticky(false);
}
};
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
return (
<div
className={`transition-all duration-300 ${isSticky ? "fixed top-[70px] left-0 w-full backdrop-blur shadow-lg " + className : "block w-full bg-transparent"}`}
>
{children}
</div>
);
}

@ -31,7 +31,7 @@
"use client"; "use client";
import { useState } from "react"; import { useState } from "react";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import toast from "react-hot-toast"; import { toast } from "sonner";
import { Check } from "lucide-react"; import { Check } from "lucide-react";
import useClipboard from "@/lib/useClipboard"; import useClipboard from "@/lib/useClipboard";

@ -31,20 +31,10 @@
"use client"; "use client";
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import { Toaster } from "react-hot-toast"; import { Toaster } from "../ui/sonner";
export default function ThemedToaster() { export default function ThemedToaster() {
const { resolvedTheme } = useTheme(); const { resolvedTheme } = useTheme();
return ( return <Toaster position="bottom-center" richColors />;
<Toaster
position="bottom-center"
reverseOrder={false}
toastOptions={
resolvedTheme == "dark"
? { style: { background: "#333", color: "#fff" } }
: undefined
}
/>
);
} }

@ -0,0 +1,64 @@
/*
* 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 { cn } from "@/lib/utils";
export function LoadingSpinner({ className }: { className?: string }) {
return (
<div className={cn("h-5 w-5", className)}>
<div
style={{
position: "relative",
top: "50%",
left: "50%",
}}
className={cn("loading-spinner", "h-5 w-5", className)}
>
{[...Array(12)].map((_, i) => (
<div
key={i}
style={{
animationDelay: `${-1.2 + 0.1 * i}s`,
background: "gray",
position: "absolute",
borderRadius: "1rem",
width: "30%",
height: "8%",
left: "-10%",
top: "-4%",
transform: `rotate(${30 * i}deg) translate(120%)`,
}}
className="animate-spinner"
/>
))}
</div>
</div>
);
}

@ -0,0 +1,31 @@
"use client"
import { useTheme } from "next-themes"
import { Toaster as Sonner } from "sonner"
type ToasterProps = React.ComponentProps<typeof Sonner>
const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme()
return (
<Sonner
theme={theme as ToasterProps["theme"]}
className="toaster group"
toastOptions={{
classNames: {
toast:
"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
description: "group-[.toast]:text-muted-foreground",
actionButton:
"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
cancelButton:
"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
},
}}
{...props}
/>
)
}
export { Toaster }

@ -59,7 +59,7 @@ const TabsTrigger = React.forwardRef<
<TabsPrimitive.Trigger <TabsPrimitive.Trigger
ref={ref} ref={ref}
className={cn( className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm", "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:text-foreground data-[state=active]:shadow-sm",
className className
)} )}
{...props} {...props}

42
src/config/affiliates.ts Normal file

@ -0,0 +1,42 @@
/*
* 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 { SVGProps } from "react";
export const affiliates: {
name: string;
shop: string;
line: string;
mode: string[];
otherLinks: {
name: string;
icon: (props: SVGProps<SVGSVGElement>) => JSX.Element;
}[];
}[] = [];

@ -29,7 +29,7 @@
*/ */
import { OnlineServer } from "./types/mh-server"; import { OnlineServer } from "./types/mh-server";
import toast from "react-hot-toast"; import { toast } from "sonner";
import { getMOTDFromServer } from "./api"; import { getMOTDFromServer } from "./api";
var numberOfItemsInView = 20; var numberOfItemsInView = 20;

@ -30,7 +30,7 @@
import { serverOwned } from "./api"; import { serverOwned } from "./api";
import { OnlineServer, ServerResponse } from "./types/mh-server"; import { OnlineServer, ServerResponse } from "./types/mh-server";
import toast from "react-hot-toast"; import { toast } from "sonner";
export default class ServerSingle { export default class ServerSingle {
private name = ""; private name = "";

@ -28,7 +28,7 @@
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
*/ */
import toast from "react-hot-toast" import { toast } from "sonner";
/** A hook to properly write text to the clipboard without triggering a client-side error /** A hook to properly write text to the clipboard without triggering a client-side error
* @version 1.0 * @version 1.0

@ -178,6 +178,14 @@ const config = {
transform: "none", transform: "none",
}, },
}, },
spinner: {
"0%": {
opacity: "1",
},
"100%": {
opacity: "0",
},
},
shimmer: { shimmer: {
"0%, 90%, 100%": { "0%, 90%, 100%": {
"background-position": "calc(-100% - var(--shimmer-width)) 0", "background-position": "calc(-100% - var(--shimmer-width)) 0",
@ -194,6 +202,7 @@ const config = {
"accordion-up": "accordion-up 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out",
"image-glow": "image-glow 4100ms 600ms ease-out forwards", "image-glow": "image-glow 4100ms 600ms ease-out forwards",
shimmer: "shimmer 8s infinite", shimmer: "shimmer 8s infinite",
spinner: "spinner 1.2s linear infinite",
"fade-in": "fade-in 1000ms var(--animation-delay, 0ms) ease forwards", "fade-in": "fade-in 1000ms var(--animation-delay, 0ms) ease forwards",
"caret-blink": "caret-blink 1.25s ease-out infinite", "caret-blink": "caret-blink 1.25s ease-out infinite",
"border-beam": "border-beam calc(var(--duration)*1s) infinite linear", "border-beam": "border-beam calc(var(--duration)*1s) infinite linear",

@ -4322,9 +4322,9 @@ globby@^11.1.0:
slash "^3.0.0" slash "^3.0.0"
goober@^2.1.10: goober@^2.1.10:
version "2.1.14" version "2.1.16"
resolved "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz" resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.16.tgz#7d548eb9b83ff0988d102be71f271ca8f9c82a95"
integrity sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg== integrity sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==
gopd@^1.0.1: gopd@^1.0.1:
version "1.0.1" version "1.0.1"
@ -6344,10 +6344,10 @@ next-css-obfuscator@^2.2.16:
recoverable-random "^1.0.5" recoverable-random "^1.0.5"
yargs "^17.7.2" yargs "^17.7.2"
next-themes@^0.3.0: next-themes@^0.4.3:
version "0.3.0" version "0.4.3"
resolved "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz" resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.4.3.tgz#ea54552d5986936d177eed393ea50b658ae44800"
integrity sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w== integrity sha512-nG84VPkTdUHR2YeD89YchvV4I9RbiMAql3GiLEQlPvq1ioaqPaIReK+yMRdg/zgiXws620qS1rU30TiWmmG9lA==
next@14.2.10: next@14.2.10:
version "14.2.10" version "14.2.10"
@ -6890,7 +6890,7 @@ react-hook-form@^7.52.2:
react-hot-toast@^2.4.1: react-hot-toast@^2.4.1:
version "2.4.1" version "2.4.1"
resolved "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz" resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.4.1.tgz#df04295eda8a7b12c4f968e54a61c8d36f4c0994"
integrity sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ== integrity sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==
dependencies: dependencies:
goober "^2.1.10" goober "^2.1.10"
@ -7422,6 +7422,11 @@ snakecase-keys@5.4.4:
snake-case "^3.0.4" snake-case "^3.0.4"
type-fest "^2.5.2" type-fest "^2.5.2"
sonner@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/sonner/-/sonner-1.7.0.tgz#f59a2a70e049a179b6fbd1bb1bf2619d5ced07c0"
integrity sha512-W6dH7m5MujEPyug3lpI2l3TC3Pp1+LTgK0Efg+IHDrBbtEjyCmCHHo6yfNBOsf1tFZ6zf+jceWwB38baC8yO9g==
source-map-js@^1.0.1, source-map-js@^1.0.2, source-map-js@^1.2.0: source-map-js@^1.0.1, source-map-js@^1.0.2, source-map-js@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz" resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz"