From 6e1a9558b1e6d4cbb3bb61e25b5d9190ac240e69 Mon Sep 17 00:00:00 2001 From: Ajesh R Pai Date: Wed, 7 Feb 2024 12:56:14 +0530 Subject: [PATCH] * Fixes: the switch from one wp.com site to another wp.com Fixes: the issue in which dashboard cards shown were not correct, the collect job for cards was not cancelled properly which resulted in dashboard cards shown in the wrong site. --- .../cards/DashboardCardsViewModelSlice.kt | 28 ++-- .../cards/dashboard/CardViewModelSlice.kt | 139 ++++++++++-------- .../activity/ActivityLogCardViewModelSlice.kt | 13 +- .../pages/PagesCardViewModelSlice.kt | 14 +- .../posts/PostsCardViewModelSlice.kt | 12 +- .../todaysstats/TodaysStatsViewModelSlice.kt | 12 +- .../dynamiccard/DynamicCardsViewModelSlice.kt | 24 ++- 7 files changed, 160 insertions(+), 82 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/DashboardCardsViewModelSlice.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/DashboardCardsViewModelSlice.kt index 27db2f502707..86a7ecb4a958 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/DashboardCardsViewModelSlice.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/DashboardCardsViewModelSlice.kt @@ -3,7 +3,9 @@ package org.wordpress.android.ui.mysite.cards import androidx.lifecycle.MutableLiveData import androidx.lifecycle.distinctUntilChanged import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.ui.mysite.BlazeCardViewModelSlice import org.wordpress.android.ui.mysite.MySiteCardAndItem @@ -42,6 +44,8 @@ class DashboardCardsViewModelSlice @Inject constructor( ) { private lateinit var scope: CoroutineScope + private var job: Job? = null + private val _onSnackbar = MutableLiveData>() val onSnackbarMessage = merge( _onSnackbar, @@ -169,16 +173,19 @@ class DashboardCardsViewModelSlice @Inject constructor( } fun buildCards(site: SiteModel) { - jpMigrationSuccessCardViewModelSlice.buildCard() - jetpackInstallFullPluginCardViewModelSlice.buildCard(site) - blazeCardViewModelSlice.buildCard(site) - bloggingPromptCardViewModelSlice.buildCard(site) - bloganuaryNudgeCardViewModelSlice.buildCard() - personalizeCardViewModelSlice.buildCard() - quickLinksItemViewModelSlice.buildCard(site) - plansCardViewModelSlice.buildCard(site) - cardViewModelSlice.buildCard(site) - quickStartCardViewModelSlice.build(site) + job?.cancel() + job = scope.launch { + jpMigrationSuccessCardViewModelSlice.buildCard() + jetpackInstallFullPluginCardViewModelSlice.buildCard(site) + blazeCardViewModelSlice.buildCard(site) + bloggingPromptCardViewModelSlice.buildCard(site) + bloganuaryNudgeCardViewModelSlice.buildCard() + personalizeCardViewModelSlice.buildCard() + quickLinksItemViewModelSlice.buildCard(site) + plansCardViewModelSlice.buildCard(site) + cardViewModelSlice.buildCard(site) + quickStartCardViewModelSlice.build(site) + } } @@ -197,6 +204,7 @@ class DashboardCardsViewModelSlice @Inject constructor( fun onCleared() { quickLinksItemViewModelSlice.onCleared() + job?.cancel() scope.cancel() } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/CardViewModelSlice.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/CardViewModelSlice.kt index 23d47d274450..d3d7b4f8a09e 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/CardViewModelSlice.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/CardViewModelSlice.kt @@ -2,10 +2,9 @@ package org.wordpress.android.ui.mysite.cards.dashboard import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.distinctUntilChanged import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.async +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import org.wordpress.android.R @@ -54,11 +53,48 @@ class CardViewModelSlice @Inject constructor( ) { private lateinit var scope: CoroutineScope + private var collectJob: Job? = null + private var fetchJob: Job? = null + private val _isRefreshing = MutableLiveData() val isRefreshing: LiveData = _isRefreshing - private val _uiModel = MutableLiveData() - val uiModel: LiveData = _uiModel.distinctUntilChanged() + val uiModel: MutableLiveData = merge( + dynamicCardsViewModelSlice.topDynamicCards, + todaysStatsViewModelSlice.uiModel, + pagesCardViewModelSlice.uiModel, + postsCardViewModelSlice.uiModel, + activityLogCardViewModelSlice.uiModel, + dynamicCardsViewModelSlice.bottomDynamicCards + ) { topDynamicCards, todaysStatsCard, pagesCard, postsCard, activityCard, bottomDynamicCards -> + val state = mergeUiModels( + topDynamicCards, + todaysStatsCard, + pagesCard, + postsCard, + activityCard, + bottomDynamicCards + ) + state + } as MutableLiveData + + private fun mergeUiModels( + topDynamicCards: List?, + todaysStatsCard: MySiteCardAndItem.Card.TodaysStatsCard?, + pagesCard: MySiteCardAndItem.Card.PagesCard?, + postsCard: List?, + activityCard: MySiteCardAndItem.Card.ActivityCard?, + bottomDynamicCards: List? + ): CardsState { + val cards = mutableListOf() + topDynamicCards?.let { cards.addAll(topDynamicCards) } + todaysStatsCard?.let { cards.add(todaysStatsCard) } + pagesCard?.let { cards.add(pagesCard) } + postsCard?.let { cards.addAll(postsCard) } + activityCard?.let { cards.add(activityCard) } + bottomDynamicCards?.let { cards.addAll(bottomDynamicCards) } + return CardsState.Success(cards) + } private val _onNavigation = MutableLiveData>() val onNavigation = merge( @@ -94,7 +130,8 @@ class CardViewModelSlice @Inject constructor( ) { _isRefreshing.postValue(true) // fetch data from store and then refresh the data from the server - scope.launch(bgDispatcher) { + collectJob?.cancel() + collectJob = scope.launch(bgDispatcher) { cardsStore.getCards(siteModel) .map { it.model } .map { cards -> cards?.filter { getCardTypes(siteModel).contains(it.type) } } @@ -109,7 +146,8 @@ class CardViewModelSlice @Inject constructor( selectedSite: SiteModel ) { _isRefreshing.postValue(true) - scope.launch(bgDispatcher) { + fetchJob?.cancel() + fetchJob = scope.launch(bgDispatcher) { val payload = CardsRestClient.FetchCardsPayload( selectedSite, getCardTypes(selectedSite), @@ -157,28 +195,28 @@ class CardViewModelSlice @Inject constructor( } private fun postErrorState() { - if ((_uiModel.value == null) || isUiModelEmpty()){ + if ((uiModel.value == null) || isUiModelEmpty()) { // if the - _uiModel.postValue( + uiModel.postValue( CardsState.ErrorState( MySiteCardAndItem.Card.ErrorCard( onRetryClick = ListItemInteraction.create(this::onDashboardErrorRetry) ) ) ) - } else if (_uiModel.value is CardsState.ErrorState) { + } else if (uiModel.value is CardsState.ErrorState) { // if the error state is already posted, then post the snackbar message - _onSnackbarMessage.postValue( - Event( - SnackbarMessageHolder(UiString.UiStringRes(R.string.my_site_dashboard_update_error)) - ) + _onSnackbarMessage.postValue( + Event( + SnackbarMessageHolder(UiString.UiStringRes(R.string.my_site_dashboard_update_error)) ) - } + ) + } _isRefreshing.postValue(false) } private fun isUiModelEmpty(): Boolean { - return (_uiModel.value is CardsState.Success) && (_uiModel.value as CardsState.Success).cards.isEmpty() + return (uiModel.value is CardsState.Success) && (uiModel.value as CardsState.Success).cards.isEmpty() } private fun onDashboardErrorRetry() { @@ -188,63 +226,46 @@ class CardViewModelSlice @Inject constructor( fun postState(cards: List?) { _isRefreshing.postValue(false) if (cards.isNullOrEmpty()) { - _uiModel.postValue(CardsState.Success(emptyList())) + uiModel.postValue(CardsState.Success(emptyList())) return } scope.launch { - val result = mutableListOf() - - val topDynamicCards = async { - dynamicCardsViewModelSlice.buildTopDynamicCards( - cards.firstOrNull { it is CardModel.DynamicCardsModel } as? CardModel.DynamicCardsModel - ) - } - - val todayStatsCard = async { - todaysStatsViewModelSlice.buildTodaysStatsCard( - cards.firstOrNull { it is CardModel.TodaysStatsCardModel } as? CardModel.TodaysStatsCardModel - ) - } + dynamicCardsViewModelSlice.buildTopDynamicCards( + cards.firstOrNull { it is CardModel.DynamicCardsModel } as? CardModel.DynamicCardsModel + ) - val postCard = async { - postsCardViewModelSlice.buildPostCard( - cards.firstOrNull { it is CardModel.PostsCardModel } as? CardModel.PostsCardModel - ) - } - val pagesCard = async { - pagesCardViewModelSlice.buildCard( - cards.firstOrNull { it is CardModel.PagesCardModel } as? CardModel.PagesCardModel - ) - } + todaysStatsViewModelSlice.buildTodaysStatsCard( + cards.firstOrNull { it is CardModel.TodaysStatsCardModel } as? CardModel.TodaysStatsCardModel + ) - val activityCard = async { - activityLogCardViewModelSlice.buildCard( - cards.firstOrNull { it is CardModel.ActivityCardModel } as? CardModel.ActivityCardModel - ) - } + postsCardViewModelSlice.buildPostCard( + cards.firstOrNull { it is CardModel.PostsCardModel } as? CardModel.PostsCardModel + ) - val bottomDynamicCards = async { - dynamicCardsViewModelSlice.buildBottomDynamicCards( - cards.firstOrNull { it is CardModel.DynamicCardsModel } as? CardModel.DynamicCardsModel - ) - } + pagesCardViewModelSlice.buildCard( + cards.firstOrNull { it is CardModel.PagesCardModel } as? CardModel.PagesCardModel + ) - result.apply { - topDynamicCards.await()?.let { addAll(it) } - todayStatsCard.await()?.let { add(it) } - postCard.await().let { addAll(it) } - pagesCard.await()?.let { add(it) } - activityCard.await()?.let { add(it) } - bottomDynamicCards.await()?.let { addAll(it) } - }.toList() + activityLogCardViewModelSlice.buildCard( + cards.firstOrNull { it is CardModel.ActivityCardModel } as? CardModel.ActivityCardModel + ) - _uiModel.postValue(CardsState.Success(result)) + dynamicCardsViewModelSlice.buildBottomDynamicCards( + cards.firstOrNull { it is CardModel.DynamicCardsModel } as? CardModel.DynamicCardsModel + ) } } fun clearValue() { - _uiModel.postValue(null) + uiModel.postValue(CardsState.Success(emptyList())) + collectJob?.cancel() + fetchJob?.cancel() + dynamicCardsViewModelSlice.clearValue() + todaysStatsViewModelSlice.clearValue() + pagesCardViewModelSlice.clearValue() + postsCardViewModelSlice.clearValue() + activityLogCardViewModelSlice.clearValue() } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/activity/ActivityLogCardViewModelSlice.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/activity/ActivityLogCardViewModelSlice.kt index c1c8c9796c93..3cb2354f080c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/activity/ActivityLogCardViewModelSlice.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/activity/ActivityLogCardViewModelSlice.kt @@ -1,6 +1,7 @@ package org.wordpress.android.ui.mysite.cards.dashboard.activity import androidx.annotation.VisibleForTesting +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import org.wordpress.android.fluxc.model.dashboard.CardModel import org.wordpress.android.ui.mysite.MySiteCardAndItem @@ -21,14 +22,17 @@ class ActivityLogCardViewModelSlice @Inject constructor( private val appPrefsWrapper: AppPrefsWrapper, private val activityCardBuilder: ActivityCardBuilder ) { + private val _uiModel = MutableLiveData() + val uiModel = _uiModel as LiveData + private val _onNavigation = MutableLiveData>() val onNavigation = _onNavigation private val _refresh = MutableLiveData>() val refresh = _refresh - fun buildCard(activityCardModel: CardModel.ActivityCardModel?): MySiteCardAndItem.Card.ActivityCard? { - return activityCardBuilder.build(getActivityLogCardBuilderParams(activityCardModel)) + fun buildCard(activityCardModel: CardModel.ActivityCardModel?) { + _uiModel.postValue(activityCardBuilder.build(getActivityLogCardBuilderParams(activityCardModel))) } @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @@ -75,8 +79,13 @@ class ActivityLogCardViewModelSlice @Inject constructor( _onNavigation.value = Event(SiteNavigationAction.OpenActivityLog(requireNotNull(selectedSiteRepository.getSelectedSite()))) } + private fun onActivityCardMoreMenuClick() = cardsTracker.trackCardMoreMenuClicked(CardsTracker.Type.ACTIVITY.label) + fun clearValue() { + _uiModel.value = null + } + enum class MenuItemType(val label: String) { ALL_ACTIVITY("all_activity"), HIDE_THIS("hide_this") diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/pages/PagesCardViewModelSlice.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/pages/PagesCardViewModelSlice.kt index 33a7b3c263a5..816e4fc72a2f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/pages/PagesCardViewModelSlice.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/pages/PagesCardViewModelSlice.kt @@ -1,5 +1,6 @@ package org.wordpress.android.ui.mysite.cards.dashboard.pages +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import org.wordpress.android.fluxc.model.dashboard.CardModel.PagesCardModel import org.wordpress.android.ui.mysite.MySiteCardAndItem @@ -17,14 +18,19 @@ class PagesCardViewModelSlice @Inject constructor( private val appPrefsWrapper: AppPrefsWrapper, private val pagesCardBuilder: PagesCardBuilder ) { + private val _uiModel = MutableLiveData() + val uiModel: LiveData = _uiModel + private val _onNavigation = MutableLiveData>() val onNavigation = _onNavigation private val _refresh = MutableLiveData>() val refresh = _refresh - fun buildCard(pagesCardModel: PagesCardModel?): MySiteCardAndItem.Card.PagesCard? { - return pagesCardBuilder.build(getPagesCardBuilderParams(pagesCardModel)) + fun buildCard(pagesCardModel: PagesCardModel?) { + _uiModel.postValue( + pagesCardBuilder.build(getPagesCardBuilderParams(pagesCardModel)) + ) } fun getPagesCardBuilderParams(pagesCardModel: PagesCardModel?): PagesCardBuilderParams { @@ -100,6 +106,10 @@ class PagesCardViewModelSlice @Inject constructor( ) ) } + + fun clearValue() { + _uiModel.postValue(null) + } } enum class PagesMenuItemType(val label: String) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/posts/PostsCardViewModelSlice.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/posts/PostsCardViewModelSlice.kt index 67a313e31000..104766119393 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/posts/PostsCardViewModelSlice.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/posts/PostsCardViewModelSlice.kt @@ -1,5 +1,6 @@ package org.wordpress.android.ui.mysite.cards.dashboard.posts +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import org.wordpress.android.fluxc.model.dashboard.CardModel.PostsCardModel import org.wordpress.android.ui.mysite.MySiteCardAndItem @@ -18,14 +19,17 @@ class PostsCardViewModelSlice @Inject constructor( private val appPrefsWrapper: AppPrefsWrapper, private val postCardBuilder: PostCardBuilder ) { + private val _uiModel = MutableLiveData?>() + val uiModel = _uiModel as LiveData?> + private val _onNavigation = MutableLiveData>() val onNavigation = _onNavigation private val _refresh = MutableLiveData>() val refresh = _refresh - fun buildPostCard(postsCardModel: PostsCardModel?): List { - return postCardBuilder.build(getPostsCardBuilderParams(postsCardModel)) + fun buildPostCard(postsCardModel: PostsCardModel?) { + _uiModel.postValue(postCardBuilder.build(getPostsCardBuilderParams(postsCardModel))) } fun getPostsCardBuilderParams(postsCardModel: PostsCardModel?) : PostCardBuilderParams { @@ -111,4 +115,8 @@ class PostsCardViewModelSlice @Inject constructor( PostCardType.SCHEDULED -> PostMenuCard.SCHEDULED_POSTS } } + + fun clearValue() { + _uiModel.postValue(null) + } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/todaysstats/TodaysStatsViewModelSlice.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/todaysstats/TodaysStatsViewModelSlice.kt index 48d45bc8042f..28bad0840fe5 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/todaysstats/TodaysStatsViewModelSlice.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/todaysstats/TodaysStatsViewModelSlice.kt @@ -1,5 +1,6 @@ package org.wordpress.android.ui.mysite.cards.dashboard.todaysstats +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import org.wordpress.android.fluxc.model.dashboard.CardModel.TodaysStatsCardModel import org.wordpress.android.ui.jetpackoverlay.JetpackFeatureRemovalPhaseHelper @@ -19,6 +20,9 @@ class TodaysStatsViewModelSlice @Inject constructor( private val appPrefsWrapper: AppPrefsWrapper, private val todaysStatsCardBuilder: TodaysStatsCardBuilder ) { + private val _uiModel = MutableLiveData() + val uiModel = _uiModel as LiveData + private val _onNavigation = MutableLiveData>() val onNavigation = _onNavigation @@ -26,8 +30,8 @@ class TodaysStatsViewModelSlice @Inject constructor( val refresh = _refresh - fun buildTodaysStatsCard(todaysStatsCardModel: TodaysStatsCardModel?): MySiteCardAndItem.Card.TodaysStatsCard? { - return todaysStatsCardBuilder.build(getTodaysStatsBuilderParams(todaysStatsCardModel)) + fun buildTodaysStatsCard(todaysStatsCardModel: TodaysStatsCardModel?) { + _uiModel.postValue(todaysStatsCardBuilder.build(getTodaysStatsBuilderParams(todaysStatsCardModel))) } fun getTodaysStatsBuilderParams(todaysStatsCardModel: TodaysStatsCardModel?): TodaysStatsCardBuilderParams { @@ -93,6 +97,10 @@ class TodaysStatsViewModelSlice @Inject constructor( _onNavigation.value = Event(SiteNavigationAction.OpenStatsInsights(selectedSite)) } } + + fun clearValue() { + _uiModel.value = null + } } enum class TodaysStatsMenuItemType(val label: String) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dynamiccard/DynamicCardsViewModelSlice.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dynamiccard/DynamicCardsViewModelSlice.kt index 09c71441cbf7..d5a04181af15 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dynamiccard/DynamicCardsViewModelSlice.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dynamiccard/DynamicCardsViewModelSlice.kt @@ -23,19 +23,28 @@ class DynamicCardsViewModelSlice @Inject constructor( private val _refresh = MutableLiveData>() val refresh = _refresh as LiveData> - fun buildTopDynamicCards(dynamicCardsModel: CardModel.DynamicCardsModel?): List? { - return dynamicCardsBuilder.build( + private val _topDynamicCards = MutableLiveData?>() + val topDynamicCards = _topDynamicCards as LiveData?> + + fun buildTopDynamicCards(dynamicCardsModel: CardModel.DynamicCardsModel?) { + _topDynamicCards.postValue( + dynamicCardsBuilder.build( getBuilderParams(dynamicCardsModel), CardModel.DynamicCardsModel.CardOrder.TOP ) + ) } - fun buildBottomDynamicCards(dynamicCardsModel: CardModel.DynamicCardsModel?): - List? { - return dynamicCardsBuilder.build( + private val _bottomDynamicCards = MutableLiveData?>() + val bottomDynamicCards = _bottomDynamicCards as LiveData?> + + fun buildBottomDynamicCards(dynamicCardsModel: CardModel.DynamicCardsModel?) { + _bottomDynamicCards.postValue( + dynamicCardsBuilder.build( getBuilderParams(dynamicCardsModel), CardModel.DynamicCardsModel.CardOrder.BOTTOM ) + ) } fun getBuilderParams(dynamicCards: CardModel.DynamicCardsModel?): DynamicCardsBuilderParams { @@ -81,4 +90,9 @@ class DynamicCardsViewModelSlice @Inject constructor( fun resetShown() { tracker.resetShown() } + + fun clearValue() { + _topDynamicCards.value = null + _bottomDynamicCards.value = null + } }