Skip to content

Commit

Permalink
Merge pull request #19869 from wordpress-mobile/move-site-menu-items-…
Browse files Browse the repository at this point in the history
…to-viewmodel-slice

Update: MySiteViewModel Architecture
  • Loading branch information
AjeshRPai authored Mar 25, 2024
2 parents c04dc18 + cbee896 commit 93d9440
Show file tree
Hide file tree
Showing 87 changed files with 4,463 additions and 5,786 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.wordpress.android.ui.mysite

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import org.wordpress.android.fluxc.store.AccountStore
import org.wordpress.android.ui.jetpackoverlay.JetpackFeatureRemovalPhaseHelper
import org.wordpress.android.ui.mysite.MySiteUiState.PartialState.AccountData
import org.wordpress.android.util.BuildConfigWrapper
import javax.inject.Inject

class AccountDataViewModelSlice @Inject constructor(
private val accountStore: AccountStore,
private val buildConfigWrapper: BuildConfigWrapper,
private val jetpackFeatureRemovalPhaseHelper: JetpackFeatureRemovalPhaseHelper
) {
private lateinit var scope: CoroutineScope

private val _isRefreshing = MutableLiveData<Boolean>()
val isRefreshing: LiveData<Boolean> = _isRefreshing

private val _uiModel = MutableLiveData<AccountData?>()
val uiModel: LiveData<AccountData?> = _uiModel

fun initialize(scope: CoroutineScope) {
this.scope = scope
}

fun onResume() {
scope.launch {
if(!shouldBuildCard()) _uiModel.postValue(null)
_isRefreshing.postValue(true)
val account = accountStore.account
account?.let {
val url = account.avatarUrl.orEmpty()
val name =account.displayName?.ifEmpty {
account.userName.orEmpty()
}.orEmpty()
_uiModel.postValue(AccountData(url, name))
}?: {
_uiModel.postValue(null)
}
_isRefreshing.postValue(false)
}
}

fun onRefresh() {
onResume()
}

fun onCleared() {
scope.cancel()
}

private fun shouldBuildCard(): Boolean {
return (!buildConfigWrapper.isJetpackApp
&& jetpackFeatureRemovalPhaseHelper.shouldRemoveJetpackFeatures())
}
}
Original file line number Diff line number Diff line change
@@ -1,41 +1,120 @@
package org.wordpress.android.ui.mysite

import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.distinctUntilChanged
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.wordpress.android.Result
import org.wordpress.android.analytics.AnalyticsTracker
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.fluxc.model.blaze.BlazeCampaignModel
import org.wordpress.android.modules.BG_THREAD
import org.wordpress.android.ui.blaze.BlazeFeatureUtils
import org.wordpress.android.ui.blaze.BlazeFlowSource
import org.wordpress.android.ui.blaze.blazecampaigns.campaigndetail.CampaignDetailPageSource
import org.wordpress.android.ui.blaze.blazecampaigns.campaignlisting.CampaignListingPageSource
import org.wordpress.android.ui.blaze.blazecampaigns.campaignlisting.FetchCampaignListUseCase
import org.wordpress.android.ui.mysite.MySiteCardAndItemBuilderParams.BlazeCardBuilderParams
import org.wordpress.android.ui.mysite.MySiteCardAndItemBuilderParams.BlazeCardBuilderParams.CampaignWithBlazeCardBuilderParams
import org.wordpress.android.ui.mysite.MySiteCardAndItemBuilderParams.BlazeCardBuilderParams.PromoteWithBlazeCardBuilderParams
import org.wordpress.android.ui.mysite.MySiteUiState.PartialState.BlazeCardUpdate
import org.wordpress.android.ui.mysite.cards.blaze.BlazeCardBuilder
import org.wordpress.android.ui.mysite.cards.blaze.MostRecentCampaignUseCase
import org.wordpress.android.ui.mysite.cards.dashboard.CardsTracker
import org.wordpress.android.util.NetworkUtilsWrapper
import org.wordpress.android.viewmodel.Event
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton

@Singleton
class BlazeCardViewModelSlice @Inject constructor(
@param:Named(BG_THREAD) private val bgDispatcher: CoroutineDispatcher,
private val blazeFeatureUtils: BlazeFeatureUtils,
private val selectedSiteRepository: SelectedSiteRepository,
private val cardsTracker: CardsTracker
private val cardsTracker: CardsTracker,
private val blazeCardBuilder: BlazeCardBuilder,
private val networkUtilsWrapper: NetworkUtilsWrapper,
private val fetchCampaignListUseCase: FetchCampaignListUseCase,
private val mostRecentCampaignUseCase: MostRecentCampaignUseCase,
) {
private lateinit var scope: CoroutineScope

private val _onNavigation = MutableLiveData<Event<SiteNavigationAction>>()
val onNavigation = _onNavigation

private val _refresh = MutableLiveData<Event<Boolean>>()
val refresh = _refresh
private val _isRefreshing = MutableLiveData<Boolean>()
val isRefreshing: LiveData<Boolean> = _isRefreshing

private val _uiModel = MutableLiveData<MySiteCardAndItem.Card.BlazeCard?>()
val uiModel = _uiModel.distinctUntilChanged()

fun initialize(scope: CoroutineScope) {
this.scope = scope
}

fun buildCard(site: SiteModel) {
_isRefreshing.postValue(true)
scope.launch(bgDispatcher){
if (blazeFeatureUtils.shouldShowBlazeCardEntryPoint(site)) {
if (blazeFeatureUtils.shouldShowBlazeCampaigns()) {
fetchCampaigns(site)
} else {
// show blaze promo card if campaign feature is not available
showPromoteWithBlazeCard()
}
} else {
postState(false)
}
}
}

private suspend fun fetchCampaigns(site: SiteModel) {
if (networkUtilsWrapper.isNetworkAvailable().not()) {
getMostRecentCampaignFromDb(site)
} else {
when (fetchCampaignListUseCase.execute(site = site, offset = 0)) {
is Result.Success -> getMostRecentCampaignFromDb(site)
// there are no campaigns or if there is an error , show blaze promo card
is Result.Failure -> showPromoteWithBlazeCard()
}
}
}

private suspend fun getMostRecentCampaignFromDb(site: SiteModel) {
when(val result = mostRecentCampaignUseCase.execute(site)) {
is Result.Success -> postState(true, campaign = result.value)
is Result.Failure -> showPromoteWithBlazeCard()
}
}

private fun showPromoteWithBlazeCard() {
postState(true)
}

fun getBlazeCardBuilderParams(blazeCardUpdate: BlazeCardUpdate?): BlazeCardBuilderParams? {
return blazeCardUpdate?.let {
if (it.blazeEligible) {
it.campaign?.let { campaign ->
private fun postState(isBlazeEligible: Boolean, campaign: BlazeCampaignModel? = null) {
_isRefreshing.postValue(false)
if(isBlazeEligible) {
buildBlazeCard(campaign)?.let {
_uiModel.postValue(it)
}
} else {
_uiModel.postValue(null)
}
}


private fun buildBlazeCard(campaign: BlazeCampaignModel? = null): MySiteCardAndItem.Card.BlazeCard? {
return getBlazeCardBuilderParams(campaign).let { blazeCardBuilder.build(it) }
}

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal fun getBlazeCardBuilderParams(campaign: BlazeCampaignModel? = null): BlazeCardBuilderParams {
return campaign?.let {
getCampaignWithBlazeCardBuilderParams(campaign)
} ?: getPromoteWithBlazeCardBuilderParams()
} else null
}
}

private fun getCampaignWithBlazeCardBuilderParams(campaign: BlazeCampaignModel) =
Expand Down Expand Up @@ -89,6 +168,7 @@ class BlazeCardViewModelSlice @Inject constructor(
)
}


private fun getPromoteWithBlazeCardBuilderParams() =
PromoteWithBlazeCardBuilderParams(
onClick = this::onPromoteWithBlazeCardClick,
Expand All @@ -99,7 +179,6 @@ class BlazeCardViewModelSlice @Inject constructor(
)
)


private fun onPromoteCardLearnMoreClick() {
cardsTracker.trackCardMoreMenuItemClicked(
CardsTracker.Type.PROMOTE_WITH_BLAZE.label,
Expand Down Expand Up @@ -138,7 +217,7 @@ class BlazeCardViewModelSlice @Inject constructor(
selectedSiteRepository.getSelectedSite()?.let {
blazeFeatureUtils.hideBlazeCard(it.siteId)
}
_refresh.value = Event(true)
_uiModel.postValue(null)
}

private fun onPromoteCardMoreMenuClick() {
Expand Down Expand Up @@ -170,6 +249,10 @@ class BlazeCardViewModelSlice @Inject constructor(
_onNavigation.value =
Event(SiteNavigationAction.OpenPromoteWithBlazeOverlay(source = BlazeFlowSource.DASHBOARD_CARD))
}

fun clearValue() {
_uiModel.postValue(null)
}
}

enum class CampaignCardMenuItem(val label: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import org.wordpress.android.modules.BG_THREAD
import org.wordpress.android.ui.mysite.MySiteCardAndItem.Card.BloggingPromptCard
import org.wordpress.android.ui.mysite.cards.dashboard.bloggingprompts.BloggingPromptsCardAnalyticsTracker
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference
import javax.inject.Inject
import javax.inject.Named

Expand All @@ -26,30 +25,9 @@ class BloggingPromptsCardTrackHelper @Inject constructor(
) {
private var dashboardUpdateDebounceJob: Job? = null

private val latestPromptCardVisible = AtomicReference<Boolean?>(null)
private val waitingToTrack = AtomicBoolean(false)
private val currentSite = AtomicReference<Int?>(null)

private fun onDashboardRefreshed(state: MySiteViewModel.State.SiteSelected?) {
val bloggingPromptCards = state?.dashboardData
?.filterIsInstance<BloggingPromptCard>()
?: listOf()

latestPromptCardVisible.get()?.let { isPromptCardVisible ->
val attribution = bloggingPromptCards.firstOrNull()?.attribution
if (isPromptCardVisible) tracker.trackMySiteCardViewed(attribution)
waitingToTrack.set(false)
} ?: run {
waitingToTrack.set(true)
}
}


fun onDashboardCardsUpdated(scope: CoroutineScope, state: MySiteViewModel.State.SiteSelected?) {
val bloggingPromptCards = state?.dashboardData
?.filterIsInstance<BloggingPromptCard>()
?: listOf()
private val waitingToTrack = AtomicBoolean(true)

fun onDashboardCardsUpdated(scope: CoroutineScope, bloggingPromptCards: List<BloggingPromptCard>) {
// cancel any existing job (debouncing mechanism)
dashboardUpdateDebounceJob?.cancel()

Expand All @@ -58,8 +36,6 @@ class BloggingPromptsCardTrackHelper @Inject constructor(

// add a delay (debouncing mechanism)
delay(PROMPT_CARD_VISIBLE_DEBOUNCE)

latestPromptCardVisible.set(isVisible)
if (isVisible && waitingToTrack.getAndSet(false)) {
val attribution = bloggingPromptCards.firstOrNull()?.attribution
tracker.trackMySiteCardViewed(attribution)
Expand All @@ -72,15 +48,8 @@ class BloggingPromptsCardTrackHelper @Inject constructor(
}
}

fun onResume(state: MySiteViewModel.State.SiteSelected?) {
onDashboardRefreshed(state)
}

fun onSiteChanged(siteId: Int?, state: MySiteViewModel.State.SiteSelected?) {
if (currentSite.getAndSet(siteId) != siteId) {
latestPromptCardVisible.set(null)
onDashboardRefreshed(state)
}
fun onSiteChanged() {
waitingToTrack.set(true)
}

private val BloggingPromptCard.attribution: String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import org.wordpress.android.ui.mysite.cards.nocards.NoCardsMessageViewHolder
import org.wordpress.android.ui.mysite.cards.personalize.PersonalizeCardViewHolder
import org.wordpress.android.ui.mysite.cards.quicklinksitem.QuickLinkRibbonViewHolder
import org.wordpress.android.ui.mysite.cards.quickstart.QuickStartCardViewHolder
import org.wordpress.android.ui.mysite.cards.siteinfo.SiteInfoHeaderCardViewholder
import org.wordpress.android.ui.mysite.cards.siteinfo.SiteInfoHeaderCardViewHolder
import org.wordpress.android.ui.mysite.cards.sotw2023.WpSotw2023NudgeCardViewHolder
import org.wordpress.android.ui.mysite.items.categoryheader.MySiteCategoryItemEmptyViewHolder
import org.wordpress.android.ui.mysite.items.categoryheader.MySiteCategoryItemViewHolder
Expand All @@ -80,7 +80,7 @@ class MySiteAdapter(
@Suppress("ComplexMethod")
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MySiteCardAndItemViewHolder<*> {
return when (viewType) {
MySiteCardAndItem.Type.SITE_INFO_CARD.ordinal -> SiteInfoHeaderCardViewholder(parent, imageManager)
MySiteCardAndItem.Type.SITE_INFO_CARD.ordinal -> SiteInfoHeaderCardViewHolder(parent, imageManager)
MySiteCardAndItem.Type.QUICK_LINK_RIBBON.ordinal -> QuickLinkRibbonViewHolder(parent)
MySiteCardAndItem.Type.DOMAIN_REGISTRATION_CARD.ordinal -> DomainRegistrationViewHolder(parent)
MySiteCardAndItem.Type.QUICK_START_CARD.ordinal -> QuickStartCardViewHolder(parent, uiHelpers)
Expand Down Expand Up @@ -136,7 +136,7 @@ class MySiteAdapter(
@Suppress("ComplexMethod")
override fun onBindViewHolder(holder: MySiteCardAndItemViewHolder<*>, position: Int) {
when (holder) {
is SiteInfoHeaderCardViewholder -> holder.bind(getItem(position) as SiteInfoHeaderCard)
is SiteInfoHeaderCardViewHolder -> holder.bind(getItem(position) as SiteInfoHeaderCard)
is QuickLinkRibbonViewHolder -> holder.bind(getItem(position) as QuickLinksItem)
is DomainRegistrationViewHolder -> holder.bind(getItem(position) as DomainRegistrationCard)
is QuickStartCardViewHolder -> holder.bind(getItem(position) as QuickStartCard)
Expand Down
Loading

0 comments on commit 93d9440

Please sign in to comment.