Skip to content

feat(menu): swipe to minimize the full-screen tip card - #1318

Merged
bmc08gt merged 2 commits into
code/cashfrom
feat/tip-card-swipe-minimize
Aug 24, 2026
Merged

feat(menu): swipe to minimize the full-screen tip card#1318
bmc08gt merged 2 commits into
code/cashfrom
feat/tip-card-swipe-minimize

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Full screen on the "You" tab could only be left through the Close row, so this adds the swipe back down.

How it works

The expansion is one 0..1 progress rather than a boolean. Card size, its travel out of the slot, the page fading beneath it and the Close row all read the same value, so a drag can park the transition part-way through and hand it back where it found it. Tap still drives the same progress to an end with a spring.

Every reader takes the progress inside a graphicsLayer lambda, so a drag redraws rather than recomposing the page.

Settling a flick

A release settles on the spring iOS already uses for this transition (response: 0.45, dampingFraction: 0.85), carrying the gesture's velocity into it. That velocity is what produces the overshoot, not the damping ratio, and iOS never hits it because YouScreen has no drag gesture: its spring always starts from a standstill, where this damping ratio overshoots half a percent.

Uncapped, the hardest flick sailed about 18% of the travel past the resting end, lifting the card clear out of its slot and up under the status bar. Capping the carried velocity holds it to 5.6%, roughly 10dp:

velocity cap worst overshoot
2.0 1.0%
3.0 3.1%
4.0 (chosen) 5.6%
5.0 8.4%
uncapped 17.8%

TipCardOvershootTest drives the animation a frame at a time off a BroadcastFrameClock, so those are the values the card is drawn at rather than where the animation was aimed. It holds both bounds — the bounce has to be there, and it has to be small — measured from every tenth of the travel, since where a flick is released changes how far it carries past.

The "Full Screen" caption

The caption is a later sibling than the card, so it paints on top of it while the growing card travels down across its slot: about 224dp of travel per unit of progress against 24dp of clearance. Faded over the whole expansion it stayed legible at the point it ended up printed across the card's face. It now fades over the first 8% of the progress, ahead of the card arriving at around 11%.

Known gap

The tab bar still switches on the committed intent rather than tracking the drag — HideTabBar is boolean/counted, not progress-driven. Making it follow the progress is a separate change.

Full screen on the "You" tab could only be left through the Close row. The
expansion is now one 0..1 progress rather than a boolean: card size, its
travel out of the slot, the page fading beneath it and the Close row all
read the same value, so a drag can park the transition part-way through and
hand it back where it found it. Every reader takes the progress inside a
graphicsLayer lambda, so a drag redraws rather than recomposing the page.

A release settles on the spring iOS already uses for this transition
(response 0.45, damping fraction 0.85), carrying the gesture's velocity into
it. Uncapped, the hardest flick sailed about 18% of the travel past the
resting end, which lifts the card clear out of its slot and up under the
status bar. Capping the carried velocity holds that to 5.6%, roughly 10dp,
without flattening the bounce into a cut. TipCardOvershootTest drives the
animation a frame at a time off a BroadcastFrameClock and holds both bounds:
some bounce, and not much.

The "Full Screen" caption is a later sibling than the card, so it paints on
top of it while the growing card travels down across its slot. It now fades
out over the first 8% of the expansion, ahead of the card reaching that spot
at around 11%.

One gap: the tab bar still switches on the committed intent rather than
tracking the drag, because HideTabBar is boolean rather than progress-driven.
@github-actions github-actions Bot added the type: feature New functionality label Aug 24, 2026
The Android swipe was ours, not a port: the gesture sat on the card, it took
half the travel to close, and pushing an expanded card down did nothing. iOS
PR #639 settled all three differently. Bringing them across:

- Attach the drag to the page, not the card. Expanded, the card is 302dp of a
  wider display, so a pull that lands in the margin missed entirely. Nothing
  scrolls while the card is up, so there is no scroll to contend with.
- Close at 0.3 of the travel rather than halfway.
- Give the card 0.25 resistance when it is pushed down past full screen, and
  return it on the shorter spring iOS uses for a pull that fell short
  (response 0.3, so stiffness 439).
- Hand back the touch slop the recogniser swallowed on the first delta, so the
  card starts where the finger did instead of jumping the slop distance.

Two divergences stay. The settle still carries a capped release velocity,
because Android has a swipe where iOS only has a tap and so has no velocity to
carry. The page content still fades on progress rather than on the boolean,
because our card is opaque at this call site and a boolean fade shows through
a translucent one.
@bmc08gt
bmc08gt merged commit 54bcb13 into code/cash Aug 24, 2026
3 checks passed
@bmc08gt
bmc08gt deleted the feat/tip-card-swipe-minimize branch August 24, 2026 19:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feature New functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant