Skip to content

feat: report save availability to the host - #792

Merged
dcalhoun merged 11 commits into
trunkfrom
feat/save-availability-events
Oct 20, 2026
Merged

dcalhoun merged 11 commits into
trunkfrom
feat/save-availability-events

Conversation

@dcalhoun

@dcalhoun dcalhoun commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

What?

Expose entity save availability via new bridge event.

Why?

Close CMM-2552. Currently, hosts own post saving and rely on their own internal tracking for enabling UI for saving posts. In preparation for GutenbergKit managing post saving via Gutenberg's APIs, GutenbergKit must expose the current save availability status. This is opt-in functionality, not a breaking change.

How?

Introduce a new save availability event and callback for iOS and Android respectively. These are utilized anytime the editor's store modifies its relevant state.

Testing Instructions

  1. Open an existing post
  2. Insert an Image block
  3. Verify the Save button is enabled
  4. Upload a new image
  5. Verify the Save button is disabled until the upload completes

Accessibility Testing Instructions

N/A, no navigation changes.

Screenshots or screencast

N/A, no navigation changes.


AI-generated details

Phase 2 of the CMM-2024 editor-driven saving plan. Hosts can now disable their save controls while the editor reports that saving is locked, e.g. during a media upload, instead of persisting temporary blob: URLs.

  • JS: useSyncSaveAvailability sends onEditorSaveAvailabilityChanged { isSaveable, isSavingLocked } once the editor is ready and then on each change, deduplicated. isDirty/isSaving are left out until the editor saves posts. Adapted from feat: add real editor-driven save post mechanism #445 (co-authored).
  • iOS: editor(_:didUpdateSaveAvailability:) with a default no-op implementation, so the change is non-breaking; EditorSaveAvailability struct.
  • Android: setSaveAvailabilityListener(SaveAvailabilityListener?), delivering GutenbergView.SaveAvailability on the main thread.
  • Demo apps: Save is disabled while saving is locked.
  • After unmount: reporting stops when the editor unmounts. Hosts should disregard the last state after onEditorUnavailable.

What locks saving: block media uploads lock saving through the editor's mediaUpload setting. That includes uploads routed through the native upload server, since the middleware runs inside the same request. Media picked through the host's native media library doesn't lock the editor.

Testing: JS unit tests for the hook; Swift decoding test; Kotlin listener test. JS, Swift and Kotlin unit suites and lint pass locally.

🤖 Generated with Claude Code

@github-actions github-actions Bot added the [Type] Enhancement A suggestion for improvement. label Oct 14, 2026
@wpmobilebot

wpmobilebot commented Oct 14, 2026 •

Copy link
Copy Markdown

XCFramework Build

This PR's XCFramework is available for testing. Add the following to your Package.swift:

.package(url: "https://github.com/wordpress-mobile/GutenbergKit", branch: "pr-build/792")

Built from 381e1a4

dcalhoun and others added 10 commits October 19, 2026 11:42
Hosts save natively and need to know when saving is unsafe, such as while
an upload holds the editor's save lock, so they can disable their save
controls instead of persisting temporary blob URLs.

Co-Authored-By: Jeremy Massel <1123407+jkmassel@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lets hosts disable their save controls while the editor has locked saving.
The default no-op implementation keeps the change non-breaking.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lets hosts disable their save controls while the editor has locked saving.
The listener is notified on the main thread, in order with the editor's
availability callbacks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Demonstrates the save availability event: saving mid-upload would persist
temporary media URLs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first report fires once the post is set up, before editorDidLoad
and onEditorAvailable, and is not resent, so hosts that wait for load
would miss it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Upstream mediaUpload clears the batch's lock on any file's error, so
hosts can see saving unlocked while other files are still uploading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Android receives the payload values positionally and iOS decodes them by
key, so reordering or renaming the keys would silently break hosts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The editor never returns to not-ready while mounted, so the effect's
dependencies already prevent repeat reports. This matches
useSyncHistoryControls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Matches the other listener setters; GutenbergView already clears every
listener on detach, so hosts don't need to pass null.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dcalhoun
dcalhoun force-pushed the feat/save-availability-events branch from 71ee705 to daad6f4 Compare October 19, 2026 15:42
@dcalhoun
dcalhoun marked this pull request as ready for review October 19, 2026 15:50
@dcalhoun
dcalhoun requested a review from crazytonyli October 19, 2026 15:51

@crazytonyli crazytonyli 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.

I guess the idea is that the app should disable save button when isSavingLocked. But what if the save action is still triggered, in code, not by user tapping the save button, would/should the GBK throw an error somehow, or is that acceptable?

Disabling save controls alone leaves saves started in code, such as autosaves, free to persist temporary media URLs mid-upload. GBK can't enforce the lock while hosts save natively.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dcalhoun

dcalhoun commented Oct 20, 2026 •

Copy link
Copy Markdown
Member Author

I guess the idea is that the app should disable save button when isSavingLocked. But what if the save action is still triggered, in code, not by user tapping the save button, would/should the GBK throw an error somehow, or is that acceptable?

Good question. The lock is guidance, and hosts should check it before programmatic saves. I updated the documentation to better communicate this.

For comparison: the web editor disables various UI mechanisms (save button, keyboard shortcuts), but it does not prevent programmatic saves of in-flight uploads. Once saving moves to GutenbergKit, it will return a skipped result with a reason rather than throwing. This will take GutenbergKit beyond the web's behavior, but feels appropriate as the host cannot otherwise see the editor state.

Much of this is in a local plan document. I'll move those to a Linear project/issues for better visibility (Update: See P-CMM-11379). Thank you for the question.

@dcalhoun
dcalhoun merged commit 988e4ce into trunk Oct 20, 2026
25 checks passed
@dcalhoun
dcalhoun deleted the feat/save-availability-events branch October 20, 2026 15:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Type] Enhancement A suggestion for improvement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants