Skip to content

fix(workouts): open the manual-workout sheet full-height, not .medium - #450

Merged
ryanbr merged 1 commit into
ryanbr:mainfrom
digitalerdude:fix/workout-add-sheet-recents-clip
Jul 15, 2026
Merged

ryanbr merged 1 commit into
ryanbr:mainfrom
digitalerdude:fix/workout-add-sheet-recents-clip

Conversation

@digitalerdude

Copy link
Copy Markdown

Problem

Opening Workouts → Workout hinzufügen and tapping into the Sportart field clipped the sheet's
own header, the "SPORTART" label/field, and the Recent/catalogue suggestion panel (#297/#315) off
the top of the screen once the keyboard appeared — the suggestion list collapsed to a barely-visible
sliver.

Root cause

ManualWorkoutSheet opened at .medium detent (noopSheetPresentation(largeFirst: false)). Its
content is a plain VStack with no ScrollView, and the suggestion panel is a floating
.overlay(alignment: .bottom) anchored under the Sportart field (intentionally, so it isn't
squeezed to zero height by the fixed fields below it). When the keyboard appears, the sheet stays at
.medium height with nothing to absorb the overflow, so the top of the content — including the
field the overlay is anchored to — gets clipped off above the sheet's visible edge, taking the
overlay with it.

Every other short-form sheet in the app already opens at largeFirst: true
(WhatsNewView, SettingsView, HowNoopWorksView, UpdatesInboxView, TrendsReportView,
ScoringGuideView, AppleWatchSetupView) — ManualWorkoutSheet was one of only two holdouts still
using .medium, from before the Recent-sports overlay existed.

Fix

Open ManualWorkoutSheet at .large instead of .medium (one line,
Strand/Screens/ManualWorkoutSheet.swift), giving the form + keyboard + floating suggestion panel
enough room to coexist without clipping. StartWorkoutSheet in the same file (live-start picker) is
untouched — its suggestion list is an inline bounded ScrollView, not an off-screen-anchored
overlay, and it wasn't reported/reproduced.

No Kotlin/Android mirror needed — pure SwiftUI presentation-detent change, no analytics/storage/
migration touched.

Test plan

  • xcodegen generate && xcodebuild -scheme NOOPiOS -destination 'platform=iOS Simulator,name=iPhone 17' build — succeeds
  • Verified in iOS Simulator with the software keyboard: before the fix, the suggestion panel
    collapsed to a sliver exactly as reported; after the fix, the header, Sportart field, and full
    Recent/catalogue suggestion list stay visible and legible with the keyboard up

At .medium, the fixed-height VStack has no ScrollView to absorb the
keyboard's squeeze, so the header, Sportart field, and the Recent/
catalogue overlay anchored beneath it got clipped off the top of the
sheet once the keyboard appeared — the overlay collapsed to a barely
visible sliver.
@ryanbr
ryanbr marked this pull request as ready for review July 15, 2026 02:42
@ryanbr
ryanbr merged commit 85ea850 into ryanbr:main Jul 15, 2026
DX23876 pushed a commit to DX23876/noop that referenced this pull request Jul 22, 2026
…ryanbr#450)

At .medium, the fixed-height VStack has no ScrollView to absorb the
keyboard's squeeze, so the header, Sportart field, and the Recent/
catalogue overlay anchored beneath it got clipped off the top of the
sheet once the keyboard appeared — the overlay collapsed to a barely
visible sliver.
ryanbr pushed a commit that referenced this pull request Aug 15, 2026
…fting it

Rebased onto current main: applied the ScrollView refactor to the current
formContent (which now carries the #1203 distance row) and kept the
NoopChromeSurface() background (main changed it from surfaceOverlay since
this branch was opened). Logic/structure otherwise unchanged from #521.

#450 raised the sheet to .large but left a bare fixed-height VStack, so iOS
keyboard-avoidance rigidly shifts the whole block (clipping the header/Sport
field + its suggestion overlay off-screen, and colliding the footer with the
system Done accessory). Wrapping the iOS body in a ScrollView gives avoidance
somewhere to scroll instead. macOS's fixed 420pt panel is untouched.
ryanbr added a commit that referenced this pull request Aug 15, 2026
…fting it (#1336)

Rebased onto current main: applied the ScrollView refactor to the current
formContent (which now carries the #1203 distance row) and kept the
NoopChromeSurface() background (main changed it from surfaceOverlay since
this branch was opened). Logic/structure otherwise unchanged from #521.

#450 raised the sheet to .large but left a bare fixed-height VStack, so iOS
keyboard-avoidance rigidly shifts the whole block (clipping the header/Sport
field + its suggestion overlay off-screen, and colliding the footer with the
system Done accessory). Wrapping the iOS body in a ScrollView gives avoidance
somewhere to scroll instead. macOS's fixed 420pt panel is untouched.

Co-authored-by: Max Schädlich <schaedlich.max@gmail.com>
simoncad7 pushed a commit to simoncad7/noop that referenced this pull request Aug 17, 2026
…ryanbr#450)

At .medium, the fixed-height VStack has no ScrollView to absorb the
keyboard's squeeze, so the header, Sportart field, and the Recent/
catalogue overlay anchored beneath it got clipped off the top of the
sheet once the keyboard appeared — the overlay collapsed to a barely
visible sliver.
simoncad7 pushed a commit to simoncad7/noop that referenced this pull request Aug 17, 2026
…fting it (ryanbr#1336)

Rebased onto current main: applied the ScrollView refactor to the current
formContent (which now carries the ryanbr#1203 distance row) and kept the
NoopChromeSurface() background (main changed it from surfaceOverlay since
this branch was opened). Logic/structure otherwise unchanged from ryanbr#521.

ryanbr#450 raised the sheet to .large but left a bare fixed-height VStack, so iOS
keyboard-avoidance rigidly shifts the whole block (clipping the header/Sport
field + its suggestion overlay off-screen, and colliding the footer with the
system Done accessory). Wrapping the iOS body in a ScrollView gives avoidance
somewhere to scroll instead. macOS's fixed 420pt panel is untouched.

Co-authored-by: Max Schädlich <schaedlich.max@gmail.com>
rntcruz23 pushed a commit to rntcruz23/noop that referenced this pull request Aug 26, 2026
Another 320-commit batch, 9 conflicted files. Applied the lesson from the
last sync's regression: for every "one side looks deleted" hunk, checked
whether that content had moved elsewhere in the file (or a new file) before
resolving, rather than assuming a block is safe to keep/drop verbatim.

- Strand/Screens/ManualWorkoutSheet.swift: upstream restructured `body` into
  a `formContent` computed property wrapped by a new #if os(macOS)/#else
  split (fixing keyboard-avoidance clipping, ryanbr#450/ryanbr#697) - the platform
  frame/sheet-presentation/keyboardDoneToolbar modifiers that used to sit on
  the fork's HEAD tail now live on the NEW wrapping `body`, already merged in
  cleanly earlier in the file. Kept only the fork-only bits genuinely absent
  elsewhere: the two onChange(of:) reference-prefill triggers. Verified via
  grep that the platform-modifier chain already appears earlier, unconflicted.
- Strand/Screens/SettingsView.swift (2 conflicts): the fork's ryanbr#103
  protocolCheckSection/fiveMGVerifiedLine and upstream's new spo2CandidateCard
  (ryanbr#103/queue-11a - the WHOOP 5/MG @82 SpO2 candidate byte, corr +0.99 vs the
  WHOOP app over an 8-night validation, split out so Oura-only installs can
  reach it too) are independent computed properties, each still called
  exactly once outside the conflict. Reconstructed protocolCheckSection's own
  closing braces (previously shared with spo2CandidateCard's identical-text
  tail, which is what made git present it as ambiguous) and kept both in
  full, plus the fork's auditIcon/auditColor/auditTitle helpers the check
  section calls.
- android WhoopBleClient.kt (2 conflicts): upstream fixed a real bug (ryanbr#1635) -
  the WHOOP 5/MG bond declaration used to fire on ANY characteristic
  completion, not just the actual CLIENT_HELLO ack (a DISABLE_ALARM
  completion landing on the same characteristic was mistaken for a bond).
  Took upstream's properly-gated version wholesale and re-inserted the one
  fork-only side effect the old flow had (`auditNote { it.noteBond(...) }`,
  ryanbr#103) into the correct point inside the new gate. Second conflict: unioned
  two independent per-connection reset() fields on each side.
- android DevicesScreen.kt: upstream fixed a real multi-strap bug (ryanbr#1633
  follow-up) - firmware now resolves per-device instead of one global key
  (a second paired WHOOP could show the first one's firmware). Combined with
  the fork's verifiedFacts/inputsSummary (ryanbr#103) fields, which upstream's tree
  doesn't have.
- android WorkoutsScreen.kt: upstream's icon-matching refinement (dedicated
  Kayaking/Surfing/Sailing/ScubaDiving icons, "spin"->cycling, "polo"->swim)
  is a strict superset of the fork's coarser version - confirmed every branch
  the fork's HEAD side provided (cricket/baseball/martial-arts/hiit) already
  exists, unconflicted, immediately after where the conflict closes. Took
  upstream's side; dropped the now-fully-redundant HEAD fragment.
- android SettingsScreen.kt: non-overlapping imports, unioned.
- build.gradle.kts + project.yml: upstream well ahead (10.6.1 vs the fork's
  9.3.4) - took wholesale.
- altstore-source.json: fork manifest kept wholesale, as before.

Verified: every touched Kotlin file compiled with the real Kotlin 1.9.24
compiler - zero syntax errors, zero duplicate-declaration/ambiguity errors,
zero unresolved references on any symbol this merge touched. Both touched
Swift files' brace/paren balance checked and cross-referenced against both
parent branches (matching, none introduced by this merge).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012pwRSEnFWdsYMC7CLXWNEW
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