113 lines
2.7 KiB
TypeScript
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>
|
|
);
|
|
};
|