Skip to content

Commit 82411a0

Browse files
authored
Fix editor exit hang when GutenbergView never finishes loading (#22882)
Track ready state from `setEditorDidBecomeAvailable` and short-circuit the save-on-exit flow to `UpdateFromEditor.Failed` when the editor isn't ready. Bound the bridge latch with a 5s timeout that also routes through `Failed`. Both paths skip mutating the `PostModel`, so the user's existing draft is preserved when the editor stalls. Fixes #22878
1 parent fc05d3b commit 82411a0

3 files changed

Lines changed: 39 additions & 5 deletions

File tree

RELEASE-NOTES.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
26.8
44
-----
5-
5+
* [**] Resolved an issue where the editor could become impossible to exit when it failed to load.
66

77
26.7
88
-----

WordPress/src/main/java/org/wordpress/android/ui/posts/GutenbergKitActivity.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1878,15 +1878,24 @@ class GutenbergKitActivity : BaseAppCompatActivity(), EditorImageSettingsListene
18781878
updateAndSavePostAsync(listener, isFinishing)
18791879
}
18801880

1881+
@Suppress("ReturnCount")
18811882
private fun updateFromEditor(oldContent: String, isFinishing: Boolean = false): UpdateFromEditor {
18821883
editorFragment?.let {
1884+
// Don't read title/content from a stalled editor — doing so would
1885+
// overwrite the in-memory PostModel with empty strings, and the
1886+
// postChanged observer would then persist that empty state to
1887+
// SQLite. See issue #22878.
1888+
if (!it.isEditorReady()) {
1889+
AppLog.w(AppLog.T.EDITOR, "Skipping content update: Gutenberg editor not ready")
1890+
return UpdateFromEditor.Failed(java.lang.Exception("Gutenberg editor not ready"))
1891+
}
18831892
return try {
18841893
// To reduce redundant bridge events emitted to the Gutenberg editor, we get title and content at once
18851894
val titleAndContent = it.getTitleAndContent(oldContent, isFinishing)
18861895
val title = titleAndContent.first as String
18871896
val content = titleAndContent.second as String
18881897
PostFields(title, content)
1889-
} catch (e: EditorFragmentAbstract.EditorFragmentNotAddedException) {
1898+
} catch (e: GutenbergKitEditorFragmentBase.EditorFragmentNotAddedException) {
18901899
AppLog.e(AppLog.T.EDITOR, "Impossible to save the post, we weren't able to update it.")
18911900
UpdateFromEditor.Failed(e)
18921901
}

WordPress/src/main/java/org/wordpress/android/ui/posts/editor/GutenbergKitEditorFragment.kt

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import org.wordpress.gutenberg.GutenbergView.TitleAndContentCallback
4242
import org.wordpress.gutenberg.Media
4343
import org.wordpress.gutenberg.model.EditorConfiguration
4444
import java.util.concurrent.CountDownLatch
45+
import java.util.concurrent.TimeUnit
4546
import javax.inject.Inject
4647

4748
class GutenbergKitEditorFragment : GutenbergKitEditorFragmentBase() {
@@ -51,6 +52,9 @@ class GutenbergKitEditorFragment : GutenbergKitEditorFragmentBase() {
5152
private var gutenbergView: GutenbergView? = null
5253
private var isHtmlModeEnabled = false
5354

55+
@Volatile
56+
private var editorReady = false
57+
5458
private val textWatcher = LiveTextWatcher()
5559
private var historyChangeListener: HistoryChangeListener? = null
5660
private var featuredImageChangeListener: FeaturedImageChangeListener? = null
@@ -257,7 +261,10 @@ class GutenbergKitEditorFragment : GutenbergKitEditorFragmentBase() {
257261
}
258262
)
259263

264+
editorReady = false
265+
260266
gutenbergView.setEditorDidBecomeAvailable {
267+
editorReady = true
261268
mEditorFragmentListener.onEditorFragmentContentReady(
262269
ArrayList<Any?>(), false
263270
)
@@ -434,8 +441,19 @@ class GutenbergKitEditorFragment : GutenbergKitEditorFragmentBase() {
434441
)
435442

436443
val finalResult = try {
437-
latch.await()
438-
result[0]
444+
val completed = latch.await(
445+
GET_TITLE_AND_CONTENT_TIMEOUT_SECONDS, TimeUnit.SECONDS
446+
)
447+
if (!completed) {
448+
AppLog.w(
449+
AppLog.T.EDITOR,
450+
"Timed out waiting for title and content from " +
451+
"Gutenberg editor"
452+
)
453+
null
454+
} else {
455+
result[0]
456+
}
439457
} catch (e: InterruptedException) {
440458
AppLog.w(
441459
AppLog.T.EDITOR,
@@ -446,7 +464,10 @@ class GutenbergKitEditorFragment : GutenbergKitEditorFragmentBase() {
446464
null
447465
}
448466

449-
return finalResult ?: Pair("", "")
467+
// Surface failure to the caller as a checked exception so it can
468+
// skip mutating the PostModel rather than persisting empty content
469+
// over the user's existing draft. See issue #22878.
470+
return finalResult ?: throw EditorFragmentNotAddedException()
450471
}
451472

452473
override fun getEditorName(): String {
@@ -517,6 +538,8 @@ class GutenbergKitEditorFragment : GutenbergKitEditorFragmentBase() {
517538
super.onDestroy()
518539
}
519540

541+
fun isEditorReady(): Boolean = editorReady
542+
520543
fun setXPostsEnabled(enabled: Boolean) {
521544
isXPostsEnabled = enabled
522545
}
@@ -551,6 +574,8 @@ class GutenbergKitEditorFragment : GutenbergKitEditorFragmentBase() {
551574
private const val CAPTURE_PHOTO_PERMISSION_REQUEST_CODE = 101
552575
private const val CAPTURE_VIDEO_PERMISSION_REQUEST_CODE = 102
553576

577+
private const val GET_TITLE_AND_CONTENT_TIMEOUT_SECONDS = 5L
578+
554579
fun newInstance(
555580
configuration: EditorConfiguration,
556581
site: SiteModel

0 commit comments

Comments
 (0)