mirror of
https://github.com/DeveloLongScript/MHSF.git
synced 2026-05-07 14:54:58 -05:00
bump: 0.9 - new sorting
This commit is contained in:
parent
0a347d3eac
commit
4e4dd9cfe6
@ -3,7 +3,10 @@ import { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "the MHSF project by dvelo",
|
||||
description: `currently running in ${process.env.NEXT_PUBLIC_VERCEL_ENV} | commit (${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA}) ${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE} by ${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME}`,
|
||||
description:
|
||||
process.env.NEXT_PUBLIC_VERCEL_ENV != undefined
|
||||
? `currently running in ${process.env.NEXT_PUBLIC_VERCEL_ENV} | commit (${(process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA as string).substring(0, 7)}}) "${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE}" by ${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME}`
|
||||
: "currently running in dev",
|
||||
};
|
||||
|
||||
export default function Home() {
|
||||
|
||||
11
src/app/sort/favorites/page.tsx
Normal file
11
src/app/sort/favorites/page.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import FavoriteSortView from "@/components/FavoritesSortView";
|
||||
|
||||
export default function FavoritesSort() {
|
||||
return (
|
||||
<main>
|
||||
<div className="pt-[60px] p-4">
|
||||
<FavoriteSortView />
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@ -167,6 +167,7 @@ export function OfflineServerCB() {
|
||||
const [customized, setCustomized] = useState(false);
|
||||
const [obj, setObj] = useState<ServerResponse | object>({});
|
||||
const [vb, setVB] = useState(false);
|
||||
const { isSignedIn } = useUser();
|
||||
const router = useRouter();
|
||||
const [starred, setStarred] = useState(false);
|
||||
const { resolvedTheme } = useTheme();
|
||||
@ -257,9 +258,10 @@ export function OfflineServerCB() {
|
||||
)}
|
||||
<CommandGroup heading="Server Actions">
|
||||
<CommandItem
|
||||
onSelect={() =>
|
||||
router.push("/server/" + (obj as ServerResponse).name + "/")
|
||||
}
|
||||
onSelect={() => {
|
||||
router.push("/server/" + (obj as ServerResponse).name + "/");
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<Server className="mr-2 h-4 w-4" />
|
||||
Open Server Page
|
||||
@ -271,6 +273,7 @@ export function OfflineServerCB() {
|
||||
toast.success("Done!");
|
||||
});
|
||||
}}
|
||||
disabled={!isSignedIn}
|
||||
>
|
||||
<Star
|
||||
className="mr-2 h-4 w-4"
|
||||
@ -285,11 +288,12 @@ export function OfflineServerCB() {
|
||||
{!starred ? "F" : "Unf"}avorite Server
|
||||
</CommandItem>
|
||||
<CommandItem
|
||||
onSelect={() =>
|
||||
onSelect={() => {
|
||||
router.push(
|
||||
"/server/" + (obj as ServerResponse).name + "/statistics"
|
||||
)
|
||||
}
|
||||
);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<Database className="mr-2 h-4 w-4" />
|
||||
See Statistics
|
||||
@ -330,6 +334,7 @@ export function ServerCommandBar() {
|
||||
const [vb, setVB] = useState(false);
|
||||
const router = useRouter();
|
||||
const { resolvedTheme } = useTheme();
|
||||
const { isSignedIn } = useUser();
|
||||
const [owned, setOwned] = useState(false);
|
||||
const [starred, setStarred] = useState(false);
|
||||
const [serverSingle, setSingle] = useState<ServerSingle>(
|
||||
@ -406,7 +411,10 @@ export function ServerCommandBar() {
|
||||
|
||||
<CommandGroup heading="Server Actions">
|
||||
<CommandItem
|
||||
onSelect={() => router.push("/server/" + serverName + "/")}
|
||||
onSelect={() => {
|
||||
router.push("/server/" + serverName + "/");
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<Server className="mr-2 h-4 w-4" />
|
||||
Open Server Page
|
||||
@ -418,6 +426,7 @@ export function ServerCommandBar() {
|
||||
toast.success("Done!");
|
||||
});
|
||||
}}
|
||||
disabled={!isSignedIn}
|
||||
>
|
||||
<Star
|
||||
className="mr-2 h-4 w-4"
|
||||
@ -432,9 +441,10 @@ export function ServerCommandBar() {
|
||||
{!starred ? "F" : "Unf"}avorite Server
|
||||
</CommandItem>
|
||||
<CommandItem
|
||||
onSelect={() =>
|
||||
router.push("/server/" + serverName + "/statistics")
|
||||
}
|
||||
onSelect={() => {
|
||||
router.push("/server/" + serverName + "/statistics");
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<Database className="mr-2 h-4 w-4" />
|
||||
See Statistics
|
||||
@ -509,11 +519,14 @@ export function CommandBar() {
|
||||
+Shift+K
|
||||
</CommandShortcut>
|
||||
</CommandItem>
|
||||
<CommandItem disabled>
|
||||
<CommandItem
|
||||
onSelect={() => {
|
||||
events.emit("cmd-event-sort");
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<ArrowDown01 className="mr-2 h-4 w-4" />
|
||||
<span>
|
||||
Sort Servers - <i>coming soon</i>
|
||||
</span>
|
||||
Sort Servers
|
||||
</CommandItem>
|
||||
<CommandItem
|
||||
onSelect={() => {
|
||||
@ -672,6 +685,49 @@ export function RandomServerDialog() {
|
||||
);
|
||||
}
|
||||
|
||||
export function SubSortCommandBar() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const router = useRouter();
|
||||
|
||||
useEffectOnce(() => {
|
||||
events.on("cmd-event-sort", () => {
|
||||
setOpen(true);
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<CommandDialog open={open} onOpenChange={setOpen}>
|
||||
<CommandInput placeholder="Type a command or search..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No results found.</CommandEmpty>
|
||||
|
||||
<CommandGroup heading="Sorts">
|
||||
<CommandItem
|
||||
onSelect={() => {
|
||||
router.push("/sort/favorites");
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<Star className="mr-2 h-4 w-4" />
|
||||
<span>Favorites</span>
|
||||
</CommandItem>
|
||||
</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>
|
||||
</CommandDialog>
|
||||
);
|
||||
}
|
||||
|
||||
export function SubLinkCommandBar() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const { resolvedTheme } = useTheme();
|
||||
@ -771,6 +827,7 @@ export function FavoriteBar() {
|
||||
key={c}
|
||||
onSelect={() => {
|
||||
router.push("/server/" + c);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
{c}
|
||||
@ -802,6 +859,7 @@ export function CommandBarer() {
|
||||
<CommandBar />
|
||||
<SearchCommandBar />
|
||||
<ServerCommandBar />
|
||||
<SubSortCommandBar />
|
||||
<OfflineServerCB />
|
||||
<RandomServerDialog />
|
||||
</>
|
||||
|
||||
267
src/components/FavoritesSortView.tsx
Normal file
267
src/components/FavoritesSortView.tsx
Normal file
@ -0,0 +1,267 @@
|
||||
"use client";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Spinner } from "./ui/spinner";
|
||||
import { sortedFavorites } from "@/lib/api";
|
||||
import InfiniteScroll from "react-infinite-scroll-component";
|
||||
import { Button } from "./ui/button";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
||||
import { RadioGroup, RadioGroupItem } from "./ui/radio-group";
|
||||
import { Label } from "./ui/label";
|
||||
import { useRouter } from "@/lib/useRouter";
|
||||
import { OnlineServer } from "@/lib/types/mh-server";
|
||||
import ServerCard from "./ServerCard";
|
||||
import { CircleUser, Copy, Info, Layers, Network } from "lucide-react";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
|
||||
import { BorderBeam } from "./effects/border-beam";
|
||||
import Stat from "./Stat";
|
||||
import { Separator } from "./ui/separator";
|
||||
import { Card, CardDescription, CardHeader, CardTitle } from "./ui/card";
|
||||
import Link from "next/link";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export default function FavoriteSortView() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [list, setList] = useState<{ server: string; favorites: number }[]>([]);
|
||||
const [allItems, setAllItems] = useState<
|
||||
Array<{ server: string; favorites: number }>
|
||||
>([]);
|
||||
const [upNumber, setUpNumber] = useState(0);
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
const [totalServers, setTotalServers] = useState(0);
|
||||
const [totalPlayers, setTotalPlayers] = useState(0);
|
||||
const [online, setOnline] = useState<any>({});
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
sortedFavorites().then((c) => {
|
||||
if (typeof c !== "boolean") {
|
||||
const slicedArray = c.slice(0, 20);
|
||||
setAllItems(slicedArray);
|
||||
setList(c);
|
||||
fetch("https://api.minehut.com/servers").then((b) =>
|
||||
b.json().then((c) => {
|
||||
c.servers.forEach((v: OnlineServer) =>
|
||||
setOnline(setManipulate(online, v.name, v))
|
||||
);
|
||||
setLoading(false);
|
||||
setTotalPlayers(c.total_players);
|
||||
setTotalServers(c.total_servers);
|
||||
})
|
||||
);
|
||||
|
||||
console.log(list);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return <Spinner className="flex items-center" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="max-lg:grid-cols-2 grid grid-cols-2 gap-4 ">
|
||||
<Stat
|
||||
title="Players online"
|
||||
desc={totalPlayers.toString()}
|
||||
icon={CircleUser}
|
||||
/>
|
||||
<Stat
|
||||
title={
|
||||
<div
|
||||
className={
|
||||
totalServers >= 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
|
||||
className={
|
||||
totalServers >= 3200
|
||||
? "bg-clip-text text-transparent bg-gradient-to-r from-cyan-500 to-blue-500 "
|
||||
: ""
|
||||
}
|
||||
>
|
||||
{totalServers.toString()}
|
||||
</div>
|
||||
{totalServers >= 3200 && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Info size={16} className="ml-2" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="font-normal">
|
||||
The server amount is over 3.2k, meaning that new servers
|
||||
have to go into a queue before being able to be online.{" "}
|
||||
<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}
|
||||
>
|
||||
{totalServers >= 3200 && (
|
||||
<BorderBeam
|
||||
size={135}
|
||||
duration={12}
|
||||
delay={9}
|
||||
colorFrom="rgb(6 182 212)"
|
||||
colorTo="rgb(59 130 246)"
|
||||
/>
|
||||
)}
|
||||
</Stat>
|
||||
</div>
|
||||
<br />
|
||||
<Separator />
|
||||
<br />
|
||||
<Popover>
|
||||
<PopoverTrigger>
|
||||
<Button className="ml-2" variant="secondary">
|
||||
Sort
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<RadioGroup
|
||||
defaultValue="option-two"
|
||||
onValueChange={() => router.push("/")}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="option-one" id="option-one" />
|
||||
<Label htmlFor="option-one">Online Players</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="option-two" id="option-two" />
|
||||
<Label htmlFor="option-two">Favorites</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="ml-2"
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
sortedFavorites().then((c) => {
|
||||
if (typeof c !== "boolean") {
|
||||
const slicedArray = c.slice(0, 20);
|
||||
setAllItems(slicedArray);
|
||||
setList(c);
|
||||
fetch("https://api.minehut.com/servers").then((b) =>
|
||||
b.json().then((c) => {
|
||||
c.servers.forEach((v: OnlineServer) =>
|
||||
setOnline(setManipulate(online, v.name, v))
|
||||
);
|
||||
setLoading(false);
|
||||
setTotalPlayers(c.total_players);
|
||||
setTotalServers(c.total_servers);
|
||||
})
|
||||
);
|
||||
|
||||
console.log(list);
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<InfiniteScroll
|
||||
dataLength={list.length}
|
||||
hasMore={hasMore}
|
||||
next={() => {
|
||||
const newUpNumber = upNumber + 1;
|
||||
const slicedArray = list.slice(
|
||||
newUpNumber * 20,
|
||||
newUpNumber * 20 + 20
|
||||
);
|
||||
console.log(slicedArray.length);
|
||||
|
||||
setAllItems((prev) => [...prev, ...slicedArray]);
|
||||
if (slicedArray.length !== 20) setHasMore(false);
|
||||
setUpNumber(newUpNumber);
|
||||
}}
|
||||
loader={<br />}
|
||||
endMessage={
|
||||
<p style={{ textAlign: "center" }}>
|
||||
<br />
|
||||
<strong>You've seen it all</strong>
|
||||
</p>
|
||||
}
|
||||
style={{ overflow: "hidden", paddingLeft: 6 }}
|
||||
>
|
||||
<div className="grid sm:grid-cols-4 gap-4">
|
||||
{allItems.map((v) => {
|
||||
if (v.favorites == 0) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
if (online[v.server] != undefined)
|
||||
return (
|
||||
<ServerCard mini b={online[v.server]} favs={v.favorites} />
|
||||
);
|
||||
else
|
||||
return (
|
||||
<Card className="h-[226px]">
|
||||
<CardHeader>
|
||||
<CardTitle>{v.server}</CardTitle>
|
||||
<CardDescription>
|
||||
{v.favorites} favorited
|
||||
<br />
|
||||
<Button
|
||||
size="icon"
|
||||
variant="secondary"
|
||||
className="min-w-[128px] max-w-[328px] h-[32px] mt-2 ml-2 max-md:hidden"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(
|
||||
v.server + ".mshf.minehut.gg"
|
||||
);
|
||||
toast.success("Copied IP to clipboard");
|
||||
}}
|
||||
>
|
||||
<Copy size={18} />
|
||||
<code className="ml-2">{v.server}</code>
|
||||
</Button>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Link href={"/server/" + v.server}>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="secondary"
|
||||
className="w-[32px] h-[32px] mt-2 ml-2 max-md:hidden"
|
||||
>
|
||||
<Layers size={18} />
|
||||
</Button>
|
||||
</Link>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
Open up the server page to see more information about
|
||||
the server
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</InfiniteScroll>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function setManipulate(object: any, change: string, value: any) {
|
||||
let newObject = object;
|
||||
newObject[change] = value;
|
||||
return newObject;
|
||||
}
|
||||
@ -5,7 +5,7 @@ import {
|
||||
ContextMenuContent,
|
||||
ContextMenuSeparator,
|
||||
} from "@/components/ui/context-menu";
|
||||
import toast from "react-hot-toast";
|
||||
import toast, { LoaderIcon } from "react-hot-toast";
|
||||
import {
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
@ -15,7 +15,7 @@ import {
|
||||
} from "./ui/card";
|
||||
import IconDisplay from "./IconDisplay";
|
||||
import { TagShower } from "./ServerList";
|
||||
import { Copy, EllipsisVertical, Layers } from "lucide-react";
|
||||
import { Copy, EllipsisVertical, Layers, Star } from "lucide-react";
|
||||
import { Button } from "./ui/button";
|
||||
import {
|
||||
Drawer,
|
||||
@ -29,15 +29,35 @@ import { Tooltip } from "@radix-ui/react-tooltip";
|
||||
import { TooltipContent, TooltipTrigger } from "./ui/tooltip";
|
||||
import { useRouter } from "@/lib/useRouter";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { favoriteServer, isFavorited } from "@/lib/api";
|
||||
import { useUser } from "@clerk/nextjs";
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
export default function ServerCard({ b, motd }: any) {
|
||||
export default function ServerCard({ b, motd, mini, favs }: any) {
|
||||
const router = useRouter();
|
||||
const [favoriteStar, setFavoriteStar] = useState(false);
|
||||
const [favoriteLoading, setFavoriteLoading] = useState(true);
|
||||
const { isSignedIn } = useUser();
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
||||
return (
|
||||
<ContextMenu>
|
||||
<ContextMenu
|
||||
onOpenChange={(open) => {
|
||||
if (open && isSignedIn)
|
||||
isFavorited(b.name).then((c) => {
|
||||
setFavoriteStar(c);
|
||||
setFavoriteLoading(false);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<ContextMenuTrigger>
|
||||
<Card
|
||||
key={b.name}
|
||||
className="min-h-[450px] max-h-[450px] mb-4 flex items-start"
|
||||
className={
|
||||
(!mini ? "min-h-[450px] max-h-[450px]" : "") +
|
||||
" mb-4 flex items-start"
|
||||
}
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle className="m-0">
|
||||
@ -115,7 +135,7 @@ export default function ServerCard({ b, motd }: any) {
|
||||
<span className="pl-1">
|
||||
{b.playerData.playerCount}{" "}
|
||||
{b.playerData.playerCount == 1 ? "player" : "players"}{" "}
|
||||
currently online
|
||||
currently online {favs && <>• {favs} favorited</>}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@ -177,10 +197,12 @@ export default function ServerCard({ b, motd }: any) {
|
||||
</ContextMenu>
|
||||
</CardDescription>
|
||||
<CardContent>
|
||||
<span
|
||||
dangerouslySetInnerHTML={{ __html: motd }}
|
||||
className="w-[30px] text-center break-all overflow-hidden"
|
||||
/>
|
||||
{motd && (
|
||||
<span
|
||||
dangerouslySetInnerHTML={{ __html: motd }}
|
||||
className="w-[30px] text-center break-all overflow-hidden"
|
||||
/>
|
||||
)}
|
||||
</CardContent>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
@ -193,9 +215,6 @@ export default function ServerCard({ b, motd }: any) {
|
||||
}}
|
||||
>
|
||||
Copy server IP
|
||||
<div className="RightSlot">
|
||||
<Copy size={18} />
|
||||
</div>
|
||||
</ContextMenuItem>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuItem
|
||||
@ -205,6 +224,40 @@ export default function ServerCard({ b, motd }: any) {
|
||||
>
|
||||
Open server page
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem
|
||||
onClick={() => {
|
||||
router.push("/server/" + b.name + "/statistics");
|
||||
}}
|
||||
>
|
||||
Open statistics page
|
||||
</ContextMenuItem>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuItem
|
||||
onClick={() => {
|
||||
setFavoriteLoading(true);
|
||||
favoriteServer(b.name).then(() => {
|
||||
setFavoriteLoading(false);
|
||||
setFavoriteStar(!favoriteStar);
|
||||
});
|
||||
}}
|
||||
disabled={!isSignedIn || favoriteLoading}
|
||||
>
|
||||
{!favoriteLoading && (
|
||||
<Star
|
||||
size={16}
|
||||
className="mr-2 text-white"
|
||||
fill={
|
||||
favoriteStar
|
||||
? resolvedTheme == "dark"
|
||||
? "white"
|
||||
: "black"
|
||||
: "transparent"
|
||||
}
|
||||
/>
|
||||
)}{" "}
|
||||
{favoriteLoading && <LoaderIcon className="mr-2" />}
|
||||
{favoriteStar && isSignedIn ? "Unf" : "F"}avorite Server
|
||||
</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
);
|
||||
|
||||
@ -37,6 +37,8 @@ import { useEffectOnce } from "@/lib/useEffectOnce";
|
||||
import ServerCard from "./ServerCard";
|
||||
import events from "@/lib/commandEvent";
|
||||
import { BorderBeam } from "@/components/effects/border-beam";
|
||||
import { Label } from "./ui/label";
|
||||
import { useRouter } from "@/lib/useRouter";
|
||||
|
||||
export default function ServerList() {
|
||||
const [loading, setLoading]: any = useState(true);
|
||||
@ -59,6 +61,7 @@ export default function ServerList() {
|
||||
const [nameFilters, setNameFilters] = useState<any>({});
|
||||
const [inErrState, setErrState] = useState(false);
|
||||
const [servers, setServers] = useState<Array<OnlineServer>>([]);
|
||||
const router = useRouter();
|
||||
const [filters, setFilters] = useState<
|
||||
Array<(server: OnlineServer) => Promise<boolean>>
|
||||
>([]);
|
||||
@ -125,7 +128,7 @@ export default function ServerList() {
|
||||
title="Players online"
|
||||
desc={serverList.getExtraData().total_players.toString()}
|
||||
icon={CircleUser}
|
||||
></Stat>
|
||||
/>
|
||||
<Stat
|
||||
title={
|
||||
<div
|
||||
@ -522,6 +525,28 @@ export default function ServerList() {
|
||||
))}
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<Popover>
|
||||
<PopoverTrigger>
|
||||
<Button className="ml-2" variant="secondary">
|
||||
Sort
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<RadioGroup
|
||||
defaultValue="option-one"
|
||||
onValueChange={() => router.push("/sort/favorites")}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="option-one" id="option-one" />
|
||||
<Label htmlFor="option-one">Online Players</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="option-two" id="option-two" />
|
||||
<Label htmlFor="option-two">Favorites</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="md:ml-3 "
|
||||
@ -629,7 +654,7 @@ export default function ServerList() {
|
||||
<br />
|
||||
<br />
|
||||
<code className="border p-3 rounded">
|
||||
{randomData.name}.mshf.minehut.gg{" "}
|
||||
{randomData.name}.minehut.gg{" "}
|
||||
<Button
|
||||
size="icon"
|
||||
className="ml-1 h-[20px]"
|
||||
|
||||
@ -84,6 +84,16 @@ export default function TextFromPathname() {
|
||||
</BreadcrumbItem>
|
||||
</>
|
||||
)}
|
||||
{pathname == "/sort/favorites" && (
|
||||
<>
|
||||
<BreadcrumbSeparator className="max-sm:hidden" />
|
||||
<BreadcrumbItem>Sort</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>Favorites</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,27 +1,27 @@
|
||||
"use client"
|
||||
"use client";
|
||||
|
||||
import * as React from "react"
|
||||
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"
|
||||
import { Check, ChevronRight, Circle } from "lucide-react"
|
||||
import * as React from "react";
|
||||
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
|
||||
import { Check, ChevronRight, Circle } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const ContextMenu = ContextMenuPrimitive.Root
|
||||
const ContextMenu = ContextMenuPrimitive.Root;
|
||||
|
||||
const ContextMenuTrigger = ContextMenuPrimitive.Trigger
|
||||
const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
|
||||
|
||||
const ContextMenuGroup = ContextMenuPrimitive.Group
|
||||
const ContextMenuGroup = ContextMenuPrimitive.Group;
|
||||
|
||||
const ContextMenuPortal = ContextMenuPrimitive.Portal
|
||||
const ContextMenuPortal = ContextMenuPrimitive.Portal;
|
||||
|
||||
const ContextMenuSub = ContextMenuPrimitive.Sub
|
||||
const ContextMenuSub = ContextMenuPrimitive.Sub;
|
||||
|
||||
const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup
|
||||
const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
|
||||
|
||||
const ContextMenuSubTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
|
||||
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, children, ...props }, ref) => (
|
||||
<ContextMenuPrimitive.SubTrigger
|
||||
@ -36,8 +36,8 @@ const ContextMenuSubTrigger = React.forwardRef<
|
||||
{children}
|
||||
<ChevronRight className="ml-auto h-4 w-4" />
|
||||
</ContextMenuPrimitive.SubTrigger>
|
||||
))
|
||||
ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName
|
||||
));
|
||||
ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
|
||||
|
||||
const ContextMenuSubContent = React.forwardRef<
|
||||
React.ElementRef<typeof ContextMenuPrimitive.SubContent>,
|
||||
@ -51,8 +51,8 @@ const ContextMenuSubContent = React.forwardRef<
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName
|
||||
));
|
||||
ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
|
||||
|
||||
const ContextMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof ContextMenuPrimitive.Content>,
|
||||
@ -62,19 +62,19 @@ const ContextMenuContent = React.forwardRef<
|
||||
<ContextMenuPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-80 data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</ContextMenuPrimitive.Portal>
|
||||
))
|
||||
ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName
|
||||
));
|
||||
ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
|
||||
|
||||
const ContextMenuItem = React.forwardRef<
|
||||
React.ElementRef<typeof ContextMenuPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
|
||||
inset?: boolean
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<ContextMenuPrimitive.Item
|
||||
@ -86,8 +86,8 @@ const ContextMenuItem = React.forwardRef<
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName
|
||||
));
|
||||
ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
|
||||
|
||||
const ContextMenuCheckboxItem = React.forwardRef<
|
||||
React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>,
|
||||
@ -109,9 +109,9 @@ const ContextMenuCheckboxItem = React.forwardRef<
|
||||
</span>
|
||||
{children}
|
||||
</ContextMenuPrimitive.CheckboxItem>
|
||||
))
|
||||
));
|
||||
ContextMenuCheckboxItem.displayName =
|
||||
ContextMenuPrimitive.CheckboxItem.displayName
|
||||
ContextMenuPrimitive.CheckboxItem.displayName;
|
||||
|
||||
const ContextMenuRadioItem = React.forwardRef<
|
||||
React.ElementRef<typeof ContextMenuPrimitive.RadioItem>,
|
||||
@ -132,13 +132,13 @@ const ContextMenuRadioItem = React.forwardRef<
|
||||
</span>
|
||||
{children}
|
||||
</ContextMenuPrimitive.RadioItem>
|
||||
))
|
||||
ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName
|
||||
));
|
||||
ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
|
||||
|
||||
const ContextMenuLabel = React.forwardRef<
|
||||
React.ElementRef<typeof ContextMenuPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
|
||||
inset?: boolean
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<ContextMenuPrimitive.Label
|
||||
@ -150,8 +150,8 @@ const ContextMenuLabel = React.forwardRef<
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName
|
||||
));
|
||||
ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
|
||||
|
||||
const ContextMenuSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof ContextMenuPrimitive.Separator>,
|
||||
@ -162,8 +162,8 @@ const ContextMenuSeparator = React.forwardRef<
|
||||
className={cn("-mx-1 my-1 h-px bg-border", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName
|
||||
));
|
||||
ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName;
|
||||
|
||||
const ContextMenuShortcut = ({
|
||||
className,
|
||||
@ -177,9 +177,9 @@ const ContextMenuShortcut = ({
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
ContextMenuShortcut.displayName = "ContextMenuShortcut"
|
||||
);
|
||||
};
|
||||
ContextMenuShortcut.displayName = "ContextMenuShortcut";
|
||||
|
||||
export {
|
||||
ContextMenu,
|
||||
@ -197,4 +197,4 @@ export {
|
||||
ContextMenuSubContent,
|
||||
ContextMenuSubTrigger,
|
||||
ContextMenuRadioGroup,
|
||||
}
|
||||
};
|
||||
|
||||
@ -30,12 +30,12 @@ export async function increaseNum(client: MongoClient, server: string) {
|
||||
const find = await collection.find({ server: server }).toArray();
|
||||
|
||||
if (find.length == 0) {
|
||||
collection.insertOne({ server: server, favorites: 1 });
|
||||
collection.insertOne({ server: server, favorites: 1, date: new Date() });
|
||||
} else {
|
||||
const entry = find[0];
|
||||
collection.findOneAndReplace(
|
||||
{ server: server },
|
||||
{ server: server, favorites: entry.favorites + 1 }
|
||||
{ server: server, favorites: entry.favorites + 1, date: new Date() }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
20
src/pages/api/v0/sorting/favorites.ts
Normal file
20
src/pages/api/v0/sorting/favorites.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { MongoClient } from "mongodb";
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("mhsf");
|
||||
const collection = db.collection("meta");
|
||||
|
||||
const all = await collection.find().toArray();
|
||||
const sorted = all.sort((a, b) => a.favorites - b.favorites);
|
||||
sorted.reverse();
|
||||
res.send({ results: sorted });
|
||||
|
||||
client.close();
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
export const version = "b-0.8.0";
|
||||
export const version = "b-0.9.0";
|
||||
|
||||
const User = ({ user }: { user: string }) => (
|
||||
<span className="cursor-pointer bg-[rgba(255,165,0,0.25);] rounded p-[2.5px]">
|
||||
@ -44,6 +44,25 @@ export const Changelog = () => (
|
||||
</code>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version b-0.9.0 (August 15th 2024)
|
||||
</strong>
|
||||
<ul>
|
||||
<li>• Adding favorites sorting option</li>
|
||||
<li>• Fixed right-click context menu on the server list</li>
|
||||
<li>• Fixed metadata bugs</li>
|
||||
</ul>
|
||||
<br />
|
||||
<i>
|
||||
Hey! Update on statistics. Recently, we have figured out the Minehut API
|
||||
is blocked to Vercel servers (atleast the <code>/servers</code>{" "}
|
||||
endpoint). I'm actively trying to find a loop-hole so that statistics
|
||||
works correctly. Thank you {":)"}
|
||||
</i>
|
||||
<br />
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version b-0.8.0 (August 11th 2024)
|
||||
@ -135,7 +154,13 @@ export const Changelog = () => (
|
||||
<div>
|
||||
<strong>All developers that helped out:</strong>
|
||||
<Link href="https://dvelo.vercel.app">
|
||||
<Image src="/imgs/badge1.png" alt="cool badge" width={88} height={31} />
|
||||
<Image
|
||||
src="/imgs/badge1.png"
|
||||
alt="cool badge"
|
||||
width={88}
|
||||
height={31}
|
||||
className="w-[88px] h-[31px]"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
</>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user