diff --git a/app/src/main/java/com/kickstarter/models/User.kt b/app/src/main/java/com/kickstarter/models/User.kt index 1473b9d679..d2212f0301 100644 --- a/app/src/main/java/com/kickstarter/models/User.kt +++ b/app/src/main/java/com/kickstarter/models/User.kt @@ -11,6 +11,7 @@ class User private constructor( private val artsCultureNewsletter: Boolean, private val avatar: Avatar, private val backedProjectsCount: Int, + private val backingActionCount: Int, private val createdProjectsCount: Int, private val draftProjectsCount: Int, private val erroredBackingsCount: Int, @@ -66,6 +67,7 @@ class User private constructor( fun artsCultureNewsletter() = this.artsCultureNewsletter fun avatar() = this.avatar fun backedProjectsCount() = this.backedProjectsCount + fun backingActionCount() = this.backingActionCount fun createdProjectsCount() = this.createdProjectsCount fun draftProjectsCount() = this.draftProjectsCount fun erroredBackingsCount() = this.erroredBackingsCount @@ -123,6 +125,7 @@ class User private constructor( private var artsCultureNewsletter: Boolean = false, private var avatar: Avatar = Avatar.builder().build(), private var backedProjectsCount: Int = 0, + private var backingActionCount: Int = 0, private var createdProjectsCount: Int = 0, private var draftProjectsCount: Int = 0, private var erroredBackingsCount: Int = 0, @@ -178,6 +181,7 @@ class User private constructor( fun artsCultureNewsletter(arN: Boolean?) = apply { this.artsCultureNewsletter = arN ?: false } fun avatar(avatar: Avatar?) = apply { avatar?.let { this.avatar = it } } fun backedProjectsCount(bPC: Int?) = apply { this.backedProjectsCount = bPC ?: 0 } + fun backingActionCount(bPC: Int?) = apply { this.backingActionCount = bPC ?: 0 } fun draftProjectsCount(dPC: Int?) = apply { this.draftProjectsCount = dPC ?: 0 } fun createdProjectsCount(cPC: Int?) = apply { this.createdProjectsCount = cPC ?: 0 } fun erroredBackingsCount(eBC: Int?) = apply { this.erroredBackingsCount = eBC ?: 0 } @@ -233,6 +237,7 @@ class User private constructor( artsCultureNewsletter = artsCultureNewsletter, avatar = avatar, backedProjectsCount = backedProjectsCount, + backingActionCount = backingActionCount, createdProjectsCount = createdProjectsCount, draftProjectsCount = draftProjectsCount, erroredBackingsCount = erroredBackingsCount, @@ -299,6 +304,7 @@ class User private constructor( artsCultureNewsletter = artsCultureNewsletter, avatar = avatar, backedProjectsCount = backedProjectsCount, + backingActionCount = backingActionCount, createdProjectsCount = createdProjectsCount, draftProjectsCount = draftProjectsCount, erroredBackingsCount = erroredBackingsCount, @@ -376,6 +382,7 @@ class User private constructor( alumniNewsletter() == obj.alumniNewsletter() && artsCultureNewsletter() == obj.artsCultureNewsletter() && backedProjectsCount() == obj.backedProjectsCount() && + backingActionCount() == obj.backingActionCount() && createdProjectsCount() == obj.createdProjectsCount() && draftProjectsCount() == obj.draftProjectsCount() && name() == obj.name() && diff --git a/app/src/main/java/com/kickstarter/ui/viewholders/discoverydrawer/LoggedInViewHolder.kt b/app/src/main/java/com/kickstarter/ui/viewholders/discoverydrawer/LoggedInViewHolder.kt index d977e242fb..99188b47e2 100644 --- a/app/src/main/java/com/kickstarter/ui/viewholders/discoverydrawer/LoggedInViewHolder.kt +++ b/app/src/main/java/com/kickstarter/ui/viewholders/discoverydrawer/LoggedInViewHolder.kt @@ -2,6 +2,7 @@ package com.kickstarter.ui.viewholders.discoverydrawer import android.graphics.drawable.Drawable import android.util.Pair +import android.view.View import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.ContextCompat import com.kickstarter.R @@ -67,11 +68,29 @@ class LoggedInViewHolder( } }.addToDisposable(disposables) + this.viewModel.outputs.backingActionCount() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + when { + it.isNullOrZero() -> binding.backingActionCount.visibility = View.GONE + else -> { + binding.backingActionCount.text = NumberUtils.format(it) + binding.backingActionCount.visibility = View.VISIBLE + binding.backingActionCount.setTextColor(ContextCompat.getColor(context(), R.color.kds_alert)) + } + } + }.addToDisposable(disposables) + this.viewModel.outputs.pledgedProjectsIsVisible() .observeOn(AndroidSchedulers.mainThread()) .subscribe { binding.drawerProjectAlerts.visibility = it.toVisibility() } .addToDisposable(disposables) + this.viewModel.outputs.backingsV2IsVisible() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { binding.drawerBackings.visibility = it.toVisibility() } + .addToDisposable(disposables) + this.viewModel.outputs.pledgedProjectsIndicatorIsVisible() .compose>(combineLatestPair(delegate.darkThemeEnabled())) .observeOn(AndroidSchedulers.mainThread()) @@ -92,6 +111,7 @@ class LoggedInViewHolder( binding.drawerProfile.setOnClickListener { this.delegate.loggedInViewHolderProfileClick(this, user) } binding.userContainer.setOnClickListener { this.delegate.loggedInViewHolderProfileClick(this, user) } binding.drawerProjectAlerts.setOnClickListener { this.delegate.loggedInViewHolderPledgedProjectsClick(this) } + binding.drawerBackings.setOnClickListener { this.delegate.loggedInViewHolderPledgedProjectsClick(this) } }.addToDisposable(disposables) binding.drawerActivity.setOnClickListener { this.delegate.loggedInViewHolderActivityClick(this) } diff --git a/app/src/main/java/com/kickstarter/viewmodels/LoggedInViewHolderViewModel.kt b/app/src/main/java/com/kickstarter/viewmodels/LoggedInViewHolderViewModel.kt index 15e8f7b947..9f0d6349fb 100644 --- a/app/src/main/java/com/kickstarter/viewmodels/LoggedInViewHolderViewModel.kt +++ b/app/src/main/java/com/kickstarter/viewmodels/LoggedInViewHolderViewModel.kt @@ -7,6 +7,7 @@ import com.kickstarter.libs.featureflag.FlipperFlagKey import com.kickstarter.libs.rx.transformers.Transformers import com.kickstarter.libs.utils.extensions.addToDisposable import com.kickstarter.libs.utils.extensions.intValueOrZero +import com.kickstarter.libs.utils.extensions.isFalse import com.kickstarter.libs.utils.extensions.isTrue import com.kickstarter.libs.utils.extensions.isZero import com.kickstarter.models.User @@ -29,6 +30,9 @@ interface LoggedInViewHolderViewModel { /** Emits the user's unseen activity and errored backings count. */ fun activityCount(): Observable + /** Emits the user's backing action count. */ + fun backingActionCount(): Observable + /** Emits the color resource ID of the activity count text color. */ fun activityCountTextColor(): Observable @@ -47,6 +51,7 @@ interface LoggedInViewHolderViewModel { /** Emits the user to pass to delegate. */ fun user(): Observable fun pledgedProjectsIsVisible(): Observable + fun backingsV2IsVisible(): Observable fun pledgedProjectsIndicatorIsVisible(): Observable } @@ -64,7 +69,9 @@ interface LoggedInViewHolderViewModel { private val unreadMessagesCount = BehaviorSubject.create() private val userOutput = BehaviorSubject.create() private val pledgedProjectsIsVisible = BehaviorSubject.create() + private val backingsV2IsVisible = BehaviorSubject.create() private val pledgedProjectsIndicatorIsVisible = BehaviorSubject.create() + private val backingActionCount = BehaviorSubject.create() private val disposables = CompositeDisposable() val inputs: Inputs = this @@ -115,6 +122,11 @@ interface LoggedInViewHolderViewModel { .subscribe { this.pledgedProjectsIndicatorIsVisible.onNext(it) } .addToDisposable(disposables) + this.user + .map { it.backingActionCount() } + .subscribe { this.backingActionCount.onNext(it) } + .addToDisposable(disposables) + featureFlagClient.isBackendEnabledFlag(this.apolloClient.userPrivacy(), FlipperFlagKey.FLIPPER_PLEDGED_PROJECTS_OVERVIEW) .compose(Transformers.neverErrorV2()) .map { ffEnabledBackend -> @@ -122,8 +134,13 @@ interface LoggedInViewHolderViewModel { return@map ffEnabledMobile && ffEnabledBackend } + .filter { featureFlagClient.getBoolean(FlagKey.ANDROID_PLEDGED_PROJECTS_OVERVIEW_V2).isFalse() } .subscribe { this.pledgedProjectsIsVisible.onNext(it) } .addToDisposable(disposables) + + Observable.just(featureFlagClient.getBoolean(FlagKey.ANDROID_PLEDGED_PROJECTS_OVERVIEW_V2).isTrue()) + .subscribe { this.backingsV2IsVisible.onNext(it) } + .addToDisposable(disposables) } override fun configureWith(user: User) { @@ -137,6 +154,8 @@ interface LoggedInViewHolderViewModel { override fun activityCount(): Observable = this.activityCount + override fun backingActionCount(): Observable = this.backingActionCount + override fun activityCountTextColor(): Observable = this.activityCountTextColor override fun avatarUrl(): Observable = this.avatarUrl @@ -150,6 +169,9 @@ interface LoggedInViewHolderViewModel { override fun user(): Observable = this.userOutput override fun pledgedProjectsIsVisible(): Observable = this.pledgedProjectsIsVisible + + override fun backingsV2IsVisible(): Observable = this.backingsV2IsVisible + override fun pledgedProjectsIndicatorIsVisible(): Observable = this.pledgedProjectsIndicatorIsVisible } } diff --git a/app/src/main/res/drawable/ic_rectangle_stack.xml b/app/src/main/res/drawable/ic_rectangle_stack.xml new file mode 100644 index 0000000000..2b25678b59 --- /dev/null +++ b/app/src/main/res/drawable/ic_rectangle_stack.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/app/src/main/res/layout/discovery_drawer_logged_in_view.xml b/app/src/main/res/layout/discovery_drawer_logged_in_view.xml index ed03aef70b..fd88a774f4 100644 --- a/app/src/main/res/layout/discovery_drawer_logged_in_view.xml +++ b/app/src/main/res/layout/discovery_drawer_logged_in_view.xml @@ -126,6 +126,49 @@ android:visibility="gone"/> + + + + + + + + + + + + + () private val activityCountTextColor = TestSubscriber() + private val backingActionCount = TestSubscriber() private val avatarUrl = TestSubscriber() private val dashboardRowIsGone = TestSubscriber() private val name = TestSubscriber() private val unreadMessagesCount = TestSubscriber() private val user = TestSubscriber() private val pledgedProjectsIsVisible = TestSubscriber() + private val backingsV2IsVisible = TestSubscriber() private val pledgedProjectsIndicatorIsVisible = TestSubscriber() private val disposables = CompositeDisposable() @@ -34,12 +36,14 @@ class LoggedInViewHolderViewModelTest : KSRobolectricTestCase() { this.vm = LoggedInViewHolderViewModel.ViewModel(environment) this.vm.outputs.activityCount().subscribe { this.activityCount.onNext(it) }.addToDisposable(disposables) this.vm.outputs.activityCountTextColor().subscribe { this.activityCountTextColor.onNext(it) }.addToDisposable(disposables) + this.vm.outputs.backingActionCount().subscribe { this.backingActionCount.onNext(it) }.addToDisposable(disposables) this.vm.outputs.avatarUrl().subscribe { this.avatarUrl.onNext(it) }.addToDisposable(disposables) this.vm.outputs.dashboardRowIsGone().subscribe { this.dashboardRowIsGone.onNext(it) }.addToDisposable(disposables) this.vm.outputs.name().subscribe { this.name.onNext(it) }.addToDisposable(disposables) this.vm.outputs.unreadMessagesCount().subscribe { this.unreadMessagesCount.onNext(it) }.addToDisposable(disposables) this.vm.outputs.user().subscribe { this.user.onNext(it) }.addToDisposable(disposables) this.vm.outputs.pledgedProjectsIsVisible().subscribe { this.pledgedProjectsIsVisible.onNext(it) }.addToDisposable(disposables) + this.vm.outputs.backingsV2IsVisible().subscribe { this.backingsV2IsVisible.onNext(it) }.addToDisposable(disposables) this.vm.outputs.pledgedProjectsIndicatorIsVisible().subscribe { this.pledgedProjectsIndicatorIsVisible.onNext(it) }.addToDisposable(disposables) } @@ -204,7 +208,10 @@ class LoggedInViewHolderViewModelTest : KSRobolectricTestCase() { } val ffClient = object : MockFeatureFlagClient() { override fun getBoolean(FlagKey: FlagKey): Boolean { - return true + return when (FlagKey) { + com.kickstarter.libs.featureflag.FlagKey.ANDROID_PLEDGED_PROJECTS_OVERVIEW_V2 -> false + else -> true + } } } @@ -216,6 +223,7 @@ class LoggedInViewHolderViewModelTest : KSRobolectricTestCase() { setUpEnvironment(environment) this.pledgedProjectsIsVisible.assertValue(true) + this.backingsV2IsVisible.assertValue(false) } @Test @@ -240,7 +248,10 @@ class LoggedInViewHolderViewModelTest : KSRobolectricTestCase() { } val ffClient = object : MockFeatureFlagClient() { override fun getBoolean(FlagKey: FlagKey): Boolean { - return true + return when (FlagKey) { + com.kickstarter.libs.featureflag.FlagKey.ANDROID_PLEDGED_PROJECTS_OVERVIEW_V2 -> false + else -> true + } } } @@ -252,6 +263,7 @@ class LoggedInViewHolderViewModelTest : KSRobolectricTestCase() { setUpEnvironment(environment) this.pledgedProjectsIsVisible.assertValue(false) + this.backingsV2IsVisible.assertValue(false) } @Test @@ -291,6 +303,44 @@ class LoggedInViewHolderViewModelTest : KSRobolectricTestCase() { this.pledgedProjectsIsVisible.assertValue(false) } + @Test + fun `test v2 feature flag enabled, show v2 and not v1`() { + val privacy = UserPrivacy( + "Some Name", + "some@email.com", + true, + true, + true, + true, + "USD", + enabledFeatures = listOf("some_key_here") + ) + + val apolloClient = object : MockApolloClientV2() { + override fun userPrivacy(): Observable { + return Observable.just( + privacy + ) + } + } + + val ffClient = object : MockFeatureFlagClient() { + override fun getBoolean(FlagKey: FlagKey): Boolean { + return true + } + } + + val environment = environment().toBuilder() + .apolloClientV2(apolloClient) + .featureFlagClient(ffClient) + .build() + + setUpEnvironment(environment) + + this.pledgedProjectsIsVisible.assertNoValues() + this.backingsV2IsVisible.assertValue(true) + } + @After fun clear() { disposables.clear()