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

Commit

Permalink
[IVY-2863] Tags UI Composables (#3005)
Browse files Browse the repository at this point in the history
* [IVY-2863] Tags UI Composables

* [IVY-2863] Fix Unit Tests

* [IVY-2863] Tags Refactor & Bug Fixes

* [IVY-2863] Tagbased Filtering in Reports Screen

* [IVy-2863] Tags Refactor
  • Loading branch information
Vishwa-Raghavendra authored Mar 3, 2024
1 parent 8096044 commit f60fbe8
Show file tree
Hide file tree
Showing 30 changed files with 597 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,5 @@ com.ivy.reports.PeriodFilter
com.ivy.reports.AccountsFilter
com.ivy.reports.CategoriesFilter
com.ivy.reports.AmountFilter
com.ivy.reports.KeywordsFilter
com.ivy.reports.KeywordsFilter
com.ivy.reports.OthersFilter
120 changes: 114 additions & 6 deletions screen/reports/src/main/java/com/ivy/reports/FilterOverlay.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.ivy.reports

import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.BoxWithConstraintsScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
Expand All @@ -19,6 +21,7 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
Expand All @@ -35,13 +38,16 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import com.ivy.base.model.TransactionType
import com.ivy.data.model.Tag
import com.ivy.design.l0_system.UI
import com.ivy.design.l0_system.style
import com.ivy.domain.legacy.ui.theme.components.ListItem
import com.ivy.legacy.IvyWalletPreview
import com.ivy.legacy.datamodel.Account
import com.ivy.legacy.datamodel.Category
import com.ivy.legacy.ivyWalletCtx
import com.ivy.legacy.ui.component.tags.AddTagButton
import com.ivy.legacy.ui.component.tags.ShowTagModal
import com.ivy.legacy.utils.capitalizeLocal
import com.ivy.legacy.utils.springBounce
import com.ivy.resources.R
Expand All @@ -68,20 +74,27 @@ import com.ivy.wallet.ui.theme.modal.ChoosePeriodModalData
import com.ivy.wallet.ui.theme.modal.edit.AmountModal
import com.ivy.wallet.ui.theme.toComposeColor
import com.ivy.wallet.ui.theme.wallet.AmountCurrencyB1Row
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import java.util.UUID
import kotlin.math.roundToInt

@Suppress("LongMethod")
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun BoxWithConstraintsScope.FilterOverlay(
visible: Boolean,

baseCurrency: String,
accounts: List<Account>,
categories: List<Category>,
allTags: ImmutableList<Tag>,

filter: ReportFilter?,
onClose: () -> Unit,
onSetFilter: (ReportFilter?) -> Unit
onSetFilter: (ReportFilter?) -> Unit,
onTagSearch: (String) -> Unit
) {
val percentVisible by animateFloatAsState(
targetValue = if (visible) 1f else 0f,
Expand Down Expand Up @@ -109,6 +122,12 @@ fun BoxWithConstraintsScope.FilterOverlay(
var maxAmountModalShown by remember { mutableStateOf(false) }
var includeKeywordModalShown by remember { mutableStateOf(false) }
var excludeKeywordModalShown by remember { mutableStateOf(false) }
var tagModalVisible by remember { mutableStateOf(false) }
val selectedTags by remember(localFilter) {
derivedStateOf {
localFilter?.selectedTags?.toImmutableList() ?: persistentListOf()
}
}

if (percentVisible > 0.01f) {
Column(
Expand Down Expand Up @@ -241,6 +260,15 @@ fun BoxWithConstraintsScope.FilterOverlay(
}
)

FilterDivider()

OthersFilter(
filter = localFilter,
onTagButtonClicked = {
tagModalVisible = true
}
)

Spacer(Modifier.height(196.dp))
}
}
Expand Down Expand Up @@ -356,6 +384,86 @@ fun BoxWithConstraintsScope.FilterOverlay(
.toSet().toList() // filter duplicated
)
}

ShowTagModal(
visible = tagModalVisible,
selectOnlyMode = true,
onDismiss = {
tagModalVisible = false
// Reset TagList, avoids showing incorrect tag list if user had searched for a tag previously
onTagSearch("")
},
allTagList = allTags,
selectedTagList = selectedTags,
onTagAdd = {
// Do Nothing
},
onTagEdit = { oldTag, newTag ->
// Do Nothing
},
onTagDelete = {
// Do Nothing
},
onTagSelected = {
localFilter = nonNullFilter(localFilter).copy(
selectedTags = nonNullFilter(localFilter).selectedTags.plus(it)
)
},
onTagDeSelected = {
localFilter = nonNullFilter(localFilter).copy(
selectedTags = nonNullFilter(localFilter).selectedTags.minus(it)
)
},
onTagSearch = {
onTagSearch(it)
}
)
}

@Composable
fun ColumnScope.OthersFilter(
filter: ReportFilter?,
onTagButtonClicked: () -> Unit
) {
FilterTitleText(
text = stringResource(R.string.others_optional),
active = false
)

TagFilter(
selectedTags = filter?.selectedTags?.toImmutableList() ?: persistentListOf(),
onTagButtonClicked = onTagButtonClicked
)
}

@Composable
fun ColumnScope.TagFilter(
selectedTags: ImmutableList<Tag>,
onTagButtonClicked: () -> Unit,
@Suppress("UnusedParameter") modifier: Modifier = Modifier
) {
Text(
modifier = Modifier.padding(start = 32.dp, top = 16.dp),
text = stringResource(R.string.tags),
style = UI.typo.b2.style(
fontWeight = FontWeight.ExtraBold
)
)

Spacer(Modifier.height(12.dp))

if (selectedTags.isEmpty()) {
AddKeywordButton(
modifier = Modifier.padding(start = 24.dp),
text = "Select Tags"
) {
onTagButtonClicked()
}
} else {
AddTagButton(transactionAssociatedTags = selectedTags) {
onTagButtonClicked()
}
}
}

@Composable
Expand Down Expand Up @@ -825,11 +933,9 @@ private fun Keyword(
}

@Composable
private fun AddKeywordButton(
text: String,
onCLick: () -> Unit
) {
private fun AddKeywordButton(text: String, modifier: Modifier = Modifier, onCLick: () -> Unit) {
IvyOutlinedButton(
modifier = modifier,
text = text,
iconStart = R.drawable.ic_plus,
padding = 10.dp,
Expand Down Expand Up @@ -903,8 +1009,10 @@ private fun Preview() {
maxAmount = 13256.27,
),
onClose = { },
allTags = persistentListOf(),
onSetFilter = {
}
},
onTagSearch = { }
)
}
}
7 changes: 5 additions & 2 deletions screen/reports/src/main/java/com/ivy/reports/ReportFilter.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.ivy.reports

import com.ivy.base.model.TransactionType
import com.ivy.data.model.Tag
import com.ivy.legacy.data.model.TimePeriod
import com.ivy.legacy.datamodel.Account
import com.ivy.legacy.datamodel.Category
Expand All @@ -16,7 +17,8 @@ data class ReportFilter(
val minAmount: Double?,
val maxAmount: Double?,
val includeKeywords: List<String>,
val excludeKeywords: List<String>
val excludeKeywords: List<String>,
val selectedTags: List<Tag>
) {
companion object {
fun emptyFilter(
Expand All @@ -30,7 +32,8 @@ data class ReportFilter(
includeKeywords = emptyList(),
excludeKeywords = emptyList(),
minAmount = null,
maxAmount = null
maxAmount = null,
selectedTags = emptyList()
)
}

Expand Down
4 changes: 4 additions & 0 deletions screen/reports/src/main/java/com/ivy/reports/ReportScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ private fun BoxWithConstraintsScope.UI(
accounts = state.accounts,
categories = state.categories,
filter = state.filter,
allTags = state.allTags,
onClose = {
onEventHandler.invoke(
ReportScreenEvent.OnFilterOverlayVisible(
Expand All @@ -304,6 +305,9 @@ private fun BoxWithConstraintsScope.UI(
},
onSetFilter = {
onEventHandler.invoke(ReportScreenEvent.OnFilter(filter = it))
},
onTagSearch = {
onEventHandler.invoke(ReportScreenEvent.OnTagSearch(data = it))
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ sealed class ReportScreenEvent {
data class OnUpcomingExpanded(val upcomingExpanded: Boolean) : ReportScreenEvent()
data class OnOverdueExpanded(val overdueExpanded: Boolean) : ReportScreenEvent()
data class OnFilterOverlayVisible(val filterOverlayVisible: Boolean) : ReportScreenEvent()
data class OnTagSearch(val data: String) : ReportScreenEvent()
data class OnTreatTransfersAsIncomeExpense(val transfersAsIncomeExpense: Boolean) :
ReportScreenEvent()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package com.ivy.reports

import com.ivy.data.model.Transaction
import com.ivy.base.legacy.TransactionHistoryItem
import com.ivy.data.model.Tag
import com.ivy.legacy.datamodel.Account
import com.ivy.legacy.datamodel.Category
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import java.util.*

@Suppress("DataClassDefaultValues")
data class ReportScreenState(
val baseCurrency: String = "",
val balance: Double = 0.0,
Expand All @@ -30,5 +32,6 @@ data class ReportScreenState(
val transactions: ImmutableList<Transaction> = persistentListOf(),
val filterOverlayVisible: Boolean = false,
val showTransfersAsIncExpCheckbox: Boolean = false,
val treatTransfersAsIncExp: Boolean = false
val treatTransfersAsIncExp: Boolean = false,
val allTags: ImmutableList<Tag> = persistentListOf()
)
Loading

0 comments on commit f60fbe8

Please sign in to comment.