fix(ramps): dynamic per-provider BottomSheet background for checkout WebView (TMCU-1087)#33429
fix(ramps): dynamic per-provider BottomSheet background for checkout WebView (TMCU-1087)#33429georgewrmarshall wants to merge 12 commits into
Conversation
|
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. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Transak native checkout skips colors
- Checkout now falls back to providerName when providerCode is missing, enabling Transak color mapping.
Or push these changes by commenting:
@cursor push 4372fdb7e7
Preview (4372fdb7e7)
diff --git a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx
--- a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx
+++ b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx
@@ -120,7 +120,9 @@
headlessSessionId,
} = params ?? {};
- const providerColors = getProviderWebviewColors(providerCode);
+ const providerColors = getProviderWebviewColors(
+ providerCode ?? providerName,
+ );
const isDark = themeAppearance === AppThemeKey.dark;
const providerBg = providerColors
? isDarkYou can send follow-ups to the cloud agent here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Missing balance cache skips check
- If the balance cache is missing or undefined we now assume an existing balance, avoiding the first-time deposit animation for restored or funded accounts.
- ✅ Fixed: Search clear spurious abandon
- After a result tap we clear the last-emitted search refs so a subsequent clear does not emit a spurious PERPS_SEARCH_ABANDONED.
Or push these changes by commenting:
@cursor push 3ae56def83
Preview (3ae56def83)
diff --git a/app/components/UI/Money/utils/firstTimeDeposit.ts b/app/components/UI/Money/utils/firstTimeDeposit.ts
--- a/app/components/UI/Money/utils/firstTimeDeposit.ts
+++ b/app/components/UI/Money/utils/firstTimeDeposit.ts
@@ -47,8 +47,11 @@
MoneyAccountBalanceServiceQueryKeys.GET_MONEY_ACCOUNT_BALANCE,
primaryMoneyAccount.address,
]);
- if (!balance?.totalBalance) {
- return false;
+ // If the cache is missing or the shape is unknown, err on the side of
+ // assuming an existing balance so we do NOT incorrectly show a first-time
+ // deposit animation for restored/already-funded accounts.
+ if (!balance || balance.totalBalance == null) {
+ return true;
}
const totalBalance = new BigNumber(balance.totalBalance);
diff --git a/app/components/UI/Perps/Views/PerpsMarketListView/PerpsMarketListView.tsx b/app/components/UI/Perps/Views/PerpsMarketListView/PerpsMarketListView.tsx
--- a/app/components/UI/Perps/Views/PerpsMarketListView/PerpsMarketListView.tsx
+++ b/app/components/UI/Perps/Views/PerpsMarketListView/PerpsMarketListView.tsx
@@ -266,6 +266,10 @@
[PERPS_EVENT_PROPERTY.ASSET]: market.symbol,
});
searchResultTappedRef.current = true;
+ // Clear the last-emitted query so a subsequent explicit clear does NOT
+ // emit a spurious PERPS_SEARCH_ABANDONED for a search that converted.
+ lastEmittedSearchQueryRef.current = '';
+ lastEmittedSearchResultsCountRef.current = undefined;
} else if (showFavoritesOnly) {
source_section = PERPS_EVENT_VALUE.SOURCE_SECTION.WATCHLIST;
} else if (marketTypeFilter !== 'all') {You can send follow-ups to the cloud agent here.
6032ebe to
50a9c74
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: WebView loses theme background
- Set WebView backgroundColor to providerBg with a theme default fallback to prevent white flash when provider color is unknown.
Or push these changes by commenting:
@cursor push f6dcd2770e
Preview (f6dcd2770e)
diff --git a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts
--- a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts
+++ b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts
@@ -8,8 +8,9 @@
},
webview: {
// providerBg matches the provider iframe — prevents a flash before content loads.
- // Falls back to undefined (transparent) if the provider color is unknown.
- backgroundColor: params.providerBg,
+ // Falls back to the theme surface when the provider color is unknown.
+ backgroundColor:
+ params.providerBg ?? params.theme.colors.background.default,
},
});
diff --git a/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts b/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts
--- a/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts
+++ b/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts
@@ -8,8 +8,9 @@
},
webview: {
// providerBg matches the provider iframe — prevents a flash before content loads.
- // Falls back to undefined (transparent) if the provider color is unknown.
- backgroundColor: params.providerBg,
+ // Falls back to the theme surface when the provider color is unknown.
+ backgroundColor:
+ params.providerBg ?? params.theme.colors.background.default,
},
});You can send follow-ups to the cloud agent here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: WebView bg ignores useStyles vars
- Updated both Checkout style sheets to read backgroundColor from params.vars.providerBg and adjusted the params type accordingly.
Or push these changes by commenting:
@cursor push 7328c91260
Preview (7328c91260)
diff --git a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts
--- a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts
+++ b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts
@@ -1,7 +1,10 @@
import { StyleSheet } from 'react-native';
import { Theme } from '../../../../../../util/theme/models';
-const styleSheet = (params: { theme: Theme; providerBg?: string }) =>
+const styleSheet = (params: {
+ theme: Theme;
+ vars: { providerBg?: string };
+}) =>
StyleSheet.create({
headerWithoutPadding: {
paddingVertical: 0,
@@ -10,7 +13,7 @@
// Matches the provider iframe background to suppress the native white
// flash before content loads. Falls back to undefined (transparent) for
// unknown providers.
- backgroundColor: params.providerBg,
+ backgroundColor: params.vars.providerBg,
},
});
diff --git a/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts b/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts
--- a/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts
+++ b/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts
@@ -1,7 +1,10 @@
import { StyleSheet } from 'react-native';
import { Theme } from '../../../../../util/theme/models';
-const styleSheet = (params: { theme: Theme; providerBg?: string }) =>
+const styleSheet = (params: {
+ theme: Theme;
+ vars: { providerBg?: string };
+}) =>
StyleSheet.create({
headerWithoutPadding: {
paddingVertical: 0,
@@ -10,7 +13,7 @@
// Matches the provider iframe background to suppress the native white
// flash before content loads. Falls back to undefined (transparent) for
// unknown providers.
- backgroundColor: params.providerBg,
+ backgroundColor: params.vars.providerBg,
},
});You can send follow-ups to the cloud agent here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: WebView fallback mismatches sheet
- Updated both Checkout WebView styles to fallback to getElevatedSurfaceColor(theme) instead of background.alternative, matching BottomSheet and eliminating the visible seam.
Or push these changes by commenting:
@cursor push 45ae0c4cd7
Preview (45ae0c4cd7)
diff --git a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts
--- a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts
+++ b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts
@@ -1,5 +1,6 @@
import { StyleSheet } from 'react-native';
import { Theme } from '../../../../../../util/theme/models';
+import { getElevatedSurfaceColor } from '../../../../../../util/theme/themeUtils';
const styleSheet = (params: { theme: Theme; providerBg?: string }) =>
StyleSheet.create({
@@ -8,10 +9,9 @@
},
webview: {
// Matches the provider iframe background to suppress the native white
- // flash before content loads. Falls back to background.alternative
- // (the BottomSheet default) for unknown providers.
- backgroundColor:
- params.providerBg ?? params.theme.colors.background.alternative,
+ // flash before content loads. Falls back to the elevated surface color
+ // used by BottomSheet chrome for unknown providers.
+ backgroundColor: params.providerBg ?? getElevatedSurfaceColor(params.theme),
},
});
diff --git a/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts b/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts
--- a/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts
+++ b/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts
@@ -1,5 +1,6 @@
import { StyleSheet } from 'react-native';
import { Theme } from '../../../../../util/theme/models';
+import { getElevatedSurfaceColor } from '../../../../../util/theme/themeUtils';
const styleSheet = (params: { theme: Theme; providerBg?: string }) =>
StyleSheet.create({
@@ -8,10 +9,9 @@
},
webview: {
// Matches the provider iframe background to suppress the native white
- // flash before content loads. Falls back to background.alternative
- // (the BottomSheet default) for unknown providers.
- backgroundColor:
- params.providerBg ?? params.theme.colors.background.alternative,
+ // flash before content loads. Falls back to the elevated surface color
+ // used by BottomSheet chrome for unknown providers.
+ backgroundColor: params.providerBg ?? getElevatedSurfaceColor(params.theme),
},
});You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit ef3e67d. Configure here.
…t (TMCU-1087) Replaces the hardcoded Transak-specific colors with a provider lookup so the checkout BottomSheet surface always matches the embedded iframe, regardless of which provider is shown. Adds app/components/UI/Ramp/constants/providerWebviewColors.ts — a single source of truth mapping provider IDs to their iframe background colors for dark and light mode. Unknown providers fall back to the design-system surface. Applies to both the native unified flow (twClassName on the MMDS BottomSheet) and the legacy aggregator flow (style prop on the component-library BottomSheet). The WebView backgroundColor is also set via the same lookup to prevent a content-load flash. Transak is the only provider with known colors so far. TODO entries in the constants file mark the remaining ~6 providers pending visual verification.
… (TMCU-1087) Moves the dark-mode hex values to app/styles/common.ts (the established home for custom HEX constants) and imports brandColor.white from @metamask/design-tokens for all light-mode entries, reducing standalone hex literals. Adds MoonPay checkout dark color (#131416). Banxa and remaining providers are still marked as TODOs pending visual verification.
… background.alternative (TMCU-1087)
6e67f34 to
a605387
Compare
…olors with default fallback (TMCU-1087) - Adds a 'default' entry to PROVIDER_WEBVIEW_COLORS mirroring the MMDS BottomSheet surface (background.alternative dark / background.default light), so the function never returns undefined and the WebView always has a matching background. - Fixes stylesheet params to use params.vars.providerBg — useStyles wraps extra params in a vars object, so params.providerBg was always undefined before, meaning the WebView backgroundColor was never actually being applied.
… string (TMCU-1087)
| // color is known, ensuring the WebView background always matches the sheet. | ||
| default: { | ||
| dark: darkTheme.colors.background.alternative, | ||
| light: lightTheme.colors.background.default, |
There was a problem hiding this comment.
The 'default' entry lives in the map rather than as a separate constant so it naturally participates in the same lookup path. Any provider ID not matched by the substring search resolves to this entry, giving the WebView a background that matches the MMDS BottomSheet's own default surface without any special-case logic in the caller.
| const key = | ||
| providerCode && | ||
| Object.keys(PROVIDER_WEBVIEW_COLORS).find( | ||
| (k) => k !== 'default' && providerCode.toLowerCase().includes(k), |
There was a problem hiding this comment.
Substring matching (e.g. 'transak' matches 'transak-native') is intentional — ramp provider IDs often have suffixes like '-native' or '-v2' that vary across SDK versions. The k !== 'default' guard keeps the sentinel entry from accidentally matching provider IDs that happen to contain the word 'default'.
| import { Theme } from '../../../../../util/theme/models'; | ||
|
|
||
| const styleSheet = (params: { theme: Theme }) => | ||
| const styleSheet = (params: { theme: Theme; vars: { providerBg: string } }) => |
There was a problem hiding this comment.
The useStyles hook wraps any extra params in a vars object at runtime, so accessing params.providerBg directly would always be undefined. The type annotation here makes the correct access path (params.vars.providerBg) explicit and enforced by TypeScript. This was the root cause of the WebView backgroundColor never being applied before this fix.
| isFullscreen | ||
| isInteractable={!Device.isAndroid()} | ||
| keyboardAvoidingViewEnabled={false} | ||
| style={providerBgStyle} |
There was a problem hiding this comment.
The component-library BottomSheet accepts a style prop that is spread last onto the dialog surface container, so it correctly overrides the default background. This is the legacy aggregator flow — the MMDS BottomSheet used in the native flow uses twClassName with Tailwind arbitrary values instead (style goes to the KeyboardAvoidingView wrapper there, not the surface).
| isFullscreen | ||
| isInteractable={!Device.isAndroid()} | ||
| keyboardAvoidingViewEnabled={false} | ||
| twClassName={providerBgTwClassName} |
There was a problem hiding this comment.
The MMDS BottomSheet's style prop applies to its KeyboardAvoidingView wrapper, not the dialog surface itself. The twClassName prop with a Tailwind arbitrary value (bg-[#hex]) is the correct way to override the surface background in MMDS. The computed class is built from the resolved providerBg string so it adapts dynamically to both the provider and current theme.
| // outside our control. We match them in the checkout BottomSheet so the | ||
| // native chrome feels seamless with the embedded webview. Update only if | ||
| // a provider changes their iframe theme colors. | ||
| transakCheckoutDark: '#1a1a1a', |
There was a problem hiding this comment.
Only dark-mode hex values are stored here because light-mode for all current providers is plain white (brandColor.white). If a provider ever needs a non-white light-mode background, this pattern would need to extend to a light constant as well. The dark values can't use design tokens because they are provider-specific colors outside the design system.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #33429 +/- ##
==========================================
+ Coverage 84.42% 84.47% +0.04%
==========================================
Files 6139 6160 +21
Lines 164072 164650 +578
Branches 40080 40236 +156
==========================================
+ Hits 138523 139088 +565
- Misses 16054 16056 +2
- Partials 9495 9506 +11 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| // Matches the provider iframe background to suppress the native white | ||
| // flash before content loads. Unknown providers receive the BottomSheet | ||
| // default surface color from getProviderWebviewColors. | ||
| backgroundColor: params.vars.providerBg, |
There was a problem hiding this comment.
|







Description
The Buy flow checkout opens a provider's payment page inside a native
BottomSheet. Because we have no control over the background color of the provider's embedded webview, there is a visible mismatch between the BottomSheet surface and the provider's iframe — the native chrome looks like a container rather than a seamless extension of the checkout experience.This mismatch existed before, but the pure-black BottomSheet update (MMDS) made it significantly more jarring — particularly with Transak, one of our top providers, whose iframe uses a near-black dark background that reads as a visible border against the default sheet surface.
To improve the experience we are intentionally overriding the design system BottomSheet background color to match each provider's known iframe background. This breaks the design system convention deliberately: the goal is that users should not perceive the native BottomSheet as a separate container from the provider's checkout page, making the flow feel more immersive and native.
What this PR does:
getProviderWebviewColors(providerCode, isDark)— a utility that maps known provider IDs (transak,moonpay,banxa) to their dark/light iframe backgrounds, with a safe fallback to the default MMDS BottomSheet surface for unknown providers.app/styles/common.tsalongside other custom constants.BottomSheetsurface and theWebViewbackground — so neither the sheet chrome nor the brief pre-load gap shows a mismatched color.Changelog
CHANGELOG entry: Improved the provider checkout experience in the Buy flow by making the BottomSheet background match the provider's embedded payment page
Related issues
Refs: TMCU-1087
Manual testing steps
Screenshots/Recordings
Transak
MoonPay
Banxa
After
ramps.after.mov
Pre-merge author checklist
Performance checks (if applicable)
trace()for usage andaddTokenfor an exampleFor performance guidelines and tooling, see the Performance Guide.
Pre-merge reviewer checklist