Release: Quarkus backend, Keycloak login, game history, clock, bot, tutorial and rematch #3

Merged
jhalitaksoy merged 88 commits from develop into main 2026-10-17 03:23:38 +03:00
2 changed files with 14 additions and 3 deletions
Showing only changes of commit 8671bb2553 - Show all commits

View File

@ -7,6 +7,7 @@ import {
DEFAULT_ANIMATION_INTERVAL, DEFAULT_ANIMATION_INTERVAL,
GameRecord, GameRecord,
getBoardAfterMove, getBoardAfterMove,
getClockAtMove,
getColorByBrightness, getColorByBrightness,
getPlayerDisplayName, getPlayerDisplayName,
PitAnimator, PitAnimator,
@ -28,6 +29,7 @@ import HeaderbarIcon from "../headerbar/HeaderbarIcon";
import HeaderbarTitle from "../headerbar/HeaderbarTitle"; import HeaderbarTitle from "../headerbar/HeaderbarTitle";
import ThemeSwitchMenu from "../headerbar/ThemeSwitchMenu"; import ThemeSwitchMenu from "../headerbar/ThemeSwitchMenu";
import PageContainer from "../PageContainer"; import PageContainer from "../PageContainer";
import GameClockView from "../GameClockView";
import Row from "../Row"; import Row from "../Row";
import MoveList from "./MoveList"; import MoveList from "./MoveList";
@ -149,6 +151,12 @@ const ReplayView: FunctionComponent<{ context: Context, theme: Theme, record: Ga
? [record.player1, stones[PLAYER1_STORE], record.player2, stones[PLAYER2_STORE]] ? [record.player1, stones[PLAYER1_STORE], record.player2, stones[PLAYER2_STORE]]
: [record.player2, stones[PLAYER2_STORE], record.player1, stones[PLAYER1_STORE]]; : [record.player2, stones[PLAYER2_STORE], record.player1, stones[PLAYER1_STORE]];
const atEnd = moveNumber >= total; const atEnd = moveNumber >= total;
// Each player's clock after the shown move; games saved before the game clock have none.
const clockTimes = getClockAtMove(record, moveNumber);
const renderClock = (player: PlayerSummary) => clockTimes && (
<GameClockView context={context} running={false}
timeMs={player === record.player1 ? clockTimes.player1Ms : clockTimes.player2Ms} />
);
const controlButton = (label: string, title: string, onClick: () => void, disabled = false) => ( const controlButton = (label: string, title: string, onClick: () => void, disabled = false) => (
<button className="control" title={title} aria-label={title} disabled={disabled} onClick={onClick} <button className="control" title={title} aria-label={title} disabled={disabled} onClick={onClick}
@ -183,7 +191,7 @@ const ReplayView: FunctionComponent<{ context: Context, theme: Theme, record: Ga
)} )}
</div> </div>
<div className="player" style={{ color: textColor }}>{`${name(topPlayer)} · ${topStore}`}</div> <div className="player" style={{ color: textColor }}>{`${name(topPlayer)} · ${topStore}`}{renderClock(topPlayer)}</div>
<BoardView <BoardView
game={createGameAtMove(baseGame, moves, moveNumber)} game={createGameAtMove(baseGame, moves, moveNumber)}
context={context} context={context}
@ -191,7 +199,7 @@ const ReplayView: FunctionComponent<{ context: Context, theme: Theme, record: Ga
boardViewModel={boardViewModel} boardViewModel={boardViewModel}
revert={revert} revert={revert}
onPitSelect={() => undefined} /> onPitSelect={() => undefined} />
<div className="player" style={{ color: textColor }}>{`${name(bottomPlayer)} · ${bottomStore}`}</div> <div className="player" style={{ color: textColor }}>{`${name(bottomPlayer)} · ${bottomStore}`}{renderClock(bottomPlayer)}</div>
<div className="controls" style={{ color: textColor }}> <div className="controls" style={{ color: textColor }}>
<div className="buttons"> <div className="buttons">
@ -248,6 +256,9 @@ const ReplayView: FunctionComponent<{ context: Context, theme: Theme, record: Ga
.player { .player {
margin: 6px 0; margin: 6px 0;
font-weight: bold; font-weight: bold;
display: flex;
align-items: center;
gap: 8px;
} }
.controls { .controls {
flex-shrink: 0; flex-shrink: 0;

View File

@ -15,7 +15,7 @@ Work through the tasks in order; each ends with its tests passing and a commit.
- [x] 5. **Web app, live game**: `useGameClock`, `GameClockView`, clocks in `GamePage` on desktop and mobile - [x] 5. **Web app, live game**: `useGameClock`, `GameClockView`, clocks in `GamePage` on desktop and mobile
layouts; remove the offline countdown, and with it `TURN_TIMEOUT_MS`, `INACTIVE_TIMEOUT_MS`, layouts; remove the offline countdown, and with it `TURN_TIMEOUT_MS`, `INACTIVE_TIMEOUT_MS`,
`UserConnectionInfo.offlineSince` and `OpponentOfflineCountdown` from core (the web app uses them until then). `UserConnectionInfo.offlineSince` and `OpponentOfflineCountdown` from core (the web app uses them until then).
- [ ] 6. **Web app, replay**: clocks in `ReplayView` from `getClockAtMove`. - [x] 6. **Web app, replay**: clocks in `ReplayView` from `getClockAtMove`.
- [ ] 7. **Mobile app**: clocks in `GameScreen`; type check and bundle. - [ ] 7. **Mobile app**: clocks in `GameScreen`; type check and bundle.
- [ ] 8. **End to end and docs**: Chrome tests from `design.md`; README, `AGENTS.md` and `TODO.md` (release the mobile - [ ] 8. **End to end and docs**: Chrome tests from `design.md`; README, `AGENTS.md` and `TODO.md` (release the mobile
update before or with the backend; the out of scope items); mark `game-history` R3.3 and R3.4 as replaced. update before or with the backend; the out of scope items); mark `game-history` R3.3 and R3.4 as replaced.