From 20359b7a8f77fcbbfd1641a7b49ad247c764488c Mon Sep 17 00:00:00 2001 From: Halit Aksoy Date: Fri, 16 Oct 2026 01:39:36 +0300 Subject: [PATCH] feat(frontend): show the game clocks in live games Both clocks sit next to the players' names and count down in server time; the running one is highlighted and turns red under 30 seconds. They replace the offline countdown, so core drops TURN_TIMEOUT_MS, INACTIVE_TIMEOUT_MS, offlineSince and its text. Long info texts wrap on phones. --- .../frontend/src/components/GameClockView.tsx | 40 +++++++++++++++++ apps/frontend/src/components/UserStatus.tsx | 7 ++- apps/frontend/src/hooks/useGameClock.ts | 27 ++++++++++++ apps/frontend/src/routes/GamePage.tsx | 43 +++++++------------ docs/specs/game-clock/tasks.md | 2 +- packages/core/src/localization/en.ts | 1 - packages/core/src/localization/tr.ts | 1 - packages/core/src/models/GameHistory.ts | 5 --- .../core/src/models/UserConnectionInfo.ts | 2 - 9 files changed, 88 insertions(+), 40 deletions(-) create mode 100644 apps/frontend/src/components/GameClockView.tsx create mode 100644 apps/frontend/src/hooks/useGameClock.ts diff --git a/apps/frontend/src/components/GameClockView.tsx b/apps/frontend/src/components/GameClockView.tsx new file mode 100644 index 0000000..72660ff --- /dev/null +++ b/apps/frontend/src/components/GameClockView.tsx @@ -0,0 +1,40 @@ +import * as React from 'react'; +import { FunctionComponent } from 'react'; +import { CLOCK_LOW_TIME_MS, formatClock, getColorByBrightness } from "@mancala/core"; +import { Context } from '../context/context'; + +// Readable on every theme; the themes have no warning color of their own. +const LOW_TIME_COLOR = "#b3261e"; +const LOW_TIME_TEXT_COLOR = "#ffffff"; + +// One player's clock: highlighted while it runs, in a warning color when the running clock is low. +const GameClockView: FunctionComponent<{ + context: Context, + timeMs: number, + running: boolean, +}> = ({ context, timeMs, running }) => { + const theme = context.themeManager.theme; + const lowTime = running && timeMs < CLOCK_LOW_TIME_MS; + const background = lowTime ? LOW_TIME_COLOR : running ? theme.playerTurnColor : "transparent"; + const color = lowTime ? LOW_TIME_TEXT_COLOR : running + ? getColorByBrightness(theme.playerTurnColor, theme.textColor, theme.textLightColor) + : getColorByBrightness(theme.background, theme.textColor, theme.textLightColor); + return ( + + {formatClock(timeMs)} + + + ); +} + +export default GameClockView; diff --git a/apps/frontend/src/components/UserStatus.tsx b/apps/frontend/src/components/UserStatus.tsx index c4f767c..a9ddc98 100644 --- a/apps/frontend/src/components/UserStatus.tsx +++ b/apps/frontend/src/components/UserStatus.tsx @@ -13,8 +13,10 @@ const UserStatus: FunctionComponent<{ user: User, layoutMode: LayoutMode, visible?: boolean, - style?: React.CSSProperties -}> = ({ context, user, layoutMode, visible, style }) => { + style?: React.CSSProperties, + // Shown after the online status, e.g. the player's clock. + accessory?: React.ReactNode, +}> = ({ context, user, layoutMode, visible, style, accessory }) => { if (visible === false) return <>; const { t } = useTranslation(); @@ -29,6 +31,7 @@ const UserStatus: FunctionComponent<{ {user.isAnonymous ? t("Anonymous") : user.name}
+ {accessory && <>{accessory}}