Dashboard: Stop repeating the same values on the charging card - #25
Merged
Conversation
The card stated the same numbers up to six times: the level appeared in the reading line, twice in a "40% -> 78%" row, as the curve's end label and twice more in the chart legend. On a device held at its limit every span collapsed to the same figure, so the card read as an echo of itself. The middle row is gone. Elapsed time moves into the card header beside the chevron via a new typed headerStatus on AmplyNavigationCard - the header floats its trailing content and reports only the title's height, so this costs no vertical space. It is a String rather than a composable slot on purpose: the header sits inside the card's own tap target, so interactive content there would have to opt out of the surface tap. A reserved minimum title width plus a weighted status keep long values (duration never rolls over into days, so "123h 59m" is reachable) from starving either the title or the chevron. The level range survives in the chart legend, stated as the session's range rather than the plotted curve's span. The live curve is a bounded recent window, so its own span quietly narrows from "40->80%" to "79->80%" while the charge is still the same charge. percentRangeLabel defaults to the curve's span, so an explicit null means "no range" instead of falling back to the window - which is how the hub teaser, whose headline already carries the range, keeps from stating it twice. The compact chart drops its end labels, and no longer describes those endpoints to screen readers either: its hosts render the current values one line above, and the labels come from the last recorded sample, which lags by a recorder tick. Dropping them also frees the right gutter for the curve. It also refuses to draw without real variation. A device at its limit produces flat series, and a zero-range series self-normalizes to the canvas midpoint, so flat lines stacked into what looked like a plotted trend but was really the "no range" fallback. Variation must come from an adjacent non-null pair, because the path breaks at nulls: [40, null, 41] is two one-point segments and draws nothing. Both live surfaces now share one elapsed clock. Its first tick is aligned to the next whole session-minute (a fixed minute delay starting at 2m59s would hold the curve back to 3m59s), and it reads the clock on composition rather than trusting a caller's captured snapshot, so a card scrolling back into a lazy list doesn't re-enter holding a stale time. Store screenshots are regenerated. Their fixture now pins "now" so a live session has a believable age, and carries a real CC/CV curve - three linear series self-normalize to identical shapes and drew on top of each other.
d4rken
marked this pull request as ready for review
July 26, 2026 07:45
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
The charging card on the dashboard kept saying the same thing over and over. On a phone parked at its charge limit it managed to state "80%" six separate times in one card, and the temperature three times.
Technical Context
Why the range is stated by the host, not the chart. The dashboard's live curve is a bounded recent window (300 samples decimated to 60), so its own span is not the session's.
StatsCurveChart.percentRangeLabeltherefore defaults to the plotted span but accepts an explicitnullmeaning "no range" — without that distinction, one legend entry would mean two different things depending on whether the host had data. The hub teaser passesnullbecause its headline already carries the range; the dashboard card passes the session range.Why
headerStatusis aString, not a slot. The header sits inside the card's own click target, so interactive content there would have to opt out of the surface tap; typing it as text makes that impossible rather than merely discouraged.AmplyCardHeaderfloats trailing content and reports only the title's height, which is why this costs nothing vertically. It gained a reserved minimum title width, and the status is weighted so the chevron is measured first —StatsFormat.durationnever rolls over into days, so123h 59mis reachable and would otherwise starve the title or the arrow.Why the curve gate needs adjacent variation.
LineChartbreaks its path at nulls, so[40, null, 41]is two one-point segments that draw nothing — "two distinct values somewhere" would have passed. And flat series matter because a zero-range series self-normalizes to the canvas midpoint: several of them stack into a convincing-looking trend that is really the "no range" fallback.Accessibility. Compact charts stop describing their endpoints too, not just hiding the labels — otherwise the same stale-by-one-tick contradiction just moves into the screen reader. The legend is real text and is already read aloud.
The shared clock. Both live surfaces now use one
rememberLiveElapsedMillis. Its first tick aligns to the next whole session-minute, because a fixed minute-long delay starting at 2m59s would hold the curve back until 3m59s. It also reads the clock on composition instead of trusting the caller's captured value, so a card scrolling back into the lazy list does not re-enter holding a stale timestamp.Screenshot fixtures.
DashboardScreengained an injectable clock so a fixture can render a live session at a believable age instead of "0m". The fixture curve was also made non-linear — three linear series self-normalize to identical shapes and drew exactly on top of each other.Review guidance
The states with no chart — the first three minutes of a session, a flat limit hold, null-gapped data — deliberately show no range at all. That is the accepted cost of reclaiming the row, and there is a test pinning it rather than leaving it implicit.
Draft: unit tests, lint and both assemblies pass, but this has not had a device pass yet.