|
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