2024-07-23 18:49:21 -05:00
|
|
|
"use client";
|
2024-08-24 12:37:08 -05:00
|
|
|
import { useEffect, useRef, useState } from "react";
|
2024-07-23 18:49:21 -05:00
|
|
|
import { Separator } from "@/components/ui/separator";
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
import { Badge } from "./ui/badge";
|
|
|
|
|
import ServersList from "@/lib/list";
|
2024-08-24 12:37:08 -05:00
|
|
|
import {
|
|
|
|
|
CircleUser,
|
|
|
|
|
Network,
|
|
|
|
|
Sun,
|
|
|
|
|
Check,
|
|
|
|
|
XIcon,
|
|
|
|
|
Info,
|
|
|
|
|
ArrowDownZA,
|
|
|
|
|
LogIn,
|
|
|
|
|
ImageIcon,
|
|
|
|
|
} from "lucide-react";
|
2024-07-23 18:49:21 -05:00
|
|
|
import Stat from "./Stat";
|
|
|
|
|
import {
|
|
|
|
|
Dialog,
|
|
|
|
|
DialogContent,
|
|
|
|
|
DialogDescription,
|
|
|
|
|
DialogHeader,
|
|
|
|
|
DialogTitle,
|
|
|
|
|
DialogTrigger,
|
|
|
|
|
} from "@/components/ui/dialog";
|
|
|
|
|
import {
|
|
|
|
|
Tooltip,
|
|
|
|
|
TooltipContent,
|
|
|
|
|
TooltipTrigger,
|
|
|
|
|
} from "@/components/ui/tooltip";
|
|
|
|
|
import toast from "react-hot-toast";
|
2024-09-03 23:56:15 -05:00
|
|
|
import { allTags, allCategories } from "@/config/tags";
|
2024-07-23 18:49:21 -05:00
|
|
|
import IconDisplay from "./IconDisplay";
|
|
|
|
|
import InfiniteScroll from "react-infinite-scroll-component";
|
|
|
|
|
import { Spinner } from "./ui/spinner";
|
2024-08-03 09:51:45 -05:00
|
|
|
import { CommandIcon } from "lucide-react";
|
2024-08-08 17:56:40 -05:00
|
|
|
import { OnlineServer } from "@/lib/types/mh-server";
|
2024-07-23 18:49:21 -05:00
|
|
|
import { useEffectOnce } from "@/lib/useEffectOnce";
|
|
|
|
|
import ServerCard from "./ServerCard";
|
2024-08-03 09:51:45 -05:00
|
|
|
import events from "@/lib/commandEvent";
|
|
|
|
|
import { BorderBeam } from "@/components/effects/border-beam";
|
2024-08-15 23:24:15 -05:00
|
|
|
import { useRouter } from "@/lib/useRouter";
|
2024-08-17 14:15:30 -05:00
|
|
|
import {
|
|
|
|
|
Menubar,
|
|
|
|
|
MenubarCheckboxItem,
|
|
|
|
|
MenubarContent,
|
|
|
|
|
MenubarItem,
|
|
|
|
|
MenubarMenu,
|
|
|
|
|
MenubarRadioGroup,
|
|
|
|
|
MenubarRadioItem,
|
|
|
|
|
MenubarSeparator,
|
|
|
|
|
MenubarShortcut,
|
|
|
|
|
MenubarSub,
|
|
|
|
|
MenubarSubContent,
|
|
|
|
|
MenubarSubTrigger,
|
|
|
|
|
MenubarTrigger,
|
|
|
|
|
} from "@/components/ui/menubar";
|
2024-08-18 01:15:27 -05:00
|
|
|
import ClientFadeIn from "./ClientFadeIn";
|
|
|
|
|
import { Skeleton } from "./ui/skeleton";
|
2024-08-22 23:44:00 -05:00
|
|
|
import useClipboard from "@/lib/useClipboard";
|
2024-08-24 12:37:08 -05:00
|
|
|
import { SignedIn, SignedOut, useUser } from "@clerk/nextjs";
|
|
|
|
|
import Link from "next/link";
|
|
|
|
|
import SparklesText from "./effects/sparkles-text";
|
|
|
|
|
import Particles from "./effects/particles";
|
|
|
|
|
import { useTheme } from "next-themes";
|
|
|
|
|
import { ChatBubbleIcon, InputIcon } from "@radix-ui/react-icons";
|
|
|
|
|
import Marquee from "./effects/marquee";
|
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
|
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
|
|
|
|
import { SignInPopover } from "./clerk/SignInPopoverButton";
|
|
|
|
|
import { BentoCard, BentoGrid } from "./effects/bento-grid";
|
|
|
|
|
|
|
|
|
|
const features = [
|
|
|
|
|
{
|
|
|
|
|
Icon: ChatBubbleIcon,
|
|
|
|
|
name: "Add a Discord widget",
|
|
|
|
|
description:
|
|
|
|
|
"Show where your players talk to each-other, including an online users count.",
|
2024-09-03 23:56:15 -05:00
|
|
|
href: "/docs/guides/customization",
|
2024-08-24 12:37:08 -05:00
|
|
|
cta: "Learn more",
|
|
|
|
|
background: <span />,
|
|
|
|
|
className: "lg:row-start-1 lg:row-end-2 lg:col-start-2 lg:col-end-3",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Icon: InputIcon,
|
|
|
|
|
name: "Descriptions",
|
2024-09-03 23:56:15 -05:00
|
|
|
href: "/docs/guides/customization",
|
2024-08-24 12:37:08 -05:00
|
|
|
cta: "Learn more",
|
|
|
|
|
description:
|
|
|
|
|
"Format your descriptions using Markdown to show what your server has to offer.",
|
|
|
|
|
background: <span />,
|
|
|
|
|
className: "lg:col-start-1 lg:col-end-2 lg:row-start-1 lg:row-end-2",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Icon: ImageIcon,
|
|
|
|
|
name: "Banners",
|
2024-09-03 23:56:15 -05:00
|
|
|
href: "/docs/guides/customization",
|
2024-08-24 12:37:08 -05:00
|
|
|
cta: "Learn more",
|
|
|
|
|
description:
|
|
|
|
|
"Show a banner with can contain images that show on your server page.",
|
|
|
|
|
background: <span />,
|
|
|
|
|
className: "lg:col-start-3 lg:col-end-4 lg:row-start-1 lg:row-end-2",
|
|
|
|
|
},
|
|
|
|
|
];
|
2024-07-23 18:49:21 -05:00
|
|
|
|
|
|
|
|
export default function ServerList() {
|
|
|
|
|
const [loading, setLoading]: any = useState(true);
|
|
|
|
|
const [randomText, setRandomText] = useState("");
|
|
|
|
|
const [motdList, setMotdList] = useState<any>({});
|
|
|
|
|
const allText = [""];
|
|
|
|
|
const getRandomText = () => {
|
|
|
|
|
return allText[Math.floor(Math.random() * allText.length)];
|
|
|
|
|
};
|
|
|
|
|
const [templateFilter, setTemplateFilter] = useState(false);
|
|
|
|
|
const [random, setRandom] = useState(false);
|
|
|
|
|
const [serverList, setServerList] = useState(new ServersList([]));
|
|
|
|
|
const [textCopied, setTextCopied] = useState(false);
|
2024-09-03 23:56:15 -05:00
|
|
|
const [padding, setPadding] = useState<string>("0");
|
2024-07-23 18:49:21 -05:00
|
|
|
const bigger = async (server: OnlineServer) =>
|
|
|
|
|
server.playerData.playerCount > 15;
|
|
|
|
|
const smaller = async (server: OnlineServer) =>
|
|
|
|
|
!server.staticInfo.alwaysOnline &&
|
|
|
|
|
server.playerData.playerCount < 15 &&
|
|
|
|
|
server.playerData.playerCount > 7;
|
|
|
|
|
const [nameFilters, setNameFilters] = useState<any>({});
|
|
|
|
|
const [inErrState, setErrState] = useState(false);
|
|
|
|
|
const [servers, setServers] = useState<Array<OnlineServer>>([]);
|
2024-08-22 23:44:00 -05:00
|
|
|
const clipboard = useClipboard();
|
2024-08-15 23:24:15 -05:00
|
|
|
const router = useRouter();
|
2024-09-03 23:56:15 -05:00
|
|
|
const { user, isSignedIn } = useUser();
|
2024-08-24 12:37:08 -05:00
|
|
|
const [pOS, setpOS] = useState(false);
|
2024-09-03 23:56:15 -05:00
|
|
|
const [ipr, setIPR] = useState<string>("4");
|
|
|
|
|
const [am, setAM] = useState<boolean>(false);
|
2024-07-23 18:49:21 -05:00
|
|
|
const [filters, setFilters] = useState<
|
|
|
|
|
Array<(server: OnlineServer) => Promise<boolean>>
|
|
|
|
|
>([]);
|
|
|
|
|
const [randomData, setRandomData] = useState<OnlineServer | undefined>(
|
2024-08-03 09:51:45 -05:00
|
|
|
undefined
|
2024-07-23 18:49:21 -05:00
|
|
|
);
|
2024-08-24 12:37:08 -05:00
|
|
|
const { resolvedTheme } = useTheme();
|
|
|
|
|
const [color, setColor] = useState("#ffffff");
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setColor(resolvedTheme === "dark" ? "#ffffff" : "#000000");
|
|
|
|
|
}, [resolvedTheme]);
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-09-03 23:56:15 -05:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (isSignedIn) {
|
|
|
|
|
console.log(user.publicMetadata);
|
|
|
|
|
setIPR((user.publicMetadata.ipr as string | undefined) || "4");
|
|
|
|
|
setPadding((user.publicMetadata.pad as string | undefined) || "0");
|
|
|
|
|
setpOS((user.publicMetadata.srv as boolean | undefined) || false);
|
|
|
|
|
setAM(pOS != false || padding != "0" || ipr != "4");
|
|
|
|
|
}
|
|
|
|
|
}, [isSignedIn, user]);
|
|
|
|
|
|
2024-07-23 18:49:21 -05:00
|
|
|
useEffectOnce(() => {
|
|
|
|
|
setRandomText(getRandomText());
|
|
|
|
|
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);
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
.catch(() => setErrState(true));
|
|
|
|
|
});
|
2024-08-24 12:37:08 -05:00
|
|
|
|
|
|
|
|
const ref = useRef(null);
|
|
|
|
|
const [clickedPage, setClickedPage] = useState("banners");
|
|
|
|
|
const [hero, setHero] = useState(false);
|
2024-07-23 18:49:21 -05:00
|
|
|
if (inErrState) {
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<div className="flex justify-center">
|
|
|
|
|
<XIcon />
|
|
|
|
|
<br />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex justify-center">
|
|
|
|
|
Hmm. Something is wrong. Reload the page.
|
|
|
|
|
</div>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (loading) {
|
|
|
|
|
return (
|
|
|
|
|
<>
|
2024-08-24 12:37:08 -05:00
|
|
|
<SignedIn>
|
|
|
|
|
<div className="md:grid md:grid-cols-3 gap-4 max-lg:grid-cols-2">
|
|
|
|
|
<Skeleton className="h-[112px] rounded-xl" />
|
|
|
|
|
<Skeleton className="h-[112px] rounded-xl" />
|
|
|
|
|
<Skeleton className="h-[112px] rounded-xl" />
|
|
|
|
|
</div>
|
|
|
|
|
<br />
|
|
|
|
|
<Separator />
|
|
|
|
|
<br />
|
|
|
|
|
<div className="md:grid md:grid-cols-4 gap-4">
|
|
|
|
|
<Skeleton className="h-[450px] rounded-xl" />
|
|
|
|
|
<Skeleton className="h-[450px] rounded-xl" />
|
|
|
|
|
<Skeleton className="h-[450px] rounded-xl" />
|
|
|
|
|
<Skeleton className="h-[450px] rounded-xl" />
|
|
|
|
|
</div>
|
|
|
|
|
</SignedIn>
|
2024-07-23 18:49:21 -05:00
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
2024-09-03 23:56:15 -05:00
|
|
|
<div style={!pOS ? { padding: `${padding}px` } : undefined}>
|
2024-08-24 12:37:08 -05:00
|
|
|
<div className="p-0 branding-hero">
|
|
|
|
|
<>
|
|
|
|
|
{(!isSignedIn || hero) && (
|
2024-09-03 23:56:15 -05:00
|
|
|
<div className=" py-2 max-lg:h-[370vh] lg:h-[300vh] relative mx-auto mt-20 max-w-7xl px-6 text-center md:px-8 ">
|
2024-08-24 12:37:08 -05:00
|
|
|
<Particles
|
|
|
|
|
className="absolute inset-0 -z-10 block"
|
|
|
|
|
quantity={100}
|
|
|
|
|
ease={70}
|
|
|
|
|
size={0.05}
|
|
|
|
|
staticity={40}
|
|
|
|
|
color={color}
|
|
|
|
|
/>
|
|
|
|
|
<SparklesText className="animate-fade-in -translate-y-4 text-balance bg-gradient-to-br from-black from-30% to-black/40 bg-clip-text pb-6 text-5xl font-semibold leading-none tracking-tighter text-transparent opacity-0 [--animation-delay:200ms] sm:text-6xl md:text-7xl lg:text-8xl dark:from-white dark:to-white/40">
|
|
|
|
|
<>
|
|
|
|
|
Meet MHSF, <br className="hidden md:block" /> the modern
|
|
|
|
|
server finder
|
|
|
|
|
</>
|
|
|
|
|
</SparklesText>
|
|
|
|
|
<p className="animate-fade-in mb-12 -translate-y-4 text-balance text-lg tracking-tight text-gray-400 opacity-0 [--animation-delay:400ms] md:text-xl">
|
|
|
|
|
MHSF is the next generation server list for Minehut, with
|
|
|
|
|
interactive filters, <br className="hidden md:block" />{" "}
|
|
|
|
|
intuitive keyboard shortcuts, and everything between.
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<Link href="#serverlist">
|
|
|
|
|
<Button className="animate-fade-in -translate-y-4 gap-1 rounded-lg text-white opacity-0 ease-in-out [--animation-delay:600ms] dark:text-black">
|
|
|
|
|
Look and see <ArrowDownZA size={18} />
|
|
|
|
|
</Button>
|
|
|
|
|
</Link>
|
|
|
|
|
<Popover>
|
|
|
|
|
<PopoverTrigger>
|
|
|
|
|
<SignedOut>
|
|
|
|
|
<Button
|
|
|
|
|
variant="outline"
|
|
|
|
|
className="animate-fade-in -translate-y-4 ml-2 gap-1 rounded-lg opacity-0 ease-in-out [--animation-delay:600ms]"
|
|
|
|
|
>
|
|
|
|
|
Login <LogIn size={18} />
|
|
|
|
|
</Button>
|
|
|
|
|
</SignedOut>
|
|
|
|
|
</PopoverTrigger>
|
|
|
|
|
<PopoverContent className="w-full">
|
|
|
|
|
<SignInPopover />
|
|
|
|
|
</PopoverContent>
|
|
|
|
|
</Popover>
|
|
|
|
|
<div
|
|
|
|
|
ref={ref}
|
|
|
|
|
className="relative mt-[8rem] animate-fade-up opacity-0 [--animation-delay:400ms] [perspective:2000px] after:absolute after:inset-0 after:z-50 after:[background:linear-gradient(to_top,hsl(var(--background))_30%,transparent)]"
|
2024-08-07 16:37:54 -05:00
|
|
|
>
|
2024-08-24 12:37:08 -05:00
|
|
|
<div
|
|
|
|
|
className={` rounded-xl border border-white/10 bg-white bg-opacity-[0.01] before:absolute before:bottom-1/2 before:left-0 before:top-0 before:h-full before:w-full before:opacity-0 before:[filter:blur(180px)] before:[background-image:linear-gradient(to_bottom,var(--color-one),var(--color-one),transparent_40%)] before:animate-image-glow`}
|
|
|
|
|
>
|
|
|
|
|
<BorderBeam
|
|
|
|
|
size={200}
|
|
|
|
|
duration={12}
|
|
|
|
|
delay={11}
|
|
|
|
|
colorFrom="var(--color-one)"
|
|
|
|
|
colorTo="var(--color-two)"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<img
|
|
|
|
|
src="/branding/full-desktop.png"
|
|
|
|
|
alt="Hero Image"
|
|
|
|
|
className="relative hidden size-full rounded-[inherit] border object-contain dark:block"
|
|
|
|
|
/>
|
|
|
|
|
<img
|
|
|
|
|
src="/branding/full-desktop-light.png"
|
|
|
|
|
alt="Hero Image"
|
|
|
|
|
className="relative block size-full rounded-[inherit] border object-contain dark:hidden"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2024-08-07 16:37:54 -05:00
|
|
|
</div>
|
2024-08-24 12:37:08 -05:00
|
|
|
<br />
|
|
|
|
|
<br />
|
|
|
|
|
<span className="animate-fade-in -translate-y-4 bg-green-400/60 px-4 py-2 rounded">
|
|
|
|
|
For players
|
|
|
|
|
</span>
|
|
|
|
|
<br />
|
|
|
|
|
<br />
|
|
|
|
|
<h1 className="animate-fade-in -translate-y-4 text-balance bg-gradient-to-br from-black from-30% to-black/40 bg-clip-text pb-6 text-2xl font-semibold leading-none tracking-tighter text-transparent opacity-0 [--animation-delay:200ms] sm:text-2xl md:text-3xl lg:text-4xl dark:from-white dark:to-white/40">
|
|
|
|
|
Find what you want now, not later
|
|
|
|
|
</h1>
|
|
|
|
|
<p className="animate-fade-in mb-12 -translate-y-4 text-balance text-lg tracking-tight text-gray-400 opacity-0 [--animation-delay:400ms] md:text-xl">
|
|
|
|
|
Use interactive filters and customization modes to find the
|
|
|
|
|
server of your choice
|
|
|
|
|
<br className="hidden md:block" /> in less than 10 minutes.
|
|
|
|
|
</p>
|
|
|
|
|
<div className="relative flex h-[300px] w-full flex-col items-center justify-center overflow-hidden rounded-lg bg-background ">
|
|
|
|
|
<Marquee className="[--duration:30s]">
|
|
|
|
|
{serverList.currentServers.slice(0, 20).map((server) => (
|
|
|
|
|
<div
|
2024-08-24 12:40:02 -05:00
|
|
|
key={server.name}
|
2024-08-24 12:37:08 -05:00
|
|
|
className={cn(
|
|
|
|
|
"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] " +
|
|
|
|
|
"dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15]"
|
|
|
|
|
)}
|
|
|
|
|
onClick={() => router.push(`/server/${server.name}`)}
|
|
|
|
|
>
|
|
|
|
|
<div className="items-center gap-2 p-4">
|
|
|
|
|
<div>
|
|
|
|
|
<div
|
|
|
|
|
className={
|
|
|
|
|
"text-lg font-bold dark:text-white" +
|
|
|
|
|
(server.author == undefined ? " mt-2" : "")
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<IconDisplay server={server} className="mr-2" />
|
|
|
|
|
{server.name}
|
|
|
|
|
</div>
|
|
|
|
|
{server.author && (
|
|
|
|
|
<p className="text-sm dark:text-white/40">
|
|
|
|
|
by {server.author}
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</Marquee>
|
|
|
|
|
<Marquee reverse className="[--duration:30s]">
|
|
|
|
|
{serverList.currentServers.slice(0, 20).map((server) => (
|
|
|
|
|
<div
|
2024-08-24 12:40:02 -05:00
|
|
|
key={server.name}
|
2024-08-24 12:37:08 -05:00
|
|
|
className={cn(
|
|
|
|
|
"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] " +
|
|
|
|
|
"dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15]"
|
|
|
|
|
)}
|
|
|
|
|
onClick={() => router.push(`/server/${server.name}`)}
|
|
|
|
|
>
|
|
|
|
|
<div className="items-center gap-2 p-4">
|
|
|
|
|
<div>
|
|
|
|
|
<div
|
|
|
|
|
className={
|
|
|
|
|
"text-lg font-bold dark:text-white" +
|
|
|
|
|
(server.author == undefined ? " mt-2" : "")
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<IconDisplay server={server} className="mr-2" />
|
|
|
|
|
{server.name}
|
|
|
|
|
</div>
|
|
|
|
|
{server.author && (
|
|
|
|
|
<p className="text-sm dark:text-white/40">
|
|
|
|
|
by {server.author}
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</Marquee>
|
|
|
|
|
<div className="pointer-events-none absolute inset-y-0 left-0 w-1/3 bg-gradient-to-r from-white dark:from-background" />
|
|
|
|
|
|
|
|
|
|
<div className="pointer-events-none absolute inset-y-0 right-0 w-1/3 bg-gradient-to-l from-white dark:from-background" />
|
|
|
|
|
</div>
|
|
|
|
|
<br />
|
|
|
|
|
<span className="animate-fade-in -translate-y-4 bg-yellow-400/60 px-4 py-2 rounded">
|
|
|
|
|
For server owners
|
|
|
|
|
</span>
|
|
|
|
|
<br />
|
|
|
|
|
<br />
|
|
|
|
|
<h1 className="animate-fade-in -translate-y-4 text-balance bg-gradient-to-br from-black from-30% to-black/40 bg-clip-text pb-6 text-2xl font-semibold leading-none tracking-tighter text-transparent opacity-0 [--animation-delay:200ms] sm:text-2xl md:text-3xl lg:text-4xl dark:from-white dark:to-white/40">
|
|
|
|
|
Make your server stand out
|
|
|
|
|
</h1>
|
|
|
|
|
<p className="animate-fade-in mb-12 -translate-y-4 text-balance text-lg tracking-tight text-gray-400 opacity-0 [--animation-delay:400ms] md:text-xl">
|
|
|
|
|
Servers can have custom banners, Discord widgets, color schemes,
|
|
|
|
|
and descriptions, making your server stand out with information
|
|
|
|
|
that can be shown to players.
|
|
|
|
|
</p>
|
|
|
|
|
<BentoGrid className="max-h-[100px]">
|
|
|
|
|
{features.map((feature, idx) => (
|
|
|
|
|
<BentoCard key={idx} {...feature} />
|
|
|
|
|
))}
|
|
|
|
|
</BentoGrid>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<Separator />
|
|
|
|
|
<br />
|
|
|
|
|
</>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="p-4">
|
|
|
|
|
<ClientFadeIn id="serverlist">
|
|
|
|
|
<div className="max-lg:grid-cols-2 grid grid-cols-3 gap-4">
|
|
|
|
|
<Stat
|
|
|
|
|
title="Players online"
|
|
|
|
|
desc={serverList.getExtraData().total_players.toString()}
|
|
|
|
|
icon={CircleUser}
|
|
|
|
|
/>
|
|
|
|
|
<Stat
|
|
|
|
|
title={
|
2024-08-18 01:15:27 -05:00
|
|
|
<div
|
|
|
|
|
className={
|
|
|
|
|
serverList.getExtraData().total_servers >= 3200
|
2024-08-24 12:37:08 -05:00
|
|
|
? "bg-clip-text text-transparent bg-gradient-to-r from-cyan-500 to-blue-500"
|
2024-08-18 01:15:27 -05:00
|
|
|
: ""
|
|
|
|
|
}
|
|
|
|
|
>
|
2024-08-24 12:37:08 -05:00
|
|
|
Servers online{" "}
|
2024-08-18 01:15:27 -05:00
|
|
|
</div>
|
2024-08-24 12:37:08 -05:00
|
|
|
}
|
|
|
|
|
className="relative z-0"
|
|
|
|
|
desc={
|
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<div
|
|
|
|
|
className={
|
|
|
|
|
serverList.getExtraData().total_servers >= 3200
|
|
|
|
|
? "bg-clip-text text-transparent bg-gradient-to-r from-cyan-500 to-blue-500 "
|
|
|
|
|
: ""
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
{serverList.getExtraData().total_servers.toString()}
|
|
|
|
|
</div>
|
|
|
|
|
{serverList.getExtraData().total_servers >= 3200 && (
|
|
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger>
|
|
|
|
|
<Info size={16} className="ml-2" />
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent className="font-normal">
|
|
|
|
|
The server amount is over 3.2k, meaning that new servers
|
|
|
|
|
have to go into a queue before being able to be online.{" "}
|
|
|
|
|
<br />
|
|
|
|
|
(the server count isn't entirely accurate, so sometimes
|
|
|
|
|
you might not go into a queue even when the server count
|
|
|
|
|
is at 3.2k)
|
|
|
|
|
</TooltipContent>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
icon={Network}
|
|
|
|
|
>
|
|
|
|
|
{serverList.getExtraData().total_servers >= 3200 && (
|
|
|
|
|
<BorderBeam
|
|
|
|
|
size={135}
|
|
|
|
|
duration={12}
|
|
|
|
|
delay={9}
|
|
|
|
|
colorFrom="rgb(6 182 212)"
|
|
|
|
|
colorTo="rgb(59 130 246)"
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</Stat>
|
|
|
|
|
<Stat
|
|
|
|
|
title="Current most popular server (in filter)"
|
|
|
|
|
className="max-lg:col-span-2"
|
|
|
|
|
desc={
|
|
|
|
|
<>
|
|
|
|
|
{serverList.currentServers[0] != undefined
|
|
|
|
|
? serverList.currentServers[0].name
|
|
|
|
|
: "None"}{" "}
|
|
|
|
|
{serverList.currentServers[0] != undefined && (
|
|
|
|
|
<IconDisplay server={serverList.currentServers[0]} />
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
}
|
|
|
|
|
icon={Sun}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</ClientFadeIn>
|
|
|
|
|
<br id="serverlist" className="pb-14" />
|
|
|
|
|
<Separator />
|
|
|
|
|
<ClientFadeIn delay={100}>
|
2024-09-03 23:56:15 -05:00
|
|
|
<Menubar className="mt-3 ml-2 border rounded p-2 shadow">
|
2024-08-24 12:37:08 -05:00
|
|
|
<MenubarMenu>
|
|
|
|
|
<MenubarTrigger>Servers</MenubarTrigger>
|
|
|
|
|
<MenubarContent>
|
|
|
|
|
<MenubarItem
|
|
|
|
|
onSelect={() => events.emit("search-request-event")}
|
|
|
|
|
>
|
|
|
|
|
Search Servers
|
|
|
|
|
<MenubarShortcut className="flex items-center ml-3">
|
|
|
|
|
<CommandIcon size={14} />
|
|
|
|
|
+Shift+K
|
|
|
|
|
</MenubarShortcut>
|
|
|
|
|
</MenubarItem>
|
|
|
|
|
<MenubarItem
|
|
|
|
|
onSelect={() => {
|
|
|
|
|
setRandomData(serverList.getRandomServer());
|
|
|
|
|
setRandom(true);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Pick Random Server
|
|
|
|
|
</MenubarItem>
|
|
|
|
|
<MenubarSeparator />
|
|
|
|
|
<MenubarItem
|
|
|
|
|
onSelect={() => {
|
|
|
|
|
toast.promise(
|
|
|
|
|
new Promise((s, e) => {
|
|
|
|
|
setLoading(true);
|
|
|
|
|
serverList
|
|
|
|
|
.fetchDataAndFilter()
|
|
|
|
|
.then(() => {
|
|
|
|
|
serverList.moveListDown();
|
|
|
|
|
|
|
|
|
|
let stringList: Array<{
|
|
|
|
|
server: string;
|
|
|
|
|
motd: string;
|
|
|
|
|
}> = [];
|
|
|
|
|
let obj: any = {};
|
2024-08-17 14:15:30 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
serverList.currentServers.forEach((b) => {
|
|
|
|
|
stringList.push({ motd: b.motd, server: b.name });
|
2024-08-18 01:15:27 -05:00
|
|
|
});
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
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();
|
2024-08-18 01:15:27 -05:00
|
|
|
});
|
2024-08-24 12:37:08 -05:00
|
|
|
}),
|
|
|
|
|
{
|
|
|
|
|
success: "Succesfully refreshed servers",
|
|
|
|
|
loading: "Refreshing...",
|
|
|
|
|
error: "Error while refreshing",
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Refresh
|
|
|
|
|
</MenubarItem>
|
|
|
|
|
</MenubarContent>
|
|
|
|
|
</MenubarMenu>
|
|
|
|
|
<MenubarMenu>
|
|
|
|
|
<MenubarTrigger>Filter</MenubarTrigger>
|
|
|
|
|
<MenubarContent className="max-h-[400px] overflow-auto">
|
|
|
|
|
<MenubarRadioGroup
|
|
|
|
|
onValueChange={(v) => {
|
|
|
|
|
toast.promise(
|
|
|
|
|
new Promise((g, b) => {
|
|
|
|
|
if (v == "smaller") {
|
|
|
|
|
setTemplateFilter(true);
|
|
|
|
|
var filt = nameFilters;
|
|
|
|
|
filt["smaller-tf"] = true;
|
|
|
|
|
filt["bigger-tf"] = false;
|
|
|
|
|
setNameFilters(filt);
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
var filt2 = filters;
|
|
|
|
|
filt2.push(smaller);
|
|
|
|
|
if (filt2.includes(bigger)) {
|
|
|
|
|
filt2.splice(filt2.indexOf(bigger), 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);
|
|
|
|
|
g(undefined);
|
2024-08-18 01:15:27 -05:00
|
|
|
});
|
2024-07-23 18:49:21 -05:00
|
|
|
});
|
2024-08-24 12:37:08 -05:00
|
|
|
} else if (v == "bigger") {
|
|
|
|
|
setTemplateFilter(true);
|
|
|
|
|
var filt = nameFilters;
|
|
|
|
|
filt["smaller-tf"] = false;
|
|
|
|
|
filt["bigger-tf"] = true;
|
|
|
|
|
setNameFilters(filt);
|
|
|
|
|
var filt2 = filters;
|
|
|
|
|
filt2.push(bigger);
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
filt2.splice(filt2.indexOf(smaller), 1);
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
setFilters(filt2);
|
|
|
|
|
serverList.editFilters(filters);
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
serverList.fetchDataAndFilter().then(() => {
|
|
|
|
|
serverList.moveListDown();
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
let stringList: Array<{
|
|
|
|
|
server: string;
|
|
|
|
|
motd: string;
|
|
|
|
|
}> = [];
|
|
|
|
|
let obj: any = {};
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
serverList.currentServers.forEach((b) => {
|
|
|
|
|
stringList.push({ motd: b.motd, server: b.name });
|
|
|
|
|
});
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
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);
|
|
|
|
|
g(undefined);
|
2024-08-18 01:15:27 -05:00
|
|
|
});
|
2024-07-23 18:49:21 -05:00
|
|
|
});
|
2024-08-24 12:37:08 -05:00
|
|
|
} else {
|
|
|
|
|
var filt = nameFilters;
|
|
|
|
|
filt["smaller-tf"] = false;
|
|
|
|
|
filt["bigger-tf"] = false;
|
|
|
|
|
setNameFilters(filt);
|
|
|
|
|
setTemplateFilter(false);
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
var filt2 = filters;
|
|
|
|
|
filt2.splice(filt2.indexOf(smaller), 1);
|
|
|
|
|
filt2.splice(filt2.indexOf(bigger), 1);
|
|
|
|
|
setFilters(filt2);
|
|
|
|
|
console.log(filters, filters.includes(smaller));
|
|
|
|
|
serverList.editFilters(filters);
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
serverList.fetchDataAndFilter().then(() => {
|
|
|
|
|
serverList.moveListDown();
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
let stringList: Array<{
|
|
|
|
|
server: string;
|
|
|
|
|
motd: string;
|
|
|
|
|
}> = [];
|
|
|
|
|
let obj: any = {};
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
serverList.currentServers.forEach((b) => {
|
|
|
|
|
stringList.push({ motd: b.motd, server: b.name });
|
|
|
|
|
});
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
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);
|
|
|
|
|
g(undefined);
|
2024-08-18 01:15:27 -05:00
|
|
|
});
|
2024-07-23 18:49:21 -05:00
|
|
|
});
|
2024-08-24 12:37:08 -05:00
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
{
|
|
|
|
|
error: "Error while changing filters",
|
|
|
|
|
loading: "Changing filters...",
|
|
|
|
|
success: "Changed filters!",
|
2024-08-18 01:15:27 -05:00
|
|
|
}
|
2024-08-24 12:37:08 -05:00
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
value={(() => {
|
|
|
|
|
if (nameFilters["smaller-tf"]) {
|
|
|
|
|
return "smaller";
|
|
|
|
|
} else if (nameFilters["bigger-tf"]) {
|
|
|
|
|
return "bigger";
|
|
|
|
|
} else {
|
|
|
|
|
return "none";
|
2024-07-23 18:49:21 -05:00
|
|
|
}
|
2024-08-24 12:37:08 -05:00
|
|
|
})()}
|
|
|
|
|
>
|
|
|
|
|
<MenubarRadioItem value="smaller">
|
|
|
|
|
<div className="block">
|
|
|
|
|
Only allow smaller servers
|
|
|
|
|
<br />
|
|
|
|
|
<span className="text-sm text-muted-foreground">
|
|
|
|
|
Only allow servers that have the player range 7-15, and
|
|
|
|
|
cannot <br />
|
|
|
|
|
be Always Online.
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</MenubarRadioItem>
|
|
|
|
|
<MenubarRadioItem value="bigger">
|
|
|
|
|
<div className="block">
|
|
|
|
|
Only allow bigger servers
|
|
|
|
|
<br />
|
|
|
|
|
<span className="text-sm text-muted-foreground">
|
|
|
|
|
Only allow servers with more than 15 players.
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</MenubarRadioItem>
|
|
|
|
|
<MenubarRadioItem value="none">
|
|
|
|
|
No/custom requirements
|
|
|
|
|
</MenubarRadioItem>
|
|
|
|
|
</MenubarRadioGroup>
|
|
|
|
|
<MenubarSeparator />
|
|
|
|
|
<MenubarSub>
|
|
|
|
|
<span className="text-sm text-muted-foreground ml-2">
|
|
|
|
|
Tags
|
|
|
|
|
</span>
|
|
|
|
|
</MenubarSub>
|
|
|
|
|
{allTags.map((tag) => (
|
|
|
|
|
<div key={tag.docsName}>
|
|
|
|
|
{tag.docsName && tag.__filter == undefined && (
|
|
|
|
|
<MenubarCheckboxItem
|
|
|
|
|
disabled={templateFilter && tag.__disab != undefined}
|
|
|
|
|
id={tag.docsName}
|
|
|
|
|
checked={(() => {
|
|
|
|
|
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 = {};
|
|
|
|
|
|
|
|
|
|
serverList.currentServers.forEach((b) => {
|
|
|
|
|
stringList.push({ motd: b.motd, server: b.name });
|
|
|
|
|
});
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
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);
|
2024-08-18 01:15:27 -05:00
|
|
|
});
|
2024-08-17 14:15:30 -05:00
|
|
|
});
|
2024-08-24 12:37:08 -05:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Badge variant={tag.role} className="mr-1">
|
|
|
|
|
{tag.docsName}
|
|
|
|
|
</Badge>
|
|
|
|
|
</MenubarCheckboxItem>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
<MenubarSeparator />
|
|
|
|
|
<MenubarSub>
|
|
|
|
|
<span className="text-sm text-muted-foreground ml-2">
|
|
|
|
|
Categories
|
|
|
|
|
</span>
|
|
|
|
|
</MenubarSub>
|
|
|
|
|
{allCategories.map((categorie) => (
|
|
|
|
|
<MenubarCheckboxItem
|
|
|
|
|
id={categorie.name}
|
|
|
|
|
key={categorie.name}
|
|
|
|
|
onCheckedChange={async (b) => {
|
|
|
|
|
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();
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
let stringList: Array<{
|
|
|
|
|
server: string;
|
|
|
|
|
motd: string;
|
|
|
|
|
}> = [];
|
|
|
|
|
let obj: any = {};
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
serverList.currentServers.forEach((b) => {
|
|
|
|
|
stringList.push({ motd: b.motd, server: b.name });
|
|
|
|
|
});
|
2024-07-23 18:49:21 -05:00
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
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);
|
2024-08-18 01:15:27 -05:00
|
|
|
});
|
2024-07-23 18:49:21 -05:00
|
|
|
});
|
2024-08-24 12:37:08 -05:00
|
|
|
}}
|
|
|
|
|
checked={(() => {
|
|
|
|
|
return nameFilters["c-" + categorie.name];
|
|
|
|
|
})()}
|
2024-08-18 01:15:27 -05:00
|
|
|
>
|
2024-08-24 12:37:08 -05:00
|
|
|
<Badge variant={categorie.role} className="mr-1">
|
|
|
|
|
{categorie.name}
|
|
|
|
|
</Badge>
|
|
|
|
|
</MenubarCheckboxItem>
|
|
|
|
|
))}
|
|
|
|
|
</MenubarContent>
|
|
|
|
|
</MenubarMenu>
|
|
|
|
|
<MenubarMenu>
|
|
|
|
|
<MenubarTrigger>View</MenubarTrigger>
|
|
|
|
|
<MenubarContent>
|
|
|
|
|
<MenubarSub>
|
|
|
|
|
<MenubarSubTrigger>Grid</MenubarSubTrigger>
|
|
|
|
|
<MenubarSubContent>
|
|
|
|
|
<MenubarRadioGroup value={ipr} onValueChange={setIPR}>
|
|
|
|
|
<MenubarRadioItem value="4">
|
|
|
|
|
4 items per row
|
|
|
|
|
</MenubarRadioItem>
|
|
|
|
|
<MenubarRadioItem value="5">
|
|
|
|
|
5 items per row
|
|
|
|
|
</MenubarRadioItem>
|
|
|
|
|
<MenubarRadioItem value="6">
|
|
|
|
|
6 items per row
|
|
|
|
|
</MenubarRadioItem>
|
|
|
|
|
</MenubarRadioGroup>
|
|
|
|
|
</MenubarSubContent>
|
|
|
|
|
</MenubarSub>
|
|
|
|
|
<MenubarSub>
|
|
|
|
|
<MenubarSubTrigger>Padding</MenubarSubTrigger>
|
|
|
|
|
<MenubarSubContent>
|
|
|
|
|
<MenubarRadioGroup
|
|
|
|
|
value={padding.toString()}
|
2024-09-03 23:56:15 -05:00
|
|
|
onValueChange={(v) => setPadding(v)}
|
2024-08-24 12:37:08 -05:00
|
|
|
>
|
|
|
|
|
<MenubarRadioItem value="0">Default</MenubarRadioItem>
|
|
|
|
|
<MenubarSeparator />
|
|
|
|
|
<MenubarRadioItem value="15">15px</MenubarRadioItem>
|
|
|
|
|
<MenubarRadioItem value="30">30px</MenubarRadioItem>
|
|
|
|
|
<MenubarRadioItem value="40">40px</MenubarRadioItem>
|
|
|
|
|
<MenubarRadioItem value="60">60px</MenubarRadioItem>
|
|
|
|
|
<MenubarRadioItem value="100">100px</MenubarRadioItem>
|
|
|
|
|
<MenubarRadioItem value="200">200px</MenubarRadioItem>
|
|
|
|
|
</MenubarRadioGroup>
|
|
|
|
|
<MenubarSeparator />
|
|
|
|
|
<MenubarCheckboxItem checked={pOS} onCheckedChange={setpOS}>
|
|
|
|
|
Only use padding on servers
|
|
|
|
|
</MenubarCheckboxItem>
|
|
|
|
|
</MenubarSubContent>
|
|
|
|
|
</MenubarSub>
|
|
|
|
|
<MenubarSub>
|
|
|
|
|
<MenubarSubTrigger>Sort</MenubarSubTrigger>
|
|
|
|
|
<MenubarSubContent>
|
|
|
|
|
<MenubarRadioGroup
|
|
|
|
|
value="joins"
|
|
|
|
|
onValueChange={(c) =>
|
|
|
|
|
c == "favorites" && router.push("/sort/favorites")
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<MenubarRadioItem value="joins">
|
|
|
|
|
Players Online
|
|
|
|
|
</MenubarRadioItem>
|
|
|
|
|
<MenubarRadioItem value="favorites">
|
|
|
|
|
Favorites
|
|
|
|
|
</MenubarRadioItem>
|
|
|
|
|
</MenubarRadioGroup>
|
|
|
|
|
</MenubarSubContent>
|
|
|
|
|
</MenubarSub>
|
2024-09-03 23:56:15 -05:00
|
|
|
<MenubarSeparator />
|
2024-08-24 12:37:08 -05:00
|
|
|
<SignedIn>
|
|
|
|
|
<MenubarCheckboxItem checked={hero} onCheckedChange={setHero}>
|
|
|
|
|
Show Hero
|
|
|
|
|
</MenubarCheckboxItem>
|
|
|
|
|
</SignedIn>
|
2024-09-03 23:56:15 -05:00
|
|
|
<MenubarItem onClick={() => router.push("/docs")}>
|
|
|
|
|
View the docs
|
|
|
|
|
</MenubarItem>
|
|
|
|
|
{am && (
|
|
|
|
|
<MenubarItem onClick={() => router.push("/account/settings")}>
|
|
|
|
|
Currently using settings in your preferences
|
|
|
|
|
</MenubarItem>
|
|
|
|
|
)}
|
2024-08-24 12:37:08 -05:00
|
|
|
</MenubarContent>
|
|
|
|
|
</MenubarMenu>
|
|
|
|
|
</Menubar>
|
|
|
|
|
</ClientFadeIn>
|
|
|
|
|
|
|
|
|
|
<Dialog open={random} onOpenChange={setRandom}>
|
|
|
|
|
<DialogContent>
|
|
|
|
|
{randomData == undefined && <>No data to randomize</>}
|
|
|
|
|
{randomData != undefined && (
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
<DialogTitle>
|
|
|
|
|
<IconDisplay server={randomData} /> {randomData.name}
|
|
|
|
|
{randomData.author != undefined ? (
|
|
|
|
|
<div className="text-sm text-muted-foreground">
|
|
|
|
|
by {randomData.author}
|
|
|
|
|
</div>
|
2024-07-23 18:49:21 -05:00
|
|
|
) : (
|
2024-08-24 12:37:08 -05:00
|
|
|
<br />
|
2024-07-23 18:49:21 -05:00
|
|
|
)}
|
2024-08-24 12:37:08 -05:00
|
|
|
<TagShower server={randomData} />
|
|
|
|
|
</DialogTitle>
|
|
|
|
|
<DialogDescription className="float-left inline">
|
|
|
|
|
<span className="flex items-center">
|
|
|
|
|
{randomData.playerData.playerCount == 0 ? (
|
|
|
|
|
<div
|
|
|
|
|
className="items-center border"
|
|
|
|
|
style={{
|
|
|
|
|
width: ".5rem",
|
|
|
|
|
height: ".5rem",
|
|
|
|
|
borderRadius: "9999px",
|
|
|
|
|
}}
|
|
|
|
|
/>
|
2024-07-23 18:49:21 -05:00
|
|
|
) : (
|
2024-08-24 12:37:08 -05:00
|
|
|
<div
|
|
|
|
|
className="items-center"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: "#0cce6b",
|
|
|
|
|
width: ".5rem",
|
|
|
|
|
height: ".5rem",
|
|
|
|
|
borderRadius: "9999px",
|
|
|
|
|
}}
|
|
|
|
|
/>
|
2024-07-23 18:49:21 -05:00
|
|
|
)}
|
|
|
|
|
|
2024-08-24 12:37:08 -05:00
|
|
|
<span className="pl-1">
|
|
|
|
|
{randomData.playerData.playerCount}{" "}
|
|
|
|
|
{randomData.playerData.playerCount == 1
|
|
|
|
|
? "player"
|
|
|
|
|
: "players"}{" "}
|
|
|
|
|
currently online
|
|
|
|
|
</span>
|
|
|
|
|
</span>
|
|
|
|
|
<br />
|
|
|
|
|
<strong>Server IP</strong>
|
|
|
|
|
<br />
|
|
|
|
|
<br />
|
|
|
|
|
<code className="border p-3 rounded">
|
|
|
|
|
{randomData.name}.minehut.gg{" "}
|
|
|
|
|
<Button
|
|
|
|
|
size="icon"
|
|
|
|
|
className="ml-1 h-[20px]"
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setTextCopied(true);
|
|
|
|
|
clipboard.writeText(
|
|
|
|
|
randomData.name + ".mshf.minehut.gg"
|
|
|
|
|
);
|
|
|
|
|
toast.success("Copied!");
|
|
|
|
|
setTimeout(() => setTextCopied(false), 1000);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{textCopied ? (
|
|
|
|
|
<Check size={16} className="flex items-center" />
|
|
|
|
|
) : (
|
|
|
|
|
<p>Copy</p>
|
|
|
|
|
)}
|
|
|
|
|
</Button>
|
|
|
|
|
</code>
|
|
|
|
|
</DialogDescription>
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
)}
|
|
|
|
|
</DialogContent>
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
|
|
|
|
<br />
|
|
|
|
|
<InfiniteScroll
|
|
|
|
|
dataLength={serverList.currentServers.length}
|
|
|
|
|
hasMore={serverList.hasMore}
|
|
|
|
|
next={() => {
|
|
|
|
|
serverList.moveListDown();
|
|
|
|
|
let stringList: Array<{ server: string; motd: string }> = [];
|
|
|
|
|
serverList.currentServers.forEach((b) => {
|
|
|
|
|
stringList.push({ motd: b.motd, server: b.name });
|
2024-07-23 18:49:21 -05:00
|
|
|
});
|
2024-08-24 12:37:08 -05:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
loader={<Spinner className="flex items-center" />}
|
|
|
|
|
endMessage={
|
|
|
|
|
<p
|
|
|
|
|
style={{ textAlign: "center" }}
|
|
|
|
|
dangerouslySetInnerHTML={{
|
|
|
|
|
__html:
|
|
|
|
|
randomText + "<br /> <strong>You've seen it all</strong>",
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
}
|
|
|
|
|
style={{
|
|
|
|
|
overflow: "hidden !important",
|
2024-09-03 23:56:15 -05:00
|
|
|
paddingLeft: pOS ? `${padding}px` : 6,
|
|
|
|
|
paddingRight: pOS ? `${padding}px` : 6,
|
2024-08-24 12:37:08 -05:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<ClientFadeIn delay={200}>
|
|
|
|
|
{/** This looks stupid, but its the only way that works */}
|
|
|
|
|
<div
|
|
|
|
|
className={
|
|
|
|
|
" sm:grid " +
|
|
|
|
|
(ipr == "4"
|
|
|
|
|
? "lg:grid-cols-4"
|
|
|
|
|
: ipr == "5"
|
|
|
|
|
? "lg:grid-cols-5"
|
|
|
|
|
: ipr == "6"
|
|
|
|
|
? "lg:grid-cols-6"
|
|
|
|
|
: "") +
|
|
|
|
|
" gap-4 sm:grid-cols-2"
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
{servers.map((b: any) => (
|
|
|
|
|
<>
|
|
|
|
|
<ServerCard b={b} motd={motdList[b.name]} />
|
|
|
|
|
</>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</ClientFadeIn>
|
|
|
|
|
</InfiniteScroll>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2024-07-23 18:49:21 -05:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-11 22:14:36 -05:00
|
|
|
export function TagShower(props: {
|
|
|
|
|
server: OnlineServer;
|
|
|
|
|
className?: string;
|
|
|
|
|
unclickable?: boolean;
|
|
|
|
|
}) {
|
2024-07-23 18:49:21 -05:00
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
|
const [compatiableTags, setCompatiableTags] = useState<
|
|
|
|
|
Array<{
|
|
|
|
|
name: string;
|
|
|
|
|
docsName?: string;
|
|
|
|
|
tooltip: string;
|
|
|
|
|
htmlDocs: string;
|
|
|
|
|
role:
|
|
|
|
|
| "default"
|
|
|
|
|
| "destructive"
|
|
|
|
|
| "outline"
|
|
|
|
|
| "secondary"
|
|
|
|
|
| "red"
|
|
|
|
|
| "orange"
|
|
|
|
|
| "yellow"
|
|
|
|
|
| "green"
|
|
|
|
|
| "lime"
|
|
|
|
|
| "blue"
|
|
|
|
|
| "teal"
|
|
|
|
|
| "cyan"
|
|
|
|
|
| "violet"
|
|
|
|
|
| "indigo"
|
|
|
|
|
| "purple"
|
|
|
|
|
| "fuchsia"
|
|
|
|
|
| "pink";
|
|
|
|
|
}>
|
|
|
|
|
>([]);
|
|
|
|
|
|
|
|
|
|
useEffectOnce(() => {
|
|
|
|
|
if (loading) {
|
|
|
|
|
allTags.forEach((tag) => {
|
|
|
|
|
tag.condition(props.server).then((b) => {
|
|
|
|
|
if (b && tag.primary) {
|
|
|
|
|
tag.name(props.server).then((n) => {
|
|
|
|
|
compatiableTags.push({
|
|
|
|
|
name: n,
|
|
|
|
|
docsName: tag.docsName,
|
|
|
|
|
tooltip: tag.tooltipDesc,
|
|
|
|
|
htmlDocs: tag.htmlDocs,
|
|
|
|
|
role: tag.role == undefined ? "secondary" : tag.role,
|
|
|
|
|
});
|
|
|
|
|
setLoading(false);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (loading) {
|
|
|
|
|
return <></>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
2024-08-22 23:44:00 -05:00
|
|
|
<div className="font-normal tracking-normal">
|
2024-07-23 18:49:21 -05:00
|
|
|
{compatiableTags.map((t) => (
|
|
|
|
|
<>
|
2024-08-11 22:14:36 -05:00
|
|
|
{props.unclickable && (
|
|
|
|
|
<Badge variant={t.role} className={props.className}>
|
|
|
|
|
{t.name}
|
|
|
|
|
</Badge>
|
|
|
|
|
)}
|
|
|
|
|
{!props.unclickable && (
|
|
|
|
|
<Dialog key={t.name}>
|
|
|
|
|
<DialogTrigger>
|
|
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger>
|
|
|
|
|
<Badge variant={t.role} className={props.className}>
|
|
|
|
|
{t.name}
|
|
|
|
|
</Badge>
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent>
|
|
|
|
|
<div className="font-normal">
|
|
|
|
|
{t.tooltip}
|
|
|
|
|
<br />
|
|
|
|
|
Click the tag to learn more about it.
|
|
|
|
|
</div>
|
|
|
|
|
</TooltipContent>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
</DialogTrigger>
|
|
|
|
|
<DialogContent>
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
<DialogTitle>
|
|
|
|
|
{'"'}
|
|
|
|
|
{t.docsName == undefined ? t.name : t.docsName}
|
|
|
|
|
{'"'} documentation
|
|
|
|
|
</DialogTitle>
|
|
|
|
|
<DialogDescription
|
|
|
|
|
dangerouslySetInnerHTML={{
|
|
|
|
|
__html: t.htmlDocs,
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
</DialogContent>
|
|
|
|
|
</Dialog>
|
|
|
|
|
)}
|
2024-07-23 18:49:21 -05:00
|
|
|
</>
|
|
|
|
|
))}
|
2024-08-22 23:44:00 -05:00
|
|
|
</div>
|
2024-07-23 18:49:21 -05:00
|
|
|
);
|
|
|
|
|
}
|