From 5dfa882091fb57a564f8f7681b8f46ac69b1ecdc Mon Sep 17 00:00:00 2001 From: Suyash Mittal Date: Sat, 20 Jan 2024 17:24:30 +0530 Subject: [PATCH] added date format setting and grouping on transactions --- app/build.gradle.kts | 1 + .../data/settings/AppSettings.kt | 3 +- .../creditmanager/domain/util/DateFormat.kt | 9 + .../presentation/settings/SettingsEvent.kt | 3 + .../presentation/settings/SettingsScreen.kt | 181 +++++++++++++++--- .../settings/SettingsViewModel.kt | 13 ++ .../transactions/TransactionsScreen.kt | 54 ++++-- .../transactions/TransactionsState.kt | 3 +- .../transactions/component/TransactionItem.kt | 6 +- 9 files changed, 224 insertions(+), 49 deletions(-) create mode 100644 app/src/main/java/com/suyash/creditmanager/domain/util/DateFormat.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8f8c6f3..bc57e11 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -50,6 +50,7 @@ android { jvmTarget = "1.8" } buildFeatures { + buildConfig = true compose = true } composeOptions { diff --git a/app/src/main/java/com/suyash/creditmanager/data/settings/AppSettings.kt b/app/src/main/java/com/suyash/creditmanager/data/settings/AppSettings.kt index 3505035..0c83666 100644 --- a/app/src/main/java/com/suyash/creditmanager/data/settings/AppSettings.kt +++ b/app/src/main/java/com/suyash/creditmanager/data/settings/AppSettings.kt @@ -1,9 +1,10 @@ package com.suyash.creditmanager.data.settings +import com.suyash.creditmanager.domain.util.DateFormat import kotlinx.serialization.Serializable @Serializable data class AppSettings( val countryCode: String = "IN", - val dateFormat: String = "dd/MM/yyyy" + val dateFormat: DateFormat = DateFormat.DDMMYYYY ) \ No newline at end of file diff --git a/app/src/main/java/com/suyash/creditmanager/domain/util/DateFormat.kt b/app/src/main/java/com/suyash/creditmanager/domain/util/DateFormat.kt new file mode 100644 index 0000000..af8310d --- /dev/null +++ b/app/src/main/java/com/suyash/creditmanager/domain/util/DateFormat.kt @@ -0,0 +1,9 @@ +package com.suyash.creditmanager.domain.util + +import java.time.format.DateTimeFormatter + +enum class DateFormat(val format: String, val formatter: DateTimeFormatter) { + DDMMYYYY("dd/MM/yyyy", DateTimeFormatter.ofPattern("dd/MM/yyyy")), + MMDDYYYY("MM/dd/yyyy", DateTimeFormatter.ofPattern("MM/dd/yyyy")), + YYYYMMDD("yyyy/MM/dd", DateTimeFormatter.ofPattern("yyyy/MM/dd")), +} \ No newline at end of file diff --git a/app/src/main/java/com/suyash/creditmanager/presentation/settings/SettingsEvent.kt b/app/src/main/java/com/suyash/creditmanager/presentation/settings/SettingsEvent.kt index 0f0a3db..ef17456 100644 --- a/app/src/main/java/com/suyash/creditmanager/presentation/settings/SettingsEvent.kt +++ b/app/src/main/java/com/suyash/creditmanager/presentation/settings/SettingsEvent.kt @@ -1,6 +1,9 @@ package com.suyash.creditmanager.presentation.settings +import com.suyash.creditmanager.domain.util.DateFormat + sealed class SettingsEvent { data class UpdateCurrency(val countryCode: String): SettingsEvent() + data class UpdateDateFormat(val dateFormat: DateFormat): SettingsEvent() } \ No newline at end of file diff --git a/app/src/main/java/com/suyash/creditmanager/presentation/settings/SettingsScreen.kt b/app/src/main/java/com/suyash/creditmanager/presentation/settings/SettingsScreen.kt index 2a4dd3b..451b319 100644 --- a/app/src/main/java/com/suyash/creditmanager/presentation/settings/SettingsScreen.kt +++ b/app/src/main/java/com/suyash/creditmanager/presentation/settings/SettingsScreen.kt @@ -3,7 +3,6 @@ package com.suyash.creditmanager.presentation.settings import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -13,6 +12,8 @@ import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.selection.selectableGroup import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.CalendarToday +import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.outlined.Payments import androidx.compose.material3.Card import androidx.compose.material3.ExperimentalMaterial3Api @@ -30,11 +31,15 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.platform.UriHandler import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController +import com.suyash.creditmanager.BuildConfig +import com.suyash.creditmanager.domain.util.DateFormat import java.util.Locale @Composable @@ -43,9 +48,13 @@ fun SettingsScreen( navController: NavController, viewModel: SettingsViewModel = hiltViewModel() ) { + val uriHandler: UriHandler = LocalUriHandler.current var openCurrencyDialog by rememberSaveable { mutableStateOf(false) } + var openDateFormatDialog by rememberSaveable { + mutableStateOf(false) + } Scaffold( topBar = { @@ -55,38 +64,97 @@ fun SettingsScreen( } ) { paddingValues -> - Row( - modifier = Modifier - .padding(paddingValues) - .fillMaxWidth() - .clickable { - openCurrencyDialog = true - }, - verticalAlignment = Alignment.CenterVertically + Column( + modifier = Modifier.padding(paddingValues) ) { - Icon( - imageVector = Icons.Outlined.Payments, - contentDescription = "Currency Country", - modifier = Modifier.padding(start = 16.dp) - ) - Column( + Row( modifier = Modifier .fillMaxWidth() - .padding(16.dp) + .clickable { + openCurrencyDialog = true + }, + verticalAlignment = Alignment.CenterVertically ) { - Text( - text = "Currency Country", - style = MaterialTheme.typography.titleMedium + Icon( + imageVector = Icons.Outlined.Payments, + contentDescription = "Currency Country", + modifier = Modifier.padding(start = 16.dp) ) - Text( - text = "${viewModel.countryCode.value} - ${ - Locale( - "", - viewModel.countryCode.value - ).displayName - }", - style = MaterialTheme.typography.bodyMedium, + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Text( + text = "Currency Country", + style = MaterialTheme.typography.titleMedium + ) + Text( + text = "${viewModel.countryCode.value} - ${ + Locale( + "", + viewModel.countryCode.value + ).displayName + }", + style = MaterialTheme.typography.bodyMedium, + ) + } + } + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + openDateFormatDialog = true + }, + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Outlined.CalendarToday, + contentDescription = "Date Format", + modifier = Modifier.padding(start = 16.dp) ) + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Text( + text = "Date Format", + style = MaterialTheme.typography.titleMedium + ) + Text( + text = viewModel.dateFormat.value.format, + style = MaterialTheme.typography.bodyMedium, + ) + } + } + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + uriHandler.openUri("https://github.com/suyash01/credit-manager/releases") + }, + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Outlined.Info, + contentDescription = "Check for Updates", + modifier = Modifier.padding(start = 16.dp) + ) + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Text( + text = "Check for Updates", + style = MaterialTheme.typography.titleMedium + ) + Text( + text = "v${BuildConfig.VERSION_NAME}", + style = MaterialTheme.typography.bodyMedium, + ) + } } } @@ -99,7 +167,6 @@ fun SettingsScreen( Card( modifier = Modifier .fillMaxWidth() - .fillMaxHeight() .padding(vertical = 16.dp), shape = RoundedCornerShape(16.dp), ) { @@ -152,5 +219,63 @@ fun SettingsScreen( } } } + if (openDateFormatDialog) { + Dialog( + onDismissRequest = { + openDateFormatDialog = false + } + ) { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp), + shape = RoundedCornerShape(16.dp), + ) { + LazyColumn( + modifier = Modifier + .selectableGroup() + ) { + itemsIndexed(DateFormat.entries) { _, it -> + Row( + modifier = Modifier + .fillMaxWidth() + .height(56.dp) + .selectable( + selected = (it == viewModel.dateFormat.value), + onClick = { + viewModel.onEvent( + SettingsEvent.UpdateDateFormat(it) + ) + }, + role = Role.RadioButton + ) + .padding(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + RadioButton( + selected = (it == viewModel.dateFormat.value), + onClick = null + ) + Text( + text = it.format, + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(start = 16.dp) + ) + } + } + } + TextButton( + onClick = { + openDateFormatDialog = false + }, + modifier = Modifier + .align(Alignment.End) + .padding(horizontal = 16.dp, vertical = 8.dp) + ) { + Text("Close") + } + } + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/suyash/creditmanager/presentation/settings/SettingsViewModel.kt b/app/src/main/java/com/suyash/creditmanager/presentation/settings/SettingsViewModel.kt index f059e0b..2220d45 100644 --- a/app/src/main/java/com/suyash/creditmanager/presentation/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/suyash/creditmanager/presentation/settings/SettingsViewModel.kt @@ -6,6 +6,7 @@ import androidx.datastore.core.DataStore import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.suyash.creditmanager.data.settings.AppSettings +import com.suyash.creditmanager.domain.util.DateFormat import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import java.util.Locale @@ -19,6 +20,9 @@ class SettingsViewModel @Inject constructor( private val _countryCode = mutableStateOf("IN") val countryCode: State = _countryCode + private val _dateFormat = mutableStateOf(DateFormat.DDMMYYYY) + val dateFormat: State = _dateFormat + private val _countries = mutableStateOf(listOf(Locale("", "IN"))) val countries: State> = _countries @@ -26,6 +30,7 @@ class SettingsViewModel @Inject constructor( viewModelScope.launch { dataStore.data.collect { _countryCode.value = it.countryCode + _dateFormat.value = it.dateFormat } } @@ -48,6 +53,14 @@ class SettingsViewModel @Inject constructor( } } } + is SettingsEvent.UpdateDateFormat -> { + _dateFormat.value = event.dateFormat + viewModelScope.launch { + dataStore.updateData { + it.copy(dateFormat = event.dateFormat) + } + } + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/suyash/creditmanager/presentation/transactions/TransactionsScreen.kt b/app/src/main/java/com/suyash/creditmanager/presentation/transactions/TransactionsScreen.kt index d1f39b4..99e10ed 100644 --- a/app/src/main/java/com/suyash/creditmanager/presentation/transactions/TransactionsScreen.kt +++ b/app/src/main/java/com/suyash/creditmanager/presentation/transactions/TransactionsScreen.kt @@ -1,6 +1,7 @@ package com.suyash.creditmanager.presentation.transactions import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Column @@ -17,6 +18,7 @@ import androidx.compose.material3.AlertDialog import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -31,7 +33,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import com.suyash.creditmanager.presentation.transactions.component.TransactionItem @@ -69,22 +73,40 @@ fun TransactionsScreen( LazyColumn ( modifier = Modifier.padding(contentPadding) ) { - items(viewModel.state.value.transactions) { transaction -> - TransactionItem( - transaction = transaction, - countryCode = viewModel.state.value.countryCode, - dateFormat = viewModel.state.value.dateFormat, - modifier = Modifier - .fillMaxWidth() - .combinedClickable( - onClick = { }, - onLongClick = { - haptics.performHapticFeedback(HapticFeedbackType.LongPress) - viewModel.onEvent(TransactionsEvent.ToggleBottomSheet(transaction)) - isBottomSheetOpen = true - } - ) - ) + val groupedTxn = viewModel.state.value.transactions.groupBy { it.date } + groupedTxn.forEach { (date, transactions) -> + stickyHeader { + Text( + text = date.format(viewModel.state.value.dateFormat.formatter), + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier + .fillMaxWidth() + .background(MaterialTheme.colorScheme.primaryContainer) + .padding(start = 16.dp) + ) + } + items(transactions) { transaction -> + TransactionItem( + transaction = transaction, + countryCode = viewModel.state.value.countryCode, + dateFormat = viewModel.state.value.dateFormat, + modifier = Modifier + .fillMaxWidth() + .combinedClickable( + onClick = { }, + onLongClick = { + haptics.performHapticFeedback(HapticFeedbackType.LongPress) + viewModel.onEvent( + TransactionsEvent.ToggleBottomSheet( + transaction + ) + ) + isBottomSheetOpen = true + } + ) + ) + } } } if(openDeleteConfirmationDialog) { diff --git a/app/src/main/java/com/suyash/creditmanager/presentation/transactions/TransactionsState.kt b/app/src/main/java/com/suyash/creditmanager/presentation/transactions/TransactionsState.kt index 10c6b57..5da5025 100644 --- a/app/src/main/java/com/suyash/creditmanager/presentation/transactions/TransactionsState.kt +++ b/app/src/main/java/com/suyash/creditmanager/presentation/transactions/TransactionsState.kt @@ -1,6 +1,7 @@ package com.suyash.creditmanager.presentation.transactions import com.suyash.creditmanager.domain.model.Transaction +import com.suyash.creditmanager.domain.util.DateFormat import com.suyash.creditmanager.domain.util.OrderType import com.suyash.creditmanager.domain.util.TransactionOrder @@ -9,6 +10,6 @@ data class TransactionsState( val transactionOrder: TransactionOrder = TransactionOrder.Date(OrderType.Ascending), val selectedTransaction: Transaction? = null, val countryCode: String = "IN", - val dateFormat: String = "dd/MM/yyyy", + val dateFormat: DateFormat = DateFormat.DDMMYYYY, val isBottomSheetVisible: Boolean = false ) diff --git a/app/src/main/java/com/suyash/creditmanager/presentation/transactions/component/TransactionItem.kt b/app/src/main/java/com/suyash/creditmanager/presentation/transactions/component/TransactionItem.kt index d7983a1..f4b8566 100644 --- a/app/src/main/java/com/suyash/creditmanager/presentation/transactions/component/TransactionItem.kt +++ b/app/src/main/java/com/suyash/creditmanager/presentation/transactions/component/TransactionItem.kt @@ -15,17 +15,17 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.suyash.creditmanager.domain.model.Transaction +import com.suyash.creditmanager.domain.util.DateFormat import com.suyash.creditmanager.domain.util.TransactionType import com.suyash.creditmanager.presentation.util.CCUtils import com.suyash.creditmanager.ui.theme.Credit import com.suyash.creditmanager.ui.theme.Debit -import java.time.format.DateTimeFormatter @Composable fun TransactionItem( transaction: Transaction, countryCode: String, - dateFormat: String, + dateFormat: DateFormat, modifier: Modifier = Modifier ) { Column( @@ -65,7 +65,7 @@ fun TransactionItem( style = MaterialTheme.typography.bodySmall, ) Text( - text = transaction.date.format(DateTimeFormatter.ofPattern(dateFormat)), + text = transaction.date.format(dateFormat.formatter), style = MaterialTheme.typography.bodySmall, ) }