fix(#23,#24,#27): theme preference wiring, dd MMM yyyy dates, calendar icon in DateField #38

Merged
admin merged 3 commits from fix/23-24-27-ui-polish into main 2026-06-28 14:32:57 +00:00
6 changed files with 50 additions and 15 deletions
@@ -8,9 +8,12 @@ import android.view.ViewTreeObserver
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.core.util.Consumer import androidx.core.util.Consumer
import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.core.screen.Screen
@@ -18,6 +21,9 @@ import cafe.adriel.voyager.core.stack.StackEvent
import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.NavigatorDisposeBehavior import cafe.adriel.voyager.navigator.NavigatorDisposeBehavior
import cafe.adriel.voyager.transitions.ScreenTransition import cafe.adriel.voyager.transitions.ScreenTransition
import dev.achmad.ledgerr.di.util.inject
import dev.achmad.ledgerr.domain.preference.AppPreference
import dev.achmad.ledgerr.domain.preference.AppTheme as AppThemePref
import dev.achmad.ledgerr.ui.screens.home.HomeScreen import dev.achmad.ledgerr.ui.screens.home.HomeScreen
import dev.achmad.ledgerr.ui.theme.AppTheme import dev.achmad.ledgerr.ui.theme.AppTheme
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
@@ -53,7 +59,17 @@ class MainActivity : ComponentActivity() {
enableEdgeToEdge() enableEdgeToEdge()
setContent { setContent {
AppTheme { val systemDark = isSystemInDarkTheme()
val appThemePref by inject<AppPreference>()
.appTheme()
.changes()
.collectAsState(initial = AppThemePref.SYSTEM)
val darkTheme = when (appThemePref) {
AppThemePref.LIGHT -> false
AppThemePref.DARK -> true
AppThemePref.SYSTEM -> systemDark
}
AppTheme(darkTheme = darkTheme) {
val slideDistance = rememberSlideDistance() val slideDistance = rememberSlideDistance()
Navigator( Navigator(
screen = initialScreen, screen = initialScreen,
@@ -1,9 +1,14 @@
package dev.achmad.ledgerr.ui.components package dev.achmad.ledgerr.ui.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.CalendarMonth
import androidx.compose.material3.DatePicker import androidx.compose.material3.DatePicker
import androidx.compose.material3.DatePickerDialog import androidx.compose.material3.DatePickerDialog
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
@@ -16,10 +21,10 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import dev.achmad.ledgerr.R import dev.achmad.ledgerr.R
import dev.achmad.ledgerr.ui.util.dateFormatter
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.time.ZoneId import java.time.ZoneId
import java.time.format.DateTimeFormatter
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@@ -32,14 +37,17 @@ fun DateField(
var showPicker by remember { mutableStateOf(false) } var showPicker by remember { mutableStateOf(false) }
OutlinedTextField( OutlinedTextField(
value = date.format(DateTimeFormatter.ISO_LOCAL_DATE), value = date.format(dateFormatter()),
onValueChange = {}, onValueChange = {},
label = { Text(label) }, label = { Text(label) },
readOnly = true, readOnly = true,
modifier = modifier.fillMaxWidth(), modifier = modifier.fillMaxWidth().clickable { showPicker = true },
trailingIcon = { trailingIcon = {
TextButton(onClick = { showPicker = true }) { IconButton(onClick = { showPicker = true }) {
Text(text = stringResource(R.string.action_pick)) Icon(
imageVector = Icons.Outlined.CalendarMonth,
contentDescription = stringResource(R.string.action_pick),
)
} }
}, },
) )
@@ -3,10 +3,12 @@ package dev.achmad.ledgerr.ui.components
import android.net.Uri import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.CalendarMonth
import androidx.compose.material.icons.outlined.IosShare import androidx.compose.material.icons.outlined.IosShare
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.DatePicker import androidx.compose.material3.DatePicker
@@ -30,6 +32,7 @@ import androidx.compose.ui.unit.dp
import dev.achmad.ledgerr.R import dev.achmad.ledgerr.R
import dev.achmad.ledgerr.domain.expense.model.DateRange import dev.achmad.ledgerr.domain.expense.model.DateRange
import dev.achmad.ledgerr.ui.components.preference.widget.TextPreferenceWidget import dev.achmad.ledgerr.ui.components.preference.widget.TextPreferenceWidget
import dev.achmad.ledgerr.ui.util.dateFormatter
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.time.ZoneId import java.time.ZoneId
@@ -170,16 +173,20 @@ private fun DateField(
var showPicker by remember { mutableStateOf(false) } var showPicker by remember { mutableStateOf(false) }
OutlinedTextField( OutlinedTextField(
value = date.format(DateTimeFormatter.ISO_LOCAL_DATE), value = date.format(dateFormatter()),
onValueChange = {}, onValueChange = {},
label = { Text(label) }, label = { Text(label) },
readOnly = true, readOnly = true,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = 4.dp), .padding(vertical = 4.dp)
.clickable { showPicker = true },
trailingIcon = { trailingIcon = {
TextButton(onClick = { showPicker = true }) { IconButton(onClick = { showPicker = true }) {
Text(text = stringResource(R.string.action_pick)) Icon(
imageVector = Icons.Outlined.CalendarMonth,
contentDescription = stringResource(R.string.action_pick),
)
} }
}, },
) )
@@ -70,8 +70,8 @@ import dev.achmad.ledgerr.ui.components.SingleSelectFilterChipGroup
import dev.achmad.ledgerr.ui.screens.add_edit_expense.AddEditExpenseScreen import dev.achmad.ledgerr.ui.screens.add_edit_expense.AddEditExpenseScreen
import dev.achmad.ledgerr.ui.screens.add_edit_recurring.AddEditRecurringScreen import dev.achmad.ledgerr.ui.screens.add_edit_recurring.AddEditRecurringScreen
import dev.achmad.ledgerr.ui.screens.import_bank_statement.ImportBankStatementScreen import dev.achmad.ledgerr.ui.screens.import_bank_statement.ImportBankStatementScreen
import dev.achmad.ledgerr.ui.util.dateFormatter
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.time.format.DateTimeFormatter
object ExpenseListScreen : Screen { object ExpenseListScreen : Screen {
@@ -365,7 +365,7 @@ private fun ExpenseRow(
) )
} }
Text( Text(
text = item.expense.date.format(DateTimeFormatter.ISO_LOCAL_DATE), text = item.expense.date.format(dateFormatter()),
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
) )
@@ -412,7 +412,7 @@ private fun RecurringRow(
RecurringInterval.YEARLY -> stringResource(R.string.add_edit_recurring_interval_yearly) RecurringInterval.YEARLY -> stringResource(R.string.add_edit_recurring_interval_yearly)
} }
Text( Text(
text = "$intervalText · ${item.recurring.nextDueDate.format(DateTimeFormatter.ISO_LOCAL_DATE)}", text = "$intervalText · ${item.recurring.nextDueDate.format(dateFormatter())}",
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
) )
@@ -77,8 +77,8 @@ import dev.achmad.ledgerr.ui.screens.add_edit_expense.AddEditExpenseScreen
import dev.achmad.ledgerr.ui.screens.expenses.ExpenseListScreen import dev.achmad.ledgerr.ui.screens.expenses.ExpenseListScreen
import dev.achmad.ledgerr.ui.screens.import_bank_statement.ImportBankStatementScreen import dev.achmad.ledgerr.ui.screens.import_bank_statement.ImportBankStatementScreen
import dev.achmad.ledgerr.ui.screens.settings.SettingsScreen import dev.achmad.ledgerr.ui.screens.settings.SettingsScreen
import dev.achmad.ledgerr.ui.util.dateFormatter
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.time.format.DateTimeFormatter
object HomeScreen : Screen { object HomeScreen : Screen {
@@ -443,7 +443,7 @@ private fun ExpenseRow(item: ExpenseWithCategory) {
) )
} }
Text( Text(
text = item.expense.date.format(DateTimeFormatter.ISO_LOCAL_DATE), text = item.expense.date.format(dateFormatter()),
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
) )
@@ -1,6 +1,7 @@
package dev.achmad.ledgerr.ui.util package dev.achmad.ledgerr.ui.util
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.util.Locale
fun timeFormatter(is24Hour: Boolean): DateTimeFormatter { fun timeFormatter(is24Hour: Boolean): DateTimeFormatter {
return if (is24Hour) { return if (is24Hour) {
@@ -10,6 +11,9 @@ fun timeFormatter(is24Hour: Boolean): DateTimeFormatter {
} }
} }
fun dateFormatter(): DateTimeFormatter =
DateTimeFormatter.ofPattern("dd MMM yyyy", Locale.getDefault())
fun String.toTitleCase(): String { fun String.toTitleCase(): String {
return this.lowercase().split(" ").joinToString(" ") { word -> return this.lowercase().split(" ").joinToString(" ") { word ->
word.replaceFirstChar { it.uppercase() } word.replaceFirstChar { it.uppercase() }