Skip to content

Feature: Update fallback paywall#3178

Merged
JZDesign merged 20 commits into
jzdesign/fallback-paywall-4-wire-validationfrom
jzdesign/fallback-paywall-plug-in-the-ui
Mar 12, 2026
Merged

Feature: Update fallback paywall#3178
JZDesign merged 20 commits into
jzdesign/fallback-paywall-4-wire-validationfrom
jzdesign/fallback-paywall-plug-in-the-ui

Conversation

@JZDesign

@JZDesign JZDesign commented Mar 5, 2026

Copy link
Copy Markdown
Contributor

Wires in the default paywall into the sdk for use. Adds previews for emerge snapshot testing

Part 5 of the fallback paywall feature breaking up #2945. Stacked on #3134


Note

Medium Risk
Introduces a new paywall rendering path that can trigger purchase/restore actions from the fallback UI, so regressions could affect paywall display and transaction initiation when validation warnings occur.

Overview
Adds a fallback “default paywall” flow: InternalPaywall now renders DefaultPaywallView whenever a legacy paywall loads with a validationWarning, wiring its purchase/restore actions through the existing PaywallViewModel and reusing a shared screenModeBackground modifier.

Refactors DefaultPaywallView layout to use Scaffold with a fixed footer and LazyColumn, adds preview-only overrides plus new Compose previews, and introduces a deterministic DualColorImageGenerator used by previews and a new AppStyleExtractor prominent-colors test. Also removes developer-facing debug strings from strings.xml in favor of local constants, and updates .gitignore to exclude .cursor/.

Written by Cursor Bugbot for commit 42d3af2. This will update automatically on new commits. Configure here.

JZDesign added 3 commits March 5, 2026 09:02
When a paywall has a validation warning, render the DefaultPaywallView (with CloseButton) instead of the normal template. Cache the Activity and a CoroutineScope via LocalContext.current.getActivity() and rememberCoroutineScope(), and launch purchase/restore handlers from the scope (logging if Activity is null). Also update CompositionLocalProvider to reuse the cached activity. Added necessary imports for DefaultPaywallView, rememberCoroutineScope and kotlinx.coroutines.launch.
Introduce DualColorImageGenerator for deterministic preview/test images and add multiple DefaultPaywall previews (including dark and localized variants). Extend DefaultPaywallView with DefaultPaywallPreviewOverrides to allow injecting appName, appIconBitmap, prominentColors and isDebugBuild for previews, and only run color extraction when needed. Update AppStyleExtractor tests to use the generator images and adjust assertions accordingly. Also add .cursor/.gitignore to ignore plans/.
Make previews and the paywall layout respect light/dark themes and avoid content being obscured by the footer. Previews now pick a colorScheme from isSystemInDarkTheme and apply a background. DefaultPaywallView measures the footer height (onSizeChanged + LocalDensity) and adds matching bottom padding to the scrollable content so items aren’t hidden behind the footer; removed the hard background on the footer and adjusted text colors to use MaterialTheme.colorScheme.onSurface. Minor import and layout adjustments to support these changes.
@JZDesign JZDesign changed the base branch from main to jzdesign/fallback-paywall-4-wire-validation March 5, 2026 15:34
@JZDesign JZDesign marked this pull request as ready for review March 5, 2026 16:38
@JZDesign JZDesign requested a review from a team as a code owner March 5, 2026 16:38
@JZDesign JZDesign requested a review from a team March 5, 2026 16:40
Comment thread .cursor/.gitignore Outdated

@vegaro vegaro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Tested it both in debug and release and it looks great. Good job! Good to go after you take a look at those suggestions 😄

Replace manual Column layout and footer measurement with a Scaffold and bottomBar. Extract the footer into DefaultPaywallFooter composable and use LazyColumn for the product list (selectableGroup, contentPadding). Remove LocalDensity/footerHeight tracking and rely on Scaffold padding/navigationBarsPadding to handle insets. Preserve existing warning/app icon logic and update imports accordingly for a cleaner, more resilient layout.
@emerge-tools

emerge-tools Bot commented Mar 11, 2026

Copy link
Copy Markdown

📸 Snapshot Test

Base build not found

No build was found for the base commit 144849b. This is required to generate a snapshot diff for your pull request.

It's possible that you created a branch off the base commit before all of the CI steps have finished processing, e.g. the one that uploads a build to our system. If that's the case, no problem! Just wait and this will eventually resolve.


🛸 Powered by Emerge Tools

@JZDesign

JZDesign commented Mar 11, 2026

Copy link
Copy Markdown
Contributor Author

@vegaro The lint is failing because we aren't localizing everything (the debug strings were supposed to be English only)

Should I localize? I can't find a way to ignore the lint rules in the strings.xml file… I suppose I could not put them in there at all, and then supress warnings in the composable that come from using strings that aren't in the strings.xml file…

Add a new DeveloperFacingPaywallStrings object (debug-only) with GO_TO_DASHBOARD and TITLE constants, and update DefaultPaywallWarning and DefaultPaywallView to reference it instead of the previous DebugPaywallStrings. The old inline DebugPaywallStrings definition was removed to centralize developer-facing debug strings; no functional behavior changes.
@JZDesign JZDesign merged commit 5f0de5b into jzdesign/fallback-paywall-4-wire-validation Mar 12, 2026
5 of 25 checks passed
@JZDesign JZDesign deleted the jzdesign/fallback-paywall-plug-in-the-ui branch March 12, 2026 14:57
JZDesign added a commit that referenced this pull request Mar 12, 2026
## Summary
- Adds validationWarning: PaywallWarning? field to
PaywallState.Loaded.Legacy
- Threads validationWarning through
OfferingToStateMapper.toLegacyPaywallState
- Passes validationResult.warning from PaywallViewModel.calculateState
to the state mapper

When a paywall fails validation, the warning is now carried on the
loaded state, ready for the UI layer (PR 5) to check and render the
DefaultPaywallView instead of the template paywall.

Part 4 of the fallback paywall feature breaking up #2945. Stacked on
#3133.

- Add PaywallWarning type for fallback paywall #3131 
- Add color helpers, AppStyleExtractor, and CircleOutlined icon #3132
- Add default paywall UI components #3133
- Wire validation warning through paywall state pipeline #3134 (This PR)
- Plug in Paywall #3178
- Remove Big background image and clean up CI


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk additive change: it only carries an optional `PaywallWarning`
through state creation and does not alter validation or rendering logic
yet.
> 
> **Overview**
> **Threads paywall validation warnings through the Legacy paywall
state.** `PaywallState.Loaded.Legacy` now includes an optional
`validationWarning: PaywallWarning?`.
> 
> `PaywallViewModel.calculateState` passes `validationResult.warning`
into `Offering.toLegacyPaywallState`, and the mapper stores it on the
created legacy state so the UI can later decide to show a
fallback/default paywall when validation issues occur.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
64f72f8. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

count = 2,
dispatcher = Dispatchers.Main,
)
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

runBlocking with Dispatchers.Main risks deadlock on main thread

Medium Severity

DualColorImageGenerator.create calls runBlocking containing getProminentColorsFromBitmap(dispatcher = Dispatchers.Main), which internally does withContext(Dispatchers.Main). If the lazy properties (redGreen, blueGreen, purpleOrange) are first accessed from the main thread — as happens during Compose preview rendering — runBlocking blocks the main thread while withContext(Dispatchers.Main) tries to dispatch work back to it, causing a deadlock. Using Dispatchers.Default (or Dispatchers.Unconfined) instead of Dispatchers.Main would avoid this.

Fix in Cursor Fix in Web

github-merge-queue Bot pushed a commit that referenced this pull request Mar 13, 2026
## Summary
- Adds DefaultPaywallView composable: the main fallback paywall with
dynamic app icon theming, package list, purchase and restore buttons
- Adds DefaultPaywallWarning: debug-only warning panel showing what went
wrong, with RevenueCat branding and dashboard links
- Adds DefaultProductCell: selectable product row with animated color
transitions
- Adds RevenueCat branding image and string resources for the default
paywall

Part 3 of the fallback paywall feature breaking up #2945. Stacked on
#3132.

- Add PaywallWarning type for fallback paywall #3131 
- Add color helpers, AppStyleExtractor, and CircleOutlined icon #3132
- Add default paywall UI components #3133 (This PR)
- Wire validation warning through paywall state pipeline #3134
- Plug in Paywall #3178
- Remove Big background image and clean up CI



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Adds a new fallback paywall UI path for legacy paywalls and threads a
new `validationWarning` through state mapping, which can change what
users see when paywall validation fails (though warning UI is
debug-only). Main risk is UI/behavior regressions around package
selection, purchase/restore entrypoints, and full-screen vs sheet
background handling.
> 
> **Overview**
> Adds a new **default/fallback paywall** Compose UI
(`DefaultPaywallView`) that themes itself from the app icon, shows a
selectable package list, and provides localized *Purchase*/*Restore*
actions, plus a debug-only `DefaultPaywallWarning` panel (with a
Dashboard link) and preview/test utilities.
> 
> Wires a new `validationWarning: PaywallWarning?` through
`PaywallValidationResult` mapping into `PaywallState.Loaded.Legacy`, and
updates `InternalPaywall` to render the new default paywall when the
warning is present (including refactoring background handling via
`screenModeBackground`).
> 
> Updates test/mocks to support the new legacy fallback flow (selection
mutates state; purchase captures selected package id; restore uses
`restorePurchases()` entrypoint) and adds string resources for the
default paywall across locales; also ignores `.cursor/` in `.gitignore`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
6ca1786. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants