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) => {
if (c != "") {
fetch("https://api.minehut.com/server/" + c + "?byName=true").then( fetch("https://api.minehut.com/server/" + c + "?byName=true").then(
(l) => { (l) => {
if (l.ok) { if (l.ok) {
console.log("found!");
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);
const [serverName, setServerName] = useState("");
const [obj, setObj] = useState<OnlineServer | object>({});
const [vb, setVB] = useState(false);
const router = useRouter();
const [owned, setOwned] = useState(false);
const [serverSingle, setSingle] = useState<ServerSingle>(
new ServerSingle(serverName)
);
useEffect(() => {
events.on("cmd-server", (info) => {
serverSingle.setName(info.serverName);
if (serverSingle != undefined)
(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>
)}
<CommandGroup heading="Server Actions">
<CommandItem <CommandItem
onSelect={() => { onClick={() => router.push("/server/" + serverName + "/")}
router.push("/server/" + searchRes.name); >
<Server className="mr-2 h-4 w-4" />
Open Server Page
</CommandItem>
<CommandItem
onClick={() => {
favoriteServer(serverName).then(() => toast.success("Done!"));
}} }}
> >
<div className="block"> <Star className="mr-2 h-4 w-4" />
<span className="font-medium">{searchRes.name}</span> <br /> Favorite Server
<code className="text-gray-500 text-[14px]"> </CommandItem>
{searchRes.joins} total joins {" "} <CommandItem
{searchRes.online ? "Online" : "Offline"} onClick={() => router.push("/server/" + serverName + "/statistics")}
</code> >
</div> <Database className="mr-2 h-4 w-4" />
See Statistics
</CommandItem> </CommandItem>
</CommandGroup> </CommandGroup>
)} <CommandGroup heading="Hierarchy">
<CommandSeparator />
<CommandGroup heading="Popular Servers">
{serverList.map((b: OnlineServer) => (
<CommandItem <CommandItem
key={b.name}
onSelect={() => { onSelect={() => {
router.push("/server/" + b.name); setOpen(false);
if (vb) events.emit("search-request-event-back");
if (!vb) events.emit("search-request-event");
}} }}
> >
<div className="block"> <ArrowLeft className="mr-2 h-4 w-4" />
<span className="font-medium">{b.name}</span> <br /> Go back
<code className="text-gray-500 text-[14px]">
<TagShower server={b} />
</code>
</div>
</CommandItem> </CommandItem>
))} {vb && (
<CommandItem
onSelect={() => {
setOpen(false);
events.emit("cmd-event");
}}
>
<ArrowLeft className="mr-2 h-4 w-4" />
Back to main
</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>
<div>
<i <i
className={ className={
props.server.icon != null (props.server.icon != null
? "icon-minecraft icon-minecraft-" + ? "icon-minecraft icon-minecraft-" +
props.server.icon.replaceAll("_", "-").toLowerCase() props.server.icon.replaceAll("_", "-").toLowerCase() +
: "icon-minecraft icon-minecraft-oak-sign" " "
: "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,6 +761,12 @@ export function TagShower(props: { server: OnlineServer; className?: string }) {
<> <>
{compatiableTags.map((t) => ( {compatiableTags.map((t) => (
<> <>
{props.unclickable && (
<Badge variant={t.role} className={props.className}>
{t.name}
</Badge>
)}
{!props.unclickable && (
<Dialog key={t.name}> <Dialog key={t.name}>
<DialogTrigger> <DialogTrigger>
<Tooltip> <Tooltip>
@ -789,6 +799,7 @@ export function TagShower(props: { server: OnlineServer; className?: string }) {
</DialogHeader> </DialogHeader>
</DialogContent> </DialogContent>
</Dialog> </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">

@ -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>
</> </>
); );