88 lines
2.7 KiB
TypeScript
88 lines
2.7 KiB
TypeScript
import { useEffect, useRef, useState } from "preact/hooks";
|
|
import { StatusManager } from "./statusManager.tsx";
|
|
import { IS_BROWSER } from "$fresh/runtime.ts";
|
|
import { PlayerData } from "../util/players.ts";
|
|
import { Button } from "../components/Button.tsx";
|
|
import { JSX } from "preact/jsx-runtime";
|
|
import { FunctionComponent } from "preact";
|
|
|
|
export function PlayerList(
|
|
{ whitelist, banlist, oplist }: {
|
|
whitelist: PlayerData[];
|
|
banlist: PlayerData[];
|
|
oplist: PlayerData[];
|
|
},
|
|
) {
|
|
const [players, setPlayers] = useState<PlayerData[]>([]);
|
|
|
|
useEffect(() => {
|
|
if (!IS_BROWSER) return;
|
|
configureEventSource();
|
|
}, []);
|
|
|
|
const eventSource = useRef<EventSource>();
|
|
|
|
const configureEventSource = () => {
|
|
if (eventSource.current?.OPEN) eventSource.current.close();
|
|
eventSource.current = new EventSource("/api/players");
|
|
eventSource.current.addEventListener("players", (e) => {
|
|
setPlayers(JSON.parse(e.data));
|
|
});
|
|
};
|
|
|
|
const followStatusManager = (action: string) => {
|
|
if (action === "started" || action === "restarted") {
|
|
configureEventSource();
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div class="flex flex-col gap-8">
|
|
<StatusManager onAction={followStatusManager} />
|
|
<div class="grid grid-cols-2 p-8 bg-black/50 rounded-3xl">
|
|
<h2 class="col-span-2 font-pixel text-xl">Active Players</h2>
|
|
{players.map((p) => <PlayerCard player={p} />)}
|
|
</div>
|
|
<div className="grid grid-cols-3 gap-8 rounded-3xl">
|
|
<div class="flex flex-col gap-4 bg-smoke p-4 rounded-3xl">
|
|
<h2 className="font-pixel text-xl">Whitelisted Players</h2>
|
|
{whitelist.map((p) => <PlayerCard player={p} />)}
|
|
</div>
|
|
<div class="flex flex-col gap-4 bg-smoke p-4 rounded-3xl">
|
|
<h2 className="font-pixel text-xl">Banned Players</h2>
|
|
{banlist.map((p) => (
|
|
<PlayerCard player={p}>
|
|
{!!p.banReason && <p>{p.banReason}</p>}
|
|
</PlayerCard>
|
|
))}
|
|
</div>
|
|
<div class="flex flex-col gap-4 bg-smoke p-4 rounded-3xl">
|
|
<h2 className="font-pixel text-xl">Ops</h2>
|
|
{oplist.map((p) => <PlayerCard player={p} />)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const PlayerCard: FunctionComponent<{ player: PlayerData }> = (
|
|
{ player, children },
|
|
) => {
|
|
return (
|
|
<div class="flex gap-4">
|
|
<img
|
|
class="w-24 rounded-md"
|
|
src={player.avatar}
|
|
alt={`${player.username}'s avatar`}
|
|
/>
|
|
<div>
|
|
<h3>{player.username}</h3>
|
|
<small class="opacity-50">UUID: {player.id}</small>
|
|
<div class="flex items-center gap-4">
|
|
{children}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|