Skip to content

Commit

Permalink
MBL-1394 Remove RXJava usage from new AddOnsViewModel (#2044)
Browse files Browse the repository at this point in the history
* Initial commit

* Fix init crash

* Update tests

* Compare values for UI state for the not shippable test

* Remove subscription and disposable

---------

Co-authored-by: Isabel Martin <[email protected]>
  • Loading branch information
ycheng-kickstarter and Arkariang authored May 29, 2024
1 parent 8c0a861 commit 67307f6
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 76 deletions.
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
package com.kickstarter.viewmodels.projectpage

import android.util.Pair
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.kickstarter.libs.Environment
import com.kickstarter.libs.rx.transformers.Transformers
import com.kickstarter.libs.utils.RewardUtils
import com.kickstarter.libs.utils.extensions.addToDisposable
import com.kickstarter.libs.utils.extensions.isNotNull
import com.kickstarter.mock.factories.ShippingRuleFactory
import com.kickstarter.models.Location
import com.kickstarter.models.Reward
import com.kickstarter.models.ShippingRule
import com.kickstarter.ui.data.ProjectData
import io.reactivex.Observable
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.PublishSubject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
Expand All @@ -41,16 +34,12 @@ data class AddOnsUIState(
)

class AddOnsViewModel(val environment: Environment) : ViewModel() {
private val disposables = CompositeDisposable()
private val currentConfig = requireNotNull(environment.currentConfigV2())
private val apolloClient = requireNotNull(environment.apolloClientV2())

private val currentUserReward = PublishSubject.create<Reward>()
private val shippingRulesObservable = BehaviorSubject.create<List<ShippingRule>>()
private val defaultShippingRuleObservable = PublishSubject.create<ShippingRule>()

private val mutableAddOnsUIState = MutableStateFlow(AddOnsUIState())
private var addOns: List<Reward> = listOf()
private var currentUserReward: Reward = Reward.builder().build()
private var defaultShippingRule: ShippingRule = ShippingRule.builder().build()
private var currentShippingRule: ShippingRule = ShippingRule.builder().build()
private var shippingSelectorIsGone: Boolean = false
Expand All @@ -68,59 +57,27 @@ class AddOnsViewModel(val environment: Environment) : ViewModel() {
initialValue = AddOnsUIState()
)

init {
currentUserReward
.compose<Pair<Reward, List<ShippingRule>>>(
Transformers.combineLatestPair(
shippingRulesObservable
)
)
.switchMap { getDefaultShippingRule(it.second) }
.subscribe {
defaultShippingRuleObservable.onNext(it)
defaultShippingRule = it
currentShippingRule = it
viewModelScope.launch {
emitCurrentState()
}
getAddOns(noShippingRule = shippingSelectorIsGone)
}.addToDisposable(disposables)

val shippingRule = getSelectedShippingRule(defaultShippingRuleObservable, currentUserReward)

shippingRule
.distinctUntilChanged { rule1, rule2 ->
rule1.location()?.id() == rule2.location()?.id() && rule1.cost() == rule2.cost()
}
.subscribe {
currentShippingRule = it
}
.addToDisposable(disposables)
}

fun provideErrorAction(errorAction: (message: String?) -> Unit) {
this.errorAction = errorAction
}

fun provideProjectData(projectData: ProjectData) {
this.projectData = projectData

projectData.project().rewards()?.let { rewards ->
if (rewards.isNotEmpty()) {
val reward = rewards.firstOrNull { theOne ->
!theOne.isAddOn() && theOne.isAvailable() && RewardUtils.isShippable(theOne)
}
reward?.let {
apolloClient.getShippingRules(
reward = reward
).subscribe { shippingRulesEnvelope ->
if (shippingRulesEnvelope.isNotNull()) shippingRulesObservable.onNext(
shippingRulesEnvelope.shippingRules()
)
shippingRules = shippingRulesEnvelope.shippingRules()
}.addToDisposable(disposables)
} ?: run {
shippingRulesObservable.onNext(listOf())
viewModelScope.launch {
projectData.project().rewards()?.let { rewards ->
if (rewards.isNotEmpty()) {
val reward = rewards.firstOrNull { theOne ->
!theOne.isAddOn() && theOne.isAvailable() && RewardUtils.isShippable(theOne)
}
reward?.let {
apolloClient.getShippingRules(reward = reward)
.asFlow()
.collect { shippingRulesEnvelope ->
shippingRules = shippingRulesEnvelope.shippingRules()
recalculateShippingRule()
}
}
}
}
}
Expand Down Expand Up @@ -165,23 +122,17 @@ class AddOnsViewModel(val environment: Environment) : ViewModel() {
}

private fun getSelectedShippingRule(
defaultShippingRule: Observable<ShippingRule>,
reward: Observable<Reward>
): Observable<ShippingRule> {
return Observable.combineLatest(
defaultShippingRule.startWith(ShippingRuleFactory.emptyShippingRule()),
reward
) { defaultShipping, rw ->
return@combineLatest chooseShippingRule(defaultShipping, rw)
}
defaultShippingRule: ShippingRule,
reward: Reward
): ShippingRule {
return chooseShippingRule(defaultShippingRule, reward)
}

private fun chooseShippingRule(defaultShipping: ShippingRule, rw: Reward): ShippingRule =
when {
RewardUtils.isDigital(rw) || !RewardUtils.isShippable(rw) || RewardUtils.isLocalPickup(
rw
) -> ShippingRuleFactory.emptyShippingRule()
// sameReward -> backingShippingRule // TODO: When changing reward for manage pledge flow
else -> defaultShipping
}

Expand All @@ -193,11 +144,13 @@ class AddOnsViewModel(val environment: Environment) : ViewModel() {
shippingSelectorIsGone =
RewardUtils.isDigital(reward) || !RewardUtils.isShippable(reward) || RewardUtils.isLocalPickup(reward)

currentUserReward = reward

recalculateShippingRule()

viewModelScope.launch {
emitCurrentState()
}

this.currentUserReward.onNext(reward)
}

fun onShippingLocationChanged(shippingRule: ShippingRule) {
Expand All @@ -219,6 +172,32 @@ class AddOnsViewModel(val environment: Environment) : ViewModel() {
}
}

private fun recalculateShippingRule() {
viewModelScope.launch {
getDefaultShippingRule(shippingRules)
.asFlow()
.catch {
errorAction.invoke(null)
}
.collect { default ->
defaultShippingRule = default
currentShippingRule = defaultShippingRule

val newShippingRule = getSelectedShippingRule(defaultShippingRule, currentUserReward)
val oldShippingRule = currentShippingRule
if (newShippingRule.location()?.id() != oldShippingRule.location()?.id() && newShippingRule.cost() != oldShippingRule.cost()) {
currentShippingRule = newShippingRule
}
emitCurrentState()
}
}

viewModelScope.launch {
emitCurrentState()
}
getAddOns(noShippingRule = shippingSelectorIsGone)
}

private suspend fun emitCurrentState(isLoading: Boolean = false) {
mutableAddOnsUIState.emit(
AddOnsUIState(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class AddOnsViewModelTest : KSRobolectricTestCase() {
val reward = RewardFactory
.rewardHasAddOns()
.toBuilder()
.shippingPreference(Reward.ShippingPreference.NONE.name)
.shippingType(Reward.SHIPPING_TYPE_NO_SHIPPING)
.build()

val uiState = mutableListOf<AddOnsUIState>()
Expand All @@ -58,18 +58,17 @@ class AddOnsViewModelTest : KSRobolectricTestCase() {
viewModel.userRewardSelection(reward)

assertEquals(
uiState.last(),
AddOnsUIState(
shippingSelectorIsGone = true
)
uiState.last().shippingSelectorIsGone,
true
)
}

@Test
fun `test_show_location_selection_on_reward_is_shippable`() = runTest {
val reward = RewardFactory
.rewardWithShipping()
.rewardHasAddOns()
.toBuilder()
.shippingType(Reward.SHIPPING_TYPE_MULTIPLE_LOCATIONS)
.build()

val uiState = mutableListOf<AddOnsUIState>()
Expand Down

0 comments on commit 67307f6

Please sign in to comment.