Skip to content

Commit 669c3b4

Browse files
committed
Fix crash when the Prepublishing sheet is restored without a post
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
1 parent 3ab17e4 commit 669c3b4

5 files changed

Lines changed: 44 additions & 6 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class EditPostRepository
7373
val password: String
7474
get() = post!!.password
7575
val status: PostStatus
76-
get() = fromPost(getPost())
76+
get() = post?.let { fromPost(it) } ?: PostStatus.UNKNOWN
7777
val isPage: Boolean
7878
get() = post!!.isPage
7979
val isLocalDraft: Boolean

WordPress/src/main/java/org/wordpress/android/ui/posts/prepublishing/home/PrepublishingHomeFragment.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package org.wordpress.android.ui.posts.prepublishing.home
33
import android.content.Context
44
import android.os.Bundle
55
import android.view.View
6+
import androidx.fragment.app.DialogFragment
67
import androidx.fragment.app.Fragment
78
import androidx.lifecycle.ViewModelProvider
89
import androidx.recyclerview.widget.LinearLayoutManager
@@ -102,6 +103,11 @@ class PrepublishingHomeFragment : Fragment(R.layout.post_prepublishing_home_frag
102103
actionClickedListener?.onSubmitButtonClicked(publishPost)
103104
}
104105

106+
viewModel.dismissSheet.observeEvent(viewLifecycleOwner) {
107+
// allowing state loss because this can run while the host activity is already finishing
108+
(parentFragment as? DialogFragment)?.dismissAllowingStateLoss()
109+
}
110+
105111
viewModel.start(getEditPostRepository(), getSite())
106112
}
107113

WordPress/src/main/java/org/wordpress/android/ui/posts/prepublishing/home/PrepublishingHomeViewModel.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import org.wordpress.android.ui.posts.prepublishing.home.usecases.GetButtonUiSta
2323
import org.wordpress.android.ui.posts.trackPrepublishingNudges
2424
import org.wordpress.android.ui.utils.UiString.UiStringRes
2525
import org.wordpress.android.ui.utils.UiString.UiStringText
26+
import org.wordpress.android.util.AppLog
2627
import org.wordpress.android.util.StringUtils
2728
import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper
2829
import org.wordpress.android.util.merge
@@ -49,6 +50,9 @@ class PrepublishingHomeViewModel @Inject constructor(
4950
private val _onSubmitButtonClicked = MutableLiveData<Event<PublishPost>>()
5051
val onSubmitButtonClicked: LiveData<Event<PublishPost>> = _onSubmitButtonClicked
5152

53+
private val _dismissSheet = MutableLiveData<Event<Unit>>()
54+
val dismissSheet: LiveData<Event<Unit>> = _dismissSheet
55+
5256
private val _uiState = MutableLiveData<List<PrepublishingHomeItemUiState>>()
5357
private var _socialUiState: MutableLiveData<SocialUiState> = MutableLiveData(SocialUiState.Hidden)
5458

@@ -68,6 +72,14 @@ class PrepublishingHomeViewModel @Inject constructor(
6872
fun start(editPostRepository: EditPostRepository, site: SiteModel) {
6973
this.editPostRepository = editPostRepository
7074
if (isStarted) return
75+
// The sheet is a DialogFragment, so the FragmentManager can restore it after a config
76+
// change or process death before the host activity has loaded a post. Close the sheet
77+
// rather than building its UI against an empty repository.
78+
if (!editPostRepository.hasPost()) {
79+
AppLog.e(AppLog.T.POSTS, "Prepublishing sheet opened without a post; dismissing.")
80+
_dismissSheet.postValue(Event(Unit))
81+
return
82+
}
7183
isStarted = true
7284

7385
setupHomeUiState(editPostRepository, site)

WordPress/src/test/java/org/wordpress/android/ui/posts/EditPostRepositoryTest.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import org.wordpress.android.fluxc.model.post.PostLocation
1919
import org.wordpress.android.fluxc.model.post.PostStatus.DRAFT
2020
import org.wordpress.android.fluxc.model.post.PostStatus.PENDING
2121
import org.wordpress.android.fluxc.model.post.PostStatus.PUBLISHED
22+
import org.wordpress.android.fluxc.model.post.PostStatus.UNKNOWN
2223
import org.wordpress.android.fluxc.store.PostStore
2324
import org.wordpress.android.ui.posts.EditPostRepository.UpdatePostResult
2425
import org.wordpress.android.util.LocaleManagerWrapper
@@ -69,6 +70,11 @@ class EditPostRepositoryTest : BaseUnitTest() {
6970
assertThat(editPostRepository.isPostPublishable()).isFalse()
7071
}
7172

73+
@Test
74+
fun `status is UNKNOWN before initialization`() {
75+
assertThat(editPostRepository.status).isEqualTo(UNKNOWN)
76+
}
77+
7278
@Test
7379
fun `reads post for undo correctly`() {
7480
val post = PostModel()

WordPress/src/test/java/org/wordpress/android/ui/posts/PrepublishingHomeViewModelTest.kt

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class PrepublishingHomeViewModelTest : BaseUnitTest() {
7171
).doAnswer {
7272
PublishButtonUiState(it.arguments[2] as (PublishPost) -> Unit)
7373
}
74+
whenever(editPostRepository.hasPost()).thenReturn(true)
7475
whenever(editPostRepository.getEditablePost()).thenReturn(PostModel())
7576
whenever(postSettingsUtils.getPublishDateLabel(any())).thenReturn((""))
7677
whenever(site.name).thenReturn("")
@@ -86,7 +87,7 @@ class PrepublishingHomeViewModelTest : BaseUnitTest() {
8687
val expectedActionsAmount = 3
8788

8889
// act
89-
viewModel.start(mock(), site)
90+
viewModel.start(editPostRepository, site)
9091

9192
// assert
9293
assertThat(viewModel.uiState.value?.filterIsInstance(HomeUiState::class.java)?.size).isEqualTo(
@@ -163,7 +164,7 @@ class PrepublishingHomeViewModelTest : BaseUnitTest() {
163164
val expectedActionsAmount = 1
164165

165166
// act
166-
viewModel.start(mock(), site)
167+
viewModel.start(editPostRepository, site)
167168

168169
// assert
169170
assertThat(viewModel.uiState.value?.filterIsInstance(HeaderUiState::class.java)?.size).isEqualTo(
@@ -177,7 +178,7 @@ class PrepublishingHomeViewModelTest : BaseUnitTest() {
177178
val expectedActionsAmount = 1
178179

179180
// act
180-
viewModel.start(mock(), site)
181+
viewModel.start(editPostRepository, site)
181182

182183
// assert
183184
assertThat(viewModel.uiState.value?.filterIsInstance(ButtonUiState::class.java)?.size).isEqualTo(
@@ -191,7 +192,7 @@ class PrepublishingHomeViewModelTest : BaseUnitTest() {
191192
val expectedActionType = PrepublishingScreenNavigation.Publish
192193

193194
// act
194-
viewModel.start(mock(), site)
195+
viewModel.start(editPostRepository, site)
195196
val publishAction = getHomeUiState(expectedActionType)
196197
publishAction?.onNavigationActionClicked?.invoke(expectedActionType)
197198

@@ -205,7 +206,7 @@ class PrepublishingHomeViewModelTest : BaseUnitTest() {
205206
val expectedActionType = PrepublishingScreenNavigation.Tags
206207

207208
// act
208-
viewModel.start(mock(), site)
209+
viewModel.start(editPostRepository, site)
209210
val tagsAction = getHomeUiState(expectedActionType)
210211
tagsAction?.onNavigationActionClicked?.invoke(expectedActionType)
211212

@@ -397,6 +398,19 @@ class PrepublishingHomeViewModelTest : BaseUnitTest() {
397398
assertThat(uiSocialState).isEqualTo(SocialUiState.Hidden)
398399
}
399400

401+
@Test
402+
fun `given a repository with no post, when the viewModel is started, then the sheet is dismissed`() {
403+
// arrange
404+
whenever(editPostRepository.hasPost()).thenReturn(false)
405+
406+
// act
407+
viewModel.start(editPostRepository, site)
408+
409+
// assert
410+
assertThat(viewModel.dismissSheet.value?.peekContent()).isNotNull
411+
assertThat(viewModel.uiState.value).isNull()
412+
}
413+
400414
private fun getHeaderUiState() = viewModel.uiState.value?.filterIsInstance(HeaderUiState::class.java)?.first()
401415

402416
private fun getButtonUiState(): ButtonUiState? {

0 commit comments

Comments
 (0)