Files
games/packages/client/src/components/Table.tsx
2025-08-22 00:19:40 -04:00

113 lines
2.7 KiB
TypeScript

import {
Accessor,
createContext,
createEffect,
createResource,
createSignal,
For,
onCleanup,
Resource,
Show,
untrack,
} from "solid-js";
import { createStore } from "solid-js/store";
import { SimplePlayerView } from "../../../server/src/games/simple";
import api, { me } from "../api";
import { ApiType } from "../fn";
import Game from "./Game";
const [playerProfiles, setPlayerProfiles] = createStore<
Record<string, Resource<ApiType<typeof api.profile.get>>>
>({});
export const TableContext = createContext<{
players: Accessor<string[]>;
view: Accessor<SimplePlayerView | undefined>;
// submitAction: (action: Action) => Promise<any>;
}>();
export default (props: { tableKey: string }) => {
const [players, setPlayers] = createSignal<string[]>([]);
const [view, setView] = createSignal<SimplePlayerView>();
const ws = api.ws(props).subscribe();
onCleanup(() => ws.close());
ws.on("message", (evt) => {
if (evt.data.players) {
setPlayers(evt.data.players);
}
if (evt.data.view) {
setView(evt.data.view);
}
});
createEffect(() => {
players().forEach((player) => {
if (!untrack(() => playerProfiles[player])) {
const [playerProfile] = createResource(() =>
api.profile
.get({ query: { otherHumanKey: player } })
.then((r) => r.data)
);
setPlayerProfiles((prev) => ({
...prev,
[player]: playerProfile,
}));
}
});
});
return (
<TableContext.Provider value={{ view, players }}>
<div class="flex justify-around p-t-10">
<For each={players().filter((p) => p != me())}>
{(player, i) => {
const verticalOffset = () => {
const N = players().length - 1;
const x = Math.abs((2 * i() + 1) / (N * 2) - 0.5);
const y = Math.sqrt(1 - x * x);
return 1 - y;
};
return (
<div
style={{
transform: `translate(0, ${
verticalOffset() * 150
}vh)`,
}}
class="w-20 h-20 rounded-full bg-red-900 flex justify-center items-center"
>
<p style={{ "font-size": "1em" }}>
{playerProfiles[player]?.()?.name}
</p>
</div>
);
}}
</For>
</div>
<div
id="table"
class="fixed bg-radial from-orange-950 to-stone-950 border-neutral-950 border-4 top-40 bottom-20 left-10 right-10 shadow-lg"
style={{
"border-radius": "50% 50%",
}}
>
<Show when={view() == null}>
<div class="absolute center">
<button
onClick={() => ws.send({ startGame: true })}
class="button p-1 "
>
Start Game!
</button>
</div>
</Show>
</div>
<Show when={view() != null}>
<Game tableKey={props.tableKey} />
</Show>
</TableContext.Provider>
);
};