Skip to content
This repository has been archived by the owner on Nov 5, 2024. It is now read-only.

Fix issue 3364 #3431

Merged
merged 10 commits into from
Aug 27, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import androidx.annotation.DrawableRes
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraintsScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
Expand All @@ -14,12 +16,14 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
Expand Down Expand Up @@ -47,6 +51,7 @@ import com.ivy.data.model.primitive.IconAsset
import com.ivy.data.model.primitive.NotBlankTrimmedString
import com.ivy.design.l0_system.UI
import com.ivy.design.l0_system.style
import com.ivy.legacy.utils.format
import com.ivy.navigation.CategoriesScreen
import com.ivy.navigation.TransactionsScreen
import com.ivy.navigation.navigation
Expand Down Expand Up @@ -93,7 +98,7 @@ fun BoxWithConstraintsScope.CategoriesScreen(screen: CategoriesScreen) {

@Composable
private fun BoxWithConstraintsScope.UI(
state: CategoriesScreenState = CategoriesScreenState(),
state: CategoriesScreenState = CategoriesScreenState(compactCategoriesModeEnabled = false),
onEvent: (CategoriesScreenEvent) -> Unit = {}
) {
val nav = navigation()
Expand Down Expand Up @@ -155,10 +160,10 @@ private fun BoxWithConstraintsScope.UI(
}

items(state.categories, key = { it.category.id.value }) { categoryData ->
Spacer(Modifier.height(16.dp))
CategoryCard(
currency = state.baseCurrency,
categoryData = categoryData,
compactModeEnabled = state.compactCategoriesModeEnabled,
onLongClick = {
onEvent(CategoriesScreenEvent.OnReorderModalVisible(true))
}
Expand Down Expand Up @@ -240,8 +245,31 @@ private fun BoxWithConstraintsScope.UI(
private fun CategoryCard(
currency: String,
categoryData: CategoryData,
compactModeEnabled: Boolean,
onLongClick: () -> Unit,
onClick: () -> Unit
) {
val contrastColor = findContrastTextColor(categoryData.category.color.value.toComposeColor())

if (!compactModeEnabled) {
Spacer(Modifier.height(16.dp))
DefaultCategoryCard(onClick, categoryData, currency)
} else {
Spacer(Modifier.height(8.dp))
CompactCategoryCard(
categoryData = categoryData,
contrastColor = contrastColor,
currency = currency,
onClick = onClick
)
}
}

@Composable
private fun DefaultCategoryCard(
onClick: () -> Unit,
categoryData: CategoryData,
currency: String
) {
Column(
modifier = Modifier
Expand Down Expand Up @@ -272,6 +300,82 @@ private fun CategoryCard(
}
}

@Composable
private fun CompactCategoryCard(
categoryData: CategoryData,
contrastColor: Color,
currency: String,
onClick: () -> Unit
) {
val category = categoryData.category

Box(
modifier = Modifier
.padding(horizontal = 16.dp)
.border(2.dp, UI.colors.medium, UI.shapes.r4)
.clickable(
onClick = onClick
),
) {
Row(
modifier = Modifier
.padding(all = 10.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Box(
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.background(category.color.value.toComposeColor()),
contentAlignment = Alignment.Center,
) {
ItemIconSDefaultIcon(
iconName = category.icon?.id,
defaultIcon = R.drawable.ic_custom_account_s,
tint = contrastColor
)
}

Row(
modifier =
Modifier
.padding(horizontal = 8.dp)
.fillMaxWidth()
.fillMaxHeight(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = category.name.value,
style = UI.typo.b2.style(
fontWeight = FontWeight.Bold
)
)

Row(
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = categoryData.monthlyBalance.format(currency),
style = UI.typo.nB1.style(
color = UI.colors.pureInverse,
fontWeight = FontWeight.Bold
)
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = currency,
style = UI.typo.nB2.style(
color = UI.colors.pureInverse,
fontWeight = FontWeight.Medium
)
)
}
}
}
}
}

@Composable
fun AddedSpent(
monthlyIncome: Double,
Expand Down Expand Up @@ -556,10 +660,17 @@ private fun SelectTypeButton(

@Preview
@Composable
private fun Preview(theme: Theme = Theme.LIGHT) {
private fun PreviewCategoriesCompactModeEnabled(theme: Theme = Theme.LIGHT) {
ILIYANGERMANOV marked this conversation as resolved.
Show resolved Hide resolved
Preview(theme = theme, compactModeEnabled = true)
}

@Preview
@Composable
private fun Preview(theme: Theme = Theme.LIGHT, compactModeEnabled: Boolean = false) {
com.ivy.legacy.IvyWalletPreview(theme) {
val state = CategoriesScreenState(
baseCurrency = "BGN",
compactCategoriesModeEnabled = compactModeEnabled,
categories = persistentListOf(
CategoryData(
category = Category(
Expand Down Expand Up @@ -637,3 +748,13 @@ fun CategoriesScreenUiTest(isDark: Boolean) {
}
Preview(theme)
}

/** For screenshot testing */
@Composable
fun CategoriesScreenCompactUiTest(isDark: Boolean) {
val theme = when (isDark) {
true -> Theme.DARK
false -> Theme.LIGHT
}
Preview(theme, compactModeEnabled = true)
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ data class CategoriesScreenState(
val categoryModalData: CategoryModalData? = null,
val sortModalVisible: Boolean = false,
val sortOrderItems: ImmutableList<SortOrder> = SortOrder.values().toList().toImmutableList(),
val sortOrder: SortOrder = SortOrder.DEFAULT
val sortOrder: SortOrder = SortOrder.DEFAULT,
val compactCategoriesModeEnabled: Boolean,
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.ivy.base.legacy.SharedPrefs
import com.ivy.base.legacy.Transaction
import com.ivy.ui.ComposeViewModel
import com.ivy.data.repository.CategoryRepository
import com.ivy.domain.features.Features
import com.ivy.frp.action.thenMap
import com.ivy.frp.thenInvokeAfter
import com.ivy.legacy.data.model.TimePeriod
Expand Down Expand Up @@ -44,6 +45,7 @@ class CategoriesViewModel @Inject constructor(
private val accountsAct: AccountsAct,
private val trnsWithRangeAndAccFiltersAct: TrnsWithRangeAndAccFiltersAct,
private val categoryIncomeWithAccountFiltersAct: LegacyCategoryIncomeWithAccountFiltersAct,
private val features: Features,
) : ComposeViewModel<CategoriesScreenState, CategoriesScreenEvent>() {

private val baseCurrency = mutableStateOf("")
Expand All @@ -66,10 +68,16 @@ class CategoriesViewModel @Inject constructor(
reorderModalVisible = getReorderModalVisible(),
categoryModalData = getCategoryModalData(),
sortOrder = getSortOrder(),
sortModalVisible = getSortModalVisible()
sortModalVisible = getSortModalVisible(),
compactCategoriesModeEnabled = getCompactCategoriesMode(),
)
}

@Composable
private fun getCompactCategoriesMode(): Boolean {
return features.compactCategoriesMode.asEnabledState()
}

@Composable
private fun getBaseCurrency(): String {
return baseCurrency.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,16 @@ class CategoriesScreenPaparazziTest(
private val theme: PaparazziTheme,
) : PaparazziScreenshotTest() {
@Test
fun `snapshot Categories Screen`() {
fun `snapshot Categories nonCompact Screen`() {
snapshot(theme) {
CategoriesScreenUiTest(theme == PaparazziTheme.Dark)
}
}

@Test
fun `snapshot Categories compact Screen`() {
snapshot(theme) {
CategoriesScreenCompactUiTest(theme == PaparazziTheme.Dark)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.ivy.domain.features
interface Features {
val sortCategoriesAlphabetically: BoolFeature
val compactAccountsMode: BoolFeature
val compactCategoriesMode: BoolFeature

val allFeatures: List<BoolFeature>
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,30 @@ import javax.inject.Singleton

@Singleton
class IvyFeatures @Inject constructor() : Features {

override val sortCategoriesAlphabetically = BoolFeature(
key = "sort_categories_alphabetically",
name = "Sort Categories Alphabetically",
description = "Sort income and expenses" +
" categories alphabetically"
)

override val compactAccountsMode = BoolFeature(
key = "compact_account_ui",
name = "Compact account UI",
description = "Enables more compact and dense UI for the \"Accounts\" tab"
)

override val compactCategoriesMode = BoolFeature(
key = "compact_categories_ui",
name = "Compact category UI",
description = "Activates a more streamlined and space-efficient interface for the \"Categories\" tab"
ILIYANGERMANOV marked this conversation as resolved.
Show resolved Hide resolved
)

override val allFeatures: List<BoolFeature>
get() = listOf(
sortCategoriesAlphabetically,
compactAccountsMode
compactAccountsMode,
compactCategoriesMode
)
}
Loading