fix: command-bar is kinda broken

This commit is contained in:
dvelo 2024-08-13 00:59:44 -05:00
parent 0114dbbca5
commit 0cb47304cc
2 changed files with 373 additions and 56 deletions

@ -10,7 +10,6 @@ import {
CommandShortcut, CommandShortcut,
} from "@/components/ui/command"; } from "@/components/ui/command";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
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 {
@ -22,33 +21,51 @@ import {
Database, Database,
LinkIcon, LinkIcon,
Server, Server,
ServerCog,
Settings, Settings,
Star, Star,
XIcon,
} from "lucide-react"; } from "lucide-react";
import { useEffectOnce } from "@/lib/useEffectOnce"; 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 { favoriteServer, getAccountFavorites } from "@/lib/api"; import { favoriteServer, getAccountFavorites, serverOwned } from "@/lib/api";
import IconDisplay from "./IconDisplay"; import IconDisplay from "./IconDisplay";
import ServerSingle from "@/lib/single"; import ServerSingle from "@/lib/single";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { ServerResponse, OnlineServer } from "@/lib/types/mh-server";
import { m } from "framer-motion";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "./ui/dialog";
import { TagShower } from "./ServerList";
import { Button } from "./ui/button";
import { useTheme } from "next-themes";
export function SearchCommandBar() { export function SearchCommandBar() {
const [serverList, setServerList] = useState<OnlineServer[]>([]); const [serverList, setServerList] = useState<OnlineServer[]>([]);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [backEnabled, setBackEnabled] = useState(false); const [backEnabled, setBackEnabled] = useState(false);
const [searchRes, setSearchRes] = useState<any>(undefined); const [searchRes, setSearchRes] = useState<ServerResponse | undefined>(
undefined
);
const router = useRouter(); const router = useRouter();
useHotkeys("mod+shift+k", () => setOpen(true), []); useHotkeys("mod+shift+k", () => setOpen(true), []);
useEffectOnce(() => { useEffectOnce(() => {
events.on("search-request-event", () => { events.on("search-request-event", () => {
setOpen(true); setOpen(true);
setSearchRes(undefined);
}); });
events.on("search-request-event-back", () => { events.on("search-request-event-back", () => {
setOpen(true); setOpen(true);
setBackEnabled(true); setBackEnabled(true);
setSearchRes(undefined);
}); });
fetch("https://api.minehut.com/servers").then((c) => fetch("https://api.minehut.com/servers").then((c) =>
@ -59,49 +76,71 @@ export function SearchCommandBar() {
}); });
return ( return (
<CommandDialog open={open} onOpenChange={setOpen}> <CommandDialog
open={open}
onOpenChange={setOpen}
shouldFilter={searchRes == undefined}
>
<CommandInput <CommandInput
placeholder="Search for a server (offline or online)" placeholder="Search for a server (offline or online)"
onValueChange={(c) => { onValueChange={(c) => {
if (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) { l.json().then((m: any) => {
l.json().then((m: any) => { if (m.server != null) {
console.log(m);
setSearchRes(m.server); setSearchRes(m.server);
}); } else {
} else { setSearchRes(undefined);
setSearchRes(undefined); }
} });
} }
); );
} }
}} }}
/> />
<CommandList> <CommandList>
<CommandGroup heading="Popular Servers"> {searchRes != undefined && (
{serverList.map((b: OnlineServer) => ( <CommandGroup heading="Search Result">
<CommandItem <CommandItem
key={b.name}
onSelect={() => { onSelect={() => {
if (!backEnabled) if (!backEnabled) events.emit("cmd-offline", searchRes);
events.emit("cmd-server", { if (backEnabled) events.emit("cmd-offline-vb", searchRes);
serverName: b.name,
serverObject: b,
});
if (backEnabled)
events.emit("cmd-server-vb", {
serverName: b.name,
serverObject: b,
});
setOpen(false); setOpen(false);
}} }}
> >
<IconDisplay server={b} className="mr-2" /> {searchRes?.name}
{b.name}
</CommandItem> </CommandItem>
))} </CommandGroup>
</CommandGroup> )}
{searchRes == undefined && (
<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"> <CommandGroup heading="Hierarchy">
<CommandItem <CommandItem
onSelect={() => { onSelect={() => {
@ -119,6 +158,153 @@ export function SearchCommandBar() {
); );
} }
export function OfflineServerCB() {
const [open, setOpen] = useState(false);
const [customized, setCustomized] = useState(false);
const [obj, setObj] = useState<ServerResponse | object>({});
const [vb, setVB] = useState(false);
const router = useRouter();
useEffect(() => {
events.on("cmd-offline", (info: ServerResponse) => {
if (info.online) {
fetch("https://api.minehut.com/servers").then((c) => {
c.json().then((b: { servers: OnlineServer[] }) => {
const filteredRes = b.servers.filter(
(value) => value.name == info.name
);
if (filteredRes.length != 0) {
events.emit("cmd-server", {
serverName: filteredRes[0].name,
serverObject: filteredRes[0],
});
} else {
toast.error("Couldn't find server");
}
});
});
} else {
setVB(false);
setOpen(true);
setObj(info);
serverOwned(info.name).then((b) => setCustomized(b));
}
});
events.on("cmd-offline-vb", (info: ServerResponse) => {
if (info.online) {
fetch("https://api.minehut.com/servers").then((c) => {
c.json().then((b: { servers: OnlineServer[] }) => {
const filteredRes = b.servers.filter(
(value) => value.name == info.name
);
if (filteredRes.length != 0) {
events.emit("cmd-server-vb", {
serverName: filteredRes[0].name,
serverObject: filteredRes[0],
});
} else {
toast.error("Couldn't find server");
}
});
});
} else {
setOpen(true);
setVB(true);
setObj(info);
serverOwned(info.name).then((b) => setCustomized(b));
}
});
}, []);
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">
{(obj as ServerResponse).name}
</h1>
<h2 className="flex items-center text-muted-foreground">
<XIcon />
<span className="pl-1.5 text-[16px]">
Server offline currently
</span>
</h2>
<h2 className="flex items-center text-muted-foreground">
<Calendar />
<span className="pl-1.5 text-[16px]">
Created in {timeConverter((obj as ServerResponse).creation)}
</span>
</h2>
{customized && (
<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
onSelect={() =>
router.push("/server/" + (obj as ServerResponse).name + "/")
}
>
<Server className="mr-2 h-4 w-4" />
Open Server Page
</CommandItem>
<CommandItem
onSelect={() => {
favoriteServer((obj as ServerResponse).name).then(() =>
toast.success("Done!")
);
}}
>
<Star className="mr-2 h-4 w-4" />
Favorite Server
</CommandItem>
<CommandItem
onSelect={() =>
router.push(
"/server/" + (obj as ServerResponse).name + "/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
onSelect={() => {
setOpen(false);
events.emit("cmd-event");
}}
>
<ArrowLeft className="mr-2 h-4 w-4" />
Back to main
</CommandItem>
)}
</CommandGroup>
</CommandList>
</CommandDialog>
);
}
export function ServerCommandBar() { export function ServerCommandBar() {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [serverName, setServerName] = useState(""); const [serverName, setServerName] = useState("");
@ -213,7 +399,9 @@ export function ServerCommandBar() {
Favorite Server Favorite Server
</CommandItem> </CommandItem>
<CommandItem <CommandItem
onSelect={() => router.push("/server/" + serverName + "/statistics")} onSelect={() =>
router.push("/server/" + serverName + "/statistics")
}
> >
<Database className="mr-2 h-4 w-4" /> <Database className="mr-2 h-4 w-4" />
See Statistics See Statistics
@ -274,7 +462,7 @@ export function CommandBar() {
<CommandInput placeholder="Type a command or search..." /> <CommandInput placeholder="Type a command or search..." />
<CommandList> <CommandList>
<CommandEmpty>No results found.</CommandEmpty> <CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="Suggestions"> <CommandGroup heading="General">
<CommandItem <CommandItem
onSelect={() => { onSelect={() => {
setOpen(false); setOpen(false);
@ -303,16 +491,29 @@ export function CommandBar() {
<LinkIcon className="mr-2 h-4 w-4" /> <LinkIcon className="mr-2 h-4 w-4" />
<span>Links</span> <span>Links</span>
</CommandItem> </CommandItem>
<CommandItem
onSelect={() => {
setOpen(false);
events.emit("cmd-ran-server");
}}
>
<ServerCog className="mr-2 h-4 w-4" />
<span>Pick Random Server</span>
</CommandItem>
</CommandGroup> </CommandGroup>
<CommandSeparator /> <CommandSeparator />
<CommandGroup heading="Profile"> <CommandGroup heading="Profile">
<CommandItem onSelect={() => events.emit("cmd-event-favorites")}> <CommandItem
onSelect={() => {
events.emit("cmd-event-favorites");
setOpen(false);
}}
>
<Star className="mr-2 h-4 w-4" /> <Star className="mr-2 h-4 w-4" />
<span>Favorites</span> <span>Favorites</span>
</CommandItem> </CommandItem>
<CommandItem <CommandItem
onSelect={() => { onSelect={() => {
setOpen(false);
try { try {
clerk.openUserProfile(); clerk.openUserProfile();
} catch { } catch {
@ -329,8 +530,118 @@ export function CommandBar() {
); );
} }
export function RandomServerDialog() {
const [textCopied, setTextCopied] = useState(false);
const [randomData, setRandomData] = useState<OnlineServer | undefined>(
undefined
);
const [open, setOpen] = useState(false);
useEffect(() => {
events.on("cmd-ran-server", () => {
setRandomData(undefined);
setTextCopied(false);
fetch("https://api.minehut.com/servers").then((c) =>
c.json().then((b: { servers: OnlineServer[] }) => {
setRandomData(
b.servers[Math.floor(Math.random() * b.servers.length)]
);
setOpen(true);
})
);
});
}, []);
const onChange = (state: boolean) => {
if (state == false) {
setOpen(false);
events.emit("cmd-event");
} else setOpen(state);
};
return (
<Dialog open={open} onOpenChange={onChange}>
<DialogContent>
{randomData == undefined && <>No data to randomize</>}
{randomData != undefined && (
<DialogHeader>
<DialogTitle>
<IconDisplay server={randomData} /> {randomData.name}
{randomData.author != undefined ? (
<div className="text-sm text-muted-foreground">
by {randomData.author}
</div>
) : (
<br />
)}
<TagShower server={randomData} />
</DialogTitle>
<DialogDescription className="float-left inline">
<span className="flex items-center">
{randomData.playerData.playerCount == 0 ? (
<div
className="items-center border"
style={{
width: ".5rem",
height: ".5rem",
borderRadius: "9999px",
}}
/>
) : (
<div
className="items-center"
style={{
backgroundColor: "#0cce6b",
width: ".5rem",
height: ".5rem",
borderRadius: "9999px",
}}
/>
)}
<span className="pl-1">
{randomData.playerData.playerCount}{" "}
{randomData.playerData.playerCount == 1
? "player"
: "players"}{" "}
currently online
</span>
</span>
<br />
<strong>Server IP</strong>
<br />
<br />
<code className="border p-3 rounded">
{randomData.name}.mshf.minehut.gg{" "}
<Button
size="icon"
className="ml-1 h-[20px]"
onClick={() => {
setTextCopied(true);
navigator.clipboard.writeText(
randomData.name + ".mshf.minehut.gg"
);
toast.success("Copied!");
setTimeout(() => setTextCopied(false), 1000);
}}
>
{textCopied ? (
<CheckIcon size={16} className="flex items-center" />
) : (
<p>Copy</p>
)}
</Button>
</code>
</DialogDescription>
</DialogHeader>
)}
</DialogContent>
</Dialog>
);
}
export function SubLinkCommandBar() { export function SubLinkCommandBar() {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const { resolvedTheme } = useTheme();
useEffectOnce(() => { useEffectOnce(() => {
events.on("cmd-event-link", () => { events.on("cmd-event-link", () => {
@ -343,15 +654,6 @@ export function SubLinkCommandBar() {
<CommandInput placeholder="Type a command or search..." /> <CommandInput placeholder="Type a command or search..." />
<CommandList> <CommandList>
<CommandEmpty>No results found.</CommandEmpty> <CommandEmpty>No results found.</CommandEmpty>
<CommandItem
onSelect={() => {
setOpen(false);
events.emit("cmd-event");
}}
>
<ArrowLeft className="mr-2 h-4 w-4" />
<span>Go back</span>
</CommandItem>
<CommandGroup heading="Suggestions"> <CommandGroup heading="Suggestions">
<CommandItem <CommandItem
@ -361,7 +663,10 @@ export function SubLinkCommandBar() {
?.focus(); ?.focus();
}} }}
> >
<Github className="mr-2 h-4 w-4" /> <Github
className="mr-2 h-4 w-4"
fill={resolvedTheme == "dark" ? "white" : "black"}
/>
<span>GitHub</span> <span>GitHub</span>
</CommandItem> </CommandItem>
@ -374,6 +679,17 @@ export function SubLinkCommandBar() {
<span>Status Page</span> <span>Status Page</span>
</CommandItem> </CommandItem>
</CommandGroup> </CommandGroup>
<CommandGroup heading="Hierarchy">
<CommandItem
onSelect={() => {
setOpen(false);
events.emit("cmd-event");
}}
>
<ArrowLeft className="mr-2 h-4 w-4" />
Go back
</CommandItem>
</CommandGroup>
</CommandList> </CommandList>
</CommandDialog> </CommandDialog>
); );
@ -383,7 +699,6 @@ const Github = (props: SVGProps<SVGSVGElement>) => (
viewBox="0 0 256 250" viewBox="0 0 256 250"
width="1em" width="1em"
height="1em" height="1em"
fill="#24292f"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid" preserveAspectRatio="xMidYMid"
{...props} {...props}
@ -409,17 +724,6 @@ export function FavoriteBar() {
<CommandInput placeholder="Type a command or search..." /> <CommandInput placeholder="Type a command or search..." />
<CommandList> <CommandList>
<CommandEmpty>No results found.</CommandEmpty> <CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="">
<CommandItem
onSelect={() => {
setOpen(false);
events.emit("cmd-event");
}}
>
<ArrowLeft className="mr-2 h-4 w-4" />
Go back
</CommandItem>
</CommandGroup>
<CommandGroup heading="Favorites"> <CommandGroup heading="Favorites">
{favorites == undefined && ( {favorites == undefined && (
@ -442,6 +746,17 @@ export function FavoriteBar() {
</> </>
)} )}
</CommandGroup> </CommandGroup>
<CommandGroup heading="Hierarchy">
<CommandItem
onSelect={() => {
setOpen(false);
events.emit("cmd-event");
}}
>
<ArrowLeft className="mr-2 h-4 w-4" />
Go back
</CommandItem>
</CommandGroup>
</CommandList> </CommandList>
</CommandDialog> </CommandDialog>
); );
@ -454,6 +769,8 @@ export function CommandBarer() {
<CommandBar /> <CommandBar />
<SearchCommandBar /> <SearchCommandBar />
<ServerCommandBar /> <ServerCommandBar />
<OfflineServerCB />
<RandomServerDialog />
</> </>
); );
} }

@ -23,13 +23,13 @@ const Command = React.forwardRef<
)) ))
Command.displayName = CommandPrimitive.displayName Command.displayName = CommandPrimitive.displayName
interface CommandDialogProps extends DialogProps {} interface CommandDialogProps extends DialogProps { shouldFilter?: boolean }
const CommandDialog = ({ children, ...props }: CommandDialogProps) => { const CommandDialog = ({ children, shouldFilter, ...props }: CommandDialogProps) => {
return ( return (
<Dialog {...props}> <Dialog {...props}>
<DialogContent className="overflow-hidden p-0 shadow-lg"> <DialogContent className="overflow-hidden p-0 shadow-lg">
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5" shouldFilter={shouldFilter}>
{children} {children}
</Command> </Command>
</DialogContent> </DialogContent>