Skip to content

feat(bottom-tabs): retune drawer motion, add opt-in progress events - #267

Merged
huhuanming merged 5 commits into
OneKeyHQ:mainfrom
franco-chan:feat/bottom-tabs-drawer-motion
Nov 15, 2026
Merged

huhuanming merged 5 commits into
OneKeyHQ:mainfrom
franco-chan:feat/bottom-tabs-drawer-motion

Conversation

@franco-chan

Copy link
Copy Markdown
Contributor

Summary

Retunes the native left drawer on iOS and Android to a reference recording measured frame by frame, and adds an opt-in progress event so the app can drive related UI (such as fading the header avatar) on the UI thread. docs/SPEC.md is updated to match.

Behavior

Before After
Page travel 78% of width 80% of width
Page corner 64 * p 62 * min(1, p / .1) (no jump on other display corners)
Page wash gray 28 at .7 * p light: white at .5 * p; dark: gray 36 at .58 * p
Page edge none light: black shadow, opacity .16, sigma 18, no offset; dark: 1px white edge at 17%
Menu slides in from -12%, black shade .12 → 0 scales .97 → 1 about its left-center edge, opacity min(1, p / .6); the host is transparent
Tap / prop change critically damped, ≤ 500 ms cubic-bezier(.55, .6, .2, 1), 360 ms open / 290 ms close, scaled by remaining distance
Released drag critically damped, velocity clamped to ±8 spring (ω 25.5, ζ .84) that keeps finger velocity (clamped to ±12); about 2 pt overshoot after a fling, ends once at rest (≤ 600 ms)
Reversal mid-flight restarts from zero velocity continues from the displayed position at its current speed
Reduced motion none iOS: 200 ms cross-fade, no menu scale, no overshoot. Android (animator scale 0): lands immediately

API

  • onDrawerProgress?: (event) => void: displayed progress clamped to [0, 1], at most once per frame. Native sends it only while a handler is set (the wrapper derives the internal progressEventsEnabled prop), so apps that don't subscribe keep the old "no progress over the bridge" behavior.
  • drawerHostComponent? plus the BottomTabsDrawerNative export: lets an app pass Animated.createAnimatedComponent(BottomTabsDrawerNative), so a Reanimated worklet handles the event on the UI thread. The package itself does not depend on Reanimated.
  • drawerColorScheme?: "light" | "dark": picks the page palette. When omitted it follows the platform appearance. This matters on Android, where the app theme can differ from the configuration's night mode.
  • A same-target request while animating keeps the running animation and only updates whether its completion counts as a user transition. A newer prop update still cancels a stale user completion.

Verification

  • iOS: checked in the OneKey app on the iPhone 17 simulator (iOS 26.5), built from this branch's sources. Frames from simulator recordings were compared with the reference:
    • Open takes about 0.3 s and close about 0.27 s.
    • Menu opacity follows p / .6 (for example 0.74 measured vs 0.73 expected).
    • The page shadow fits sigma 18.0 pt and opacity 0.155.
    • The dark wash measures 27 against an expected 27.2, and the hairline is present.
    • The UI-thread avatar fade receives progress.
    • Fling overshoot (about 2 pt), smooth reversal, the corner ramp and the Reduce Motion cross-fade were verified on the same setup before the last refactor; those code paths are unchanged.
  • Android: :onekeyfe_react-native-bottom-tabs:compileDebugKotlin and compileDebugAndroidTestKotlin succeed (codegen includes the new props). BottomTabsDrawerTest is updated for 80% travel and the light wash, and gains two cases: palette/menu end state, and opt-in progress events.
  • JS: example/react-native/__tests__/BottomTabs.drawer.test.tsx passes 6/6. Package tsc -b passes, and yarn lint:test-integrity passes.

Not yet verified

  • The Android instrumented suite has not been run on an emulator for this revision, and the Android visuals have not been compared with the recording.
  • Physical devices on either platform.

- Retune the native left drawer on iOS and Android to a reference
  recording: 80% travel, a 62pt corner that rounds in over the first 10%,
  a light wash (white) or dark wash (gray) over the page, a soft page
  shadow in light mode and a hairline edge in dark mode, and a menu that
  scales from 0.97 about its left edge while fading in.
- Taps and prop changes ease with cubic-bezier(.55, .6, .2, 1) (0.36s open,
  0.29s close); released drags settle on a lightly underdamped spring that
  keeps the finger velocity; reversing mid-flight keeps the current speed.
- Reduce Motion cross-fades on iOS; Android lands immediately when system
  animations are off.
- Add onDrawerProgress (sent only while a handler is set),
  drawerHostComponent and the BottomTabsDrawerNative export so a UI-thread
  handler can follow the drawer, plus drawerColorScheme for apps whose
  theme can differ from the platform appearance.
- Update the spec, wrapper tests and Android native tests.
@sidmorizon

Copy link
Copy Markdown
Contributor

@codex review

@sidmorizon

Copy link
Copy Markdown
Contributor

@codex security review

@sidmorizon

Copy link
Copy Markdown
Contributor

Cursor review failed.

Remote error: cursor_permission_denied: HTTP 403 Forbidden: Cloud Agent is not available for free users. Please upgrade to Pro.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-14T09:12:01.667609Z b51d937 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cursor

cursor Bot commented Nov 14, 2026

Copy link
Copy Markdown

Skipping Bugbot: Bugbot is disabled for this repository. Visit the Bugbot dashboard to update your settings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Something went wrong. Try again later by commenting “@codex review”.

Unknown error
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@sidmorizon

Copy link
Copy Markdown
Contributor

@sidmorizon

Copy link
Copy Markdown
Contributor

Devin Cloud review ↗

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b51d937515

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

- Lay out the React menu slot at 80% so menu content reaches the native
  drawer's edge.
- Enable progress events one commit after mount, from a small host wrapper
  so the tabs do not re-render; iOS also re-sends the current position when
  Fabric attaches the event emitter after the first props.
- Keep the iOS page shadow on the same corner radius while it rounds in.
- Keep at most one Reduce Motion cross-fade snapshot and remove it on a
  drag, an animated settle, disabling, detach or resize.
- Never carry the page past its target under Reduce Motion.
A user tap or dim close under Reduce Motion now applies its final state
behind the cross-fade snapshot and reports onDrawerOpenChange (and the
haptic) when the 200 ms fade completes. A newer transition, disabling or
detach discards the pending report; a drag or resize reports it at once;
a newer non-user request for the same target cancels it.
A cross-fade now completes like a running settle: the stable state, the
settled event and the haptic follow when it lands. A drag that begins
during the fade drops it, so a stale close no longer echoes back as a prop
and cancels the drag.

Adds an iOS XCTest suite for the fade lifecycle, run hostless by the ui
group of the native tests.
@huhuanming
huhuanming enabled auto-merge (squash) November 15, 2026 08:58
@huhuanming
huhuanming merged commit dc9a547 into OneKeyHQ:main Nov 15, 2026
8 checks passed
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.

4 participants