Skip to content

today: open a session's detail from the Latest Workouts feed - #1701

Merged
ryanbr merged 1 commit into
mainfrom
workout-tiles-tappable
Aug 28, 2026
Merged

ryanbr merged 1 commit into
mainfrom
workout-tiles-tappable

Conversation

@ryanbr

@ryanbr ryanbr commented Aug 28, 2026

Copy link
Copy Markdown
Owner

Requested by @h-nry in #1694. The Today feed listed your recent sessions but every tile was inert, so the only route to a session's detail was More → Workouts.

Each tile now opens the same read-only detail its Workouts list already opens — WorkoutDetailSheet on Android, WorkoutDetailView on iOS. Reusing those instead of building a Today-specific view is what makes this safe: neither can edit or delete, so a tap from Today carries no risk that list does not already carry.

@h-nry also offered a fallback of linking to the Workouts page instead. The popup is the better of the two and cost the same, since both detail views were already built and reusable.

Two things that had to be right rather than merely compile

The sheet cannot live inside the section. Today's sections are LazyColumn items. Hosting the sheet inside TodayWorkoutsSection builds clean and looks correct, but a disposed item takes an open sheet down with it. The state lives on TodayScreen and the sheet sits with Today's other dialogs — which is where WorkoutsScreen already keeps its own. A side benefit: the section stays presentational, reporting a tap and no longer needing the view model at all.

Press feedback comes from each platform's own primitive. The first iOS draft used .buttonStyle(.plain), which gives no feedback — Android would have settled inward on press and iOS would have done nothing. WorkoutsView's rows use LiquidPressStyle, the twin of Android's liquidPress, so that is what this uses.

Parity

Android iOS
Detail opened WorkoutDetailSheet WorkoutDetailView
Press feedback liquidPress LiquidPressStyle()
Sheet host screen level screen level
Read-only yes yes

Accessibility diverges on purpose: Android gets an onClickLabel string in all seven locales, because Compose derives nothing and the Sources row beside it does the same; iOS lets Button derive its label from the tile content, exactly as the Workouts rows do. Adding an iOS string would mean ten xcstrings locales for no gain.

Not addressed here, and pre-existing: Android shows 4 tiles with a "14 days" trailing header, iOS 6 with "N total". Same section, different cap and trailing text. Worth its own issue.

Tests

TodayWorkoutTapTest guards the wiring on both platforms, including the lazy-disposal trap — which compiles cleanly and no other test here would catch.

It reads source, so it strips comments before matching. The first version did not, and failed on its own first run: this file's comments name WorkoutDetailSheet while explaining the change, so the assertion matched its own prose rather than the code. Worth knowing for any future source-audit test in this repo, which comments densely.

Verified

4,712 tests, 0 failures with --rerun-tasks --no-build-cache; Tools/i18n_audit.py --ci clean; all seven strings.xml parse with the new key present.

The iOS half is app-target Swift, which default CI does not compile — dispatching app-build for it, and I will post the result here before this merges.

Requested in #1694 by @h-nry. The Today feed listed sessions but every tile was inert, so
the only route to a session's detail was More > Workouts.

Each tile now opens the SAME read-only detail its Workouts list already opens —
WorkoutDetailSheet on Android, WorkoutDetailView on iOS. Reusing those rather than building
a Today-specific view is what makes this safe: neither can edit or delete, so a tap from
Today carries no risk the list does not already carry.

Two things the implementation had to get right rather than merely compile.

The Today sections are LazyColumn items. Hosting the sheet inside TodayWorkoutsSection looks
correct and builds clean, but a disposed item takes an open sheet down with it, so the state
lives on TodayScreen and the sheet sits with Today's other dialogs — where the Workouts
screen already keeps its own. That also leaves the section presentational: it reports a tap
and no longer needs the view model at all.

Press feedback had to come from each platform's existing primitive, not a plain button. iOS
uses LiquidPressStyle, the twin of Android's liquidPress, so the tile settles inward on both.

Accessibility diverges on purpose. Android gets an onClickLabel string in all seven locales,
because Compose derives nothing and the Sources row beside it does the same; iOS lets Button
derive its label from the tile content, exactly as the Workouts rows do.

TodayWorkoutTapTest guards the wiring, including the lazy-disposal trap, which compiles
cleanly and no other test here would catch. It reads source, so it strips comments before
matching — this file's comments name the very symbols asserted on, and the first version
matched its own prose.

Verified: 4,712 tests, 0 failures, --rerun-tasks --no-build-cache; i18n audit clean. The iOS
half is app-target Swift, so it is compiled by app-build, not the default CI.
@ryanbr
ryanbr merged commit fbcaee4 into main Aug 28, 2026
6 checks passed
@ryanbr
ryanbr deleted the workout-tiles-tappable branch August 28, 2026 07:13
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.

1 participant