Skip to content

Commit

Permalink
Refactor: Show ITR setting based on subscription status
Browse files Browse the repository at this point in the history
The ITR setting is now displayed based on the current status of the product subscription.
It will be:
- Hidden if the user is ineligible or signed out
- Expired if the subscription is inactive or expired
- Activating if the subscription is waiting for activation
- Subscribed if the subscription is active

ProductSubscriptionManager now takes a vararg of Products as we have US and ROW ITR products
  • Loading branch information
mikescamell committed Dec 17, 2024
1 parent cc134f7 commit 99d90aa
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

interface ProductSubscriptionManager {
fun entitlementStatus(product: Product): Flow<ProductStatus>

fun entitlementStatus(vararg products: Product): Flow<ProductStatus>

enum class ProductStatus {
ACTIVE,
Expand All @@ -44,9 +45,11 @@ class RealProductSubscriptionManager @Inject constructor(
private val subscriptions: Subscriptions,
) : ProductSubscriptionManager {

override fun entitlementStatus(product: Product): Flow<ProductStatus> = hasEntitlement(product).map { getEntitlementStatusInternal(it) }
override fun entitlementStatus(vararg products: Product): Flow<ProductStatus> =
hasEntitlement(*products).map { getEntitlementStatusInternal(it) }

private fun hasEntitlement(product: Product): Flow<Boolean> = subscriptions.getEntitlementStatus().map { it.contains(product) }
private fun hasEntitlement(vararg products: Product): Flow<Boolean> =
subscriptions.getEntitlementStatus().map { entitledProducts -> entitledProducts.any { products.contains(it) } }

private suspend fun getEntitlementStatusInternal(hasValidEntitlement: Boolean): ProductStatus = when {
!hasValidEntitlement -> ProductStatus.INELIGIBLE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
import androidx.core.view.isGone
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.findViewTreeViewModelStoreOwner
Expand All @@ -31,11 +32,13 @@ import com.duckduckgo.common.utils.ConflatedJob
import com.duckduckgo.common.utils.ViewViewModelFactory
import com.duckduckgo.di.scopes.ViewScope
import com.duckduckgo.navigation.api.GlobalActivityStarter
import com.duckduckgo.subscriptions.impl.R
import com.duckduckgo.subscriptions.impl.SubscriptionsConstants
import com.duckduckgo.subscriptions.impl.databinding.ViewItrSettingsBinding
import com.duckduckgo.subscriptions.impl.settings.views.ItrSettingViewModel.Command
import com.duckduckgo.subscriptions.impl.settings.views.ItrSettingViewModel.Command.OpenItr
import com.duckduckgo.subscriptions.impl.settings.views.ItrSettingViewModel.ViewState
import com.duckduckgo.subscriptions.impl.settings.views.ItrSettingViewModel.ViewState.ItrState
import com.duckduckgo.subscriptions.impl.ui.SubscriptionsWebViewActivityWithParams
import dagger.android.support.AndroidSupportInjection
import javax.inject.Inject
Expand Down Expand Up @@ -75,10 +78,6 @@ class ItrSettingView @JvmOverloads constructor(

findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel)

binding.itrSettings.setClickListener {
viewModel.onItr()
}

@SuppressLint("NoHardcodedCoroutineDispatcher")
coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)

Expand All @@ -100,10 +99,21 @@ class ItrSettingView @JvmOverloads constructor(
}

private fun renderView(viewState: ViewState) {
if (viewState.hasSubscription) {
binding.itrSettings.show()
} else {
binding.itrSettings.gone()
with(binding.itrSettings) {
when (viewState.itrState) {
is ItrState.Subscribed -> {
setStatus(isOn = true)
setLeadingIconResource(R.drawable.ic_identity_theft_restoration_color_24)
isClickable = true
setClickListener { viewModel.onItr() }
}
ItrState.Expired, ItrState.Activating -> {
isClickable = false
setStatus(isOn = false)
setLeadingIconResource(R.drawable.ic_identity_theft_restoration_grayscale_color_24)
}
ItrState.Hidden -> isGone = true
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,16 @@ import com.duckduckgo.anvil.annotations.ContributesViewModel
import com.duckduckgo.di.scopes.ViewScope
import com.duckduckgo.subscriptions.api.Product.ITR
import com.duckduckgo.subscriptions.api.Product.ROW_ITR
import com.duckduckgo.subscriptions.api.Subscriptions
import com.duckduckgo.subscriptions.impl.ProductSubscriptionManager
import com.duckduckgo.subscriptions.impl.ProductSubscriptionManager.ProductStatus.ACTIVE
import com.duckduckgo.subscriptions.impl.ProductSubscriptionManager.ProductStatus.EXPIRED
import com.duckduckgo.subscriptions.impl.ProductSubscriptionManager.ProductStatus.INACTIVE
import com.duckduckgo.subscriptions.impl.ProductSubscriptionManager.ProductStatus.INELIGIBLE
import com.duckduckgo.subscriptions.impl.ProductSubscriptionManager.ProductStatus.SIGNED_OUT
import com.duckduckgo.subscriptions.impl.ProductSubscriptionManager.ProductStatus.WAITING
import com.duckduckgo.subscriptions.impl.pixels.SubscriptionPixelSender
import com.duckduckgo.subscriptions.impl.settings.views.ItrSettingViewModel.Command.OpenItr
import com.duckduckgo.subscriptions.impl.settings.views.ItrSettingViewModel.ViewState.ItrState
import javax.inject.Inject
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
Expand All @@ -37,12 +44,13 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

@SuppressLint("NoLifecycleObserver") // we don't observe app lifecycle
@ContributesViewModel(ViewScope::class)
class ItrSettingViewModel @Inject constructor(
private val subscriptions: Subscriptions,
private val productSubscriptionManager: ProductSubscriptionManager,
private val pixelSender: SubscriptionPixelSender,
) : ViewModel(), DefaultLifecycleObserver {

Expand All @@ -52,7 +60,16 @@ class ItrSettingViewModel @Inject constructor(

private val command = Channel<Command>(1, BufferOverflow.DROP_OLDEST)
internal fun commands(): Flow<Command> = command.receiveAsFlow()
data class ViewState(val hasSubscription: Boolean = false)
data class ViewState(val itrState: ItrState = ItrState.Hidden) {

sealed class ItrState {

data object Hidden : ItrState()
data object Subscribed : ItrState()
data object Expired : ItrState()
data object Activating : ItrState()
}
}

private val _viewState = MutableStateFlow(ViewState())
val viewState = _viewState.asStateFlow()
Expand All @@ -64,8 +81,16 @@ class ItrSettingViewModel @Inject constructor(

override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner)
subscriptions.getEntitlementStatus().onEach {
_viewState.emit(viewState.value.copy(hasSubscription = ITR in it || ROW_ITR in it))

productSubscriptionManager.entitlementStatus(ITR, ROW_ITR).onEach { status ->
val itrState = when (status) {
ACTIVE -> ItrState.Subscribed
INACTIVE, EXPIRED -> ItrState.Expired
WAITING -> ItrState.Activating
SIGNED_OUT, INELIGIBLE -> ItrState.Hidden
}

_viewState.update { it.copy(itrState = itrState) }
}.launchIn(viewModelScope)
}

Expand Down

0 comments on commit 99d90aa

Please sign in to comment.