feat: align iOS third-party blocks setting with Android's Site Settings toggle - #25889
Draft
dcalhoun wants to merge 9 commits into
Draft
feat: align iOS third-party blocks setting with Android's Site Settings toggle#25889dcalhoun wants to merge 9 commits into
dcalhoun wants to merge 9 commits into
Conversation
Formatting-only change, no behavior difference. Committed separately so the following functional change reads cleanly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Store the user's per-site opt-in for third-party (plugin-provided) blocks, mirroring the Theme Styles preference pattern. The preference is local-only and never posted to the server, matching Android's SiteSettingsModel.useThirdPartyBlocks. It defaults to false so the capability stays opt-in, unlike Theme Styles which defaults to true. This is storage only. Resolving the preference against the remote feature flag and the site's advertised capability comes next. Part of CMM-2265. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Introduce ThirdPartyBlocksCapability with the three states Android's EditorCapabilityResolver uses — hidden, unsupported, available — plus resolveThirdPartyBlocks(for:) to derive them from the remote feature flag, the site's authentication, and the site's advertised capability. Treat a site whose capability has not been probed yet as supported. The probe is fire-and-forget from MySiteViewController and Site Settings, and only runs when newGutenberg is enabled, so it may not have completed — or on a fresh install, run at all — when the setting is read. getSupports reports false for both 'server said no' and 'never asked', so add hasProbedSupport(for:blog:) to tell those apart. Assuming support keeps the setting reachable on capable sites; the editor degrades gracefully if the route is genuinely absent. Also expose the @objc bridge methods the Objective-C Site Settings screen needs to show, enable, and toggle the row. Part of CMM-2265. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replace the site-type check in shouldEnablePlugins with the resolver, so the editor loads third-party blocks only when the remote flag, the site's capability, and the user's per-site preference all agree. The preference defaults to false, so no site enables plugins until the user opts in. Keep the WP.com REST API requirement the old check enforced via isAccessibleThroughWPCom. The editor assets endpoint is served through that API, so a purely self-hosted site can't serve third-party blocks even with an application password, and the capability probe can't stand in for it — an unprobed site resolves as supported by design. Rewrite the tests around the new model: the previous suite asserted the site-type behavior throughout. They now cover the flag-off, unsupported, unprobed, default-off, opted-in, per-site, and flag-cycle cases, plus the shouldEnablePlugins entry point. Pass the flag value in rather than overriding the shared FeatureFlagOverrideStore, which tests running in parallel would clobber, and obtain permanent Core Data IDs for builder-made blogs. Capability support is keyed on Blog.locallyUniqueId, which hashes the object ID, so a write made before Core Data promotes a temporary ID is read back under a different key. Part of CMM-2265. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add a per-site 'Use Third-Party Blocks (Beta)' switch to the Editor section, following the Theme Styles row's pattern. The section is omitted entirely when the resolver reports .hidden, and the row renders disabled with an explanatory footer when the site can't support the feature — matching Android, which shows the row rather than hiding it. The footer distinguishes the two unsupported reasons: a site missing the editor-assets capability gets different wording than one missing an application password. Extend the 'Editor' section header chaining to the new section, so the header still appears exactly once when earlier editor sections are absent. Unlike configureThemeStylesSelectorCell, the configure method here also restores the disabled state, so a recycled cell can't strand an interactive switch on a site that doesn't support the feature. Part of CMM-2265. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Part of CMM-2265. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Collaborator
Generated by 🚫 Danger |
Contributor
|
| App Name | WordPress | |
| Configuration | Release-Alpha | |
| Build Number | 33733 | |
| Version | PR #25889 | |
| Bundle ID | org.wordpress.alpha | |
| Commit | 7f42dc5 | |
| Installation URL | 4u99qjv2q0spg |
Contributor
|
| App Name | Jetpack | |
| Configuration | Release-Alpha | |
| Build Number | 33733 | |
| Version | PR #25889 | |
| Bundle ID | com.jetpack.alpha | |
| Commit | 7f42dc5 | |
| Installation URL | 034n4ddn3atg0 |
The capability probe checked WordPressClient.Feature.plugins, which resolves to the /wp/v2/plugins route — the plugin *management* API. Third-party blocks are served by /wpcom/v2/editor-assets, an unrelated route. WP.com Simple sites don't expose /wp/v2/plugins, so the probe recorded false and the resolver correctly reported .unsupported for sites that support the feature perfectly well. That left the Site Settings row permanently disabled and no third-party blocks in the editor. Add Feature.editorAssets, checking the same routes GutenbergKit itself uses to decide whether it can load plugin-provided blocks, including the site-namespaced form WP.com sites expose. Point the probe and the resolver at it. Feature.plugins is left in place for genuine plugin-management checks, and its stringValue is unchanged so no stored settings are invalidated. Also use sentence case for the Site Settings row to match the neighboring 'Use block editor' and 'Use theme styles' labels. Part of CMM-2265. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A WordPress.com site starts out proxied through the WP.com REST API and
switches to addressing its own API root once an application password is
created. The two roots key their discovery documents differently: the proxy
namespaces every route under /sites/{id}/, so it lists
/wpcom/v2/sites/123/editor-assets while the site's own root lists
/wpcom/v2/editor-assets.
WordPressClientFactory cached clients by blog alone, and WordPressClient
fetches its API root once in init and keeps it for the process lifetime.
When the application password arrived mid-session the transport flipped, so
fetchEditorCapabilities started computing siteId = nil and checking
non-namespaced routes against a still-cached proxy document. Every lookup
missed, which read as 'this site supports nothing' and disabled both the
theme styles and third-party blocks rows until the app was reinstalled.
Key the cache on the transport as well, and evict any client built for the
site's previous transport, so the next use fetches the matching document.
Also give SwitchTableViewCell a real isEnabled property. Disabling
flipSwitch alone left the row's tap gesture active, so the value still
toggled when the label was tapped, and WPStyleGuide.configureTableViewCell
calls sizeToFit() on layout, which restored the label's default color and
made a disabled row look enabled. Adopt it for both editor rows.
Part of CMM-2265.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The capability probe is asynchronous, but Site Settings built its rows once and never rebuilt them. viewDidLoad ran before the probe completed, so the editor rows kept whatever state they were built with — on first load, before any capability was known. The rows only corrected themselves when something else forced a re-layout, such as cancelling a back swipe. Await the probe and rebuild the section list when it lands. The rows now render from the last-known capabilities and reconcile once the fresh values arrive, rather than blocking the screen on a network round trip. Also invalidate the prefetched editor when either editor preference is toggled. The editor is warmed up ahead of time from an EditorConfiguration built with these values, so opening it after a toggle reused a cached editor that still had the old setting — third-party blocks stayed missing after switching the setting on. This applies to theme styles for the same reason. Part of CMM-2265. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
🤖 Build Failure AnalysisThis build has failures. Claude has analyzed them - check the build annotations for details. |
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.


Description
Fixes CMM-2265.
The GutenbergKit third-party blocks ("plugins") feature is surfaced differently on each platform. Android gives users a per-site toggle in Site Settings; on iOS the only toggle lives in the internal-only Debug Menu, so no end user can enable it. This adopts Android's user-facing model.
Three things compose as AND — the editor receives
plugins = trueonly when all agree:gutenberg_kit_pluginsoffhiddenunsupportedavailable(userEnabled)The remote flag stays as the outer kill switch and rollout control; the new setting is a per-site opt-in within an enabled rollout. Because the preference persists independently, toggling the flag off and on restores each site's prior choice.
Commits
Reviewable in order — each builds on the last:
style:— swift-format onGutenbergSettingsalone, so the following diffs read cleanlyfalse)ThirdPartyBlocksCapabilityresolver +@objcbridgeshouldEnablePlugins, replacing the site-type checkNotes for reviewers
The capability probe previously had no effect.
EditorDependencyManagerwrites.setSupports(.plugins, …)but nothing outside tests ever read it. This PR makes it load-bearing.Un-probed sites resolve as supported, deliberately.
fetchEditorCapabilitiesis fire-and-forget and gated behindnewGutenberg, so on a fresh install it may not have run when the setting is first read.getSupportsreturnsfalsefor both "server said no" and "never asked", so I addedhasProbedSupport(for:blog:)to separate them. Treating un-probed as unsupported would disable the row on perfectly capable sites; treating it as supported degrades to an editor without plugin blocks, which is today's behavior anyway.The WP.com REST API requirement is retained. The old check required
isAccessibleThroughWPCom. The editor-assets endpoint is served through that API, so a purely self-hosted site can't serve third-party blocks even with an application password — and the probe can't stand in for it given the point above.configureThirdPartyBlocksSelectorCellrestores the disabled state, unlike the Theme Styles equivalent it's modeled on, which only ever enables the cell. On a recycled cell that can strand an interactive switch on an unsupported site. I didn't fix the Theme Styles version here to keep the diff scoped, but it looks like a real latent bug worth a follow-up.Known limitation: because the probe is async, the row can render before the true capability is known and settles on the next pull-to-refresh or revisit. This matches existing Theme Styles behavior; making it update live would be a deliberate change beyond this scope.
Tests: the previous 7 tests asserted the old site-type behavior throughout and were rewritten rather than extended. The suite now covers flag-off, both unsupported reasons, un-probed, default-off, opted-in, per-site isolation, the flag off→on cycle, and the
shouldEnablePluginsentry point.Two test-infrastructure fixes were needed, both of which caused confusing failures:
FeatureFlagOverrideStore, which tests running in parallel would clobber.obtainPermanentIDs. Capability support is keyed onBlog.locallyUniqueId, which hashes the Core Data object ID — a write made before Core Data promotes a temporary ID is read back under a different key.Testing instructions
The feature is behind the
gutenberg_kit_pluginsremote flag, off by default. Enable it in Me → App Settings → Debug → Feature Flags → "Experimental Block Editor Plugins" (also requires "Experimental Block Editor").Worth a pass in dark mode and at larger dynamic type sizes, since the row uses an explicit
lightGrayColorfor its disabled state (inherited from the Theme Styles pattern).