Skip to content

Commit

Permalink
MBL-2029: [Phase 1] UI Updates: New copy, icon, and alert count for p…
Browse files Browse the repository at this point in the history
…po menu item in discovery overflow menu (#2234)

* update overflow menu item

* linter

---------

Co-authored-by: Leigh Douglas <[email protected]>
  • Loading branch information
leighdouglas and Leigh Douglas authored Feb 20, 2025
1 parent 02fe7a8 commit 2dd3f3b
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 2 deletions.
7 changes: 7 additions & 0 deletions app/src/main/java/com/kickstarter/models/User.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 }
Expand Down Expand Up @@ -233,6 +237,7 @@ class User private constructor(
artsCultureNewsletter = artsCultureNewsletter,
avatar = avatar,
backedProjectsCount = backedProjectsCount,
backingActionCount = backingActionCount,
createdProjectsCount = createdProjectsCount,
draftProjectsCount = draftProjectsCount,
erroredBackingsCount = erroredBackingsCount,
Expand Down Expand Up @@ -299,6 +304,7 @@ class User private constructor(
artsCultureNewsletter = artsCultureNewsletter,
avatar = avatar,
backedProjectsCount = backedProjectsCount,
backingActionCount = backingActionCount,
createdProjectsCount = createdProjectsCount,
draftProjectsCount = draftProjectsCount,
erroredBackingsCount = erroredBackingsCount,
Expand Down Expand Up @@ -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() &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<Pair<Boolean, Boolean>>(combineLatestPair(delegate.darkThemeEnabled()))
.observeOn(AndroidSchedulers.mainThread())
Expand All @@ -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) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -29,6 +30,9 @@ interface LoggedInViewHolderViewModel {
/** Emits the user's unseen activity and errored backings count. */
fun activityCount(): Observable<Int>

/** Emits the user's backing action count. */
fun backingActionCount(): Observable<Int>

/** Emits the color resource ID of the activity count text color. */
fun activityCountTextColor(): Observable<Int>

Expand All @@ -47,6 +51,7 @@ interface LoggedInViewHolderViewModel {
/** Emits the user to pass to delegate. */
fun user(): Observable<User>
fun pledgedProjectsIsVisible(): Observable<Boolean>
fun backingsV2IsVisible(): Observable<Boolean>
fun pledgedProjectsIndicatorIsVisible(): Observable<Boolean>
}

Expand All @@ -64,7 +69,9 @@ interface LoggedInViewHolderViewModel {
private val unreadMessagesCount = BehaviorSubject.create<Int>()
private val userOutput = BehaviorSubject.create<User>()
private val pledgedProjectsIsVisible = BehaviorSubject.create<Boolean>()
private val backingsV2IsVisible = BehaviorSubject.create<Boolean>()
private val pledgedProjectsIndicatorIsVisible = BehaviorSubject.create<Boolean>()
private val backingActionCount = BehaviorSubject.create<Int>()

private val disposables = CompositeDisposable()
val inputs: Inputs = this
Expand Down Expand Up @@ -115,15 +122,25 @@ 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 ->
val ffEnabledMobile = featureFlagClient.getBoolean(FlagKey.ANDROID_PLEDGED_PROJECTS_OVERVIEW)

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) {
Expand All @@ -137,6 +154,8 @@ interface LoggedInViewHolderViewModel {

override fun activityCount(): Observable<Int> = this.activityCount

override fun backingActionCount(): Observable<Int> = this.backingActionCount

override fun activityCountTextColor(): Observable<Int> = this.activityCountTextColor

override fun avatarUrl(): Observable<String> = this.avatarUrl
Expand All @@ -150,6 +169,9 @@ interface LoggedInViewHolderViewModel {
override fun user(): Observable<User> = this.userOutput

override fun pledgedProjectsIsVisible(): Observable<Boolean> = this.pledgedProjectsIsVisible

override fun backingsV2IsVisible(): Observable<Boolean> = this.backingsV2IsVisible

override fun pledgedProjectsIndicatorIsVisible(): Observable<Boolean> = this.pledgedProjectsIndicatorIsVisible
}
}
19 changes: 19 additions & 0 deletions app/src/main/res/drawable/ic_rectangle_stack.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="22dp"
android:height="22dp"
android:viewportWidth="22"
android:viewportHeight="22">
<group>
<clip-path
android:pathData="M0,0h21.314v22h-21.314z"/>
<path
android:pathData="M15.977,2.484H5.015C5.051,1.778 5.496,1.372 6.236,1.372H14.757C15.496,1.372 15.942,1.778 15.977,2.484Z"
android:fillColor="#5F6368"/>
<path
android:pathData="M17.754,4.965C17.475,4.921 17.178,4.899 16.867,4.899H4.126C3.814,4.899 3.518,4.921 3.239,4.965C3.333,4.131 3.898,3.665 4.82,3.665H16.181C17.103,3.665 17.661,4.131 17.754,4.965Z"
android:fillColor="#5F6368"/>
<path
android:pathData="M4.126,20.602H16.867C18.682,20.602 19.594,19.698 19.594,17.909V8.989C19.594,7.2 18.682,6.297 16.867,6.297H4.126C2.31,6.297 1.398,7.2 1.398,8.989V17.909C1.398,19.698 2.31,20.602 4.126,20.602Z"
android:fillColor="#5F6368"/>
</group>
</vector>
43 changes: 43 additions & 0 deletions app/src/main/res/layout/discovery_drawer_logged_in_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,49 @@
android:visibility="gone"/>

</LinearLayout>

<LinearLayout
android:id="@+id/drawer_backings"
style="@style/DrawerCountContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/tabbar_activity"
android:orientation="horizontal"
android:visibility="gone">

<TextView
android:id="@+id/backings"
style="@style/DrawerTextViewWithCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/fpo_backings"
app:drawableStartCompat="@drawable/ic_rectangle_stack" />

<TextView
android:id="@+id/backings_beta_tag"
style="@style/RewardPill"
android:padding="@dimen/grid_1"
android:layout_gravity="center"
android:textSize="@dimen/badge_superbacker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="true"
android:text="@string/Beta" />

<View
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="0dp" />

<TextView
android:id="@+id/backing_action_count"
style="@style/DrawerCountTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="2000" />

</LinearLayout>

<LinearLayout
android:id="@+id/drawer_activity"
style="@style/DrawerCountContainer"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,29 @@ class LoggedInViewHolderViewModelTest : KSRobolectricTestCase() {
private lateinit var vm: LoggedInViewHolderViewModel.ViewModel
private val activityCount = TestSubscriber<Int>()
private val activityCountTextColor = TestSubscriber<Int>()
private val backingActionCount = TestSubscriber<Int>()
private val avatarUrl = TestSubscriber<String>()
private val dashboardRowIsGone = TestSubscriber<Boolean>()
private val name = TestSubscriber<String>()
private val unreadMessagesCount = TestSubscriber<Int>()
private val user = TestSubscriber<User>()
private val pledgedProjectsIsVisible = TestSubscriber<Boolean>()
private val backingsV2IsVisible = TestSubscriber<Boolean>()
private val pledgedProjectsIndicatorIsVisible = TestSubscriber<Boolean>()
private val disposables = CompositeDisposable()

fun setUpEnvironment(environment: Environment) {
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)
}

Expand Down Expand Up @@ -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
}
}
}

Expand All @@ -216,6 +223,7 @@ class LoggedInViewHolderViewModelTest : KSRobolectricTestCase() {
setUpEnvironment(environment)

this.pledgedProjectsIsVisible.assertValue(true)
this.backingsV2IsVisible.assertValue(false)
}

@Test
Expand All @@ -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
}
}
}

Expand All @@ -252,6 +263,7 @@ class LoggedInViewHolderViewModelTest : KSRobolectricTestCase() {
setUpEnvironment(environment)

this.pledgedProjectsIsVisible.assertValue(false)
this.backingsV2IsVisible.assertValue(false)
}

@Test
Expand Down Expand Up @@ -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",
"[email protected]",
true,
true,
true,
true,
"USD",
enabledFeatures = listOf("some_key_here")
)

val apolloClient = object : MockApolloClientV2() {
override fun userPrivacy(): Observable<UserPrivacy> {
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()
Expand Down

0 comments on commit 2dd3f3b

Please sign in to comment.