Skip to content

Trim the live controls, and let the screen say more about the match - #76

Merged
JaspervdM80 merged 4 commits into
mainfrom
claude/live-match-ui-improvements-ucmcd3
Aug 11, 2026
Merged

Trim the live controls, and let the screen say more about the match#76
JaspervdM80 merged 4 commits into
mainfrom
claude/live-match-ui-improvements-ucmcd3

Conversation

@JaspervdM80

Copy link
Copy Markdown
Owner

Six things asked for from the touchline, all on /games/{id}/live.

The pause button is gone: a youth match is not paused, and it was one more
thing to hit by accident. PauseClockAsync stays in the service, and "Resume"
now appears only for a live period whose clock is somehow stopped — without
it, a match paused before this change could never be run to full time.

Every goal on the timeline now carries the score it made it, in the
scoreboard's order. ScoreProgressionReport counts forwards over the whole
match and the page looks each goal up by id, because the timeline runs newest
first and a total accumulated while rendering would count down. The rule for
which end a goal lands on moves onto GameGoal.CountsForUs, so the running
total and Game.CountOurGoals cannot disagree.

Tapping a player now offers two changes, one dropdown each: someone comes on
for them, or they trade positions with a team-mate who stays on. Choosing in
either list clears the other, so the single button always has exactly one
change to make and says which. The swap is a new MatchSubstitutionService
method that writes no GameSubstitution — nobody left the pitch, and a row
there would say they did.

"Opstelling wisselen" is now "Volgende opstelling": it brings the next
quarter's planned line-up on without stopping the clock, which is what the
"Changes at half-way" card above it is listing.

The control buttons fill their panel again. .live-control-row > * was in
the page's scoped CSS and matched nothing — every child is a MudButton, and
the MudBlazor renders carries no scope attribute. It moves to
app.css as a two-column grid, with the odd one out spanning the row.

And a "Show subs" checkbox folds the bench strip away.

claude added 4 commits August 11, 2026 16:00
Six things asked for from the touchline, all on /games/{id}/live.

The pause button is gone: a youth match is not paused, and it was one more
thing to hit by accident. PauseClockAsync stays in the service, and "Resume"
now appears only for a live period whose clock is somehow stopped — without
it, a match paused before this change could never be run to full time.

Every goal on the timeline now carries the score it made it, in the
scoreboard's order. ScoreProgressionReport counts forwards over the whole
match and the page looks each goal up by id, because the timeline runs newest
first and a total accumulated while rendering would count down. The rule for
which end a goal lands on moves onto GameGoal.CountsForUs, so the running
total and Game.CountOurGoals cannot disagree.

Tapping a player now offers two changes, one dropdown each: someone comes on
for them, or they trade positions with a team-mate who stays on. Choosing in
either list clears the other, so the single button always has exactly one
change to make and says which. The swap is a new MatchSubstitutionService
method that writes no GameSubstitution — nobody left the pitch, and a row
there would say they did.

"Opstelling wisselen" is now "Volgende opstelling": it brings the next
quarter's planned line-up on without stopping the clock, which is what the
"Changes at half-way" card above it is listing.

The control buttons fill their panel again. `.live-control-row > *` was in
the page's scoped CSS and matched nothing — every child is a MudButton, and
the <button> MudBlazor renders carries no scope attribute. It moves to
app.css as a two-column grid, with the odd one out spanning the row.

And a "Show subs" checkbox folds the bench strip away.
No game is ever paused, so PauseClockAsync and ResumeClockAsync go with the
buttons — service, tests, error messages and resource keys. The clock now
runs from kick-off until the period is whistled off, and only a period
boundary stops it, which is one fewer way for the seconds a season's minutes
are built from to be wrong.

That makes "a live period" and "a running clock" the same thing, so the
status chip's paused state was only ever the break between two periods.
Renamed to .live-status-break, and the unreachable "Paused" label is gone.

The one clock test left in that section now says what the rule is rather
than testing a stoppage that cannot happen; ending a period and starting the
next already cover BankClock's arithmetic.
Advancing a period is the one control on the live screen with no way back —
it rewrites who is on the pitch and no timeline event records it — and what
it was about to do could only be read off a card further down the screen. It
now opens a dialog that lists the swaps and the position moves, says the
clock keeps running, and offers Cancel or Next line-up.

The card and the dialog show the same thing, so the markup moves into
PlannedChangesList and takes its styling with it. Scoped CSS follows the file
that owns the elements: leaving the .live-planned-* rules in LiveMatch.razor.css
would have left both callers unstyled, which is the trap this repository has
walked into twice.

createMatch can pick the game split, because quarters is the only shape whose
live screen offers this at all.
…ion rows

A swap moves two players between slots and writes nothing down, which is
right — nobody left the pitch. But two readers took the rows for the whole
story.

RemoveSubstitutionAsync handed back the slot the substitution recorded. Sub
into slot 5, swap that player to slot 0, then undo, and two players ended up
in slot 5 with slot 0 empty — the exact "an older swap fights every change
made on that slot since" the guard above it exists to prevent, arriving by a
route the guard cannot see. It now reads the slot and position off the player
coming off, which is a no-op when nothing moved them.

GameMinutesReport seeds from the lineup as it finally stands, so a swap
credits the position moved *into* for the whole period, earlier minutes
included. Both comments claimed the opposite. Corrected, and pinned with a
test so the claim and the behaviour cannot drift apart again.

AdvancePeriodAsync leaves the clock anchor alone on purpose, which was safe
while pause existed to set it back. A row an older build left paused would
now roll on to the next line-up with the clock frozen — banking no minutes
for the rest of the half, while the new dialog promises it keeps running. It
restarts a stopped anchor, and does nothing to a game that was never paused.
@JaspervdM80
JaspervdM80 merged commit e018661 into main Aug 11, 2026
4 checks passed
@JaspervdM80
JaspervdM80 deleted the claude/live-match-ui-improvements-ucmcd3 branch August 11, 2026 22:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants