Skip to content

Fix crash when the Prepublishing sheet is restored without a post - #23148

Merged
nbradbury merged 3 commits into
trunkfrom
issue/prepublishing-null-post-npe
Jul 27, 2026
Merged

Fix crash when the Prepublishing sheet is restored without a post#23148
nbradbury merged 3 commits into
trunkfrom
issue/prepublishing-null-post-npe

Conversation

@nbradbury

@nbradbury nbradbury commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes JETPACK-ANDROID-1EVE — 24 users, live on 26.9.

The Prepublishing sheet is a DialogFragment, so the FragmentManager restores it after a config change or process death even when the host has no post loaded, and it then builds its UI against an empty EditPostRepository.

The guard goes in PrepublishingViewModel.start(), which every restore passes through, so it covers whichever screen was saved in KEY_SCREEN_STATE — Tags, Categories and Publish crash on first interaction too, not just Home. PrepublishingHomeViewModel also returns early, since that dismissal is async and the FragmentManager can restore the fragment before it commits.

Testing instructions

With Developer options → Don't keep activities on, open the Prepublishing sheet from a post, background the app, then return. Repeat from the Tags, Categories and Publish screens.

  • The app doesn't crash (it does on trunk) and the sheet closes.
  • With the setting off, publishing a post and a page still works.

The sheet is a DialogFragment, so the FragmentManager restores it after a
config change or process death regardless of whether the host activity has
loaded a post, and PrepublishingHomeFragment then built its UI against an
empty EditPostRepository. Dismiss the sheet instead, and stop
EditPostRepository.status from laundering a null post through the
unannotated PostStatus.fromPost.

Fixes JETPACK-ANDROID-1EVE
@dangermattic

dangermattic commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator
1 Warning
⚠️ PR is not assigned to a milestone.

Generated by 🚫 Danger

@wpmobilebot

wpmobilebot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

App Icon📲 You can test the changes from this Pull Request in Jetpack Android by scanning the QR code below to install the corresponding build.

App NameJetpack Android
Build TypeDebug
Versionpr23148-e9a0446
Build Number1498
Application IDcom.jetpack.android.prealpha
Commite9a0446
Installation URL19uihugghf6n8
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@wpmobilebot

wpmobilebot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

App Icon📲 You can test the changes from this Pull Request in WordPress Android by scanning the QR code below to install the corresponding build.

App NameWordPress Android
Build TypeDebug
Versionpr23148-e9a0446
Build Number1498
Application IDorg.wordpress.android.prealpha
Commite9a0446
Installation URL3l1jiasnpa470
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.11765% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 37.84%. Comparing base (3ab17e4) to head (e9a0446).

Files with missing lines Patch % Lines
...ordpress/android/ui/posts/PostListMainViewModel.kt 90.90% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##            trunk   #23148   +/-   ##
=======================================
  Coverage   37.84%   37.84%           
=======================================
  Files        2345     2345           
  Lines      127511   127517    +6     
  Branches    17709    17713    +4     
=======================================
+ Hits        48255    48261    +6     
+ Misses      75298    75297    -1     
- Partials     3958     3959    +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nbradbury
nbradbury marked this pull request as ready for review July 24, 2026 20:56
@nbradbury
nbradbury requested a review from adalpari July 25, 2026 11:48
The guard added in the previous commit only covered the HOME screen, but a
restored sheet navigates to whatever screen was saved in KEY_SCREEN_STATE. On a
restore into TAGS, CATEGORIES or PUBLISH the sheet stayed open over an empty
EditPostRepository and still crashed on the first interaction, via
requireNotNull(post) in updateAsync.

Guarding in PrepublishingViewModel.start() instead covers every screen the sheet
can restore into, and reuses the existing dismissBottomSheet channel, so the
dismissSheet LiveData and the parentFragment cast are no longer needed. The
PrepublishingHomeViewModel early return stays as a last line of defense, since
the dismissal is asynchronous and the FragmentManager can restore that fragment
before it commits.

Also reverts the EditPostRepository.status leniency: with the guard in place its
only reader is unreachable with a null post, so returning UNKNOWN just hid the
missing post from the other callers.
@nbradbury
nbradbury marked this pull request as draft July 26, 2026 14:20
@nbradbury
nbradbury removed the request for review from adalpari July 26, 2026 14:20
@nbradbury
nbradbury marked this pull request as ready for review July 27, 2026 13:48
@nbradbury
nbradbury requested a review from adalpari July 27, 2026 13:48
PostsListActivity has no configChanges, so it is recreated on rotation with a
freshly injected EditPostRepository, while PostListMainViewModel survives. Its
start() returned early on isStarted before assigning the repository and before
loading the post into it, so after any rotation the ViewModel kept writing to the
previous activity's dead repository while the restored Prepublishing sheet read
the new empty one. That is the state the sheet guard was catching: publishing
stayed broken for the rest of the activity's life, silently once the sheet began
dismissing itself instead of crashing.

Rebind on every start instead, and keep currentBottomSheetPostId in sync so the
id onSaveInstanceState reads survives a second restore.

Also makes PrepublishingViewModel.start()'s hasPost parameter required, so the
guard can't be bypassed by omission, and corrects the comment: the guard
dismisses on every restore path but does not stop the saved child fragment from
being restored and started first, since the dismissal is async.
@nbradbury
nbradbury requested review from jkmassel and oguzkocer and removed request for adalpari July 27, 2026 17:17

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

Looks good to me and works as expected. :shipit:

Claude being Claude, it shared a few ideas — I feel it always has something to say 🤷‍♂️ . I have discarded most because they're out of scope for this PR, but one might be worth considering, so I marked it as optional and shared below.

Even for that one, although I see Claude's point, I prefer the current implementation because it makes the function argument limited in scope and that in my opinion is more important than consistency.


Optional — hasPost: Boolean puts the invariant in the caller

WordPress/src/main/java/org/wordpress/android/ui/posts/prepublishing/PrepublishingViewModel.kt:66

Every sibling prepublishing ViewModel (PrepublishingHomeViewModel, PrepublishingTagsViewModel, PrepublishingCategoriesViewModel, PrepublishingPublishSettingsViewModel) receives the EditPostRepository. A snapshot boolean makes the guard only as good as the caller's getEditorHook().editPostRepository.hasPost(), and a future caller passing true disables it silently. PrepublishingBottomSheetFragment already resolves the hook, so passing the repository keeps the check inside the ViewModel and matches the rest of the package.

@nbradbury

Copy link
Copy Markdown
Contributor Author

Even for that one, although I see Claude's point, I prefer the current implementation because it makes the function argument limited in scope and that in my opinion is more important than consistency.

I agree with that. Thanks for the review!

@nbradbury
nbradbury merged commit 0946db5 into trunk Jul 27, 2026
23 checks passed
@nbradbury
nbradbury deleted the issue/prepublishing-null-post-npe branch July 27, 2026 21:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants