feat(dashboard): collapsible Pezkuwi card, minimal by default

Card now opens in a slim single-line pill showing only Trust Score.
Tapping it expands to the full card (citizen status, world Kurdish
count, referral actions); a chevron in the expanded header collapses
it back. Expand state persists across scroll/recycling within the
session but resets to collapsed on a fresh app launch.
This commit is contained in:
2026-07-02 04:15:45 -07:00
parent be887e5f50
commit 74728a8477
3 changed files with 213 additions and 114 deletions
@@ -2,6 +2,8 @@ package io.novafoundation.nova.feature_assets.presentation.balance.list.view
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
import android.transition.AutoTransition
import android.transition.TransitionManager
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@@ -29,6 +31,10 @@ class PezkuwiDashboardAdapter(
private var model: PezkuwiDashboardModel? = null private var model: PezkuwiDashboardModel? = null
private var trackingLoading: Boolean = false private var trackingLoading: Boolean = false
// Survives ViewHolder recycling (scroll) within the process, but not process restart —
// resets to collapsed (false) whenever the app is freshly opened, by design.
private var isExpanded: Boolean = false
fun setModel(model: PezkuwiDashboardModel) { fun setModel(model: PezkuwiDashboardModel) {
this.model = model this.model = model
notifyChangedIfShown() notifyChangedIfShown()
@@ -41,11 +47,11 @@ class PezkuwiDashboardAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PezkuwiDashboardHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PezkuwiDashboardHolder {
val binding = ItemPezkuwiDashboardBinding.inflate(parent.inflater(), parent, false) val binding = ItemPezkuwiDashboardBinding.inflate(parent.inflater(), parent, false)
return PezkuwiDashboardHolder(binding, handler) return PezkuwiDashboardHolder(binding, handler) { expanded -> isExpanded = expanded }
} }
override fun onBindViewHolder(holder: PezkuwiDashboardHolder, position: Int) { override fun onBindViewHolder(holder: PezkuwiDashboardHolder, position: Int) {
model?.let { holder.bind(it, trackingLoading) } model?.let { holder.bind(it, trackingLoading, isExpanded) }
} }
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
@@ -55,7 +61,8 @@ class PezkuwiDashboardAdapter(
class PezkuwiDashboardHolder( class PezkuwiDashboardHolder(
private val binder: ItemPezkuwiDashboardBinding, private val binder: ItemPezkuwiDashboardBinding,
handler: PezkuwiDashboardAdapter.Handler handler: PezkuwiDashboardAdapter.Handler,
private val onExpandedChanged: (Boolean) -> Unit
) : RecyclerView.ViewHolder(binder.root) { ) : RecyclerView.ViewHolder(binder.root) {
companion object : WithViewType { companion object : WithViewType {
@@ -67,14 +74,30 @@ class PezkuwiDashboardHolder(
binder.pezkuwiDashboardSignButton.setOnClickListener { handler.onSignClicked() } binder.pezkuwiDashboardSignButton.setOnClickListener { handler.onSignClicked() }
binder.pezkuwiDashboardShareButton.setOnClickListener { handler.onShareReferralClicked() } binder.pezkuwiDashboardShareButton.setOnClickListener { handler.onShareReferralClicked() }
binder.pezkuwiDashboardStartTrackingButton.setOnClickListener { handler.onStartTrackingClicked() } binder.pezkuwiDashboardStartTrackingButton.setOnClickListener { handler.onStartTrackingClicked() }
binder.pezkuwiDashboardCollapsedBar.setOnClickListener { setExpanded(true) }
binder.pezkuwiDashboardCollapseButton.setOnClickListener { setExpanded(false) }
} }
fun bind(model: PezkuwiDashboardModel, trackingLoading: Boolean = false) { private fun setExpanded(expanded: Boolean) {
TransitionManager.beginDelayedTransition(binder.pezkuwiDashboardRoot, AutoTransition().apply { duration = 200 })
binder.pezkuwiDashboardCollapsedBar.visibility = if (expanded) View.GONE else View.VISIBLE
binder.pezkuwiDashboardExpandedContent.visibility = if (expanded) View.VISIBLE else View.GONE
onExpandedChanged(expanded)
}
fun bind(model: PezkuwiDashboardModel, trackingLoading: Boolean = false, isExpanded: Boolean = false) {
bindRoles(model.roles) bindRoles(model.roles)
binder.pezkuwiDashboardTrustValue.text = model.trustScore binder.pezkuwiDashboardTrustValue.text = model.trustScore
binder.pezkuwiDashboardTrustValueCollapsed.text = model.trustScore
binder.pezkuwiDashboardWelatiCount.text = model.welatiCount binder.pezkuwiDashboardWelatiCount.text = model.welatiCount
bindButtons(model.citizenshipStatus) bindButtons(model.citizenshipStatus)
// Reflect current expand state without animating (this runs on every bind/rebind,
// e.g. after RecyclerView recycling — animation is only for user-initiated toggles).
binder.pezkuwiDashboardCollapsedBar.visibility = if (isExpanded) View.GONE else View.VISIBLE
binder.pezkuwiDashboardExpandedContent.visibility = if (isExpanded) View.VISIBLE else View.GONE
val showTracking = !model.isTrackingScore && model.citizenshipStatus == CitizenshipStatus.APPROVED val showTracking = !model.isTrackingScore && model.citizenshipStatus == CitizenshipStatus.APPROVED
binder.pezkuwiDashboardStartTrackingButton.visibility = if (showTracking) View.VISIBLE else View.GONE binder.pezkuwiDashboardStartTrackingButton.visibility = if (showTracking) View.VISIBLE else View.GONE
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Minimal collapsed-state pill: solid brand green, fully rounded -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#009639" />
<corners android:radius="22dp" />
</shape>
@@ -10,151 +10,218 @@
app:strokeWidth="0dp"> app:strokeWidth="0dp">
<LinearLayout <LinearLayout
android:id="@+id/pezkuwiDashboardRoot"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/bg_pezkuwi_dashboard" android:orientation="vertical">
android:orientation="vertical"
android:padding="18dp">
<!-- Header: flame badge + title/roles (left), citizen count (right) --> <!-- Collapsed state: minimal single-line pill showing only Trust Score.
Default/visible on first bind each app session. -->
<LinearLayout <LinearLayout
android:id="@+id/pezkuwiDashboardCollapsedBar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="44dp"
android:background="@drawable/bg_pezkuwi_dashboard_collapsed"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal"
android:paddingHorizontal="16dp">
<ImageView <ImageView
android:layout_width="40dp" android:layout_width="18dp"
android:layout_height="40dp" android:layout_height="18dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="8dp"
android:src="@drawable/ic_nevroz_flame" /> android:src="@drawable/ic_nevroz_flame" />
<LinearLayout <TextView
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:orientation="vertical"> android:text="@string/pezkuwi_dashboard_trust_score"
android:textColor="@android:color/white"
android:textSize="13sp"
android:textStyle="bold" />
<TextView <TextView
android:id="@+id/pezkuwiDashboardTitle" android:id="@+id/pezkuwiDashboardTrustValueCollapsed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:textColor="@android:color/white"
android:textSize="15sp"
android:textStyle="bold" />
<ImageView
android:layout_width="18dp"
android:layout_height="18dp"
android:src="@drawable/ic_chevron_down"
app:tint="@android:color/white" />
</LinearLayout>
<!-- Expanded state: full card, hidden by default. -->
<LinearLayout
android:id="@+id/pezkuwiDashboardExpandedContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_pezkuwi_dashboard"
android:orientation="vertical"
android:padding="18dp"
android:visibility="gone">
<!-- Header: flame badge + title/roles (left), citizen count + collapse chevron (right) -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:orientation="horizontal">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginEnd="12dp"
android:src="@drawable/ic_nevroz_flame" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/pezkuwiDashboardTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pezkuwi_dashboard_title"
android:textColor="#E0FFFFFF"
android:textSize="18sp"
android:textStyle="bold" />
<com.google.android.flexbox.FlexboxLayout
android:id="@+id/pezkuwiDashboardRoles"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
app:alignItems="center"
app:flexWrap="wrap"
app:justifyContent="flex_start" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/pezkuwi_dashboard_title" android:gravity="end"
android:textColor="#E0FFFFFF" android:orientation="vertical">
android:textSize="18sp"
android:textStyle="bold" />
<com.google.android.flexbox.FlexboxLayout <TextView
android:id="@+id/pezkuwiDashboardRoles" android:id="@+id/pezkuwiDashboardWelatiCount"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="3dp" android:layout_gravity="end"
app:alignItems="center" android:textColor="#2FC864"
app:flexWrap="wrap" android:textSize="24sp"
app:justifyContent="flex_start" /> android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:text="@string/pezkuwi_dashboard_kurds_title"
android:textColor="#7AFFFFFF"
android:textSize="10sp" />
</LinearLayout>
<ImageView
android:id="@+id/pezkuwiDashboardCollapseButton"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginStart="4dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:focusable="true"
android:padding="6dp"
android:src="@drawable/ic_chevron_up"
app:tint="#7AFFFFFF" />
</LinearLayout> </LinearLayout>
<!-- Trust score -->
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="end" android:layout_marginTop="14dp"
android:orientation="vertical"> android:gravity="center_vertical"
android:orientation="horizontal">
<TextView <TextView
android:id="@+id/pezkuwiDashboardWelatiCount"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:text="@string/pezkuwi_dashboard_trust_score"
android:textColor="#2FC864" android:textColor="#7AFFFFFF"
android:textSize="24sp" android:textSize="12sp" />
<TextView
android:id="@+id/pezkuwiDashboardTrustValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textColor="#FDB813"
android:textSize="16sp"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView <com.google.android.material.button.MaterialButton
android:id="@+id/pezkuwiDashboardStartTrackingButton"
style="@style/Widget.MaterialComponents.Button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="32dp"
android:layout_gravity="end" android:layout_marginStart="8dp"
android:text="@string/pezkuwi_dashboard_kurds_title" android:minWidth="0dp"
android:textColor="#7AFFFFFF" android:paddingHorizontal="10dp"
android:textSize="10sp" /> android:text="@string/pezkuwi_dashboard_start_tracking"
android:textAllCaps="false"
android:textColor="@android:color/white"
android:textSize="11sp"
android:visibility="gone"
app:backgroundTint="#009639"
app:cornerRadius="8dp" />
</LinearLayout> </LinearLayout>
</LinearLayout>
<!-- Trust score -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="14dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pezkuwi_dashboard_trust_score"
android:textColor="#7AFFFFFF"
android:textSize="12sp" />
<TextView
android:id="@+id/pezkuwiDashboardTrustValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textColor="#FDB813"
android:textSize="16sp"
android:textStyle="bold" />
<!-- Action buttons -->
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/pezkuwiDashboardStartTrackingButton" android:id="@+id/pezkuwiDashboardBasvuruButton"
style="@style/Widget.MaterialComponents.Button" android:layout_width="match_parent"
android:layout_width="wrap_content" android:layout_height="48dp"
android:layout_height="32dp" android:layout_marginTop="16dp"
android:layout_marginStart="8dp" android:text="@string/pezkuwi_dashboard_basvuru"
android:minWidth="0dp"
android:paddingHorizontal="10dp"
android:text="@string/pezkuwi_dashboard_start_tracking"
android:textAllCaps="false" android:textAllCaps="false"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="11sp" android:textStyle="bold"
android:visibility="gone"
app:backgroundTint="#009639" app:backgroundTint="#009639"
app:cornerRadius="8dp" /> app:cornerRadius="14dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/pezkuwiDashboardSignButton"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="8dp"
android:text="@string/citizenship_sign"
android:textAllCaps="false"
android:textColor="@android:color/white"
app:backgroundTint="#E2231A"
app:cornerRadius="14dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/pezkuwiDashboardShareButton"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="8dp"
android:text="@string/citizenship_share_button"
android:textAllCaps="false"
android:textColor="#E0FFFFFF"
app:backgroundTint="#2A2F45"
app:cornerRadius="14dp"
app:strokeColor="#33999EC7"
app:strokeWidth="1dp" />
</LinearLayout> </LinearLayout>
<!-- Action buttons -->
<com.google.android.material.button.MaterialButton
android:id="@+id/pezkuwiDashboardBasvuruButton"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="16dp"
android:text="@string/pezkuwi_dashboard_basvuru"
android:textAllCaps="false"
android:textColor="@android:color/white"
android:textStyle="bold"
app:backgroundTint="#009639"
app:cornerRadius="14dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/pezkuwiDashboardSignButton"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="8dp"
android:text="@string/citizenship_sign"
android:textAllCaps="false"
android:textColor="@android:color/white"
app:backgroundTint="#E2231A"
app:cornerRadius="14dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/pezkuwiDashboardShareButton"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="8dp"
android:text="@string/citizenship_share_button"
android:textAllCaps="false"
android:textColor="#E0FFFFFF"
app:backgroundTint="#2A2F45"
app:cornerRadius="14dp"
app:strokeColor="#33999EC7"
app:strokeWidth="1dp" />
</LinearLayout> </LinearLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>