|
1 | 1 | <script lang="ts">
|
2 | 2 | import { browser } from "$app/env";
|
| 3 | + import { keyBy } from "lodash"; |
| 4 | + import { elapsedSeconds } from "@bgs/utils"; |
| 5 | + import { timerTime, oneLineMarked, handleError, confirm, duration, shortDuration } from "@/utils"; |
| 6 | + import type { PlayerInfo } from "@bgs/types"; |
| 7 | + import Portal from "@/modules/portal"; |
| 8 | + import clockHistory from "@iconify/icons-bi/clock-history.js"; |
| 9 | + import { Button, Icon, Badge } from "@/modules/cdk"; |
| 10 | + import { getContext, onDestroy } from "svelte"; |
| 11 | + import { GameLog, ReplayControls, GameNotes, GamePreferences, GameSettings } from "./GameSidebar"; |
| 12 | + import type { GameContext } from "@/routes/game/[gameId].svelte"; |
| 13 | + import PlayerGameAvatar from "./PlayerGameAvatar.svelte"; |
| 14 | + import { useRest } from "@/composition/useRest"; |
3 | 15 | import { useAccount } from "@/composition/useAccount";
|
4 |
| - import { useActiveGames } from "@/composition/useActiveGames"; |
5 | 16 | import { useCurrentGame } from "@/composition/useCurrentGame";
|
| 17 | + import { useActiveGames } from "@/composition/useActiveGames"; |
6 | 18 | import { useDeveloperSettings } from "@/composition/useDeveloperSettings";
|
7 |
| - import { useRest } from "@/composition/useRest"; |
8 |
| - import type { GameContext } from "@/pages/Game.svelte"; |
9 |
| - import { confirm, handleError } from "@/utils"; |
10 |
| - import type { PlayerInfo } from "@bgs/types"; |
11 |
| - import { elapsedSeconds } from "@bgs/utils"; |
12 |
| - import { keyBy } from "lodash"; |
13 |
| - import { getContext, onDestroy } from "svelte"; |
| 19 | +
|
14 | 20 | const { game, players, gameInfo }: GameContext = getContext("game");
|
15 | 21 | const { post } = useRest();
|
| 22 | +
|
16 | 23 | const { account } = useAccount();
|
17 | 24 | const { playerStatus } = useCurrentGame();
|
18 | 25 | const { addActiveGame, removeActiveGame } = useActiveGames();
|
19 | 26 | const { devGameSettings } = useDeveloperSettings();
|
| 27 | +
|
20 | 28 | let secondsCounter = 0;
|
| 29 | +
|
21 | 30 | const interval = setInterval(() => {
|
22 | 31 | if (browser && !document.hidden) {
|
23 | 32 | secondsCounter += 1;
|
24 | 33 | }
|
25 | 34 | }, 1000);
|
26 | 35 | onDestroy(() => clearInterval(interval));
|
| 36 | +
|
27 | 37 | let requestedDrop: Record<string, boolean> = {};
|
| 38 | +
|
28 | 39 | $: userId = $account?._id;
|
29 | 40 | $: playerUser = $game?.players.find((pl) => pl._id === userId);
|
30 | 41 | $: gameId = $game?._id;
|
| 42 | +
|
31 | 43 | function status(playerId: string) {
|
32 | 44 | return $playerStatus?.find((pl) => pl._id === playerId)?.status ?? "offline";
|
33 | 45 | }
|
| 46 | +
|
34 | 47 | function playerElo(playerId: string) {
|
35 | 48 | return $players.find((pl) => pl._id === playerId)?.elo ?? 0;
|
36 | 49 | }
|
| 50 | +
|
37 | 51 | $: alwaysActive = $game?.options.timing.timer?.start === $game?.options.timing.timer?.end;
|
| 52 | +
|
38 | 53 | $: currentPlayersById = keyBy($game?.currentPlayers ?? [], "_id");
|
| 54 | +
|
39 | 55 | function isCurrentPlayer(id: string) {
|
40 | 56 | return $game?.status !== "ended" && !!currentPlayersById[id];
|
41 | 57 | }
|
| 58 | +
|
42 | 59 | const onGameChanged = () => {
|
43 | 60 | if (userId) {
|
44 | 61 | if (isCurrentPlayer(userId)) {
|
|
48 | 65 | }
|
49 | 66 | }
|
50 | 67 | };
|
| 68 | +
|
51 | 69 | $: onGameChanged(), [userId, $game];
|
| 70 | +
|
52 | 71 | let remainingTimes: Record<string, number> = {};
|
| 72 | +
|
53 | 73 | function updateRemainingTimes() {
|
54 | 74 | const ret: Record<string, number> = {};
|
55 | 75 | for (const player of $game.players) {
|
56 | 76 | ret[player._id] = remainingTime(player);
|
57 | 77 | }
|
| 78 | +
|
58 | 79 | remainingTimes = ret;
|
59 | 80 | }
|
| 81 | +
|
60 | 82 | $: updateRemainingTimes(), [secondsCounter];
|
| 83 | +
|
61 | 84 | function remainingTime(player: PlayerInfo) {
|
62 | 85 | const currentPlayer = currentPlayersById[player._id];
|
63 | 86 | if (currentPlayer) {
|
64 | 87 | const spent = elapsedSeconds(new Date(currentPlayer.timerStart as any), $game.options.timing.timer);
|
65 |
| - of $game.players) { |
66 |
| - ret[ |
| 88 | + // Trick to update every second |
67 | 89 | return Math.max(player.remainingTime - spent, 0) + (secondsCounter % 1);
|
68 | 90 | }
|
69 | 91 | return Math.max(player.remainingTime, 0);
|
70 | 92 | }
|
| 93 | +
|
71 | 94 | async function voteCancel() {
|
72 | 95 | if (
|
73 | 96 | await confirm("This vote cannot be taken back. If all active players vote to cancel, the game will be cancelled.")
|
74 | 97 | ) {
|
75 | 98 | await post(`/game/${gameId}/cancel`).catch(handleError);
|
76 | 99 | }
|
77 | 100 | }
|
| 101 | +
|
78 | 102 | async function quit() {
|
79 | 103 | await post(`/game/${gameId}/quit`).catch(handleError);
|
80 | 104 | }
|
| 105 | +
|
81 | 106 | async function requestDrop(playerId: string) {
|
82 | 107 | await post(`/game/${gameId}/drop/${playerId}`).then(
|
83 | 108 | () => (requestedDrop = { ...requestedDrop, [playerId]: true }),
|
|
86 | 111 | }
|
87 | 112 | </script>
|
88 | 113 |
|
89 |
| -<div id="floating-controls" /> |
| 114 | +<div id="floating-controls"></div> |
90 | 115 | <Portal target="#sidebar">
|
91 | 116 | <h3 class="mt-75">Players</h3>
|
92 | 117 | {#each $game.players as player}
|
|
0 commit comments