Skip to content

Wire validation warning through paywall state pipeline#3134

Merged
JZDesign merged 5 commits into
jzdesign/fallback-paywall-3-default-paywall-uifrom
jzdesign/fallback-paywall-4-wire-validation
Mar 12, 2026
Merged

Wire validation warning through paywall state pipeline#3134
JZDesign merged 5 commits into
jzdesign/fallback-paywall-3-default-paywall-uifrom
jzdesign/fallback-paywall-4-wire-validation

Conversation

@JZDesign

@JZDesign JZDesign commented Feb 23, 2026

Copy link
Copy Markdown
Contributor

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.


Note

Medium Risk
Changes paywall rendering and purchase/restore initiation paths when validationWarning is present, which could affect UI flow and user actions if state is mis-set. Most changes are UI/layout refactors plus additional preview/test utilities, with limited business-logic impact.

Overview
When legacy paywall validation yields a warning, that warning is now carried through state creation (PaywallViewModel.calculateStateOffering.toLegacyPaywallStatePaywallState.Loaded.Legacy.validationWarning) and InternalPaywall conditionally renders DefaultPaywallView instead of a template paywall.

The default/fallback paywall UI is tightened up: DefaultPaywallView is refactored to a Scaffold with a sticky footer and LazyColumn, adds preview override hooks, and moves developer-facing debug strings out of strings.xml into code. This also adds new Compose previews and a deterministic DualColorImageGenerator utility, plus a new unit test case for prominent-color extraction, and ignores .cursor/ in .gitignore.

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

Adds validationWarning field to PaywallState.Loaded.Legacy and threads
it through OfferingToStateMapper.toLegacyPaywallState and
PaywallViewModel.calculateState. When a paywall fails validation, the
warning is now available on the loaded state for the UI to render the
fallback paywall.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@JZDesign JZDesign requested a review from a team as a code owner February 23, 2026 20:59
@emerge-tools

emerge-tools Bot commented Feb 23, 2026

Copy link
Copy Markdown

1 build increased size

Name Version Download Change Install Change Approval
SDKSizeTesting
com.revenuecat.testapps.sdksizetesting
1.0 (1) 11.9 MB ⬆️ 22.6 kB (0.19%) 34.4 MB ⬆️ 68.7 kB (0.2%) N/A

SDKSizeTesting 1.0 (1)
com.revenuecat.testapps.sdksizetesting

⚖️ Compare build
⏱️ Analyze build performance

Total install size change: ⬆️ 68.7 kB (0.2%)
Total download size change: ⬆️ 22.6 kB (0.19%)

Largest size changes

Item Install Size Change Download Size Change
📝 com.revenuecat.purchases.ui.revenuecatui.defaultpaywall.DefaultPa... ⬆️ 7.4 kB ⬆️ 2.4 kB
📝 com.revenuecat.purchases.ui.revenuecatui.defaultpaywall.DefaultPa... ⬆️ 2.9 kB ⬆️ 938 B
📝 com.revenuecat.purchases.ui.revenuecatui.defaultpaywall.DefaultPr... ⬆️ 2.0 kB ⬆️ 663 B
📝 com.revenuecat.purchases.ui.revenuecatui.defaultpaywall.DefaultPa... ⬆️ 2.0 kB ⬆️ 648 B
resources.arsc ⬆️ 2.4 kB ⬆️ 520 B
View Treemap

Image of diff


🛸 Powered by Emerge Tools

github-merge-queue Bot pushed a commit that referenced this pull request Mar 2, 2026
## Summary
- Adds PaywallWarning sealed class that maps PaywallValidationError to
user-facing warning messages with titles, body text, and help URLs
- Adds a warning computed property on PaywallValidationResult.Legacy to
surface the first validation error as a displayable warning

(stacked PRs) breaking up #2945.

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



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk: adds an internal warning type and a derived property without
changing validation or paywall rendering logic. Main risk is minor
mismatches in error-to-message mapping affecting debug-only messaging.
> 
> **Overview**
> Adds an internal sealed `PaywallWarning` that maps
`PaywallValidationError` to user-facing `title`, `bodyText`, and
optional `helpUrl` for displaying debug warnings on the default/fallback
paywall.
> 
> Extends `PaywallValidationResult.Legacy` with a `warning` computed
property that converts the *first* validation error into a
`PaywallWarning` for easy consumption by the default paywall UI.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
800e7fe. 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>
@JZDesign JZDesign requested a review from a team March 6, 2026 20:40
templateConfiguration = templateConfiguration,
selectedPackage = templateConfiguration.packages.default,
shouldDisplayDismissButton = shouldDisplayDismissButton,
validationWarning = validationWarning,

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.

It's strange to me that we are passing a validationWarning into a Loaded legacy paywall?

github-merge-queue Bot pushed a commit that referenced this pull request Mar 10, 2026
> [!IMPORTANT]
> This PR is all dark code, nothing is used yet. PR 5 will wire it all
together


This PR is prepping for color extraction and paywall styling. Unused
code.

## Summary
- Adds ColorComputationHelpers with WCAG 2.0 contrast ratio and
luminance calculations
- Adds AppStyleExtractor to extract app name, icon bitmap, and prominent
colors from the app icon at runtime
- Adds CircleOutlined vector icon for unselected product cells
- Includes tests for color extraction, contrast computation, and
luminance

Part 2 of the fallback paywall feature breaking up #2945. Stacked on
#3131.

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



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Adds new, currently-unused helper utilities and a vector icon with
unit tests; no changes to runtime paywall flow or persisted data.
> 
> **Overview**
> Prepares groundwork for styling by adding `AppStyleExtractor` to fetch
app name/icon and to asynchronously extract a small set of prominent
colors from a bitmap via sampling, quantization, and
similarity/brightness/alpha filtering.
> 
> Introduces `ColorComputationHelpers` with WCAG-based
luminance/contrast utilities (plus best-contrast selection) and adds a
new `CircleOutlined` Compose `ImageVector`.
> 
> Adds Robolectric tests covering prominent-color extraction edge cases
and the new color math helpers (distance, luminance, contrast, and
selection).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3619218. 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>
@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

<!-- Thank you for contributing to Purchases! Before pressing the
"Create Pull Request" button, please provide the following: -->


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

- 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
- Plug in Paywall
#3178 (This PR)
- Remove Big background image and clean up CI

<!-- CURSOR_SUMMARY -->
---

> [!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/`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
42d3af2. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@JZDesign JZDesign merged commit e420c1d into jzdesign/fallback-paywall-3-default-paywall-ui Mar 12, 2026
2 of 5 checks passed
@JZDesign JZDesign deleted the jzdesign/fallback-paywall-4-wire-validation branch March 12, 2026 14:58

@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 3 potential issues.

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

AppStyleExtractor.getAppIconBitmap(context)
}
appIconBitmap = bitmap
}

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.

LaunchedEffect extracts colors before bitmap is loaded

Medium Severity

The LaunchedEffect first calls getProminentColorsFromBitmap(appIconBitmap) when appIconBitmap is still null (in the non-preview path), which returns an empty list. Then it loads the bitmap and sets appIconBitmap = bitmap. While updating appIconBitmap re-triggers the LaunchedEffect (since it's a key), that second run will extract colors correctly — but the ordering within the effect body is misleading and causes a wasted first extraction pass on a null bitmap. More critically, when appIconBitmap is set, prominentColors state is re-initialized to emptyList() via the remember(appIconBitmap, ...) block, so the second LaunchedEffect run is needed to re-extract, creating a visible flash of default colors.

Fix in Cursor Fix in Web

Box(
modifier = Modifier
.conditional(state.isInFullScreenMode) {
Modifier

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.

Paywall impression tracked even for fallback warning state

Medium Severity

trackPaywallImpressionIfNeeded() is called at the top of LoadedPaywall before the validationWarning check. This means a paywall impression is tracked even when the paywall failed validation and the DefaultPaywallView (fallback) is shown instead of the intended template paywall. This could lead to misleading analytics, since the user never actually saw the configured paywall.

Fix in Cursor Fix in Web

Comment thread .gitignore
.gradle/
.gradletasknamecache
build/
.cursor/

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 directory added to gitignore unrelated to PR

Low Severity

The .cursor/ directory (a Cursor IDE configuration folder) was added to .gitignore. This is unrelated to the PR's purpose of wiring validation warnings through the paywall state pipeline and appears to be accidentally included personal IDE configuration.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants