ios: deplete the Dynamic Island rest ring instead of a static on/off ring - #81
Open
willchan wants to merge 1 commit into
Open
ios: deplete the Dynamic Island rest ring instead of a static on/off ring#81willchan wants to merge 1 commit into
willchan wants to merge 1 commit into
Conversation
…ring The compact/minimal Dynamic Island icon's orange ring used to just mean "resting: yes" — a static full ring for the whole rest period, same regardless of 5 seconds or 90 seconds left. It's now a ProgressView(timerInterval:) that the system animates continuously on its own: full right as rest starts, empty right as the next set is due. That matters most in `minimal`, where the ring is the only glyph shown at all (no digits) once a second Live Activity is also active. - src/native/liveActivity.ts: add restStartTime alongside restEndTime in WorkoutActivityState/toContentState (now exported for direct testing), so the widget has both ends of a fixed date range. - src/ui/workout.ts: thread restStartTime through syncLiveActivity() (now takes the TimerState itself, not just its end time) and the mount-time timer-recovery path. - LiveActivityWidget.swift: brandedIcon() draws the ring via ProgressView(timerInterval:).progressViewStyle(.circular) instead of a static strokeBorder; restStartDate()/restProgressRange() guard against the same "Range requires lowerBound <= upperBound" trap restEndDate() already guards for the digits. The range-validation logic would belong in ios/App/AppLogic per CLAUDE.md, but AppLogic isn't linked into the LiveActivityWidget extension target (only App is), and adding that link is an Xcode-GUI-only step — same constraint the existing restEndDate() next to it already lives with.
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 freeto 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
The Dynamic Island's compact/minimal orange ring around the app icon used to just mean "resting: yes" — a static full ring for the whole rest period, whether there were 90 seconds or 5 seconds left. It now depletes clockwise over the rest period: full right as rest starts, empty right as the next set is due.
This matters most in
minimal, the one Dynamic Island presentation with no text slot at all (it kicks in once a second Live Activity, e.g. Audible, is also active) — the ring there is the only signal, so making it convey progress instead of a flat yes/no gets more information into the same one glyph.How
src/native/liveActivity.ts— addedrestStartTimealongsiderestEndTimetoWorkoutActivityState/toContentState()(now exported for direct unit testing, matching the rest of this codebase's pure-function testing convention). A depleting ring needs both ends of a fixed date range;restEndTimealone isn't enough.src/ui/workout.ts—syncLiveActivity()now takes theTimerStateitself (rather than just its end time) so bothrestStartTime/restEndTimeare always derived and set together; also threaded through the mount-time timer-recovery path (browser tab resumed with a rest timer already running).LiveActivityWidget.swift—brandedIcon()draws the ring viaProgressView(timerInterval:).progressViewStyle(.circular)instead of a staticstrokeBorder. This is the same "hand SwiftUI a fixed date range once, let the system animate it continuously" mechanismText(timerInterval:)already uses for the digits — no extra Live Activity pushes needed once rest starts. AddedrestStartDate()/restProgressRange()guards against the same "Range requires lowerBound <= upperBound" traprestEndDate()already guards against for the digits.Notes
restProgressRange) would belong inios/App/AppLogicper CLAUDE.md's "new Swift logic goes in AppLogic, with a test," butAppLogicisn't currently linked into theLiveActivityWidgetextension target (onlyAppis), and adding that link is an Xcode-GUI-only step perios/MANUAL_SETUP.md— not scriptable from this session. It lives inline next torestEndDate(), which already has the same constraint.ProgressViewin.background, had no explicit frame and could render at the control's intrinsic size instead of hugging the icon — fixed by giving it an explicit.frame()and deduplicating the two now-identical ring branches into one.Testing
bun run typecheck/bun run lintclean.bunx playwright test --project=chromium): 284 passed, including two new tests fortoContentState()'srestStartTimehandling.iOSCI workflow's Simulator build to catch any compile issues.🤖 Generated with Claude Code
Generated by Claude Code