feat(mobile): Add Per Set Rest Periods Editing and Functionality - #2071
Conversation
PR Validation ResultsChange Detection
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (4)
📝 WalkthroughWalkthroughThe mobile workout flow now edits rest durations per set or superset round. It displays rest ranges, stores set-level rest metadata, uses set-specific durations during progression, and adds regression coverage. ChangesPer-set rest duration flow
Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk: 🔵 Low · up to The PR adds per-set rest editing and timing behavior. It is mergeable with owner awareness to rerun the focused mobile test and required validation because a CI failure was reported. Sequence Diagram(s)sequenceDiagram
participant User
participant ActiveWorkoutScreen
participant ExerciseSetRestSheet
participant DurationWheel
participant activeWorkoutStore
User->>ActiveWorkoutScreen: Open rest editor
ActiveWorkoutScreen->>ExerciseSetRestSheet: present exercise and set rest values
ExerciseSetRestSheet->>DurationWheel: Display selected duration
User->>DurationWheel: Select duration
DurationWheel->>ExerciseSetRestSheet: onChangeSec(seconds)
ExerciseSetRestSheet->>ActiveWorkoutScreen: onApply(changed set values)
ActiveWorkoutScreen->>activeWorkoutStore: Update set rest metadata
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (2)
SparkyFitnessMobile/src/components/RestPeriodChip.tsx (1)
26-34: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSimplify the range computation.
Line 28 tests
normalized.lengthafter the map. The map preserves length, so testvalues.lengthfirst and return early. You can also replace the manual loop withMath.min/Math.maxover the normalized array.♻️ Optional refactor
): string { - const normalized = values.map((v) => (v ?? defaultRestSec)); - if (normalized.length === 0) return formatRestLabel(defaultRestSec); - let min = normalized[0]; - let max = normalized[0]; - for (const value of normalized) { - if (value < min) min = value; - if (value > max) max = value; - } + if (values.length === 0) return formatRestLabel(defaultRestSec); + const normalized = values.map((v) => v ?? defaultRestSec); + const min = Math.min(...normalized); + const max = Math.max(...normalized);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@SparkyFitnessMobile/src/components/RestPeriodChip.tsx` around lines 26 - 34, In the range computation function containing normalized, check values.length before mapping and return formatRestLabel(defaultRestSec) for an empty input. Then replace the manual min/max loop with Math.min and Math.max applied to the normalized array, preserving the existing range-label behavior.SparkyFitnessMobile/__tests__/stores/activeWorkoutStore.test.ts (1)
1257-1279: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtend the superset coverage and drop the unused setup.
The
beforeEachon lines 1259-1261 starts a 2-round session. The test on line 1268 immediately replaces it with a 3-round session, so the setup has no effect. Remove it or make the test use it.The test also sets the same
rest_timeon both members for round 1. That hides the divergence betweenbuildStepsFromSession, which reads the anchor member's set, andrestSecBeforeNextSet, which reads the completed set. Add a case where the two members carry differentrest_timevalues in the same round, and a case where one member has fewer sets than the other. Both relate to the issue raised onSparkyFitnessMobile/src/stores/activeWorkoutStore.tslines 462-467.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@SparkyFitnessMobile/__tests__/stores/activeWorkoutStore.test.ts` around lines 1257 - 1279, Remove the unused beforeEach setup in the completeSet rest supersets tests, then extend coverage with separate cases for differing rest_time values between superset members in the same round and for one member having fewer sets than the other. Anchor the assertions to completeSet and the resulting rest.durationSec, covering both buildStepsFromSession and restSecBeforeNextSet behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@SparkyFitnessMobile/src/components/DurationWheel.tsx`:
- Around line 3-9: Remove the unused eslint-disable directive above the
WheelPicker import while preserving the explanatory comment and the import
itself.
- Around line 60-62: Update the seconds selection state in DurationWheel so
handleSecondChange preserves the raw wheel index across the 359-to-360 wrap
instead of recomputing it from currentSec. Reconcile that local index only when
the external valueSec prop changes, and ensure secondsWheelValue uses the
preserved index so WheelPicker does not jump.
In `@SparkyFitnessMobile/src/components/ExerciseSetRestSheet.tsx`:
- Around line 113-122: Update handleDone in ExerciseSetRestSheet so selecting
ALL_KEY commits selectedSeconds to every set, even when no wheel movement
changes draftBySetId; build updates for all sets in that mode, while preserving
the existing changed-only behavior for individual selections.
- Around line 81-96: Update selectedSeconds in ExerciseSetRestSheet so the
ALL_KEY branch uses the existing highestSetRest value instead of the first set’s
draft, ensuring the “All” chip and wheel display the same maximum rest duration;
move the highestSetRest memo above selectedSeconds if needed to satisfy
declaration ordering.
In `@SparkyFitnessMobile/src/components/RestPeriodChip.tsx`:
- Around line 22-44: Update the array type annotations in formatRestRangeLabel
and RestPeriodChipProps to use T[] syntax instead of Array<T>, preserving the
existing element types and optionality.
In `@SparkyFitnessMobile/src/stores/activeWorkoutStore.ts`:
- Around line 462-467: Update the round-rest calculation in the superset
grouping logic to select the first member with a set at the current round,
rather than always using members[0], while retaining the default only when no
member has a set. Apply the identical first-member-with-a-set rule in
restSecBeforeNextSet for the step that closes a round, so steps[].restSec and
the live countdown use the same duration.
---
Nitpick comments:
In `@SparkyFitnessMobile/__tests__/stores/activeWorkoutStore.test.ts`:
- Around line 1257-1279: Remove the unused beforeEach setup in the completeSet
rest supersets tests, then extend coverage with separate cases for differing
rest_time values between superset members in the same round and for one member
having fewer sets than the other. Anchor the assertions to completeSet and the
resulting rest.durationSec, covering both buildStepsFromSession and
restSecBeforeNextSet behavior.
In `@SparkyFitnessMobile/src/components/RestPeriodChip.tsx`:
- Around line 26-34: In the range computation function containing normalized,
check values.length before mapping and return formatRestLabel(defaultRestSec)
for an empty input. Then replace the manual min/max loop with Math.min and
Math.max applied to the normalized array, preserving the existing range-label
behavior.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 07a19e95-b274-4f65-aef8-84e026e5861e
📒 Files selected for processing (10)
SparkyFitnessMobile/__tests__/stores/activeWorkoutStore.test.tsSparkyFitnessMobile/src/components/ActiveWorkoutExerciseCard.tsxSparkyFitnessMobile/src/components/DurationWheel.tsxSparkyFitnessMobile/src/components/ExerciseSetRestSheet.tsxSparkyFitnessMobile/src/components/RestPeriodChip.tsxSparkyFitnessMobile/src/components/WorkoutFormExerciseList.tsxSparkyFitnessMobile/src/screens/ActiveWorkoutScreen.tsxSparkyFitnessMobile/src/stores/activeWorkoutStore.tsSparkyFitnessMobile/src/types/assets.d.tsSparkyFitnessMobile/src/types/drafts.ts
| const handleDone = useCallback(() => { | ||
| const updates: ExerciseSetRestUpdate[] = []; | ||
| for (const set of sets) { | ||
| const next = draftBySetId[set.setId]; | ||
| const initial = initialBySetId[set.setId]; | ||
| if (next !== initial) updates.push({ setId: set.setId, seconds: next }); | ||
| } | ||
| if (updates.length > 0) onApply(updates); | ||
| sheetRef.current?.dismiss(); | ||
| }, [draftBySetId, initialBySetId, onApply, sets]); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
"All" emits no update when the user does not move the wheel.
handleDone emits only sets whose draft differs from the initial value. If the user selects "All" and presses Done without moving the wheel, the drafts stay unequal and no harmonization occurs. The user selected "All" to make every set match. Consider committing every set to selectedSeconds when selectedKey === ALL_KEY, or apply the value to all sets at the moment "All" is selected.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@SparkyFitnessMobile/src/components/ExerciseSetRestSheet.tsx` around lines 113
- 122, Update handleDone in ExerciseSetRestSheet so selecting ALL_KEY commits
selectedSeconds to every set, even when no wheel movement changes draftBySetId;
build updates for all sets in that mode, while preserving the existing
changed-only behavior for individual selections.
apedley
left a comment
There was a problem hiding this comment.
Thanks again for handling this. The store stuff is well done and I appreciate the new regression tests. Some problems:
- I think I may have given you bad advice on extracting the wheel from react-native-ui-datepicker. CI is failing because jest enforces strict exports. Rather than working around that and the other inevitable problems, lets just copy paste it into our own components/ui directory with the MIT license and attribution in the file as a comment. This lets us get rid of the shim at assets.d.ts as well.
- If we're vendoring the component, there's also a bug in the wheel that we should fix that seems to only show up on iOS. The seconds column recenters to the middle repetition after every change but it also has a useEffect that calls scrollToIndex. The result is scrolling across 59-00 gives an index > 60 which then animates the wheel rotating back a full rotation. It took me a while to figure out what was happening on this bug - its subtle unless you slow down animations on the simulator.
- The failing test in WorkoutPresetDetailScreen.test.tsx is just stale and needs updating
- WorkoutFormExerciseList.test.tsx "targets the rest sheet at the pressed exercise" still mocks RestPeriodSheet which the component doesn't import anymore. Needs rewriting against ExerciseSetRestSheet present(name, set)/onApply
- Supersets..the store's setExerciseRest was the thing harmonizing rest across superset members and nothing calls it now. buildStepsFromSession takes each round's rest from the anchor member's set, so editing rest on a non-anchor member's chip does nothing when sets are completed in order. Rest in a superset belongs to the round not to the member sets so let's display it like that. That would include relabeling the chips.
Small things:
- the "All" chip in the sheet shows the max rest when formatRestRangeLabel is right there if you want it to show the range
- the sheet opens with Set 1 selected when it should be All (the most common change)
Stuff we talked about before:
- Add a dialog confirmation if changing all sets is going to overwrite a mixed set of times
- Make the Set Name/Number/All text-secondary (second most important thing) and the time text-primary (most important thing). Border should be border-subtle.
- Drop the "Selected: 1:30" text
Let me know if you have any questions or if you want me to take care of any part of it that's fine too.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (5)
SparkyFitnessMobile/src/components/ui/wheel-picker/wheel-picker.tsx (3)
151-159: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winThrowing from the effect crashes the sheet on an unmatched value.
selectedIndexis-1whenvalueis absent fromoptions. The effect then throws, and React 19 reports it to the error boundary instead of degrading gracefully. The picker also seedsscrollYandinitialScrollIndexwith-1before the effect runs.
DurationWheelclampsvalueSectomaxSecand maps seconds throughSEC_MID_OFFSET + currentSec, so every value resolves to an existing option today withmaxSec = MAX_REST_SEC. The crash becomes reachable ifmaxSecor the option builders change.Consider clamping the index instead of throwing:
♻️ Proposed refactor
- const selectedIndex = options.findIndex((item) => item.value === value); + const foundIndex = options.findIndex((item) => item.value === value); + const selectedIndex = foundIndex >= 0 ? foundIndex : 0;useEffect(() => { - if (selectedIndex < 0 || selectedIndex >= options.length) { - throw new Error( - `Selected index ${selectedIndex} is out of bounds [0, ${ - options.length - 1 - }]` - ); + if (foundIndex < 0 && __DEV__) { + console.warn(`WheelPicker: value ${value} is not in options; showing index 0.`); } - }, [selectedIndex, options]); + }, [foundIndex, value]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@SparkyFitnessMobile/src/components/ui/wheel-picker/wheel-picker.tsx` around lines 151 - 159, Replace the throwing bounds validation in the wheel picker’s selectedIndex useEffect with graceful clamping to the valid options range, so unmatched values never propagate -1 into scrollY or initialScrollIndex. Preserve valid indices unchanged and handle the options list safely when deriving the fallback index.
110-115: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win
|| 0discards valid falsy option values and hides out-of-range indices.
options[index]?.value || 0returns0in two different situations: the option value is genuinely falsy, andindexis outside the array. For a picker with string option values, an empty string becomes the number0, which never matches any option and leaves the wheel desynchronized fromvalue.
DurationWheeluses numeric values only, so the current consumer is unaffected. Guard the lookup so a missing option emits nothing.♻️ Proposed refactor
- const nextValue = options[index]?.value || 0; - if (index !== selectedIndex && nextValue !== lastEmittedValueRef.current) { + const nextOption = options[index]; + if (nextOption == null) return; + const nextValue = nextOption.value; + if (index !== selectedIndex && nextValue !== lastEmittedValueRef.current) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@SparkyFitnessMobile/src/components/ui/wheel-picker/wheel-picker.tsx` around lines 110 - 115, Update the nextValue lookup in the wheel-picker scroll handling to preserve valid falsy option values and distinguish missing options. Guard the options[index] lookup, emit and update lastEmittedValueRef only when an option exists, and emit its value unchanged; do not substitute 0 for an out-of-range index.
141-147: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReplace the
as anycast with a typed partial event.The repository guideline forbids
anyin new or edited code. Type the helper parameter to the fields it reads instead of casting the synthetic object.♻️ Proposed refactor
- const handleScrollEnd = (event: NativeSyntheticEvent<NativeScrollEvent>) => { + type ScrollOffsetEvent = { + nativeEvent: { contentOffset: { y: number } }; + }; + + const handleScrollEnd = (event: ScrollOffsetEvent) => { const offsetY = Math.min(- handleScrollEnd(syntheticEvent as any); + handleScrollEnd(syntheticEvent);
NativeSyntheticEvent<NativeScrollEvent>structurally satisfiesScrollOffsetEvent, so the momentum and drag handlers keep working without a cast.This follows the guideline "Never use
anyor// eslint-disable-next-line@typescript-eslint/no-explicit-any`` when creating functions or editing code; define explicit TypeScript types or import schemas from@workspace/shared."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@SparkyFitnessMobile/src/components/ui/wheel-picker/wheel-picker.tsx` around lines 141 - 147, Replace the as any cast in the synthetic event passed to handleScrollEnd with an explicit partial event type containing the nativeEvent.contentOffset.y fields that the helper reads. Update handleScrollEnd’s parameter type as needed to accept this minimal shape while remaining compatible with NativeSyntheticEvent<NativeScrollEvent> used by the momentum and drag handlers.Source: Coding guidelines
SparkyFitnessMobile/__tests__/components/WorkoutFormExerciseList.test.tsx (1)
746-762: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a case where a superset receives different values for two rounds.
The test applies a single update, so it cannot detect the value-collapsing behavior. Add a superset exercise with two sets and call
onApplywith two different seconds values. That test pins down the intended contract for the issue raised onSparkyFitnessMobile/src/screens/ActiveWorkoutScreen.tsxlines 520-537.mockRestSheet.onApply?.([ { setId: 'b-s1', seconds: 90 }, { setId: 'b-s2', seconds: 150 }, ]); // Assert the intended contract: either both rounds keep their own value, // or the sheet never emits differing values for a superset member.This follows the guideline "Run focused tests for the touched surface, then lint and typecheck" for
SparkyFitnessMobile/__tests__/**.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@SparkyFitnessMobile/__tests__/components/WorkoutFormExerciseList.test.tsx` around lines 746 - 762, Add a two-set superset exercise to the test around the superset rest-sheet case, invoke mockRestSheet.onApply with distinct seconds for each set, and assert the intended contract: each round retains its own value or differing values are rejected before state updates. Keep the existing setExerciseRest and isSupersetRound assertions intact, then run the focused test, lint, and typecheck.Source: Coding guidelines
SparkyFitnessMobile/src/components/ui/wheel-picker/wheel-picker-item.tsx (1)
143-153: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueThe memo comparator ignores
option,index, andheight.
customComparatorreturnstruewhenevertextClassNameandtextStyleare unchanged. React then skips the re-render even whenoption.textchanges. This is safe only while every cell keeps a stable identity.wheel-picker.tsxbuildskeyExtractorfrom${item.value}-${item.text}-${index}, so an option list change produces new keys and new mounts, which masks the risk today.Add
prevProps.option?.text === nextProps.option?.text && prevProps.index === nextProps.index && prevProps.height === nextProps.heightif you want the component to stay correct under future option-list reuse. The current behavior matches upstream, so this is optional.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@SparkyFitnessMobile/src/components/ui/wheel-picker/wheel-picker-item.tsx` around lines 143 - 153, The customComparator for WheelPickerItem must also compare option.text, index, and height before treating props as equal. Extend the comparator alongside the existing textClassName and textStyle checks so reused cells re-render when their displayed option or layout position changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@SparkyFitnessMobile/src/components/ExerciseSetRestSheet.tsx`:
- Around line 64-70: Update the mixed-rest calculation used by
handleChangeSeconds to compare the current draft map via restTimesMixed instead
of the initial snapshot initialTimesMixed. Replace the references in the handler
and its dependency array, while preserving the existing allOverwriteConfirmed
session behavior.
In `@SparkyFitnessMobile/src/components/ui/wheel-picker/wheel-picker.tsx`:
- Around line 129-149: Update handleScrollEndDrag to store its 50 ms timeout in
a ref, clear any existing timer before scheduling a new one, and clear the timer
during component unmount cleanup so stale callbacks cannot invoke
handleScrollEnd or onChange after remounting.
In `@SparkyFitnessMobile/src/screens/ActiveWorkoutScreen.tsx`:
- Around line 520-537: Update the superset branch in the exercise-rest save flow
around getSupersetRuns and store.setExerciseRest so per-round values are not
discarded: either restrict ExerciseSetRestSheet to emitting only the “All” value
for superset members, or apply each update through store.updateSetField and
separately harmonize the value across run members. Remove the redundant
updates.length check because empty updates already return earlier, and ensure
the selected-round-only case does not overwrite other rounds.
---
Nitpick comments:
In `@SparkyFitnessMobile/__tests__/components/WorkoutFormExerciseList.test.tsx`:
- Around line 746-762: Add a two-set superset exercise to the test around the
superset rest-sheet case, invoke mockRestSheet.onApply with distinct seconds for
each set, and assert the intended contract: each round retains its own value or
differing values are rejected before state updates. Keep the existing
setExerciseRest and isSupersetRound assertions intact, then run the focused
test, lint, and typecheck.
In `@SparkyFitnessMobile/src/components/ui/wheel-picker/wheel-picker-item.tsx`:
- Around line 143-153: The customComparator for WheelPickerItem must also
compare option.text, index, and height before treating props as equal. Extend
the comparator alongside the existing textClassName and textStyle checks so
reused cells re-render when their displayed option or layout position changes.
In `@SparkyFitnessMobile/src/components/ui/wheel-picker/wheel-picker.tsx`:
- Around line 151-159: Replace the throwing bounds validation in the wheel
picker’s selectedIndex useEffect with graceful clamping to the valid options
range, so unmatched values never propagate -1 into scrollY or
initialScrollIndex. Preserve valid indices unchanged and handle the options list
safely when deriving the fallback index.
- Around line 110-115: Update the nextValue lookup in the wheel-picker scroll
handling to preserve valid falsy option values and distinguish missing options.
Guard the options[index] lookup, emit and update lastEmittedValueRef only when
an option exists, and emit its value unchanged; do not substitute 0 for an
out-of-range index.
- Around line 141-147: Replace the as any cast in the synthetic event passed to
handleScrollEnd with an explicit partial event type containing the
nativeEvent.contentOffset.y fields that the helper reads. Update
handleScrollEnd’s parameter type as needed to accept this minimal shape while
remaining compatible with NativeSyntheticEvent<NativeScrollEvent> used by the
momentum and drag handlers.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 56728db6-c096-463e-bba3-d224be496ba7
📒 Files selected for processing (10)
SparkyFitnessMobile/__tests__/components/WorkoutFormExerciseList.test.tsxSparkyFitnessMobile/src/components/DurationWheel.tsxSparkyFitnessMobile/src/components/ExerciseSetRestSheet.tsxSparkyFitnessMobile/src/components/WorkoutFormExerciseList.tsxSparkyFitnessMobile/src/components/ui/wheel-picker/index.tsSparkyFitnessMobile/src/components/ui/wheel-picker/types.tsSparkyFitnessMobile/src/components/ui/wheel-picker/wheel-picker-item.tsxSparkyFitnessMobile/src/components/ui/wheel-picker/wheel-picker.style.tsSparkyFitnessMobile/src/components/ui/wheel-picker/wheel-picker.tsxSparkyFitnessMobile/src/screens/ActiveWorkoutScreen.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- SparkyFitnessMobile/src/components/DurationWheel.tsx
- SparkyFitnessMobile/src/components/WorkoutFormExerciseList.tsx
|
@Gtt1229 resolve all the conversations for us to merge. and also CI Test is in failed state. |
|
Will need help with the CI test fail. The fail is due to a fix for the janky animation for the duration wheel's seconds wheel. |
Tip
Help us review and merge your PR faster!
Please ensure you have completed the Checklist below.
For Frontend changes, please run
pnpm run validateto check for any errors.PRs that include tests and clear screenshots are highly preferred!
Note: AI-generated descriptions must be manually edited for conciseness. Do not paste raw AI summaries.
Description
Added functionality to support per set rest duration and the ability to edit it.
What problem does this PR solve?
Per set rest functionality
How did you implement the solution?
Via Android Studio with AI Assistance for React elements
Linked Issue: Closes #
#1977
How to Test
PR Type
Checklist
All PRs:
New features only:
Frontend changes (
SparkyFitnessFrontend/):pnpm run validateand it passes.en) translation file.Backend changes (
SparkyFitnessServer/):rls_policies.sqlfor any new user-specific tables.UI changes (components, screens, pages):
Mobile changes (
SparkyFitnessMobile/):Screenshots
Click to expand
Before
After
Notes for Reviewers
I would like a review of AI's implementation of the duration wheel aspects.
Summary by CodeRabbit
New Features
Bug Fixes