mirror of
https://github.com/DeveloLongScript/MHSF.git
synced 2026-05-07 18:34:59 -05:00
feat: fade-in + skeletons
This commit is contained in:
parent
d810a48dc8
commit
f0584f0dfb
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mh-stats",
|
"name": "mh-stats",
|
||||||
"version": "0.10.0",
|
"version": "0.10.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "yarn@1.22.22",
|
"packageManager": "yarn@1.22.22",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import { Inter as interFont } from "next/font/google";
|
|||||||
import { CommandBarer } from "@/components/CommandBar";
|
import { CommandBarer } from "@/components/CommandBar";
|
||||||
import ThemedToaster from "@/components/misc/ThemedToaster";
|
import ThemedToaster from "@/components/misc/ThemedToaster";
|
||||||
import UnofficalDialog from "@/components/misc/UnofficalDialog";
|
import UnofficalDialog from "@/components/misc/UnofficalDialog";
|
||||||
|
import ClientFadeIn from "@/components/ClientFadeIn";
|
||||||
|
|
||||||
const inter = interFont({ variable: "--font-inter", subsets: ["latin"] });
|
const inter = interFont({ variable: "--font-inter", subsets: ["latin"] });
|
||||||
export default async function RootLayout({
|
export default async function RootLayout({
|
||||||
@ -63,7 +64,7 @@ export default async function RootLayout({
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<NextTopLoader />
|
<NextTopLoader />
|
||||||
{children}
|
<ClientFadeIn>{children}</ClientFadeIn>
|
||||||
</div>{" "}
|
</div>{" "}
|
||||||
<ThemedToaster />
|
<ThemedToaster />
|
||||||
<CommandBarer />
|
<CommandBarer />
|
||||||
|
|||||||
@ -4,11 +4,13 @@ import { useEffect, useState } from "react";
|
|||||||
import { Card, CardDescription, CardHeader, CardTitle } from "./ui/card";
|
import { Card, CardDescription, CardHeader, CardTitle } from "./ui/card";
|
||||||
import Markdown from "react-markdown";
|
import Markdown from "react-markdown";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
|
import FadeIn from "react-fade-in/lib/FadeIn";
|
||||||
|
|
||||||
export default function AfterServerView({ server }: { server: string }) {
|
export default function AfterServerView({ server }: { server: string }) {
|
||||||
const [description, setDescription] = useState("");
|
const [description, setDescription] = useState("");
|
||||||
const [discord, setDiscord] = useState("");
|
const [discord, setDiscord] = useState("");
|
||||||
const { resolvedTheme } = useTheme();
|
const { resolvedTheme } = useTheme();
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCustomization(server).then((b) => {
|
getCustomization(server).then((b) => {
|
||||||
@ -16,36 +18,42 @@ export default function AfterServerView({ server }: { server: string }) {
|
|||||||
setDescription(b.description == null ? "" : b.description);
|
setDescription(b.description == null ? "" : b.description);
|
||||||
setDiscord(b.discord == null ? "" : b.discord);
|
setDiscord(b.discord == null ? "" : b.discord);
|
||||||
}
|
}
|
||||||
|
setLoading(false);
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
if (loading) return <></>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid sm:grid-cols-4 pl-4 pr-4 gap-3.5">
|
<>
|
||||||
{description != "" && (
|
<FadeIn>
|
||||||
<Card className="sm:col-span-3">
|
<div className="grid sm:grid-cols-4 pl-4 pr-4 gap-3.5">
|
||||||
<CardDescription className="p-4 prose dark:prose-invert">
|
{description != "" && (
|
||||||
<Markdown disallowedElements={["img"]}>{description}</Markdown>
|
<Card className="sm:col-span-3">
|
||||||
</CardDescription>
|
<CardDescription className="p-4 prose dark:prose-invert">
|
||||||
</Card>
|
<Markdown disallowedElements={["img"]}>{description}</Markdown>
|
||||||
)}
|
</CardDescription>
|
||||||
{discord != "" && (
|
</Card>
|
||||||
<Card>
|
)}
|
||||||
<CardHeader>
|
{discord != "" && (
|
||||||
<CardTitle>Discord Server</CardTitle>
|
<Card>
|
||||||
<CardDescription className="p-4 prose dark:prose-invert">
|
<CardHeader>
|
||||||
<iframe
|
<CardTitle>Discord Server</CardTitle>
|
||||||
src={`https://discord.com/widget?id=${discord}&theme=${resolvedTheme}`}
|
<CardDescription className="p-4 prose dark:prose-invert">
|
||||||
height="500"
|
<iframe
|
||||||
allowTransparency={true}
|
src={`https://discord.com/widget?id=${discord}&theme=${resolvedTheme}`}
|
||||||
className="rounded-lg lg:w-[350px]"
|
height="500"
|
||||||
sandbox="allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts"
|
allowTransparency={true}
|
||||||
/>
|
className="rounded-lg lg:w-[350px]"
|
||||||
</CardDescription>
|
sandbox="allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts"
|
||||||
</CardHeader>
|
/>
|
||||||
</Card>
|
</CardDescription>
|
||||||
)}
|
</CardHeader>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
|
</FadeIn>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/components/ClientFadeIn.tsx
Normal file
12
src/components/ClientFadeIn.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
"use client";
|
||||||
|
import FadeIn from "react-fade-in";
|
||||||
|
|
||||||
|
export default function ClientFadeIn({
|
||||||
|
children,
|
||||||
|
delay = 0,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
delay?: number;
|
||||||
|
}) {
|
||||||
|
return <FadeIn delay={delay}>{children}</FadeIn>;
|
||||||
|
}
|
||||||
@ -18,6 +18,8 @@ import { Separator } from "./ui/separator";
|
|||||||
import { Card, CardDescription, CardHeader, CardTitle } from "./ui/card";
|
import { Card, CardDescription, CardHeader, CardTitle } from "./ui/card";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
import { Skeleton } from "./ui/skeleton";
|
||||||
|
import FadeIn from "react-fade-in/lib/FadeIn";
|
||||||
|
|
||||||
export default function FavoriteSortView() {
|
export default function FavoriteSortView() {
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@ -55,7 +57,23 @@ export default function FavoriteSortView() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <Spinner className="flex items-center" />;
|
return (
|
||||||
|
<>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<Skeleton className="h-[112px] rounded-xl" />
|
||||||
|
<Skeleton className="h-[112px] rounded-xl" />
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<Separator />
|
||||||
|
<br />
|
||||||
|
<div className="grid 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -200,66 +218,68 @@ export default function FavoriteSortView() {
|
|||||||
}
|
}
|
||||||
style={{ overflow: "hidden", paddingLeft: 6 }}
|
style={{ overflow: "hidden", paddingLeft: 6 }}
|
||||||
>
|
>
|
||||||
<div className="grid sm:grid-cols-4 gap-4">
|
<FadeIn>
|
||||||
{allItems.map((v) => {
|
<div className="grid sm:grid-cols-4 gap-4">
|
||||||
if (v.favorites == 0) {
|
{allItems.map((v) => {
|
||||||
return <></>;
|
if (v.favorites == 0) {
|
||||||
}
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
if (online[v.server] != undefined)
|
if (online[v.server] != undefined)
|
||||||
return (
|
return (
|
||||||
<ServerCard
|
<ServerCard
|
||||||
mini
|
mini
|
||||||
b={online[v.server]}
|
b={online[v.server]}
|
||||||
favs={v.favorites}
|
favs={v.favorites}
|
||||||
key={v.server}
|
key={v.server}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
return (
|
return (
|
||||||
<Card className="h-[226px]" key={v.server}>
|
<Card className="h-[226px]" key={v.server}>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>{v.server}</CardTitle>
|
<CardTitle>{v.server}</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
{v.favorites} favorited
|
{v.favorites} favorited
|
||||||
<br />
|
<br />
|
||||||
<Button
|
<Button
|
||||||
size="icon"
|
size="icon"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="min-w-[128px] max-w-[328px] h-[32px] mt-2 ml-2 max-md:hidden"
|
className="min-w-[128px] max-w-[328px] h-[32px] mt-2 ml-2 max-md:hidden"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigator.clipboard.writeText(
|
navigator.clipboard.writeText(
|
||||||
v.server + ".mshf.minehut.gg"
|
v.server + ".mshf.minehut.gg"
|
||||||
);
|
);
|
||||||
toast.success("Copied IP to clipboard");
|
toast.success("Copied IP to clipboard");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Copy size={18} />
|
<Copy size={18} />
|
||||||
<code className="ml-2">{v.server}</code>
|
<code className="ml-2">{v.server}</code>
|
||||||
</Button>
|
</Button>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
<Link href={"/server/" + v.server}>
|
<Link href={"/server/" + v.server}>
|
||||||
<Button
|
<Button
|
||||||
size="icon"
|
size="icon"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="w-[32px] h-[32px] mt-2 ml-2 max-md:hidden"
|
className="w-[32px] h-[32px] mt-2 ml-2 max-md:hidden"
|
||||||
>
|
>
|
||||||
<Layers size={18} />
|
<Layers size={18} />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
Open up the server page to see more information about
|
Open up the server page to see more information
|
||||||
the server
|
about the server
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
</FadeIn>
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -10,6 +10,8 @@ import toast from "react-hot-toast";
|
|||||||
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";
|
||||||
|
import { Skeleton } from "./ui/skeleton";
|
||||||
|
import FadeIn from "react-fade-in/lib/FadeIn";
|
||||||
|
|
||||||
export default function FavoritesView() {
|
export default function FavoritesView() {
|
||||||
const [apiFavorites, setApiFavorites] = useState<any>([]);
|
const [apiFavorites, setApiFavorites] = useState<any>([]);
|
||||||
@ -40,8 +42,20 @@ export default function FavoritesView() {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Spinner className="flex items-center" />
|
<div className="grid grid-cols-4 gap-4">
|
||||||
<br />
|
<Skeleton className="h-[147px] rounded-xl" />
|
||||||
|
<Skeleton className="h-[147px] rounded-xl" />
|
||||||
|
<Skeleton className="h-[147px] rounded-xl" />
|
||||||
|
<Skeleton className="h-[147px] rounded-xl" />
|
||||||
|
<Skeleton className="h-[147px] rounded-xl" />
|
||||||
|
<Skeleton className="h-[147px] rounded-xl" />
|
||||||
|
<Skeleton className="h-[147px] rounded-xl" />
|
||||||
|
<Skeleton className="h-[147px] rounded-xl" />
|
||||||
|
<Skeleton className="h-[147px] rounded-xl" />
|
||||||
|
<Skeleton className="h-[147px] rounded-xl" />
|
||||||
|
<Skeleton className="h-[147px] rounded-xl" />
|
||||||
|
<Skeleton className="h-[147px] rounded-xl" />
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -54,53 +68,55 @@ export default function FavoritesView() {
|
|||||||
Your favorites are empty. Maybe favorite a server!
|
Your favorites are empty. Maybe favorite a server!
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="grid sm:grid-cols-4 gap-4">
|
<FadeIn>
|
||||||
{apiFavorites.map((server: ServerResponse) => (
|
<div className="grid sm:grid-cols-4 gap-4">
|
||||||
<Card key={server.name}>
|
{apiFavorites.map((server: ServerResponse) => (
|
||||||
<CardHeader>
|
<Card key={server.name}>
|
||||||
<CardTitle>{server.name}</CardTitle>
|
<CardHeader>
|
||||||
<div>
|
<CardTitle>{server.name}</CardTitle>
|
||||||
<Button
|
<div>
|
||||||
size="icon"
|
<Button
|
||||||
variant="secondary"
|
size="icon"
|
||||||
className="min-w-[128px] max-w-[328px] mb-2 h-[32px] max-md:hidden"
|
variant="secondary"
|
||||||
onClick={() => {
|
className="min-w-[128px] max-w-[328px] mb-2 h-[32px] max-md:hidden"
|
||||||
navigator.clipboard.writeText(
|
onClick={() => {
|
||||||
server.name + ".mshf.minehut.gg"
|
navigator.clipboard.writeText(
|
||||||
);
|
server.name + ".mshf.minehut.gg"
|
||||||
toast.success("Copied IP to clipboard");
|
);
|
||||||
}}
|
toast.success("Copied IP to clipboard");
|
||||||
>
|
}}
|
||||||
<Copy size={18} />
|
>
|
||||||
<code className="ml-2">{server.name}</code>
|
<Copy size={18} />
|
||||||
</Button>
|
<code className="ml-2">{server.name}</code>
|
||||||
<Tooltip>
|
</Button>
|
||||||
<TooltipTrigger>
|
<Tooltip>
|
||||||
<Button
|
<TooltipTrigger>
|
||||||
size="icon"
|
<Button
|
||||||
variant="secondary"
|
size="icon"
|
||||||
className="w-[32px] h-[32px] mb-2 ml-2 max-md:hidden"
|
variant="secondary"
|
||||||
onClick={() => {
|
className="w-[32px] h-[32px] mb-2 ml-2 max-md:hidden"
|
||||||
router.push("/server/" + server.name);
|
onClick={() => {
|
||||||
}}
|
router.push("/server/" + server.name);
|
||||||
>
|
}}
|
||||||
<Layers size={18} />
|
>
|
||||||
</Button>
|
<Layers size={18} />
|
||||||
</TooltipTrigger>
|
</Button>
|
||||||
<TooltipContent>
|
</TooltipTrigger>
|
||||||
Open up the server page to see more information about the
|
<TooltipContent>
|
||||||
server
|
Open up the server page to see more information about the
|
||||||
</TooltipContent>
|
server
|
||||||
</Tooltip>
|
</TooltipContent>
|
||||||
</div>
|
</Tooltip>
|
||||||
<code className="text-[14px]">
|
</div>
|
||||||
{convert(server.joins)} total joins •{" "}
|
<code className="text-[14px]">
|
||||||
{server.online ? "Online" : "Offline"}
|
{convert(server.joins)} total joins •{" "}
|
||||||
</code>
|
{server.online ? "Online" : "Offline"}
|
||||||
</CardHeader>
|
</code>
|
||||||
</Card>
|
</CardHeader>
|
||||||
))}
|
</Card>
|
||||||
</div>
|
))}
|
||||||
|
</div>
|
||||||
|
</FadeIn>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,8 @@ import {
|
|||||||
import { useEffectOnce } from "@/lib/useEffectOnce";
|
import { useEffectOnce } from "@/lib/useEffectOnce";
|
||||||
import { ServerResponse } from "@/lib/types/mh-server";
|
import { ServerResponse } from "@/lib/types/mh-server";
|
||||||
import { getCommunityServerFavorites, getShortTermData } from "@/lib/api";
|
import { getCommunityServerFavorites, getShortTermData } from "@/lib/api";
|
||||||
|
import { Skeleton } from "./ui/skeleton";
|
||||||
|
import FadeIn from "react-fade-in/lib/FadeIn";
|
||||||
|
|
||||||
const chartConfig = {
|
const chartConfig = {
|
||||||
player_count: {
|
player_count: {
|
||||||
@ -37,6 +39,7 @@ export function NewChart({ server }: { server: string }) {
|
|||||||
|
|
||||||
const [chartData, setChartData] = React.useState<any>([]);
|
const [chartData, setChartData] = React.useState<any>([]);
|
||||||
const [joins, setJoins] = React.useState<any>(0);
|
const [joins, setJoins] = React.useState<any>(0);
|
||||||
|
const [loading, setLoading] = React.useState(true);
|
||||||
const [favorites, setFavorites] = React.useState<any>(0);
|
const [favorites, setFavorites] = React.useState<any>(0);
|
||||||
|
|
||||||
const allNums = { player_count: joins, favorites };
|
const allNums = { player_count: joins, favorites };
|
||||||
@ -52,99 +55,109 @@ export function NewChart({ server }: { server: string }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (loading)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Skeleton className="w-full h-[437px]" />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="w-full">
|
<FadeIn>
|
||||||
<CardHeader className="flex flex-col items-stretch space-y-0 border-b p-0 sm:flex-row">
|
<Card className="w-full">
|
||||||
<div className="flex flex-1 flex-col justify-center gap-1 px-6 py-5 sm:py-6">
|
<CardHeader className="flex flex-col items-stretch space-y-0 border-b p-0 sm:flex-row">
|
||||||
<CardTitle>
|
<div className="flex flex-1 flex-col justify-center gap-1 px-6 py-5 sm:py-6">
|
||||||
{chartConfig[activeChart].label} Chart for {server}
|
<CardTitle>
|
||||||
</CardTitle>
|
{chartConfig[activeChart].label} Chart for {server}
|
||||||
<CardDescription>Showing the past 30 entries.</CardDescription>
|
</CardTitle>
|
||||||
</div>
|
<CardDescription>Showing the past 30 entries.</CardDescription>
|
||||||
<div className="flex">
|
</div>
|
||||||
{["player_count", "favorites"].map((key) => {
|
<div className="flex">
|
||||||
const chart = key as keyof typeof chartConfig;
|
{["player_count", "favorites"].map((key) => {
|
||||||
return (
|
const chart = key as keyof typeof chartConfig;
|
||||||
<button
|
return (
|
||||||
key={chart}
|
<button
|
||||||
data-active={activeChart === chart}
|
key={chart}
|
||||||
className="flex flex-1 flex-col justify-center gap-1 border-t px-6 py-4 text-left even:border-l data-[active=true]:bg-muted/50 sm:border-l sm:border-t-0 sm:px-8 sm:py-6"
|
data-active={activeChart === chart}
|
||||||
onClick={() => setActiveChart(chart)}
|
className="flex flex-1 flex-col justify-center gap-1 border-t px-6 py-4 text-left even:border-l data-[active=true]:bg-muted/50 sm:border-l sm:border-t-0 sm:px-8 sm:py-6"
|
||||||
>
|
onClick={() => setActiveChart(chart)}
|
||||||
<span className="text-xs text-muted-foreground">
|
>
|
||||||
{chartConfig[chart].label}
|
<span className="text-xs text-muted-foreground">
|
||||||
</span>
|
{chartConfig[chart].label}
|
||||||
<span className="text-lg font-bold leading-none sm:text-3xl">
|
</span>
|
||||||
{convert(allNums[chart])}
|
<span className="text-lg font-bold leading-none sm:text-3xl">
|
||||||
</span>
|
{convert(allNums[chart])}
|
||||||
</button>
|
</span>
|
||||||
);
|
</button>
|
||||||
})}
|
);
|
||||||
</div>
|
})}
|
||||||
</CardHeader>
|
</div>
|
||||||
<CardContent className="px-2 sm:p-6">
|
</CardHeader>
|
||||||
<ChartContainer
|
<CardContent className="px-2 sm:p-6">
|
||||||
config={chartConfig}
|
<ChartContainer
|
||||||
className="aspect-auto h-[250px] w-full"
|
config={chartConfig}
|
||||||
>
|
className="aspect-auto h-[250px] w-full"
|
||||||
<LineChart
|
|
||||||
accessibilityLayer
|
|
||||||
data={chartData}
|
|
||||||
margin={{
|
|
||||||
left: 12,
|
|
||||||
right: 12,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<CartesianGrid vertical={false} />
|
<LineChart
|
||||||
<XAxis
|
accessibilityLayer
|
||||||
dataKey="date"
|
data={chartData}
|
||||||
tickLine={false}
|
margin={{
|
||||||
axisLine={false}
|
left: 12,
|
||||||
tickMargin={8}
|
right: 12,
|
||||||
minTickGap={32}
|
|
||||||
tickFormatter={(value) => {
|
|
||||||
return new Date(value).toLocaleTimeString("en-US", {
|
|
||||||
timeStyle: "short",
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
<YAxis
|
<CartesianGrid vertical={false} />
|
||||||
dataKey={activeChart}
|
<XAxis
|
||||||
tickLine={false}
|
dataKey="date"
|
||||||
axisLine={false}
|
tickLine={false}
|
||||||
tickFormatter={(value) => {
|
axisLine={false}
|
||||||
return (
|
tickMargin={8}
|
||||||
value +
|
minTickGap={32}
|
||||||
(activeChart == "player_count"
|
tickFormatter={(value) => {
|
||||||
? ` plyr${value != 1 ? "s" : ""}.`
|
return new Date(value).toLocaleTimeString("en-US", {
|
||||||
: ` ${value == 1 ? "favorite" : "favrts."}`)
|
timeStyle: "short",
|
||||||
);
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ChartTooltip
|
<YAxis
|
||||||
content={
|
dataKey={activeChart}
|
||||||
<ChartTooltipContent
|
tickLine={false}
|
||||||
className="w-[150px]"
|
axisLine={false}
|
||||||
nameKey={activeChart}
|
tickFormatter={(value) => {
|
||||||
hideLabel
|
return (
|
||||||
/>
|
value +
|
||||||
}
|
(activeChart == "player_count"
|
||||||
/>
|
? ` plyr${value != 1 ? "s" : ""}.`
|
||||||
<Line
|
: ` ${value == 1 ? "favorite" : "favrts."}`)
|
||||||
dataKey={activeChart}
|
);
|
||||||
type="monotone"
|
}}
|
||||||
stroke={`var(--color-${activeChart})`}
|
/>
|
||||||
strokeWidth={2}
|
<ChartTooltip
|
||||||
dot={false}
|
content={
|
||||||
/>
|
<ChartTooltipContent
|
||||||
</LineChart>
|
className="w-[150px]"
|
||||||
</ChartContainer>
|
nameKey={activeChart}
|
||||||
</CardContent>
|
hideLabel
|
||||||
</Card>
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Line
|
||||||
|
dataKey={activeChart}
|
||||||
|
type="monotone"
|
||||||
|
stroke={`var(--color-${activeChart})`}
|
||||||
|
strokeWidth={2}
|
||||||
|
dot={false}
|
||||||
|
/>
|
||||||
|
</LineChart>
|
||||||
|
</ChartContainer>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</FadeIn>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,8 @@ import {
|
|||||||
MenubarSubTrigger,
|
MenubarSubTrigger,
|
||||||
MenubarTrigger,
|
MenubarTrigger,
|
||||||
} from "@/components/ui/menubar";
|
} from "@/components/ui/menubar";
|
||||||
|
import ClientFadeIn from "./ClientFadeIn";
|
||||||
|
import { Skeleton } from "./ui/skeleton";
|
||||||
|
|
||||||
export default function ServerList() {
|
export default function ServerList() {
|
||||||
const [loading, setLoading]: any = useState(true);
|
const [loading, setLoading]: any = useState(true);
|
||||||
@ -119,471 +121,489 @@ export default function ServerList() {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Spinner className="flex items-center" />
|
<div className="grid 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 />
|
<br />
|
||||||
<div
|
<Separator />
|
||||||
className="flex justify-center"
|
<br />
|
||||||
dangerouslySetInnerHTML={{ __html: randomText }}
|
<div className="grid grid-cols-4 gap-4">
|
||||||
></div>
|
<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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="max-lg:grid-cols-2 grid grid-cols-3 gap-4 ">
|
<ClientFadeIn>
|
||||||
<Stat
|
<div className="max-lg:grid-cols-2 grid grid-cols-3 gap-4 ">
|
||||||
title="Players online"
|
<Stat
|
||||||
desc={serverList.getExtraData().total_players.toString()}
|
title="Players online"
|
||||||
icon={CircleUser}
|
desc={serverList.getExtraData().total_players.toString()}
|
||||||
/>
|
icon={CircleUser}
|
||||||
<Stat
|
/>
|
||||||
title={
|
<Stat
|
||||||
<div
|
title={
|
||||||
className={
|
|
||||||
serverList.getExtraData().total_servers >= 3200
|
|
||||||
? "bg-clip-text text-transparent bg-gradient-to-r from-cyan-500 to-blue-500"
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Servers online{" "}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
className="relative z-0"
|
|
||||||
desc={
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
serverList.getExtraData().total_servers >= 3200
|
serverList.getExtraData().total_servers >= 3200
|
||||||
? "bg-clip-text text-transparent bg-gradient-to-r from-cyan-500 to-blue-500 "
|
? "bg-clip-text text-transparent bg-gradient-to-r from-cyan-500 to-blue-500"
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{serverList.getExtraData().total_servers.toString()}
|
Servers online{" "}
|
||||||
</div>
|
</div>
|
||||||
{serverList.getExtraData().total_servers >= 3200 && (
|
}
|
||||||
<Tooltip>
|
className="relative z-0"
|
||||||
<TooltipTrigger>
|
desc={
|
||||||
<Info size={16} className="ml-2" />
|
<div className="flex items-center">
|
||||||
</TooltipTrigger>
|
<div
|
||||||
<TooltipContent className="font-normal">
|
className={
|
||||||
The server amount is over 3.2k, meaning that new servers
|
serverList.getExtraData().total_servers >= 3200
|
||||||
have to go into a queue before being able to be online.{" "}
|
? "bg-clip-text text-transparent bg-gradient-to-r from-cyan-500 to-blue-500 "
|
||||||
<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>
|
|
||||||
<br />
|
|
||||||
<Separator />
|
|
||||||
<Menubar className="mt-3 ml-2 border rounded p-2">
|
|
||||||
<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 = {};
|
|
||||||
|
|
||||||
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
|
|
||||||
</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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} 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);
|
|
||||||
|
|
||||||
filt2.splice(filt2.indexOf(smaller), 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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
var filt = nameFilters;
|
|
||||||
filt["smaller-tf"] = false;
|
|
||||||
filt["bigger-tf"] = false;
|
|
||||||
setNameFilters(filt);
|
|
||||||
setTemplateFilter(false);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
error: "Error while changing filters",
|
|
||||||
loading: "Changing filters...",
|
|
||||||
success: "Changed filters!",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
value={(() => {
|
|
||||||
if (nameFilters["smaller-tf"]) {
|
|
||||||
return "smaller";
|
|
||||||
} else if (nameFilters["bigger-tf"]) {
|
|
||||||
return "bigger";
|
|
||||||
} else {
|
|
||||||
return "none";
|
|
||||||
}
|
|
||||||
})()}
|
|
||||||
>
|
|
||||||
<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 });
|
|
||||||
});
|
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<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();
|
|
||||||
|
|
||||||
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];
|
|
||||||
})()}
|
|
||||||
>
|
|
||||||
<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>Sort</MenubarSubTrigger>
|
|
||||||
<MenubarSubContent>
|
|
||||||
<MenubarRadioGroup
|
|
||||||
value="joins"
|
|
||||||
onValueChange={(c) =>
|
|
||||||
c == "favorites" && router.push("/sort/favorites")
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<MenubarRadioItem value="joins">
|
{serverList.getExtraData().total_servers.toString()}
|
||||||
Players Online
|
</div>
|
||||||
</MenubarRadioItem>
|
{serverList.getExtraData().total_servers >= 3200 && (
|
||||||
<MenubarRadioItem value="favorites">
|
<Tooltip>
|
||||||
Favorites
|
<TooltipTrigger>
|
||||||
</MenubarRadioItem>
|
<Info size={16} className="ml-2" />
|
||||||
</MenubarRadioGroup>
|
</TooltipTrigger>
|
||||||
</MenubarSubContent>
|
<TooltipContent className="font-normal">
|
||||||
</MenubarSub>
|
The server amount is over 3.2k, meaning that new servers
|
||||||
</MenubarContent>
|
have to go into a queue before being able to be online.{" "}
|
||||||
</MenubarMenu>
|
<br />
|
||||||
</Menubar>
|
(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 />
|
||||||
|
<Separator />
|
||||||
|
<ClientFadeIn delay={100}>
|
||||||
|
<Menubar className="mt-3 ml-2 border rounded p-2">
|
||||||
|
<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 = {};
|
||||||
|
|
||||||
|
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
|
||||||
|
</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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} 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);
|
||||||
|
|
||||||
|
filt2.splice(filt2.indexOf(smaller), 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var filt = nameFilters;
|
||||||
|
filt["smaller-tf"] = false;
|
||||||
|
filt["bigger-tf"] = false;
|
||||||
|
setNameFilters(filt);
|
||||||
|
setTemplateFilter(false);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
error: "Error while changing filters",
|
||||||
|
loading: "Changing filters...",
|
||||||
|
success: "Changed filters!",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
value={(() => {
|
||||||
|
if (nameFilters["smaller-tf"]) {
|
||||||
|
return "smaller";
|
||||||
|
} else if (nameFilters["bigger-tf"]) {
|
||||||
|
return "bigger";
|
||||||
|
} else {
|
||||||
|
return "none";
|
||||||
|
}
|
||||||
|
})()}
|
||||||
|
>
|
||||||
|
<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 });
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<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();
|
||||||
|
|
||||||
|
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];
|
||||||
|
})()}
|
||||||
|
>
|
||||||
|
<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>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>
|
||||||
|
</MenubarContent>
|
||||||
|
</MenubarMenu>
|
||||||
|
</Menubar>
|
||||||
|
</ClientFadeIn>
|
||||||
|
|
||||||
<Dialog open={random} onOpenChange={setRandom}>
|
<Dialog open={random} onOpenChange={setRandom}>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
@ -695,13 +715,15 @@ export default function ServerList() {
|
|||||||
}
|
}
|
||||||
style={{ overflow: "hidden !important", paddingLeft: 6 }}
|
style={{ overflow: "hidden !important", paddingLeft: 6 }}
|
||||||
>
|
>
|
||||||
<div className={" grid " + "grid-cols-" + ipr + " gap-4"}>
|
<ClientFadeIn delay={200}>
|
||||||
{servers.map((b: any) => (
|
<div className={" grid " + "grid-cols-" + ipr + " gap-4"}>
|
||||||
<>
|
{servers.map((b: any) => (
|
||||||
<ServerCard b={b} motd={motdList[b.name]} />
|
<>
|
||||||
</>
|
<ServerCard b={b} motd={motdList[b.name]} />
|
||||||
))}
|
</>
|
||||||
</div>
|
))}
|
||||||
|
</div>
|
||||||
|
</ClientFadeIn>
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -24,6 +24,8 @@ import { Star, X } from "lucide-react";
|
|||||||
import { favoriteServer, isFavorited } from "@/lib/api";
|
import { favoriteServer, isFavorited } from "@/lib/api";
|
||||||
import { LoadingButton } from "./ui/loading-button";
|
import { LoadingButton } from "./ui/loading-button";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
|
import { Skeleton } from "./ui/skeleton";
|
||||||
|
import FadeIn from "react-fade-in/lib/FadeIn";
|
||||||
|
|
||||||
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));
|
||||||
@ -66,12 +68,11 @@ export default function ServerView(props: { server: string }) {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Spinner className="flex items-center" />
|
<div className="grid p-4 sm:grid-cols-3 gap-4">
|
||||||
<br />
|
<Skeleton className="sm:col-span-2 h-[245px]" />
|
||||||
<div
|
|
||||||
className="flex justify-center"
|
<Skeleton className="h-[245px]" />
|
||||||
dangerouslySetInnerHTML={{ __html: randomText }}
|
</div>
|
||||||
></div>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -94,151 +95,153 @@ export default function ServerView(props: { server: string }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<FadeIn>
|
||||||
<div className="grid p-4 sm:grid-cols-3 gap-4">
|
<div className="grid p-4 sm:grid-cols-3 gap-4">
|
||||||
<Card className="sm:col-span-2">
|
<Card className="sm:col-span-2">
|
||||||
<BetterHeader>
|
<BetterHeader>
|
||||||
<CardTitle className="flex items-center">
|
<CardTitle className="flex items-center">
|
||||||
{single.grabOnline() == undefined ? (
|
{single.grabOnline() == undefined ? (
|
||||||
<div
|
<div
|
||||||
className="items-center mr-1"
|
className="items-center mr-1"
|
||||||
style={{
|
style={{
|
||||||
width: ".75rem",
|
width: ".75rem",
|
||||||
height: ".75rem",
|
height: ".75rem",
|
||||||
borderRadius: "9999px",
|
borderRadius: "9999px",
|
||||||
backgroundColor: "#ff1744",
|
backgroundColor: "#ff1744",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className="items-center mr-1"
|
className="items-center mr-1"
|
||||||
style={{
|
style={{
|
||||||
width: ".75rem",
|
width: ".75rem",
|
||||||
height: ".75rem",
|
height: ".75rem",
|
||||||
borderRadius: "9999px",
|
borderRadius: "9999px",
|
||||||
backgroundColor: "#0cce6b",
|
backgroundColor: "#0cce6b",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
{props.server}
|
|
||||||
</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
{/* 1704088800000 is the Unix time (in milliseconds) of (GMT) Monday, January 1, 2024 6:00:00 AM */}
|
|
||||||
{lastOnline < 1704088800000 &&
|
|
||||||
single.grabOnline() == undefined && (
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger>
|
|
||||||
<Badge variant="secondary">
|
|
||||||
Server too old to grab data
|
|
||||||
</Badge>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
This server was last online before 1/1/24 or{" "}
|
|
||||||
<code>(GMT) Monday, January 1, 2024 6:00:00 AM</code>,
|
|
||||||
<br />
|
|
||||||
meaning data like tags, authors and other information
|
|
||||||
about the server cannot be seen.
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
)}
|
||||||
|
{props.server}
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
{/* 1704088800000 is the Unix time (in milliseconds) of (GMT) Monday, January 1, 2024 6:00:00 AM */}
|
||||||
|
{lastOnline < 1704088800000 &&
|
||||||
|
single.grabOnline() == undefined && (
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<Badge variant="secondary">
|
||||||
|
Server too old to grab data
|
||||||
|
</Badge>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
This server was last online before 1/1/24 or{" "}
|
||||||
|
<code>(GMT) Monday, January 1, 2024 6:00:00 AM</code>,
|
||||||
|
<br />
|
||||||
|
meaning data like tags, authors and other information
|
||||||
|
about the server cannot be seen.
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
|
||||||
{single.getAuthor() != undefined && (
|
{single.getAuthor() != undefined && (
|
||||||
<p>by {single.getAuthor()}</p>
|
<p>by {single.getAuthor()}</p>
|
||||||
)}
|
)}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</BetterHeader>
|
</BetterHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<p>
|
<p>
|
||||||
<strong>Time:</strong>
|
<strong>Time:</strong>
|
||||||
<br />
|
<br />
|
||||||
<i>Last online</i>{" "}
|
<i>Last online</i>{" "}
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
<code>
|
<code>
|
||||||
{timeConverter(single.grabOffline()?.last_online)}
|
{timeConverter(single.grabOffline()?.last_online)}
|
||||||
</code>
|
</code>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<code>{single.grabOffline()?.last_online}</code> in Unix time
|
<code>{single.grabOffline()?.last_online}</code> in Unix
|
||||||
</TooltipContent>
|
time
|
||||||
</Tooltip>{" "}
|
</TooltipContent>
|
||||||
<br />
|
</Tooltip>{" "}
|
||||||
<i>Created on</i>{" "}
|
<br />
|
||||||
<Tooltip>
|
<i>Created on</i>{" "}
|
||||||
<TooltipTrigger>
|
<Tooltip>
|
||||||
<code>{timeConverter(single.grabOffline()?.creation)}</code>
|
<TooltipTrigger>
|
||||||
</TooltipTrigger>
|
<code>{timeConverter(single.grabOffline()?.creation)}</code>
|
||||||
<TooltipContent>
|
</TooltipTrigger>
|
||||||
<code>{single.grabOffline()?.creation}</code> in Unix time
|
<TooltipContent>
|
||||||
</TooltipContent>
|
<code>{single.grabOffline()?.creation}</code> in Unix time
|
||||||
</Tooltip>
|
</TooltipContent>
|
||||||
</p>
|
</Tooltip>
|
||||||
</CardContent>
|
</p>
|
||||||
</Card>
|
</CardContent>
|
||||||
<Card>
|
</Card>
|
||||||
<CardHeader>
|
<Card>
|
||||||
<CardTitle>Favorite the server?</CardTitle>
|
<CardHeader>
|
||||||
<CardDescription>
|
<CardTitle>Favorite the server?</CardTitle>
|
||||||
By favoriting the server, you can see it later.{" "}
|
<CardDescription>
|
||||||
|
By favoriting the server, you can see it later.{" "}
|
||||||
|
<SignedOut>
|
||||||
|
<strong>You need to sign in to favorite a server.</strong>
|
||||||
|
</SignedOut>
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
<SignedOut>
|
<SignedOut>
|
||||||
<strong>You need to sign in to favorite a server.</strong>
|
<SignInPopoverButton />
|
||||||
</SignedOut>
|
</SignedOut>
|
||||||
</CardDescription>
|
<SignedIn>
|
||||||
</CardHeader>
|
<LoadingButton
|
||||||
<CardContent>
|
variant={resolvedTheme == "dark" ? "outline" : "default"}
|
||||||
<SignedOut>
|
loading={loadingFavorite}
|
||||||
<SignInPopoverButton />
|
onClick={() => {
|
||||||
</SignedOut>
|
setLoadingFavorite(true);
|
||||||
<SignedIn>
|
favoriteServer(single.grabOffline()?.name as string).then(
|
||||||
<LoadingButton
|
() => {
|
||||||
variant={resolvedTheme == "dark" ? "outline" : "default"}
|
setFavorited(!favorited);
|
||||||
loading={loadingFavorite}
|
setLoadingFavorite(false);
|
||||||
onClick={() => {
|
}
|
||||||
setLoadingFavorite(true);
|
);
|
||||||
favoriteServer(single.grabOffline()?.name as string).then(
|
}}
|
||||||
() => {
|
>
|
||||||
setFavorited(!favorited);
|
{favorited && (
|
||||||
setLoadingFavorite(false);
|
<motion.div
|
||||||
}
|
animate={{ color: "yellow", fill: "yellow" }}
|
||||||
);
|
transition={{ duration: 2 }}
|
||||||
}}
|
>
|
||||||
>
|
<Star
|
||||||
{favorited && (
|
className="mr-2"
|
||||||
<motion.div
|
size="16"
|
||||||
animate={{ color: "yellow", fill: "yellow" }}
|
color="yellow"
|
||||||
transition={{ duration: 2 }}
|
fill="yellow"
|
||||||
>
|
/>
|
||||||
<Star
|
</motion.div>
|
||||||
className="mr-2"
|
)}
|
||||||
size="16"
|
{!favorited && (
|
||||||
color="yellow"
|
<motion.div
|
||||||
fill="yellow"
|
transition={{ duration: 1 }}
|
||||||
/>
|
animate={{ color: "yellow", fill: "yellow" }}
|
||||||
</motion.div>
|
>
|
||||||
)}
|
<Star className="mr-2" size="16" />
|
||||||
{!favorited && (
|
</motion.div>
|
||||||
<motion.div
|
)}
|
||||||
transition={{ duration: 1 }}
|
{favorited && "Unf"}
|
||||||
animate={{ color: "yellow", fill: "yellow" }}
|
{!favorited && "F"}avorite Server
|
||||||
>
|
</LoadingButton>
|
||||||
<Star className="mr-2" size="16" />
|
</SignedIn>
|
||||||
</motion.div>
|
</CardContent>
|
||||||
)}
|
<CardFooter>
|
||||||
{favorited && "Unf"}
|
<small>
|
||||||
{!favorited && "F"}avorite Server
|
This is unlike voting. The{" "}
|
||||||
</LoadingButton>
|
<i>amount of people who favorited are public</i>, but the server
|
||||||
</SignedIn>
|
doesn{"'"}t know who favorited, as Favorites are completely
|
||||||
</CardContent>
|
anonymous.
|
||||||
<CardFooter>
|
</small>
|
||||||
<small>
|
</CardFooter>
|
||||||
This is unlike voting. The{" "}
|
</Card>
|
||||||
<i>amount of people who favorited are public</i>, but the server
|
</div>
|
||||||
doesn{"'"}t know who favorited, as Favorites are completely
|
</FadeIn>
|
||||||
anonymous.
|
|
||||||
</small>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
export const version = "b-0.10.0";
|
export const version = "b-0.10.2";
|
||||||
|
|
||||||
const User = ({ user }: { user: string }) => (
|
const User = ({ user }: { user: string }) => (
|
||||||
<span className="cursor-pointer bg-[rgba(255,165,0,0.25);] rounded p-[2.5px]">
|
<span className="cursor-pointer bg-[rgba(255,165,0,0.25);] rounded p-[2.5px]">
|
||||||
@ -44,6 +44,17 @@ export const Changelog = () => (
|
|||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
|
<div>
|
||||||
|
<strong className="flex items-center">
|
||||||
|
Version b-0.10.2 (August 18th 2024)
|
||||||
|
</strong>
|
||||||
|
<ul>
|
||||||
|
<li>• Content fades-in on load</li>
|
||||||
|
<li>• Instead of using spinners, now we are using Skeletons</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
<div>
|
<div>
|
||||||
<strong className="flex items-center">
|
<strong className="flex items-center">
|
||||||
Version b-0.10.0 (August 17th 2024)
|
Version b-0.10.0 (August 17th 2024)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user