Merge pull request 'fix(#23,#24,#27): theme preference wiring, dd MMM yyyy dates, calendar icon in DateField' (#38) from fix/23-24-27-ui-polish into main

Reviewed-on: #38
This commit was merged in pull request #38.
This commit is contained in:
2026-06-28 14:32:56 +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.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.core.util.Consumer
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.NavigatorDisposeBehavior
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.theme.AppTheme
import kotlinx.coroutines.channels.awaitClose
@@ -53,7 +59,17 @@ class MainActivity : ComponentActivity() {
enableEdgeToEdge()
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()
Navigator(
screen = initialScreen,
@@ -1,9 +1,14 @@
package dev.achmad.ledgerr.ui.components
import androidx.compose.foundation.clickable
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.DatePickerDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
@@ -16,10 +21,10 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import dev.achmad.ledgerr.R
import dev.achmad.ledgerr.ui.util.dateFormatter
import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId
import java.time.format.DateTimeFormatter
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -32,14 +37,17 @@ fun DateField(
var showPicker by remember { mutableStateOf(false) }
OutlinedTextField(
value = date.format(DateTimeFormatter.ISO_LOCAL_DATE),
value = date.format(dateFormatter()),
onValueChange = {},
label = { Text(label) },
readOnly = true,
modifier = modifier.fillMaxWidth(),
modifier = modifier.fillMaxWidth().clickable { showPicker = true },
trailingIcon = {
TextButton(onClick = { showPicker = true }) {
Text(text = stringResource(R.string.action_pick))
IconButton(onClick = { showPicker = true }) {
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 androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.CalendarMonth
import androidx.compose.material.icons.outlined.IosShare
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.DatePicker
@@ -30,6 +32,7 @@ import androidx.compose.ui.unit.dp
import dev.achmad.ledgerr.R
import dev.achmad.ledgerr.domain.expense.model.DateRange
import dev.achmad.ledgerr.ui.components.preference.widget.TextPreferenceWidget
import dev.achmad.ledgerr.ui.util.dateFormatter
import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId
@@ -170,16 +173,20 @@ private fun DateField(
var showPicker by remember { mutableStateOf(false) }
OutlinedTextField(
value = date.format(DateTimeFormatter.ISO_LOCAL_DATE),
value = date.format(dateFormatter()),
onValueChange = {},
label = { Text(label) },
readOnly = true,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
.padding(vertical = 4.dp)
.clickable { showPicker = true },
trailingIcon = {
TextButton(onClick = { showPicker = true }) {
Text(text = stringResource(R.string.action_pick))
IconButton(onClick = { showPicker = true }) {
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_recurring.AddEditRecurringScreen
import dev.achmad.ledgerr.ui.screens.import_bank_statement.ImportBankStatementScreen
import dev.achmad.ledgerr.ui.util.dateFormatter
import kotlinx.coroutines.launch
import java.time.format.DateTimeFormatter
object ExpenseListScreen : Screen {
@@ -365,7 +365,7 @@ private fun ExpenseRow(
)
}
Text(
text = item.expense.date.format(DateTimeFormatter.ISO_LOCAL_DATE),
text = item.expense.date.format(dateFormatter()),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
@@ -412,7 +412,7 @@ private fun RecurringRow(
RecurringInterval.YEARLY -> stringResource(R.string.add_edit_recurring_interval_yearly)
}
Text(
text = "$intervalText · ${item.recurring.nextDueDate.format(DateTimeFormatter.ISO_LOCAL_DATE)}",
text = "$intervalText · ${item.recurring.nextDueDate.format(dateFormatter())}",
style = MaterialTheme.typography.bodySmall,
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.import_bank_statement.ImportBankStatementScreen
import dev.achmad.ledgerr.ui.screens.settings.SettingsScreen
import dev.achmad.ledgerr.ui.util.dateFormatter
import kotlinx.coroutines.launch
import java.time.format.DateTimeFormatter
object HomeScreen : Screen {
@@ -443,7 +443,7 @@ private fun ExpenseRow(item: ExpenseWithCategory) {
)
}
Text(
text = item.expense.date.format(DateTimeFormatter.ISO_LOCAL_DATE),
text = item.expense.date.format(dateFormatter()),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
@@ -1,6 +1,7 @@
package dev.achmad.ledgerr.ui.util
import java.time.format.DateTimeFormatter
import java.util.Locale
fun timeFormatter(is24Hour: Boolean): DateTimeFormatter {
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 {
return this.lowercase().split(" ").joinToString(" ") { word ->
word.replaceFirstChar { it.uppercase() }