Skip to content

Comments

fix: Preload GutenbergKit and invalidate cache for non-Jetpack sites#25226

Merged
dcalhoun merged 10 commits intotrunkfrom
fix/preload-gutenberg-kit-without-jetpack
Feb 21, 2026
Merged

fix: Preload GutenbergKit and invalidate cache for non-Jetpack sites#25226
dcalhoun merged 10 commits intotrunkfrom
fix/preload-gutenberg-kit-without-jetpack

Conversation

@dcalhoun
Copy link
Member

@dcalhoun dcalhoun commented Feb 5, 2026

Description

This PR fixes editor preloading and cache invalidation for non-Jetpack self-hosted sites. Previously, these features only worked for Jetpack-connected sites because the logic was in BlogDashboardViewModel, which is only used when displaying the dashboard. Non-Jetpack sites show BlogDetailsViewController (Site Menu) instead.

Changes

  1. Add LockingValue<T> utility - Thread-safe wrapper for single values that aren't Sendable (e.g., NSManagedObjectID), used for lastWarmedUpBlogID in EditorDependencyManager

  2. Consolidate editor warmup in EditorDependencyManager - Add warmUpEditor(for:) method that combines WebKit warmup with data prefetch

  3. Move editor preloading to MySiteViewController - Call warmUpEditor from MySiteViewController.configure(for:) so all site types get editor preloading

  4. Move cache invalidation to MySiteViewController - Call EditorDependencyManager.invalidate(for:) in pulledToRefresh() so all site types get cache invalidation on pull-to-refresh

  5. Remove duplicate code from BlogDashboardViewModel - Remove warmUpEditorIfNeeded() and clearEditorCache() methods since this logic is now in MySiteViewController

Root cause

Both issues stem from the same architectural problem: non-Jetpack self-hosted sites display BlogDetailsViewController (Site Menu) instead of BlogDashboardViewController (Dashboard). The editor warmup and cache invalidation logic was in BlogDashboardViewModel, which is never instantiated for non-Jetpack sites.

Fix CMM-1214.

Testing instructions

1. Editor Preloading (Non-Jetpack)

  1. Add a self-hosted site without Jetpack
  2. Navigate to the Site Menu
  3. Open the editor
  4. Verify the loading bar is NOT shown (editor should load instantly due to preloading)

2. Cache Invalidation (Non-Jetpack)

  1. Add a self-hosted site without Jetpack
  2. Pull-to-refresh on the Site Menu
  3. Open the editor
  4. Verify the loading bar IS shown (cache was invalidated, so editor reloads fresh)

3. Jetpack Sites (Regression Test)

  1. Verify editor preloading still works on Jetpack sites (no loading bar on first open)
  2. Verify cache invalidation on pull-to-refresh still works on Jetpack sites (loading bar shown after refresh)

@wpmobilebot
Copy link
Contributor

wpmobilebot commented Feb 5, 2026

🤖 Build Failure Analysis

This build has failures. Claude has analyzed them - check the build annotations for details.

@dcalhoun dcalhoun added [Type] Bug Posting/Editing Gutenberg Editing and display of Gutenberg blocks. labels Feb 5, 2026
@dcalhoun dcalhoun added this to the 26.7 milestone Feb 5, 2026
@dcalhoun
Copy link
Member Author

dcalhoun commented Feb 5, 2026

The CI failures are seemingly unrelated to these changes and should be addressed by #25224.

@dcalhoun dcalhoun marked this pull request as ready for review February 5, 2026 21:55
@dcalhoun dcalhoun requested a review from jkmassel February 5, 2026 21:55
@dcalhoun dcalhoun force-pushed the fix/preload-gutenberg-kit-without-jetpack branch from 72730cd to 7dadca0 Compare February 6, 2026 13:34
@wpmobilebot
Copy link
Contributor

wpmobilebot commented Feb 6, 2026

App Icon📲 You can test the changes from this Pull Request in WordPress by scanning the QR code below to install the corresponding build.
App NameWordPress
ConfigurationRelease-Alpha
Build Number31040
VersionPR #25226
Bundle IDorg.wordpress.alpha
Commite5828fb
Installation URL2p6oihttvab90
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@wpmobilebot
Copy link
Contributor

wpmobilebot commented Feb 6, 2026

App Icon📲 You can test the changes from this Pull Request in Jetpack by scanning the QR code below to install the corresponding build.
App NameJetpack
ConfigurationRelease-Alpha
Build Number31040
VersionPR #25226
Bundle IDcom.jetpack.alpha
Commite5828fb
Installation URL4avv1csbp8d00
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

Add a thread-safe value wrapper for single values and use it to fix
the Swift 6 warning on `_lastPluginsFlagValue` in EditorDependencyManager.
Add warmUpEditor(for:) method that combines WebKit warmup with data
prefetch, consolidating the warmup logic in a single location.
Call warmUpEditor from MySiteViewController so all site types get
editor preloading, including self-hosted sites without Jetpack that
display BlogDetailsViewController instead of the dashboard.
Remove warmUpEditorIfNeeded() since warmup is now handled by
MySiteViewController through EditorDependencyManager.warmUpEditor().
Move editor cache invalidation from BlogDashboardViewController to
MySiteViewController so all site types get cache invalidation on
pull-to-refresh.

Previously, cache invalidation only happened in
BlogDashboardViewController.pulledToRefresh(), which calls
viewModel.clearEditorCache(). Non-Jetpack self-hosted sites show
BlogDetailsViewController (Site Menu) instead of the dashboard, so
the cache invalidation never occurred for these users.

Now MySiteViewController.pulledToRefresh() handles cache invalidation
for both dashboard and site menu sections, ensuring consistent behavior
across all site types.
Add comprehensive tests for LockingValue including:
- Initialization with various types (primitives, optionals, complex types)
- Basic value access (get/set)
- withLock operations (read, modify, return results, conditional updates)
- Thread safety (concurrent reads, writes, increments, compare-and-swap)
@dcalhoun dcalhoun force-pushed the fix/preload-gutenberg-kit-without-jetpack branch from 7dadca0 to e90b2ff Compare February 20, 2026 16:04
@dangermattic
Copy link
Collaborator

1 Warning
⚠️ This PR is assigned to the milestone 26.7. This milestone is due in less than 4 days.
Please make sure to get it merged by then or assign it to a milestone with a later deadline.

Generated by 🚫 Danger

}

/// Tracks the last blog for which WebKit warmup was performed.
private let lastWarmedUpBlogID = LockingValue<NSManagedObjectID?>(nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

If you were to move this into State and use TaggedManagedObjectID<Blog>? you shouldn't need LockingValue at all.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be reset on a pull-to-refresh?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good idea. Addressed in ef66aad and 81e0466.

Copy link
Member Author

Choose a reason for hiding this comment

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

Additionally, in e5828fb, I also cleared lingering slow path or "organic" caches created from opening the editor without a dependency cache.

Those caches are currently not exposed or tracked in EditorDependencyManger. We might consider changing how GutenbergKit manages this class of caches, potentially providing host apps access to them.

}

@objc
private func pulledToRefresh() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible to get a completion handler here that we can call from the Task so we can keep the loading state until the task is complete?

Copy link
Member Author

Choose a reason for hiding this comment

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

@jkmassel possibly. I'm not sure it is worthwhile currently. I'll let Claude communicate what I struggled to put into words. WDYT?

Since pull-to-refresh only invalidates the cache (no re-prefetch), the invalidate operation is purely local I/O (deleting a directory and clearing a URLCache) with no network calls. It completes near-instantly, so tying it to the loading state wouldn't provide meaningful UX benefit.

If we later add re-prefetching after invalidation on pull-to-refresh, it would make sense to revisit this and keep the spinner active until the prefetch completes. The data prefetch is fully async and awaitable. However, EditorViewController.warmup() in GutenbergKit is fire-and-forget with no completion API, so we'd need to add one upstream to cover the full warmup flow.

Copy link
Contributor

@jkmassel jkmassel left a comment

Choose a reason for hiding this comment

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

I left a few small suggestions, but the code generally looks ok to me.

When pull-to-refresh invalidates the editor cache, also reset the
lastWarmedUpBlogID so the next warmUpEditor call re-runs WebKit warmup
instead of skipping it.
Move lastWarmedUpBlogID from a standalone LockingValue<NSManagedObjectID?>
into the existing OSAllocatedUnfairLock-backed State struct as
TaggedManagedObjectID<Blog>?. This consolidates all mutable state behind
a single lock and eliminates the LockingValue utility, which had no other
consumers.
GutenbergKit populates its on-disk asset cache whenever the editor loads,
not just through EditorDependencyManager's prefetch path. When the
in-memory cache had no entries for a blog, purge() was never called,
leaving stale on-disk assets served on subsequent editor opens. Fall back
to purging for .post when there are no in-memory cache keys to ensure the
on-disk cache is always cleared.
@sonarqubecloud
Copy link

@dcalhoun dcalhoun enabled auto-merge February 20, 2026 23:44
@dcalhoun dcalhoun added this pull request to the merge queue Feb 20, 2026
Merged via the queue into trunk with commit 1b2b101 Feb 21, 2026
27 of 32 checks passed
@dcalhoun dcalhoun deleted the fix/preload-gutenberg-kit-without-jetpack branch February 21, 2026 00:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Gutenberg Editing and display of Gutenberg blocks. Posting/Editing [Type] Bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants