wordpress-mobile / wordpress-mobile/GutenbergKit

bug(demo-android): Save can hang forever if tapped before editor loads

Open
#444 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Bug Gutenberg
Dominant language
JavaScript
Stars
29
Forks
6
Avg merge
1d 9h
Merged PRs (30d)
41

Description

Context

In PR #433 (feat/demo-edit-existing-posts), the Android demo app's EditorActivity.persistPost() uses suspendCancellableCoroutine to bridge the callback-based GutenbergView.getTitleAndContent() into a suspend function.

Bug

GutenbergView.getTitleAndContent() has an early-return guard:

if (!isEditorLoaded) {
    Log.e("GutenbergView", "You can't change the editor content until it has loaded")
    return  // callback is NEVER invoked
}

When isEditorLoaded is false, the method returns without calling the callback. In persistPost(), this means cont.resume() is never called and the coroutine suspends indefinitely:

val titleAndContent = suspendCancellableCoroutine<Pair<CharSequence, CharSequence>> { cont ->
    view.getTitleAndContent(
        originalContent = configuration.content,
        callback = object : GutenbergView.TitleAndContentCallback {
            override fun onResult(title: CharSequence, content: CharSequence) {
                if (cont.isActive) cont.resume(title to content)
            }
        }
    )
}

There is no timeout, no invokeOnCancellation, and no fallback.

The canSave guard (!isSaving && accountId != null && configuration.postId != null) does not check editor load state, so the Save button is enabled before onEditorLoaded() fires. The gutenbergViewRef is set as soon as the AndroidView factory runs — before the editor JS finishes loading — so there is a race window where the user can tap Save and trigger this path.

Impact

If triggered, isSaving remains true permanently (it is only reset in the finally block, which can't execute until the coroutine completes). The Save button is disabled for the rest of the session with no error message or way to recover.

Low probability in practice (requires tapping Save before the editor finishes loading), but severe consequence (permanently broken Save requiring app restart).

Possible fixes

Any one of these would address it:

  1. Gate canSave on editor load state — expose an isEditorLoaded signal to the Compose UI and include it in canSave, matching the iOS approach (isEditorReady && !isSaving && hasPostID)
  2. Add a timeout — wrap the suspendCancellableCoroutine in withTimeout()
  3. Always invoke the callback — change getTitleAndContent() in the library to call the callback with an error/empty result even when the editor isn't loaded, rather than silently returning

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with EditorActivity.persistPost() and GutenbergView.getTitleAndContent(), then trace the Save button's canSave guard and the onEditorLoaded() flow. Reproduce by tapping Save before the editor loads; done means the operation cannot suspend indefinitely and the Save state can recover without restarting the app.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, kotlin
Domain
mobile
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.