Release: Quarkus backend, Keycloak login, game history, clock, bot, tutorial and rematch #3
@ -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;
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user