Pre-warm image cache for workflow step states#3421
Closed
vegaro wants to merge 1 commit into
Closed
Conversation
This was referenced Apr 30, 2026
Member
Author
This stack of pull requests is managed by Graphite. Learn more about stacking. |
📸 Snapshot Test591 unchanged
🛸 Powered by Emerge Tools |
ebc6b1f to
d71ea2c
Compare
686d62d to
c9ac67b
Compare
d71ea2c to
2e368bc
Compare
c9ac67b to
2edc1e7
Compare
2e368bc to
be83bfc
Compare
2edc1e7 to
0d10d7d
Compare
0d10d7d to
e4bf0f4
Compare
be83bfc to
997ae23
Compare
e4bf0f4 to
6f01bee
Compare
997ae23 to
b6e19c5
Compare
89f4c43 to
d5cedd9
Compare
b6e19c5 to
d453daf
Compare
d5cedd9 to
5ac33ae
Compare
0b89ebd to
fbd4086
Compare
5ac33ae to
8024cbd
Compare
fbd4086 to
bc089b7
Compare
9908eb5 to
e68371c
Compare
bf8a5e4 to
ae335ff
Compare
431e314 to
cfe2af3
Compare
c6054fe to
b2695a9
Compare
54c6173 to
2fcc54d
Compare
3112a77 to
f63f313
Compare
2fcc54d to
297d4af
Compare
facumenzella
reviewed
May 4, 2026
facumenzella
left a comment
Member
There was a problem hiding this comment.
I think that warming every single component is definitely not ideal. We should do this, but smarter.
I'd cap to 1–2 steps ahead instead of the full workflow to avoid memory pressure issues.
431642b to
31b4d2c
Compare
Walks every step's component tree (background, stack, header, sticky footer, including buttons/carousels/tabs/timelines/etc.) and enqueues each unique image URL into Coil's image loader. Light + dark + low-res variants are all submitted up front, with a remembered set guarding against duplicate enqueues across recompositions. Sits on top of the two-surface workflow slide animation so that when a transition starts, the incoming surface's images are already warm in Coil's cache and don't pop in mid-slide. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
31b4d2c to
10681c4
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3421 +/- ##
=======================================
Coverage 79.45% 79.45%
=======================================
Files 362 362
Lines 14539 14539
Branches 1976 1976
=======================================
Hits 11552 11552
Misses 2190 2190
Partials 797 797 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
matteinn
pushed a commit
to matteinn/purchases-android
that referenced
this pull request
May 5, 2026
### Motivation A workflow paywall pre-rendering approach was retired in favor of a two-surface slide model. Holding every step in the slot table at once was wasteful (most are off-screen) and the prior animation needed `snapTo()` + `withFrameNanos()` workarounds to avoid first-frame flashes. ### Description Only the current step and the outgoing/incoming step are held in the slot table during a transition; all other steps are dropped. Key design: - `WorkflowPaywallUiState` carries a `pendingTransition` (`fromStepId`, `direction`, monotonic `id`) set atomically with `currentStepId` in the ViewModel. The first recomposition after navigation already knows both surfaces and their initial positions. - `key(pendingTransition.id)` creates a fresh `Animatable(0f)` during the composition phase, so frame N's draw immediately sees the correct offscreen position — no `snapTo()` or `withFrameNanos()` needed. - `LaunchedEffect` only drives `animateTo(1f)`; it no longer sets up state. - `onTransitionComplete(id)` runs after the animation finishes so the ViewModel can clear `pendingTransition`; a guard on `id` prevents stale callbacks from clobbering a newer transition. - Header pinning logic (hero/non-hero, backward) is simplified by removing the `seenStepId` gap-detection; `pendingTransition` covers both the "before animation starts" and "animating" cases with the same branch. - `navigationDirection` removed from the `PaywallViewModel` interface; direction lives exclusively in `WorkflowPendingTransition`. Image cache pre-warming for the off-screen steps is split into a follow-up PR (RevenueCat#3421) so this PR stays focused on the surface model and animation. ### Checklist - [x] Unit tests added (`LoadedWorkflowPaywallHeaderSelectionTest`) - [ ] If applicable, create follow-up issues for `purchases-ios` and hybrids <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Introduces new workflow-specific rendering and animation state for Components paywalls, which can affect navigation, touch handling, and header/hero layout during transitions. Risk is mostly UI/UX regressions (flashes, clipping, wrong header selection) rather than data/security concerns. > > **Overview** > Adds a dedicated workflow paywall renderer that uses a two-surface slide transition: `InternalPaywall` now renders `LoadedWorkflowPaywall` when `workflowState` is present, otherwise it falls back to `LoadedPaywallComponents`. > > Implements `WorkflowSlideState` to drive horizontal slide animations keyed by `pendingTransition.id` (keeping only the current + outgoing/incoming steps in composition) and adds header-selection logic to keep the correct header visible during hero/non-hero and backward transitions, covered by new unit tests (`LoadedWorkflowPaywallHeaderSelectionTest`). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 88595b3. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Member
Author
|
This is getting superseeded by #3447 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Motivation
When the two-surface workflow slide animation (#3418) starts a transition, the incoming step's images may not yet be in Coil's cache, causing a visible pop-in mid-slide. Pre-warming images for every workflow step at composition time lets transitions start with warm caches.
Description
WorkflowImagePreloaderwalks each step's component tree (background, stack, header, sticky footer — plus buttons, carousels, tabs, timelines, countdowns, icons, images, and video fallback frames) and enqueues every unique image URL into Coil's image loader. Light, dark, and low-resolution variants are all submitted up front. A remembered set guards against duplicate enqueues across recompositions.The composable is invoked from
LoadedWorkflowPaywallonce the step states are ready; it returns no UI, only side-effecting cache warming.Note
Medium Risk
Moderate risk: this adds background image preloading across workflow steps, which can increase network/memory usage and affect performance if URL enumeration or enqueueing is incorrect.
Overview
Preloads workflow paywall images into Coil ahead of time to prevent image pop-in during step-to-step slide transitions.
This introduces
WorkflowImageUrls.preloadImageUrls()to walk a components paywall tree (backgrounds, stacks, headers/footers, and nested component styles) and extract light/dark/low-res image URLs, then updatesPaywallViewModelImplto cache step states via a newcacheStepStatethat deduplicates and enqueues those URLs.InternalPaywallnow supplies anenqueueImagefunction (backed byPurchases.getImageLoaderTyped+ImageRequest) throughPaywallViewModelFactoryinto the view model.Reviewed by Cursor Bugbot for commit 10681c4. Bugbot is set up for automated code reviews on this repo. Configure here.