NTP-24432 | MM screens header animation (#14478)

This commit is contained in:
nikhil kumar
2025-01-10 15:22:41 +05:30
committed by GitHub
parent d3d99af606
commit a01d8e7c74
8 changed files with 425 additions and 179 deletions

View File

@@ -25,7 +25,6 @@ import com.navi.moneymanager.common.utils.Constants.DEBIT
import com.navi.moneymanager.common.utils.Constants.SELF_TRANSFER
import com.navi.moneymanager.common.utils.Constants.UNCATEGORIZED
import com.navi.moneymanager.postonboard.categorydetails.model.CategoryTotalSpendSectionCategoryDataData
import com.navi.moneymanager.postonboard.categorydetails.model.CategoryTotalSpendSectionData
import com.navi.moneymanager.postonboard.spendanalysis.model.TotalSpendSectionData
import com.navi.naviwidgets.utils.NaviWidgetIconUtils.DOWN_ARROW_BLACK_16
import com.navi.naviwidgets.utils.NaviWidgetIconUtils.NEW_INFO_ICON
@@ -67,10 +66,10 @@ constructor(@ApplicationContext private val context: Context) {
availableBanks: List<AccountOverview>,
selectedBankReferenceIds: Set<String>?,
categoryData: CategoryItemData?,
): CategoryTotalSpendSectionData {
): TotalSpendSectionData {
val spendTransactions = getSpendTransactions(currentMonthTransactions)
val sumOfTransactions = spendTransactions.sumOf { it.txnAmount.orZero() }
return CategoryTotalSpendSectionData(
return TotalSpendSectionData(
iconUrl =
IllustrationSource.Remote(
url = categoryData?.categoryIcon ?: TOTAL_SPEND_RUPEE_SYMBOL,

View File

@@ -8,6 +8,7 @@
package com.navi.moneymanager.common.ui.composable
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -35,6 +36,7 @@ fun MMTopBar(
backgroundColor: Color = MMColor.white,
roundedRipplePaddingValues: PaddingValues = PaddingValues(horizontal = 8.dp, vertical = 4.dp),
title: String,
labelForTitle: String? = null,
titleColor: Color = MMColor.textPrimary,
titleMaxLines: Int = 1,
navigationIcon: Int = R.drawable.ic_arrow_left_black_v2,
@@ -45,16 +47,28 @@ fun MMTopBar(
) {
CenterAlignedTopAppBar(
title = {
MMText(
text = title,
modifier = Modifier.padding(horizontal = 40.dp),
color = titleColor,
fontSize = 14.sp,
fontWeight = FontWeightEnum.NAVI_BODY_REGULAR,
textAlign = TextAlign.Center,
overflow = TextOverflow.Ellipsis,
maxLines = titleMaxLines,
)
Row(modifier = Modifier.padding(horizontal = 16.dp)) {
labelForTitle?.let { label ->
MMText(
text = "$label",
color = titleColor,
fontSize = 14.sp,
fontWeight = FontWeightEnum.NAVI_BODY_DEMI_BOLD,
textAlign = TextAlign.Center,
overflow = TextOverflow.Ellipsis,
maxLines = titleMaxLines,
)
}
MMText(
text = title,
color = titleColor,
fontSize = 14.sp,
fontWeight = FontWeightEnum.NAVI_BODY_REGULAR,
textAlign = TextAlign.Center,
overflow = TextOverflow.Ellipsis,
maxLines = titleMaxLines,
)
}
},
navigationIcon = {
IconButton(onClick = { onNavigationIconClick.invoke() }) {

View File

@@ -0,0 +1,135 @@
/*
*
* * Copyright © 2025 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.moneymanager.common.ui.composable.sectionHeaders
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.navi.common.utils.onClickWithDebounce
import com.navi.design.font.FontWeightEnum
import com.navi.moneymanager.common.illustration.model.IllustrationType
import com.navi.moneymanager.common.illustration.ui.Illustration
import com.navi.moneymanager.common.ui.composable.base.MMText
import com.navi.moneymanager.common.ui.theme.color.MMColor
import com.navi.moneymanager.common.utils.verticalShadow
import com.navi.moneymanager.postonboard.spendanalysis.model.TotalSpendSectionData
@Composable
fun TopSectionForMonthAndBankSelection(
totalSpendSection: TotalSpendSectionData?,
onMonthChangeClick: (String) -> Unit,
onBankSelectionRequest: (Set<String>) -> Unit,
) {
Column(modifier = Modifier.verticalShadow(showShadow = true, elevation = 3f)) {
HorizontalDivider(
modifier = Modifier.fillMaxWidth().height(1.dp),
color = MMColor.borderColor,
)
totalSpendSection?.let { totalSpendSection ->
TopScrolledSectionUI(totalSpendSection, onMonthChangeClick, onBankSelectionRequest)
}
}
}
@Composable
private fun TopScrolledSectionUI(
data: TotalSpendSectionData,
onMonthChangeClick: (String) -> Unit,
onBankSelectionRequest: (Set<String>) -> Unit,
) {
Row(modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max)) {
Row(
modifier =
Modifier.onClickWithDebounce { onMonthChangeClick(data.selectedMonth) }
.weight(1f)
.wrapContentHeight()
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) {
MMText(
text = data.selectedMonth.uppercase(),
color = MMColor.textPrimary,
fontSize = 12.sp,
fontWeight = FontWeightEnum.NAVI_BODY_DEMI_BOLD,
lineHeight = 18.sp,
)
Spacer(modifier = Modifier.width(6.dp))
Box(Modifier.padding(bottom = 2.dp)) {
Illustration(
illustrationType = IllustrationType.Image(data.actionIcon),
modifier = Modifier.size(16.dp),
)
}
}
VerticalDivider(
modifier = Modifier.padding(vertical = 4.dp).fillMaxHeight().width(1.dp),
color = MMColor.borderColor,
)
Column(modifier = Modifier.weight(1f).wrapContentHeight()) {
data.spendingTrendSectionData?.let { headerModel ->
if (headerModel.actionText != null) {
Row(
modifier =
Modifier.onClickWithDebounce {
onBankSelectionRequest(data.selectedBankReferenceIds)
}
.fillMaxWidth()
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) {
headerModel.prefixIcon?.let { prefixIcon ->
Illustration(
illustrationType = IllustrationType.Image(prefixIcon),
modifier = Modifier.size(16.dp),
)
}
Spacer(modifier = Modifier.width(6.dp))
MMText(
text = headerModel.actionText,
color = MMColor.textPrimary,
fontSize = 12.sp,
fontWeight = FontWeightEnum.NAVI_BODY_DEMI_BOLD,
lineHeight = 18.sp,
)
Spacer(modifier = Modifier.width(6.dp))
headerModel.suffixIcon?.let { suffixIcon ->
Box(Modifier.padding(bottom = 2.dp)) {
Illustration(
illustrationType = IllustrationType.Image(suffixIcon),
modifier = Modifier.size(16.dp),
)
}
}
}
}
}
}
}
}

View File

@@ -53,6 +53,7 @@ object Constants {
const val REVOKE_CONSENT_FINARKEIN_URL = "https://revokeconsent.finvu.in/"
const val CPS = "CPS"
const val ALL_BANKS = "All banks"
const val HEADER_SCROLL_TWEEN = 20
// datastore constants
const val IS_FIRST_MONTH_SYNC_COMPLETED = "IS_FIRST_MONTH_SYNC_COMPLETED"

View File

@@ -11,27 +11,16 @@ import com.navi.moneymanager.common.illustration.model.IllustrationSource
import com.navi.moneymanager.common.model.BarGraphData
import com.navi.moneymanager.common.model.Transaction
import com.navi.moneymanager.common.model.ZeroTransactionData
import com.navi.moneymanager.common.model.sectionHeader.SectionHeaderData
import com.navi.moneymanager.postonboard.spendanalysis.model.TotalSpendSectionData
data class CategoryDetailsScreenData(
val totalSpendSection: CategoryTotalSpendSectionData? = null,
val totalSpendSection: TotalSpendSectionData? = null,
val barGraphData: BarGraphData? = null,
val sortOption: SortOption = SortOption.RECENT_FIRST,
val transactions: List<Transaction>? = null,
val zeroTransactionData: ZeroTransactionData? = null,
)
data class CategoryTotalSpendSectionData(
val iconUrl: IllustrationSource,
val title: String,
val selectedMonth: String,
val actionIcon: IllustrationSource,
val amount: String,
val selectedBankReferenceIds: Set<String>,
val spendingTrendSectionData: SectionHeaderData? = null,
val category: CategoryTotalSpendSectionCategoryDataData,
)
data class CategoryTotalSpendSectionCategoryDataData(
val categoryId: String,
val categoryName: String,

View File

@@ -7,6 +7,11 @@
package com.navi.moneymanager.postonboard.categorydetails.ui
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.EaseInOutQuart
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
@@ -29,6 +34,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Scaffold
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -38,7 +45,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.navi.base.utils.EMPTY
import com.navi.common.R as CommonR
import com.navi.common.constants.HELP_CTA_TEXT
import com.navi.common.utils.onClickWithDebounce
import com.navi.design.font.FontWeightEnum
@@ -57,15 +63,17 @@ import com.navi.moneymanager.common.ui.composable.MMTopBar
import com.navi.moneymanager.common.ui.composable.barGraph.MMBarGraph
import com.navi.moneymanager.common.ui.composable.base.MMText
import com.navi.moneymanager.common.ui.composable.sectionHeaders.SectionHeader
import com.navi.moneymanager.common.ui.composable.sectionHeaders.TopSectionForMonthAndBankSelection
import com.navi.moneymanager.common.ui.composable.transaction.Transaction
import com.navi.moneymanager.common.ui.composable.transaction.TransactionDivider
import com.navi.moneymanager.common.ui.theme.color.MMColor
import com.navi.moneymanager.common.utils.Constants.HEADER_SCROLL_TWEEN
import com.navi.moneymanager.common.utils.roundedRippleClickable
import com.navi.moneymanager.postonboard.categorydetails.model.CategoryDetailsScreenUiEffect
import com.navi.moneymanager.postonboard.categorydetails.model.CategoryDetailsScreenUiState
import com.navi.moneymanager.postonboard.categorydetails.model.CategoryTotalSpendSectionData
import com.navi.moneymanager.postonboard.categorydetails.model.SortOption
import com.navi.moneymanager.postonboard.categorydetails.viewmodel.CategoryDetailsVM
import com.navi.moneymanager.postonboard.spendanalysis.model.TotalSpendSectionData
@Composable
fun CategoryDetailsScaffoldRenderer(
@@ -80,134 +88,181 @@ fun CategoryDetailsScaffoldRenderer(
onSortTransactionsRequest: () -> Unit,
onBarGraphElementClicked: (SelectedMonth) -> Unit,
) {
val rememberListState = rememberLazyListState()
val showScrolledStateTopSection by remember {
derivedStateOf { rememberListState.firstVisibleItemIndex > 0 }
}
Scaffold(
modifier = modifier,
containerColor = MMColor.white,
topBar = {
MMTopBar(
title = EMPTY,
labelForTitle =
if (showScrolledStateTopSection) {
categoryDetailsScreenUiState()
.screenData
?.totalSpendSection
?.category
?.categoryName ?: EMPTY
} else {
null
},
title =
if (showScrolledStateTopSection) {
categoryDetailsScreenUiState().screenData?.totalSpendSection?.amount
?: EMPTY
} else {
EMPTY
},
titleColor = MMColor.ctaPrimary,
navigationIcon = CommonR.drawable.ic_arrow_left_black_v2,
actionIconText = HELP_CTA_TEXT,
onActionClick = {
CategoryDetailsEventTrackerImpl.onCategoryDetailsHelpClicked()
getViewModel().setEffect { CategoryDetailsScreenUiEffect.Navigation.Help }
},
navigationIcon = com.navi.common.R.drawable.ic_arrow_left_black_v2,
actionIconText = if (!showScrolledStateTopSection) HELP_CTA_TEXT else null,
onActionClick =
if (!showScrolledStateTopSection) {
{
CategoryDetailsEventTrackerImpl.onCategoryDetailsHelpClicked()
getViewModel().setEffect {
CategoryDetailsScreenUiEffect.Navigation.Help
}
}
} else null,
onNavigationIconClick = {
getViewModel().setEffect { CategoryDetailsScreenUiEffect.Navigation.Back }
},
)
},
) {
LazyColumn(
modifier = Modifier.background(Color.White).padding(it).fillMaxHeight(),
state = rememberLazyListState(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
categoryDetailsScreenUiState().screenData?.totalSpendSection?.let { totalSpendSection ->
item {
CategoryTotalSpendSection(
totalSpendSection,
onMonthChangeClick,
onBankSelectionRequest,
onCategoryClick,
onInfoButtonClick = onInfoButtonClick,
)
}
}
categoryDetailsScreenUiState().screenData?.barGraphData?.let { barGraphData ->
item {
MMBarGraph(
screenName = MMScreen.CATEGORY_DETAILS.screen,
barGraphData = barGraphData,
onAverageInfoClick = onAverageInfoClick,
onBarGraphElementClicked = onBarGraphElementClicked,
)
}
}
item { Spacer(modifier = Modifier.height(16.dp)) }
val transactions = categoryDetailsScreenUiState().screenData?.transactions
if (transactions.isNullOrEmpty()) {
categoryDetailsScreenUiState().screenData?.zeroTransactionData?.let {
zeroTransactionData ->
Box {
LazyColumn(
modifier = Modifier.background(Color.White).padding(it).fillMaxHeight(),
state = rememberListState,
horizontalAlignment = Alignment.CenterHorizontally,
) {
categoryDetailsScreenUiState().screenData?.totalSpendSection?.let {
totalSpendSection ->
item {
SectionHeader(
headerModel = SectionHeaderData(title = "Category transactions"),
isActionVisible = false,
rippleStartPadding = 0.dp,
)
ZeroTransactionView(
title = zeroTransactionData.title,
illustrationSource = zeroTransactionData.illustrationSource,
modifier = Modifier.fillMaxWidth().padding(vertical = 32.dp),
CategoryTotalSpendSection(
totalSpendSection,
onMonthChangeClick,
onBankSelectionRequest,
onCategoryClick,
onInfoButtonClick = onInfoButtonClick,
)
}
}
} else {
categoryDetailsScreenUiState().screenData?.sortOption?.let { currentSortOption ->
categoryDetailsScreenUiState().screenData?.barGraphData?.let { barGraphData ->
item {
SectionHeader(
headerModel =
SectionHeaderData(
title = "Category transactions",
actionText =
stringResource(
when (currentSortOption) {
SortOption.HIGHEST_FIRST -> R.string.highest_first
SortOption.LOWEST_FIRST -> R.string.lowest_first
SortOption.RECENT_FIRST -> R.string.recent_first
}
),
suffixIcon =
IllustrationSource.Remote(
url = SORT_ICON,
placeholder = IMAGE_TRANSPARENT_PLACEHOLDER_SMALL,
),
),
onAction = onSortTransactionsRequest,
isActionVisible = true,
rippleStartPadding = 12.dp,
MMBarGraph(
screenName = MMScreen.CATEGORY_DETAILS.screen,
barGraphData = barGraphData,
onAverageInfoClick = onAverageInfoClick,
onBarGraphElementClicked = onBarGraphElementClicked,
)
}
}
itemsIndexed(transactions) { index, transaction ->
Transaction(
transaction = transaction,
onClick = { transactionId ->
CategoryDetailsEventTrackerImpl.onCategoryDetailsTransactionClicked(
transactionRank = index + 1
item { Spacer(modifier = Modifier.height(16.dp)) }
val transactions = categoryDetailsScreenUiState().screenData?.transactions
if (transactions.isNullOrEmpty()) {
categoryDetailsScreenUiState().screenData?.zeroTransactionData?.let {
zeroTransactionData ->
item {
SectionHeader(
headerModel = SectionHeaderData(title = "Category transactions"),
isActionVisible = false,
rippleStartPadding = 0.dp,
)
getViewModel().setEffect {
CategoryDetailsScreenUiEffect.Navigation.TransactionDetails(
transactionId = transactionId
)
}
},
onCategoryClick = { txn ->
CategoryDetailsEventTrackerImpl
.onCategoryDetailsTransactionCategoryClicked(
transactionRank = index + 1,
category = txn.categoryId,
)
getViewModel().setEffect {
CategoryDetailsScreenUiEffect.OpenCategoryBottomSheet(txn)
}
},
)
ZeroTransactionView(
title = zeroTransactionData.title,
illustrationSource = zeroTransactionData.illustrationSource,
modifier = Modifier.fillMaxWidth().padding(vertical = 32.dp),
)
}
}
} else {
categoryDetailsScreenUiState().screenData?.sortOption?.let { currentSortOption
->
item {
SectionHeader(
headerModel =
SectionHeaderData(
title = "Category transactions",
actionText =
stringResource(
when (currentSortOption) {
SortOption.HIGHEST_FIRST ->
R.string.highest_first
SortOption.LOWEST_FIRST -> R.string.lowest_first
SortOption.RECENT_FIRST -> R.string.recent_first
}
),
suffixIcon =
IllustrationSource.Remote(
url = SORT_ICON,
placeholder = IMAGE_TRANSPARENT_PLACEHOLDER_SMALL,
),
),
onAction = onSortTransactionsRequest,
isActionVisible = true,
rippleStartPadding = 12.dp,
)
}
}
if (index < transactions.lastIndex) {
TransactionDivider()
itemsIndexed(transactions) { index, transaction ->
Transaction(
transaction = transaction,
onClick = { transactionId ->
CategoryDetailsEventTrackerImpl.onCategoryDetailsTransactionClicked(
transactionRank = index + 1
)
getViewModel().setEffect {
CategoryDetailsScreenUiEffect.Navigation.TransactionDetails(
transactionId = transactionId
)
}
},
onCategoryClick = { txn ->
CategoryDetailsEventTrackerImpl
.onCategoryDetailsTransactionCategoryClicked(
transactionRank = index + 1,
category = txn.categoryId,
)
getViewModel().setEffect {
CategoryDetailsScreenUiEffect.OpenCategoryBottomSheet(txn)
}
},
)
if (index < transactions.lastIndex) {
TransactionDivider()
}
}
}
}
AnimatedVisibility(
modifier = Modifier.background(Color.White).padding(top = it.calculateTopPadding()),
visible = showScrolledStateTopSection,
enter =
fadeIn(tween(durationMillis = HEADER_SCROLL_TWEEN, easing = EaseInOutQuart)),
exit = fadeOut(tween(durationMillis = HEADER_SCROLL_TWEEN, easing = EaseInOutQuart)),
) {
TopSectionForMonthAndBankSelection(
totalSpendSection =
categoryDetailsScreenUiState().screenData?.totalSpendSection,
onMonthChangeClick = onMonthChangeClick,
onBankSelectionRequest = onBankSelectionRequest,
)
}
}
}
}
@Composable
private fun CategoryTotalSpendSection(
data: CategoryTotalSpendSectionData,
data: TotalSpendSectionData,
onMonthChangeClick: (String) -> Unit,
onBankSelectionRequest: (Set<String>) -> Unit,
onCategoryClick: (String) -> Unit,
@@ -313,7 +368,7 @@ private fun CategoryTotalSpendSection(
@Composable
private fun CategoryRow(
data: CategoryTotalSpendSectionData,
data: TotalSpendSectionData,
onCategoryClick: (String) -> Unit,
onInfoButtonClick: () -> Unit,
) {
@@ -325,12 +380,14 @@ private fun CategoryRow(
modifier =
Modifier.background(color = MMColor.ctaSecondary, shape = RoundedCornerShape(32.dp))
.clip(RoundedCornerShape(32.dp))
.onClickWithDebounce { onCategoryClick(data.category.categoryId) }
.onClickWithDebounce {
data.category?.categoryId?.let { categoryId -> onCategoryClick(categoryId) }
}
.padding(horizontal = 8.dp, vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
MMText(
text = data.category.categoryName,
text = data.category?.categoryName ?: EMPTY,
modifier = Modifier.padding(horizontal = 4.dp),
color = MMColor.textPrimary,
fontSize = 14.sp,
@@ -341,7 +398,7 @@ private fun CategoryRow(
modifier = Modifier.size(16.dp),
)
}
data.category.infoIconUrl?.let {
data.category?.infoIconUrl?.let {
Spacer(Modifier.width(8.dp))
Illustration(
illustrationType = IllustrationType.Image(it),

View File

@@ -11,6 +11,7 @@ import com.navi.moneymanager.common.illustration.model.IllustrationSource
import com.navi.moneymanager.common.model.BarGraphData
import com.navi.moneymanager.common.model.SpendCategorizationState
import com.navi.moneymanager.common.model.sectionHeader.SectionHeaderData
import com.navi.moneymanager.postonboard.categorydetails.model.CategoryTotalSpendSectionCategoryDataData
import com.navi.moneymanager.postonboard.dashboard.model.NavBarData
data class SpendAnalysisScreenData(
@@ -30,4 +31,5 @@ data class TotalSpendSectionData(
val amount: String,
val selectedBankReferenceIds: Set<String>,
val spendingTrendSectionData: SectionHeaderData? = null,
val category: CategoryTotalSpendSectionCategoryDataData? = null,
)

View File

@@ -7,25 +7,32 @@
package com.navi.moneymanager.postonboard.spendanalysis.ui
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.EaseInOutQuart
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import com.navi.base.utils.EMPTY
import com.navi.common.R as CommonR
import com.navi.common.R
import com.navi.common.constants.HELP_CTA_TEXT
import com.navi.moneymanager.common.analytics.SpendAnalysisEventTrackerImpl
import com.navi.moneymanager.common.illustration.model.IllustrationSource
@@ -41,8 +48,10 @@ import com.navi.moneymanager.common.ui.composable.MMTopBar
import com.navi.moneymanager.common.ui.composable.TotalSpendSectionUI
import com.navi.moneymanager.common.ui.composable.barGraph.MMBarGraph
import com.navi.moneymanager.common.ui.composable.button.PrimaryButton
import com.navi.moneymanager.common.ui.composable.sectionHeaders.TopSectionForMonthAndBankSelection
import com.navi.moneymanager.common.ui.composable.spendCategoriztion.SpendCategorizationSection
import com.navi.moneymanager.common.ui.theme.color.MMColor
import com.navi.moneymanager.common.utils.Constants.HEADER_SCROLL_TWEEN
import com.navi.moneymanager.postonboard.spendanalysis.model.SpendAnalysisScreenUiEffect
import com.navi.moneymanager.postonboard.spendanalysis.model.SpendAnalysisScreenUiState
import com.navi.moneymanager.postonboard.spendanalysis.viewmodel.SpendAnalysisVM
@@ -59,76 +68,116 @@ fun SpendAnalysisScaffoldRenderer(
onViewAllTransactionsClick: () -> Unit,
onBarGraphElementClicked: (SelectedMonth) -> Unit,
) {
val rememberListState = rememberLazyListState()
val showScrolledStateTopSection by remember {
derivedStateOf { rememberListState.firstVisibleItemIndex > 0 }
}
Scaffold(
modifier = modifier,
containerColor = MMColor.white,
topBar = {
MMTopBar(
title = EMPTY,
labelForTitle =
if (showScrolledStateTopSection) {
spendAnalysisScreenUiState().screenData?.totalSpendSection?.title ?: EMPTY
} else {
null
},
title =
if (showScrolledStateTopSection) {
spendAnalysisScreenUiState().screenData?.totalSpendSection?.amount ?: EMPTY
} else {
EMPTY
},
titleColor = MMColor.ctaPrimary,
navigationIcon = CommonR.drawable.ic_arrow_left_black_v2,
actionIconText = HELP_CTA_TEXT,
onActionClick = {
SpendAnalysisEventTrackerImpl.onSpendAnalysisScreenHelpClicked()
getViewModel().setEffect { SpendAnalysisScreenUiEffect.Navigation.Help }
},
navigationIcon = R.drawable.ic_arrow_left_black_v2,
actionIconText = if (!showScrolledStateTopSection) HELP_CTA_TEXT else null,
onActionClick =
if (!showScrolledStateTopSection) {
{
SpendAnalysisEventTrackerImpl.onSpendAnalysisScreenHelpClicked()
getViewModel().setEffect { SpendAnalysisScreenUiEffect.Navigation.Help }
}
} else null,
onNavigationIconClick = {
getViewModel().setEffect { SpendAnalysisScreenUiEffect.Navigation.Back }
},
)
},
) {
Column(
modifier =
Modifier.background(Color.White)
.padding(it)
.fillMaxHeight()
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween,
) {
Column {
Box {
LazyColumn(
modifier = Modifier.background(Color.White).padding(it).fillMaxHeight(),
state = rememberListState,
horizontalAlignment = Alignment.CenterHorizontally,
) {
spendAnalysisScreenUiState().screenData?.totalSpendSection?.let { totalSpendSection
->
TotalSpendSectionUI(
totalSpendSection,
onMonthChangeClick,
onBankSelectionRequest,
)
item {
TotalSpendSectionUI(
totalSpendSection,
onMonthChangeClick,
onBankSelectionRequest,
)
}
}
spendAnalysisScreenUiState().screenData?.barGraphData?.let { barGraphData ->
MMBarGraph(
MMScreen.SPEND_ANALYSIS.screen,
barGraphData,
onAverageInfoClick,
onBarGraphElementClicked = { selectedMonth ->
onBarGraphElementClicked(selectedMonth)
},
)
item {
MMBarGraph(
MMScreen.SPEND_ANALYSIS.screen,
barGraphData,
onAverageInfoClick,
onBarGraphElementClicked = { selectedMonth ->
onBarGraphElementClicked(selectedMonth)
},
)
}
}
item { Spacer(modifier = Modifier.height(16.dp)) }
spendAnalysisScreenUiState().screenData?.spendCategorizationState?.let {
spendCategorizationState ->
Spacer(modifier = Modifier.height(16.dp))
SpendCategorizationSection(
screenName = MMScreen.SPEND_ANALYSIS.screen,
isAddAccountSelected = false,
spendCategorizationState = spendCategorizationState,
spendCategorizationAction = { action ->
when (action) {
is SpendCategorizationAction.SelectCategory -> {
onCategoryClick(action.categoryId)
item {
SpendCategorizationSection(
screenName = MMScreen.SPEND_ANALYSIS.screen,
isAddAccountSelected = false,
spendCategorizationState = spendCategorizationState,
spendCategorizationAction = { action ->
when (action) {
is SpendCategorizationAction.SelectCategory -> {
onCategoryClick(action.categoryId)
}
else -> {}
}
else -> {}
}
},
)
},
)
}
}
spendAnalysisScreenUiState().screenData?.viewTransactionHistoryTitle?.let { title ->
PrimaryButton(title = title, onClick = onViewAllTransactionsClick)
item { PrimaryButton(title = title, onClick = onViewAllTransactionsClick) }
}
item { SpendAnalysisFooterSection() }
}
AnimatedVisibility(
modifier = Modifier.background(Color.White).padding(top = it.calculateTopPadding()),
visible = showScrolledStateTopSection,
enter =
fadeIn(tween(durationMillis = HEADER_SCROLL_TWEEN, easing = EaseInOutQuart)),
exit = fadeOut(tween(durationMillis = HEADER_SCROLL_TWEEN, easing = EaseInOutQuart)),
) {
TopSectionForMonthAndBankSelection(
totalSpendSection = spendAnalysisScreenUiState().screenData?.totalSpendSection,
onMonthChangeClick = onMonthChangeClick,
onBankSelectionRequest = onBankSelectionRequest,
)
}
SpendAnalysisFooterSection()
}
}
}