Skip to content

chore(runway): cherry-pick feat(predict): Bottom Sheet Keyboard Fix cp-7.78.0#30488

Merged
sleepytanya merged 1 commit into
release/7.78.0from
runway-cherry-pick-7.78.0-1779324860
May 21, 2026
Merged

chore(runway): cherry-pick feat(predict): Bottom Sheet Keyboard Fix cp-7.78.0#30488
sleepytanya merged 1 commit into
release/7.78.0from
runway-cherry-pick-7.78.0-1779324860

Conversation

@runway-github

@runway-github runway-github Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Description

The Predict Buy bottom-sheet introduced in
#28779 opened
with the custom keypad already rendered behind the rest of the sheet
content. The root cause was a single isInputFocused boolean inside
usePredictBuyInputState that was hard-coded to true on first render
and overloaded across five unrelated behaviours:

  1. Mounting PredictKeypad.
  2. Highlighting the amount-display "active" state.
  3. Hiding PredictBuyBottomContent while the keypad is up.
  4. Disabling PredictFeeSummary.
  5. Deferring the mm_pay relay-config side effects (updatePendingAmount
    / setPayToken) inside PredictPayWithAnyTokenInfo.

Because the only "off switch" was setIsInputFocused(false) (driven
from a Done button that doesn't exist in sheet mode) the previous PR
worked around the issue with three isSheetMode ? false : isInputFocused ternaries — Bugbot flagged this on #28779. The
workarounds also produced a confusing situation where bottom content and
keypad rendered simultaneously on first sheet open.

This PR separates the conflated meanings, parameterises the initial
state, and removes the workarounds. No behaviour change for the legacy
full-screen flow.

What changed

usePredictBuyInputState
(hooks/usePredictBuyInputState.ts)

  • Accepts an options object: { initialKeypadOpen = true }.
  • Renamed state + setter: isInputFocusedisKeypadOpen,
    setIsInputFocusedsetIsKeypadOpen.
  • Default value preserves legacy behaviour; sheet mode opts out.

PredictBuyWithAnyToken
(PredictBuyWithAnyToken.tsx)

  • Calls the hook as usePredictBuyInputState({ initialKeypadOpen: !isSheetMode }) so the sheet opens with the keypad collapsed.
  • Removed the three isSheetMode ? false : isInputFocused patches.
  • Renders PredictBuyBottomContent at the call site via {(isSheetMode || !isKeypadOpen) && <PredictBuyBottomContent ... />} instead of
    relying on the component to bail internally. Keeps the legacy "hide
    while typing" behaviour while letting the sheet show the bottom content
    (and Confirm) at all times.
  • Passes the new self-documenting shouldDeferRelaySetup={!isSheetMode && isKeypadOpen} prop to PredictPayWithAnyTokenInfo. Same effective
    value as before, but the prop name now describes its actual purpose.

PredictPayWithAnyTokenInfo
(components/PredictPayWithAnyTokenInfo)

  • Renamed prop: isInputFocusedshouldDeferRelaySetup. This is the
    genuine separation: the prop is not about UI keypad state, it's
    about pausing updatePendingAmount / setPayToken calls. Legacy mode
    still defers until the user taps Done; sheet mode never defers (relay
    must update on every keystroke since there's no Done and the user can
    tap Confirm with the keypad still open — preventing underfunded
    deposits).
  • Added a JSDoc comment explaining the contract.

PredictBuyBottomContent
(components/PredictBuyBottomContent)

  • Removed the isInputFocused prop and the if (isInputFocused) return null early return. Component is now purely structural; visibility is
    the parent's responsibility.

PredictKeypad and PredictBuyAmountSection

  • Mechanical prop renames: isInputFocusedisKeypadOpen,
    setIsInputFocusedsetIsKeypadOpen.

Legacy PredictBuyPreview
(views/PredictBuyPreview)

  • Local useState(true) renamed to isKeypadOpen for consistency with
    the rest of the tree. No behaviour change (still initialises true,
    still gates renderBottomContent).

Tests

  • All prop/state references renamed.
  • New coverage on usePredictBuyInputState for initialKeypadOpen: false and initialKeypadOpen: true.
  • New assertions in PredictBuyWithAnyToken.test.tsx that the hook is
    called with initialKeypadOpen: false in sheet mode and
    initialKeypadOpen: true in non-sheet mode.
  • PredictBuyBottomContent.test.tsx: removed the now-irrelevant
    isInputFocused is true / false describe blocks since visibility is
    caller-controlled.
  • PredictPayWithAnyTokenInfo.test.tsx: updated 43 prop references and
    renamed two test descriptions to reflect the new "relay deferral"
    intent.
  • PredictBuyPreview.test.tsx: updated renderBottomContent describe
    block descriptions.

Net behavioural effect (sheet mode)

  • Sheet opens → keypad hidden, amount display shows $0 (inactive),
    quick amounts + pay-with row + fee summary + Confirm button visible
    (Confirm disabled until amount > 0).
  • Tap amount display → keypad opens at the bottom of the sheet, stacked
    below the Confirm button (does not overlap).
  • Tap a quick amount → value set, keypad closes.
  • Tap Confirm with keypad still open → places order (relay setup is
    up-to-date because shouldDeferRelaySetup is false in sheet mode).
  • Auto-blur on banner display still works.

Legacy full-screen flow: unchanged.

Changelog

CHANGELOG entry: null

Related issues

Refs: PRED-707 (follow-up to
#28779)

Manual testing steps

Feature: Predict buy bottom-sheet keypad initial state

  Scenario: Sheet opens with keypad collapsed (flag ON)
    Given the predictBottomSheet feature flag is enabled
    And the user is on a prediction market details page

    When user taps a "Yes" or "No" outcome button
    Then a bottom sheet opens with the buy preview content
    And the custom numeric keypad is NOT visible
    And the amount display shows "$0"
    And the quick amount buttons ($20 / $50 / $100 / $250) are visible
    And the Pay with row, fee summary and Confirm button are visible
    And the Confirm button is disabled

  Scenario: Tapping the amount opens the keypad
    Given the buy bottom sheet is open with no amount entered
    And the keypad is hidden

    When user taps the amount display
    Then the custom keypad becomes visible at the bottom of the sheet
    And the keypad does NOT cover the Confirm button
    And the amount display shows the active highlight

  Scenario: Tapping a quick amount sets value and closes the keypad
    Given the keypad is open
    And the user has typed "$73"

    When user taps the "$50" quick amount button
    Then the amount becomes "$50"
    And the keypad closes
    And haptic feedback fires (Light impact)
    And the Confirm button is enabled

  Scenario: Confirming with keypad still open
    Given the keypad is open
    And the user has typed "$25"

    When user taps the Confirm button
    Then the order is placed successfully
    And the relay was configured for $25 (no underfunded deposit)
    And the sheet closes

  Scenario: Banner display auto-blurs the keypad in sheet mode
    Given the buy bottom sheet is open
    And the keypad is open

    When an order_failed or price_changed banner appears
    Then the keypad auto-closes
    And the banner + Retry CTA are visible without needing to tap Done

  Scenario: Legacy full-screen flow is unchanged (flag OFF)
    Given the predictBottomSheet feature flag is disabled
    And the user navigates to the BuyPreview screen

    When the screen mounts
    Then the keypad is open by default (matching previous behaviour)
    And tapping Done closes the keypad and reveals the bottom content
    And the relay is configured once on Done (deferred during typing)

Screenshots/Recordings

Before

Sheet opens with the keypad rendered behind the bottom content; bottom
content and keypad are both visible simultaneously on first paint. (See
screenshot in PR comments — keypad sits below Confirm even though the
user has not yet tapped the amount display.)

After

Sheet opens with the keypad hidden; bottom content (quick amounts, pay
with row, fee summary, Confirm) is the only thing visible. Tapping the
amount display reveals the keypad; tapping a quick amount or Confirm
collapses it.

bottomSheetKeyboardFix.mov

Pre-merge author checklist

Performance checks (if applicable)

  • I've tested on Android
    • Ideally on a mid-range device; emulator is acceptable
  • I've tested with a power user scenario
  • Use these power-user
    SRPs

    to import wallets with many accounts and tokens
  • I've instrumented key operations with Sentry traces for production
    performance metrics
  • See trace() for usage and
    addToken
    for an example

N/A — pure refactor of UI state semantics, no new code paths.

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the
    app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described
    in the ticket it closes and includes the necessary testing evidence such
    as recordings and or screenshots.

Note

Medium Risk
Changes how the Predict buy keypad state is tracked and used to gate
bottom content and mm_pay relay setup; mistakes could surface as
incorrect UI visibility or misconfigured deposit/payment amounts during
checkout. Scope is mostly contained to Predict buy views and covered by
updated tests.

Overview
Fixes the Predict buy bottom-sheet keypad initial state by splitting
the previously overloaded isInputFocused flag into a dedicated
isKeypadOpen state, including a new initialKeypadOpen option in
usePredictBuyInputState so sheet mode can start closed.

Updates
PredictBuyWithAnyToken/PredictKeypad/PredictBuyAmountSection to
use isKeypadOpen for keypad mounting and active styling, moves
bottom-content visibility control to the parent (removing
PredictBuyBottomContent’s internal early-return), and introduces
shouldDeferRelaySetup (replacing isInputFocused) to explicitly gate
PredictPayWithAnyTokenInfo’s relay-configuration side effects.

Refactors legacy PredictBuyPreview to the same isKeypadOpen naming
and updates/adds tests to assert the new initialization, visibility
behavior, and relay deferral propagation.

Reviewed by Cursor Bugbot for commit
98d3d4d. Bugbot is set up for automated
code reviews on this repo. Configure
here.

[f2b07b6](https://github.com/MetaMask/metamask-mobile/commit/f2b07b68c53d132a1cd38c1a32d57813e82f0fc4)

…p-7.78.0 (#30483)

## **Description**

The Predict Buy bottom-sheet introduced in
[#28779](#28779) opened
with the custom keypad already rendered behind the rest of the sheet
content. The root cause was a single `isInputFocused` boolean inside
`usePredictBuyInputState` that was hard-coded to `true` on first render
and overloaded across **five** unrelated behaviours:

1. Mounting `PredictKeypad`.
2. Highlighting the amount-display "active" state.
3. Hiding `PredictBuyBottomContent` while the keypad is up.
4. Disabling `PredictFeeSummary`.
5. Deferring the mm_pay relay-config side effects (`updatePendingAmount`
/ `setPayToken`) inside `PredictPayWithAnyTokenInfo`.

Because the only "off switch" was `setIsInputFocused(false)` (driven
from a `Done` button that doesn't exist in sheet mode) the previous PR
worked around the issue with three `isSheetMode ? false :
isInputFocused` ternaries — Bugbot flagged this on #28779. The
workarounds also produced a confusing situation where bottom content and
keypad rendered simultaneously on first sheet open.

This PR separates the conflated meanings, parameterises the initial
state, and removes the workarounds. No behaviour change for the legacy
full-screen flow.

### What changed

**`usePredictBuyInputState`
([hooks/usePredictBuyInputState.ts](app/components/UI/Predict/views/PredictBuyWithAnyToken/hooks/usePredictBuyInputState.ts))**
- Accepts an options object: `{ initialKeypadOpen = true }`.
- Renamed state + setter: `isInputFocused` → `isKeypadOpen`,
`setIsInputFocused` → `setIsKeypadOpen`.
- Default value preserves legacy behaviour; sheet mode opts out.

**`PredictBuyWithAnyToken`
([PredictBuyWithAnyToken.tsx](app/components/UI/Predict/views/PredictBuyWithAnyToken/PredictBuyWithAnyToken.tsx))**
- Calls the hook as `usePredictBuyInputState({ initialKeypadOpen:
!isSheetMode })` so the sheet opens with the keypad collapsed.
- Removed the three `isSheetMode ? false : isInputFocused` patches.
- Renders `PredictBuyBottomContent` at the call site via `{(isSheetMode
|| !isKeypadOpen) && <PredictBuyBottomContent ... />}` instead of
relying on the component to bail internally. Keeps the legacy "hide
while typing" behaviour while letting the sheet show the bottom content
(and Confirm) at all times.
- Passes the new self-documenting `shouldDeferRelaySetup={!isSheetMode
&& isKeypadOpen}` prop to `PredictPayWithAnyTokenInfo`. Same effective
value as before, but the prop name now describes its actual purpose.

**`PredictPayWithAnyTokenInfo`
([components/PredictPayWithAnyTokenInfo](app/components/UI/Predict/views/PredictBuyWithAnyToken/components/PredictPayWithAnyTokenInfo/PredictPayWithAnyTokenInfo.tsx))**
- Renamed prop: `isInputFocused` → `shouldDeferRelaySetup`. This is the
genuine separation: the prop is **not** about UI keypad state, it's
about pausing `updatePendingAmount` / `setPayToken` calls. Legacy mode
still defers until the user taps Done; sheet mode never defers (relay
must update on every keystroke since there's no Done and the user can
tap Confirm with the keypad still open — preventing underfunded
deposits).
- Added a JSDoc comment explaining the contract.

**`PredictBuyBottomContent`
([components/PredictBuyBottomContent](app/components/UI/Predict/views/PredictBuyWithAnyToken/components/PredictBuyBottomContent/PredictBuyBottomContent.tsx))**
- Removed the `isInputFocused` prop and the `if (isInputFocused) return
null` early return. Component is now purely structural; visibility is
the parent's responsibility.

**`PredictKeypad` and `PredictBuyAmountSection`**
- Mechanical prop renames: `isInputFocused` → `isKeypadOpen`,
`setIsInputFocused` → `setIsKeypadOpen`.

**Legacy `PredictBuyPreview`
([views/PredictBuyPreview](app/components/UI/Predict/views/PredictBuyPreview/PredictBuyPreview.tsx))**
- Local `useState(true)` renamed to `isKeypadOpen` for consistency with
the rest of the tree. No behaviour change (still initialises `true`,
still gates `renderBottomContent`).

**Tests**
- All prop/state references renamed.
- New coverage on `usePredictBuyInputState` for `initialKeypadOpen:
false` and `initialKeypadOpen: true`.
- New assertions in `PredictBuyWithAnyToken.test.tsx` that the hook is
called with `initialKeypadOpen: false` in sheet mode and
`initialKeypadOpen: true` in non-sheet mode.
- `PredictBuyBottomContent.test.tsx`: removed the now-irrelevant
`isInputFocused is true / false` describe blocks since visibility is
caller-controlled.
- `PredictPayWithAnyTokenInfo.test.tsx`: updated 43 prop references and
renamed two test descriptions to reflect the new "relay deferral"
intent.
- `PredictBuyPreview.test.tsx`: updated `renderBottomContent` describe
block descriptions.

### Net behavioural effect (sheet mode)

- Sheet opens → keypad hidden, amount display shows `$0` (inactive),
quick amounts + pay-with row + fee summary + Confirm button visible
(Confirm disabled until amount > 0).
- Tap amount display → keypad opens at the bottom of the sheet, stacked
**below** the Confirm button (does not overlap).
- Tap a quick amount → value set, keypad closes.
- Tap Confirm with keypad still open → places order (relay setup is
up-to-date because `shouldDeferRelaySetup` is `false` in sheet mode).
- Auto-blur on banner display still works.

Legacy full-screen flow: unchanged.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Refs: PRED-707 (follow-up to
[#28779](#28779))

## **Manual testing steps**

```gherkin
Feature: Predict buy bottom-sheet keypad initial state

  Scenario: Sheet opens with keypad collapsed (flag ON)
    Given the predictBottomSheet feature flag is enabled
    And the user is on a prediction market details page

    When user taps a "Yes" or "No" outcome button
    Then a bottom sheet opens with the buy preview content
    And the custom numeric keypad is NOT visible
    And the amount display shows "$0"
    And the quick amount buttons ($20 / $50 / $100 / $250) are visible
    And the Pay with row, fee summary and Confirm button are visible
    And the Confirm button is disabled

  Scenario: Tapping the amount opens the keypad
    Given the buy bottom sheet is open with no amount entered
    And the keypad is hidden

    When user taps the amount display
    Then the custom keypad becomes visible at the bottom of the sheet
    And the keypad does NOT cover the Confirm button
    And the amount display shows the active highlight

  Scenario: Tapping a quick amount sets value and closes the keypad
    Given the keypad is open
    And the user has typed "$73"

    When user taps the "$50" quick amount button
    Then the amount becomes "$50"
    And the keypad closes
    And haptic feedback fires (Light impact)
    And the Confirm button is enabled

  Scenario: Confirming with keypad still open
    Given the keypad is open
    And the user has typed "$25"

    When user taps the Confirm button
    Then the order is placed successfully
    And the relay was configured for $25 (no underfunded deposit)
    And the sheet closes

  Scenario: Banner display auto-blurs the keypad in sheet mode
    Given the buy bottom sheet is open
    And the keypad is open

    When an order_failed or price_changed banner appears
    Then the keypad auto-closes
    And the banner + Retry CTA are visible without needing to tap Done

  Scenario: Legacy full-screen flow is unchanged (flag OFF)
    Given the predictBottomSheet feature flag is disabled
    And the user navigates to the BuyPreview screen

    When the screen mounts
    Then the keypad is open by default (matching previous behaviour)
    And tapping Done closes the keypad and reveals the bottom content
    And the relay is configured once on Done (deferred during typing)
```

## **Screenshots/Recordings**

### **Before**

Sheet opens with the keypad rendered behind the bottom content; bottom
content and keypad are both visible simultaneously on first paint. (See
screenshot in PR comments — keypad sits below `Confirm` even though the
user has not yet tapped the amount display.)

### **After**

Sheet opens with the keypad hidden; bottom content (quick amounts, pay
with row, fee summary, Confirm) is the only thing visible. Tapping the
amount display reveals the keypad; tapping a quick amount or Confirm
collapses it.



https://github.com/user-attachments/assets/af89b78a-5103-48eb-b2c9-346ea64cd463


<!-- TODO: attach .mov / screenshots once recorded against the branch
-->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

> N/A — pure refactor of UI state semantics, no new code paths.

## **Pre-merge reviewer checklist**

- [x] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes how the Predict buy keypad state is tracked and used to gate
bottom content and mm_pay relay setup; mistakes could surface as
incorrect UI visibility or misconfigured deposit/payment amounts during
checkout. Scope is mostly contained to Predict buy views and covered by
updated tests.
> 
> **Overview**
> Fixes the Predict buy bottom-sheet keypad initial state by splitting
the previously overloaded `isInputFocused` flag into a dedicated
`isKeypadOpen` state, including a new `initialKeypadOpen` option in
`usePredictBuyInputState` so sheet mode can start closed.
> 
> Updates
`PredictBuyWithAnyToken`/`PredictKeypad`/`PredictBuyAmountSection` to
use `isKeypadOpen` for keypad mounting and active styling, moves
bottom-content visibility control to the parent (removing
`PredictBuyBottomContent`’s internal early-return), and introduces
`shouldDeferRelaySetup` (replacing `isInputFocused`) to explicitly gate
`PredictPayWithAnyTokenInfo`’s relay-configuration side effects.
> 
> Refactors legacy `PredictBuyPreview` to the same `isKeypadOpen` naming
and updates/adds tests to assert the new initialization, visibility
behavior, and relay deferral propagation.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
98d3d4d. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@runway-github runway-github Bot requested a review from a team as a code owner May 21, 2026 00:54
@github-actions

Copy link
Copy Markdown
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@metamaskbotv2 metamaskbotv2 Bot added the team-bots Bot team (for MetaMask Bot, Runway Bot, etc.) label May 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

⏭️ Smart E2E selection skipped - PR targets a release branch (release/*)

All E2E tests pre-selected.

View GitHub Actions results

@sonarqubecloud

Copy link
Copy Markdown

@sleepytanya

Copy link
Copy Markdown
Contributor

LGTM

@sleepytanya sleepytanya merged commit 7e00e32 into release/7.78.0 May 21, 2026
113 checks passed
@sleepytanya sleepytanya deleted the runway-cherry-pick-7.78.0-1779324860 branch May 21, 2026 02:08
@github-actions github-actions Bot locked and limited conversation to collaborators May 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

size-L team-bots Bot team (for MetaMask Bot, Runway Bot, etc.)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants