Skip to content

✨ Reconcile OS-granted steps, share one footer across screens - #7

Merged
dryan merged 9 commits into
stable-page-listfrom
reconcile-and-footer
Oct 2, 2026
Merged

dryan merged 9 commits into
stable-page-listfrom
reconcile-and-footer

Conversation

@dryan-gh-actions

Copy link
Copy Markdown
Contributor
  • OnboardingFlow.reconcile(isSatisfied:) records true for unanswered
    steps the app reports as already satisfied outside the flow (a
    permission iOS already granted). OnboardingFlowView takes an
    isAlreadySatisfied closure, reconciles before snapshotting its pages
    and again when the scene becomes active. Resetting the kit's own
    records, or replaying onboarding, no longer shows a page for
    something the system already allows.
  • OnboardingFooter is the one button area the step and welcome views
    share. Both slots always hold their space, visible or not, so the
    main button and Skip sit in the same spot on every screen.

Tests: 23 (20 flow/storage, 3 theme environment), all passing.

Co-Authored-By: Claude Fable 5.1 noreply@anthropic.com

- OnboardingFlow.reconcile(isSatisfied:) records true for unanswered
  steps the app reports as already satisfied outside the flow (a
  permission iOS already granted). OnboardingFlowView takes an
  isAlreadySatisfied closure, reconciles before snapshotting its pages
  and again when the scene becomes active. Resetting the kit's own
  records, or replaying onboarding, no longer shows a page for
  something the system already allows.
- OnboardingFooter is the one button area the step and welcome views
  share. Both slots always hold their space, visible or not, so the
  main button and Skip sit in the same spot on every screen.

Tests: 23 (20 flow/storage, 3 theme environment), all passing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@dryan-gh-actions
dryan-gh-actions Bot requested a review from a team October 2, 2026 07:27
dryan-gh-actions Bot and others added 8 commits October 2, 2026 07:29
Each page drew its own footer inside the TabView, so the page control
sat on top of the button area. Step and welcome views now publish their
actions upward through a preference (keyed by step ID from the
environment) and OnboardingFlowView renders one OnboardingFooter under
the TabView for the page on screen. A step view used outside a flow
still draws its own footer.

Public API of the step and welcome views is unchanged; Dolly needed no
source changes.

Tests: 30 (20 flow/storage, 10 UI), all passing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Development/testing counterpart to reset(): a tool can put a single
step back to unrecorded to set up "this one was never seen" (or keep
one in the middle answered) and replay the flow against that mix.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A bare TabView index change jumps; wrapping it in withAnimation slides
the page the way a swipe does. The welcome records inside withAnimation
too so the flow's welcome-to-pages swap crossfades instead of cutting.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The welcome was a separate screen swapped out ahead of the TabView, so
leaving it could only crossfade. It's now the first page of the same
pager when the flow still needs it: Continue slides to the first step
exactly like a swipe, swiping back to it works, and swiping past it
records it as seen. OnboardingFlowPage holds the page list logic so it
can be tested on its own.

Tests: 34 (21 flow/storage, 13 UI), all passing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Moving from a page with Skip to one without swapped the Skip Button for
a placeholder inside the animated page change, so for the length of
the fade both existed and the footer was briefly 44pt taller: the page
content and the dots sat higher, then settled down. The secondary slot
is now one stable Button that is hidden instead of swapped, the primary
slot gets an identity transition, and the flow renders a hidden footer
of the same size while a page's actions haven't arrived yet.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The footer was still two conditional branches (real footer vs hidden
placeholder) swapped while a page's actions arrived, and its content
changes animated inside the page-change transaction. Both nudge the
TabView's height mid-slide, which is the residual drift in the content
and the dots. OnboardingFooter now takes optional actions and hides
its buttons instead of being replaced, and the flow renders it with
animation disabled.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Measured on the iOS 27 simulator, TabView(.page) rendered a programmatic
page change as a cut (not a slide) under both withAnimation and
.animation(value:), settled each new page's centered content ~10pt
late (its layout box changes after the page is added), and drew no
dots at all. All three are its internals.

The pager is now a horizontal ScrollView with .paging and a
scrollPosition binding: advance() animates the position so Next slides
exactly like a swipe, pages are created up front so nothing lays out
mid-slide, and OnboardingPageIndicator draws themed dots between the
pages and the footer. Verified with a screen recording: every
transition is a 7 to 8 frame slide at 20fps and content drift after
landing is zero.

Tests: 35, all passing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A horizontal ScrollView takes its height from its content, and
containerRelativeFrame(.vertical) measured the pages against the whole
screen, so the pager grew to full height and pushed the footer (and
its Skip line) below the bottom edge. Pages are now sized from a
GeometryReader that owns the pager's actual slot above the dots and
footer.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@dryan
dryan merged commit 67d92ce into stable-page-list Oct 2, 2026
1 check passed
@dryan
dryan deleted the reconcile-and-footer branch October 2, 2026 15:54
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