mhsf-dev/src/components/ServerView.tsx
2024-07-23 18:49:21 -05:00

354 lines
10 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useState, useEffect } from "react";
import { Spinner } from "./ui/spinner";
import IconDisplay from "./IconDisplay";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
BetterHeader,
} from "@/components/ui/card";
import { motion, AnimatePresence } from "framer-motion";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import beautify from "json-beautify";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { Badge } from "./ui/badge";
import Editor from "@monaco-editor/react";
import { allTags } from "@/allTags";
import ServerSingle from "@/lib/single";
import { parse, parseExpression } from "@babel/parser";
import { NEXT_ROUTER_STATE_TREE } from "next/dist/client/components/app-router-headers";
import { Button } from "./ui/button";
import * as prettierPluginBabel from "prettier/plugins/babel";
import toast from "react-hot-toast";
import { SignedIn, SignedOut, SignInButton } from "@clerk/nextjs";
import SignInPopoverButton from "./clerk/SignInPopoverButton";
import { Sparkle, Star, X } from "lucide-react";
export default function ServerView(props: { server: string }) {
const [single, setSingle] = useState(new ServerSingle(props.server));
const [loading, setLoading] = useState(true);
const [favorited, setFavorited] = useState(false);
const [randomText, setRandomText] = useState("");
const [lastOnline, setLastOnline] = useState(0);
const [format, setFormat] = useState("");
const allText = [""];
const getRandomText = () => {
return allText[Math.floor(Math.random() * allText.length)];
};
useEffect(() => {
setRandomText(getRandomText());
single.init().then(() => {
fetch("/api/accountLocked/isFavorited", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
server: single.grabOffline()?.name,
}),
})
.then((b) => {
b.json().then((c) => {
setFavorited(c.result);
setLoading(false);
var online = single.grabOffline()?.last_online;
if (online != undefined) {
setLastOnline(online);
}
});
})
.catch(() => {
setLoading(false);
var online = single.grabOffline()?.last_online;
if (online != undefined) {
setLastOnline(online);
}
});
});
}, []);
if (loading) {
return (
<>
<Spinner className="flex items-center" />
<br />
<div
className=" flex justify-center"
dangerouslySetInnerHTML={{ __html: randomText }}
></div>
</>
);
}
return (
<>
{single.grabOnline() == undefined && (
<div className=" grid pl-4 pr-4">
<div
className=" rounded p-2"
style={{ backgroundColor: "rgba(244, 63, 94, .16)" }}
>
<strong className="flex items-center">
This server is offline <X />
</strong>
<p>
This means that the server can{"'"}t loading some resources, like
tags, icons and authors. However, all historical data still is
stored and works.
</p>
</div>
</div>
)}
<div className=" grid p-4 sm:grid-cols-3 gap-4">
<Card className=" sm:col-span-2">
<BetterHeader>
<CardTitle className=" flex items-center">
{single.grabOnline() == undefined ? (
<div
className=" items-center mr-1"
style={{
width: ".75rem",
height: ".75rem",
borderRadius: "9999px",
backgroundColor: "#ff1744",
}}
/>
) : (
<div
className=" items-center mr-1"
style={{
width: ".75rem",
height: ".75rem",
borderRadius: "9999px",
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>
)}
{single.getAuthor() != undefined && (
<p>by {single.getAuthor()}</p>
)}
</CardDescription>
</BetterHeader>
<CardContent>
<p>
<strong>Time:</strong>
<br />
<i>Last online</i>{" "}
<Tooltip>
<TooltipTrigger>
<code>
{timeConverter(single.grabOffline()?.last_online)}
</code>
</TooltipTrigger>
<TooltipContent>
<code>{single.grabOffline()?.last_online}</code> in Unix time
</TooltipContent>
</Tooltip>{" "}
<br />
<i>Created on</i>{" "}
<Tooltip>
<TooltipTrigger>
<code>{timeConverter(single.grabOffline()?.creation)}</code>
</TooltipTrigger>
<TooltipContent>
<code>{single.grabOffline()?.creation}</code> in Unix time
</TooltipContent>
</Tooltip>
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Favorite the server?</CardTitle>
<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>
<SignInPopoverButton />
</SignedOut>
<SignedIn>
<Button
variant="outline"
onClick={() => {
fetch("/api/accountLocked/favoriteServer", {
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
server: single.grabOffline()?.name,
}),
method: "POST",
}).then(() => {});
setFavorited(!favorited);
}}
>
{favorited && (
<motion.div
animate={{ color: "yellow", fill: "yellow" }}
transition={{ duration: 2 }}
>
<Star
className="mr-2"
size="16"
color="yellow"
fill="yellow"
/>
</motion.div>
)}
{!favorited && (
<motion.div
transition={{ duration: 1 }}
animate={{ color: "yellow", fill: "yellow" }}
>
<Star className="mr-2" size="16" />
</motion.div>
)}
Favorite Server
</Button>
</SignedIn>
</CardContent>
<CardFooter>
<small>
This is unlike voting. The{" "}
<i>amount of people who favorited are public</i>, but the server
doesn{"'"}t know who favorited, as Favorites are completely
anonymous.
</small>
</CardFooter>
</Card>
</div>
</>
);
}
function timeConverter(UNIX_timestamp: any) {
var a = new Date(UNIX_timestamp);
var months = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"];
var year = a.getFullYear();
var month = months[a.getMonth()];
var date = a.getDate();
var time = month + "/" + date + "/" + year;
return time;
}
export interface ServerResponse {
__unix?: string;
deletion?: Deletion;
_id: string;
categories: string[];
inheritedCategories: any[];
purchased_icons: string[];
backup_slots: number;
suspended: boolean;
server_version_type: string;
proxy: boolean;
connectedServers: any[];
motd: string;
visibility: boolean;
server_plan: string;
storage_node: string;
default_banner_image: string;
default_banner_tint: string;
owner: string;
name: string;
name_lower: string;
creation: number;
platform: string;
credits_per_day: number;
in_game: boolean;
using_cosmetics: boolean;
__v: number;
port: number;
last_online: number;
joins: number;
active_icon: string;
expired: boolean;
icon: string;
online: boolean;
maxPlayers: number;
playerCount: number;
rawPlan: string;
activeServerPlan: string;
}
export interface Deletion {
started: boolean;
started_at: number;
reason: string;
completed: boolean;
completed_at: number;
storage_completed: boolean;
storage_completed_at: number;
}
export interface OnlineServer {
staticInfo: StaticInfo;
maxPlayers: number;
name: string;
motd: string;
icon: string;
playerData: PlayerData;
connectable: boolean;
visibility: boolean;
allCategories: string[];
usingCosmetics: boolean;
author?: string;
authorRank: string;
}
export interface StaticInfo {
_id: string;
serverPlan: string;
serviceStartDate: number;
platform: string;
planMaxPlayers: number;
planRam: number;
alwaysOnline: boolean;
rawPlan: string;
connectedServers: any[];
}
export interface PlayerData {
playerCount: number;
timeNoPlayers: number;
}