bump: 0.8.0

This commit is contained in:
dvelo 2024-08-11 22:14:36 -05:00
parent 0a620523c4
commit b33191bdaa
10 changed files with 275 additions and 106 deletions

BIN
public/imgs/badge1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

@ -69,7 +69,7 @@ export default function ServerPage({ params }: { params: { server: string } }) {
<ColorProvider server={params.server}> <ColorProvider server={params.server}>
<div className={"pt-16"}> <div className={"pt-16"}>
<Banner server={params.server} /> <Banner server={params.server} />
<TabServer server={params.server} tabDef="historical" /> <TabServer server={params.server} tabDef="statistics" />
<div className="pt-8"> <div className="pt-8">
<ServerView server={params.server} /> <ServerView server={params.server} />
<div className="p-4 gap-4"> <div className="p-4 gap-4">

@ -9,15 +9,17 @@ import {
CommandSeparator, CommandSeparator,
CommandShortcut, CommandShortcut,
} from "@/components/ui/command"; } from "@/components/ui/command";
import { TagShower } from "./ServerList"; import { useEffect, useState } from "react";
import { useState } from "react";
import { OnlineServer } from "@/lib/types/mh-server"; import { OnlineServer } from "@/lib/types/mh-server";
import events from "@/lib/commandEvent"; import events from "@/lib/commandEvent";
import { useHotkeys } from "react-hotkeys-hook"; import { useHotkeys } from "react-hotkeys-hook";
import { import {
ArrowDown01, ArrowDown01,
ArrowLeft, ArrowLeft,
Calendar,
CheckIcon,
CommandIcon, CommandIcon,
Database,
LinkIcon, LinkIcon,
Server, Server,
Settings, Settings,
@ -27,7 +29,10 @@ import { useEffectOnce } from "@/lib/useEffectOnce";
import { useClerk, useUser } from "@clerk/nextjs"; import { useClerk, useUser } from "@clerk/nextjs";
import { useRouter } from "@/lib/useRouter"; import { useRouter } from "@/lib/useRouter";
import type { SVGProps } from "react"; import type { SVGProps } from "react";
import { getAccountFavorites } from "@/lib/api"; import { favoriteServer, getAccountFavorites } from "@/lib/api";
import IconDisplay from "./IconDisplay";
import ServerSingle from "@/lib/single";
import toast from "react-hot-toast";
export function SearchCommandBar() { export function SearchCommandBar() {
const [serverList, setServerList] = useState<OnlineServer[]>([]); const [serverList, setServerList] = useState<OnlineServer[]>([]);
@ -58,23 +63,46 @@ export function SearchCommandBar() {
<CommandInput <CommandInput
placeholder="Search for a server (offline or online)" placeholder="Search for a server (offline or online)"
onValueChange={(c) => { onValueChange={(c) => {
fetch("https://api.minehut.com/server/" + c + "?byName=true").then( if (c != "") {
(l) => { fetch("https://api.minehut.com/server/" + c + "?byName=true").then(
if (l.ok) { (l) => {
console.log("found!"); if (l.ok) {
l.json().then((m: any) => { l.json().then((m: any) => {
setSearchRes(m.server); setSearchRes(m.server);
console.log(searchRes); });
}); } else {
} else { setSearchRes(undefined);
setSearchRes(undefined); }
} }
} );
); }
}} }}
/> />
<CommandList> <CommandList>
<CommandGroup heading=""> <CommandGroup heading="Popular Servers">
{serverList.map((b: OnlineServer) => (
<CommandItem
key={b.name}
onSelect={() => {
if (!backEnabled)
events.emit("cmd-server", {
serverName: b.name,
serverObject: b,
});
if (backEnabled)
events.emit("cmd-server-vb", {
serverName: b.name,
serverObject: b,
});
setOpen(false);
}}
>
<IconDisplay server={b} className="mr-2" />
{b.name}
</CommandItem>
))}
</CommandGroup>
<CommandGroup heading="Hierarchy">
<CommandItem <CommandItem
onSelect={() => { onSelect={() => {
setOpen(false); setOpen(false);
@ -86,51 +114,148 @@ export function SearchCommandBar() {
<span>Go back</span> <span>Go back</span>
</CommandItem> </CommandItem>
</CommandGroup> </CommandGroup>
<CommandEmpty> </CommandList>
No results found. (Minehut deleted legacy servers) </CommandDialog>
</CommandEmpty> );
{searchRes == undefined ? ( }
""
) : ( export function ServerCommandBar() {
<CommandGroup heading="Search Results"> const [open, setOpen] = useState(false);
<CommandItem const [serverName, setServerName] = useState("");
onSelect={() => { const [obj, setObj] = useState<OnlineServer | object>({});
router.push("/server/" + searchRes.name); const [vb, setVB] = useState(false);
}} const router = useRouter();
> const [owned, setOwned] = useState(false);
<div className="block"> const [serverSingle, setSingle] = useState<ServerSingle>(
<span className="font-medium">{searchRes.name}</span> <br /> new ServerSingle(serverName)
<code className="text-gray-500 text-[14px]"> );
{searchRes.joins} total joins {" "}
{searchRes.online ? "Online" : "Offline"} useEffect(() => {
</code> events.on("cmd-server", (info) => {
</div> serverSingle.setName(info.serverName);
</CommandItem> if (serverSingle != undefined)
</CommandGroup> (serverSingle as ServerSingle).init(true).then(() => {
setServerName(info.serverName);
setObj(info.serverObject);
setOpen(true);
serverSingle.isCustomized().then((b) => setOwned(true));
});
});
events.on("cmd-server-vb", (info) => {
serverSingle.setName(info.serverName);
setVB(true);
if (serverSingle != undefined)
(serverSingle as ServerSingle).init(true).then(() => {
setServerName(info.serverName);
setObj(info.serverObject);
setOpen(true);
});
});
}, []);
return (
<CommandDialog open={open} onOpenChange={setOpen}>
<CommandInput placeholder="Type a command or search..." />
<CommandList>
{Object.keys(obj).length != 0 && (
<div className="m-4 dark:bg-ring min-h-[150px] rounded p-4 dark:text-white">
<h1 className="font-bold text-2xl">
<IconDisplay server={obj} />
{serverName}
</h1>
<h1 className="text-muted-foreground">
by {(obj as OnlineServer).author}
</h1>
<h2 className="flex items-center text-muted-foreground pt-[15px] pl-1.5">
<span className="relative flex h-[10px] w-[10px]">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-black dark:bg-[#0cce6b] opacity-75" />
<span className="relative inline-flex rounded-full h-[10px] w-[10px] bg-black dark:bg-[#0cce6b]" />
</span>{" "}
<span className="pl-1.5 text-[16px]">
{(obj as OnlineServer).playerData.playerCount} online currently
</span>
</h2>
<h2 className="flex items-center text-muted-foreground">
<Calendar />
<span className="pl-1.5 text-[16px]">
Created in{" "}
{timeConverter(
(serverSingle as ServerSingle).grabOffline()?.creation
)}
</span>
</h2>
{owned && (
<h2 className="flex items-center text-muted-foreground">
<CheckIcon />
<span className="pl-1.5 text-[16px]">
Is customized by a MHSF User
</span>
</h2>
)}
</div>
)} )}
<CommandSeparator />
<CommandGroup heading="Popular Servers"> <CommandGroup heading="Server Actions">
{serverList.map((b: OnlineServer) => ( <CommandItem
onClick={() => router.push("/server/" + serverName + "/")}
>
<Server className="mr-2 h-4 w-4" />
Open Server Page
</CommandItem>
<CommandItem
onClick={() => {
favoriteServer(serverName).then(() => toast.success("Done!"));
}}
>
<Star className="mr-2 h-4 w-4" />
Favorite Server
</CommandItem>
<CommandItem
onClick={() => router.push("/server/" + serverName + "/statistics")}
>
<Database className="mr-2 h-4 w-4" />
See Statistics
</CommandItem>
</CommandGroup>
<CommandGroup heading="Hierarchy">
<CommandItem
onSelect={() => {
setOpen(false);
if (vb) events.emit("search-request-event-back");
if (!vb) events.emit("search-request-event");
}}
>
<ArrowLeft className="mr-2 h-4 w-4" />
Go back
</CommandItem>
{vb && (
<CommandItem <CommandItem
key={b.name}
onSelect={() => { onSelect={() => {
router.push("/server/" + b.name); setOpen(false);
events.emit("cmd-event");
}} }}
> >
<div className="block"> <ArrowLeft className="mr-2 h-4 w-4" />
<span className="font-medium">{b.name}</span> <br /> Back to main
<code className="text-gray-500 text-[14px]">
<TagShower server={b} />
</code>
</div>
</CommandItem> </CommandItem>
))} )}
</CommandGroup> </CommandGroup>
</CommandList> </CommandList>
</CommandDialog> </CommandDialog>
); );
} }
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 function CommandBar() { export function CommandBar() {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const clerk = useClerk(); const clerk = useClerk();
@ -327,6 +452,7 @@ export function CommandBarer() {
<SubLinkCommandBar /> <SubLinkCommandBar />
<CommandBar /> <CommandBar />
<SearchCommandBar /> <SearchCommandBar />
<ServerCommandBar />
</> </>
); );
} }

@ -7,18 +7,24 @@ import {
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
export default function IconDisplay(props: { server: any }) { export default function IconDisplay(props: {
server: any;
className?: string;
}) {
return ( return (
<Tooltip> <Tooltip>
<TooltipTrigger> <TooltipTrigger>
<i <div>
className={ <i
props.server.icon != null className={
? "icon-minecraft icon-minecraft-" + (props.server.icon != null
props.server.icon.replaceAll("_", "-").toLowerCase() ? "icon-minecraft icon-minecraft-" +
: "icon-minecraft icon-minecraft-oak-sign" props.server.icon.replaceAll("_", "-").toLowerCase() +
} " "
/> : "icon-minecraft icon-minecraft-oak-sign ") + props.className
}
/>
</div>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
<div className="font-mono"> <div className="font-mono">

@ -699,7 +699,11 @@ export default function ServerList() {
); );
} }
export function TagShower(props: { server: OnlineServer; className?: string }) { export function TagShower(props: {
server: OnlineServer;
className?: string;
unclickable?: boolean;
}) {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [compatiableTags, setCompatiableTags] = useState< const [compatiableTags, setCompatiableTags] = useState<
Array<{ Array<{
@ -757,38 +761,45 @@ export function TagShower(props: { server: OnlineServer; className?: string }) {
<> <>
{compatiableTags.map((t) => ( {compatiableTags.map((t) => (
<> <>
<Dialog key={t.name}> {props.unclickable && (
<DialogTrigger> <Badge variant={t.role} className={props.className}>
<Tooltip> {t.name}
<TooltipTrigger> </Badge>
<Badge variant={t.role} className={props.className}> )}
{t.name} {!props.unclickable && (
</Badge> <Dialog key={t.name}>
</TooltipTrigger> <DialogTrigger>
<TooltipContent> <Tooltip>
<div className="font-normal"> <TooltipTrigger>
{t.tooltip} <Badge variant={t.role} className={props.className}>
<br /> {t.name}
Click the tag to learn more about it. </Badge>
</div> </TooltipTrigger>
</TooltipContent> <TooltipContent>
</Tooltip> <div className="font-normal">
</DialogTrigger> {t.tooltip}
<DialogContent> <br />
<DialogHeader> Click the tag to learn more about it.
<DialogTitle> </div>
{'"'} </TooltipContent>
{t.docsName == undefined ? t.name : t.docsName} </Tooltip>
{'"'} documentation </DialogTrigger>
</DialogTitle> <DialogContent>
<DialogDescription <DialogHeader>
dangerouslySetInnerHTML={{ <DialogTitle>
__html: t.htmlDocs, {'"'}
}} {t.docsName == undefined ? t.name : t.docsName}
/> {'"'} documentation
</DialogHeader> </DialogTitle>
</DialogContent> <DialogDescription
</Dialog> dangerouslySetInnerHTML={{
__html: t.htmlDocs,
}}
/>
</DialogHeader>
</DialogContent>
</Dialog>
)}
</> </>
))} ))}
</> </>

@ -12,7 +12,6 @@ export default function TabServer({
tabDef: string; tabDef: string;
}) { }) {
const [tab, setTab] = useState(tabDef); const [tab, setTab] = useState(tabDef);
const [tabLoading, setTabLoading] = useState(false);
const router = useRouter(); const router = useRouter();
return ( return (
@ -21,9 +20,8 @@ export default function TabServer({
value={tab} value={tab}
onValueChange={(tac) => { onValueChange={(tac) => {
setTab(tac); setTab(tac);
setTabLoading(true);
if (tac == "customize") router.push(`/server/${server}/customize`); if (tac == "customize") router.push(`/server/${server}/customize`);
if (tac == "historical") router.push(`/server/${server}/short-term`); if (tac == "statistics") router.push(`/server/${server}/statistics`);
if (tac == "general") router.push(`/server/${server}`); if (tac == "general") router.push(`/server/${server}`);
}} }}
className="sm:w-[500px] max-sm:w-[200px]" className="sm:w-[500px] max-sm:w-[200px]"
@ -34,8 +32,8 @@ export default function TabServer({
<div className="max-sm:hidden">General Information</div> <div className="max-sm:hidden">General Information</div>
<Home className="sm:hidden" size={18} /> <Home className="sm:hidden" size={18} />
</TabsTrigger> </TabsTrigger>
<TabsTrigger value="historical"> <TabsTrigger value="statistics">
<div className="max-sm:hidden">Short Term</div> <div className="max-sm:hidden">Statistics</div>
<Database className="sm:hidden" size={18} /> <Database className="sm:hidden" size={18} />
</TabsTrigger> </TabsTrigger>
<TabsTrigger value="customize"> <TabsTrigger value="customize">

@ -28,7 +28,7 @@ const RadioGroupItem = React.forwardRef<
<RadioGroupPrimitive.Item <RadioGroupPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"aspect-square h-4 w-4 rounded-full border border-primary text-primary focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 focus:ring-4 focus:ring-neutral-300 focus:ring-offset-current dark:focus:ring-neutral-500 duration-150 ease-in-out transition-all", "aspect-square h-4 w-4 rounded-full border border-primary text-primary focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 focus:ring-4 focus:ring-neutral-300 focus:ring-offset-current dark:focus:ring-neutral-500 duration-150 ease-in-out transition-all ",
className className
)} )}
{...props} {...props}

@ -6,15 +6,15 @@ class CommandEvents {
} }
// Method to emit events // Method to emit events
emit(eventName: string) { emit(eventName: string, info?: any) {
const event = new CustomEvent(eventName); const event = new CustomEvent(eventName, { detail: info });
this.eventTarget.dispatchEvent(event); this.eventTarget.dispatchEvent(event);
} }
// Method to listen for events // Method to listen for events
on(eventName: string, callback: () => void) { on(eventName: string, callback: (info?: any) => void) {
this.eventTarget.addEventListener(eventName, () => { this.eventTarget.addEventListener(eventName, (infoF?: any) => {
callback(); callback(infoF.detail);
}); });
} }
} }

@ -1,3 +1,4 @@
import { serverOwned } from "./api";
import { OnlineServer, ServerResponse } from "./types/mh-server"; import { OnlineServer, ServerResponse } from "./types/mh-server";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
@ -10,7 +11,15 @@ export default class ServerSingle {
constructor(name: string) { constructor(name: string) {
this.name = name; this.name = name;
} }
init(): Promise<boolean> { setName(newName: string) {
this.name = newName;
}
isCustomized(): Promise<boolean> {
return serverOwned(this.name);
}
init(skipOnline?: boolean): Promise<boolean> {
return new Promise<boolean>((g, bc) => { return new Promise<boolean>((g, bc) => {
fetch("https://api.minehut.com/server/" + this.name + "?byName=true") fetch("https://api.minehut.com/server/" + this.name + "?byName=true")
.then((d) => { .then((d) => {
@ -18,7 +27,7 @@ export default class ServerSingle {
d.json().then((m) => { d.json().then((m) => {
this.online = m.server.online; this.online = m.server.online;
this.offlineObj = m.server; this.offlineObj = m.server;
if (this.online == true) { if (this.online == true && skipOnline != true) {
fetch("https://api.minehut.com/servers").then((l) => fetch("https://api.minehut.com/servers").then((l) =>
l.json().then((o) => { l.json().then((o) => {
o.servers.forEach((j: OnlineServer) => { o.servers.forEach((j: OnlineServer) => {

@ -1,4 +1,7 @@
export const version = "b-0.7.2"; import Image from "next/image";
import Link from "next/link";
export const version = "b-0.8.0";
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]">
@ -41,6 +44,16 @@ export const Changelog = () => (
</code> </code>
</div> </div>
<br /> <br />
<div>
<strong className="flex items-center">
Version b-0.8.0 (August 11th 2024)
</strong>
<ul>
<li> Fixing up command bar</li>
<li> Renaming "Short Term" to "Statistics"</li>
</ul>
</div>
<br />
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version b-0.7.2 (August 7th 2024) Version b-0.7.2 (August 7th 2024)
@ -118,5 +131,11 @@ export const Changelog = () => (
<li> Inital release!</li> <li> Inital release!</li>
</ul> </ul>
</div> </div>
<br />
<div className="w-full justify-center">
<Link href="https://dvelo.vercel.app">
<Image src="/imgs/badge1.png" alt="cool badge" width={88} height={31} />
</Link>
</div>
</> </>
); );