Coverage Summary for Class: TransactionCreationDateFieldsKt (com.georgeci.moneysurfer.feature.transaction.creation)
| Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| TransactionCreationDateFieldsKt |
28.6%
(2/7)
|
30.8%
(8/26)
|
63.5%
(40/63)
|
58.4%
(442/757)
|
| TransactionCreationDateFieldsKt$TransactionDatePickerDialog$notFuture$1$1 |
0%
(0/3)
|
0%
(0/4)
|
0%
(0/3)
|
0%
(0/17)
|
| Total |
20%
(2/10)
|
26.7%
(8/30)
|
60.6%
(40/66)
|
57.1%
(442/774)
|
package com.georgeci.moneysurfer.feature.transaction.creation
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.DatePicker
import androidx.compose.material3.DatePickerDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.SelectableDates
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberDatePickerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import com.georgeci.moneysurfer.uikit.icons.SurferIcons
import com.georgeci.moneysurfer.uikit.semantics.SurferSemantics
import com.georgeci.moneysurfer.uikit.theme.AppTheme
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import moneysurfer.feature.transaction.generated.resources.Res
import moneysurfer.feature.transaction.generated.resources.transaction_creation_close_content_description
import moneysurfer.feature.transaction.generated.resources.transaction_creation_datetime_label
import moneysurfer.feature.transaction.generated.resources.transaction_creation_datetime_placeholder
import moneysurfer.feature.transaction.generated.resources.transaction_creation_save
import org.jetbrains.compose.resources.stringResource
import kotlin.time.Clock
import kotlin.time.Instant
@Composable
internal fun DateTimeField(
timestamp: Long?,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val fieldShape = AppTheme.shapes.extraSmall
Row(
modifier = modifier
.heightIn(min = 56.dp)
.clip(fieldShape)
.border(1.dp, AppTheme.materialColors.outline, fieldShape)
.clickable(onClick = onClick)
.padding(horizontal = 16.dp, vertical = 14.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp),
) {
Icon(
imageVector = SurferIcons.Event,
contentDescription = SurferSemantics.Decorative,
tint = AppTheme.materialColors.onSurfaceVariant,
modifier = Modifier.size(20.dp),
)
Column(modifier = Modifier.weight(1f)) {
Text(
text = stringResource(Res.string.transaction_creation_datetime_label),
style = AppTheme.typography.bodySmall,
color = AppTheme.materialColors.onSurfaceVariant,
)
Text(
text = timestamp?.let(::formatShortDate)
?: stringResource(Res.string.transaction_creation_datetime_placeholder),
style = AppTheme.typography.bodyLarge,
color = AppTheme.materialColors.onSurface,
)
}
Icon(
imageVector = SurferIcons.DropDown,
contentDescription = SurferSemantics.Decorative,
tint = AppTheme.materialColors.onSurfaceVariant,
modifier = Modifier.size(18.dp),
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun TransactionDatePickerDialog(
initialTimestamp: Long?,
onDismiss: () -> Unit,
onConfirm: (Long) -> Unit,
) {
val now = remember { Clock.System.now().toEpochMilliseconds() }
val nowYear = remember(now) {
Instant.fromEpochMilliseconds(now)
.toLocalDateTime(TimeZone.currentSystemDefault())
.year
}
val notFuture = remember(now, nowYear) {
object : SelectableDates {
override fun isSelectableDate(utcTimeMillis: Long): Boolean = utcTimeMillis <= now
override fun isSelectableYear(year: Int): Boolean = year <= nowYear
}
}
val state = rememberDatePickerState(
initialSelectedDateMillis = initialTimestamp,
selectableDates = notFuture,
)
DatePickerDialog(
onDismissRequest = onDismiss,
confirmButton = {
TextButton(
onClick = { state.selectedDateMillis?.let(onConfirm) },
enabled = state.selectedDateMillis != null,
) { Text(stringResource(Res.string.transaction_creation_save)) }
},
dismissButton = {
TextButton(onClick = onDismiss) {
Text(stringResource(Res.string.transaction_creation_close_content_description))
}
},
) {
DatePicker(state = state)
}
}
private const val MONTH_ABBREVIATION_LENGTH = 3
private fun formatShortDate(timestamp: Long): String {
val instant = Instant.fromEpochMilliseconds(timestamp)
val ld = instant.toLocalDateTime(TimeZone.currentSystemDefault()).date
val month = ld.month.name.lowercase().replaceFirstChar { it.uppercase() }.take(MONTH_ABBREVIATION_LENGTH)
val day = ld.day.toString().padStart(2, '0')
return "$day $month ${ld.year}"
}