Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified image/roborazzi/all_transactions_screen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified image/roborazzi/component_error_view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified image/roborazzi/component_transaction_card.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified image/roborazzi/home_screen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified image/roborazzi/money_flow_nav_host.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package com.prof18.moneyflow

import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
import com.prof18.moneyflow.database.model.TransactionType
Expand All @@ -11,6 +13,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
import kotlin.time.Clock

@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
Expand All @@ -35,11 +38,9 @@ class AddTransactionRoborazziTest : RoborazziTestBase() {
updateDescriptionText = {},
selectedTransactionType = TransactionType.OUTCOME,
updateTransactionType = {},
updateYear = {},
updateMonth = {},
updateDay = {},
saveDate = {},
updateSelectedDate = {},
dateLabel = "11 July 2021",
selectedDateMillis = Clock.System.now().toEpochMilliseconds(),
addTransactionAction = null,
resetAction = {},
currencyConfig = RoborazziSampleData.sampleCurrencyConfig,
Expand All @@ -49,4 +50,36 @@ class AddTransactionRoborazziTest : RoborazziTestBase() {

capture("add_transaction_screen")
}

@Test
fun captureAddTransactionDatePicker() {
val dateLabel = "11 July 2021"

composeRule.setContent {
MoneyFlowTheme {
AddTransactionScreen(
categoryState = remember { mutableStateOf(RoborazziSampleData.sampleCategory) },
navigateUp = {},
navigateToCategoryList = {},
addTransaction = {},
amountText = "10.00",
updateAmountText = {},
descriptionText = "Pizza 🍕",
updateDescriptionText = {},
selectedTransactionType = TransactionType.OUTCOME,
updateTransactionType = {},
updateSelectedDate = {},
dateLabel = dateLabel,
selectedDateMillis = Clock.System.now().toEpochMilliseconds(),
addTransactionAction = null,
resetAction = {},
currencyConfig = RoborazziSampleData.sampleCurrencyConfig,
)
}
}

composeRule.onNodeWithText(dateLabel).performClick()

capture("add_transaction_screen_date_picker")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone
import kotlinx.datetime.atStartOfDayIn
import kotlinx.datetime.toLocalDateTime
import money_flow.shared.generated.resources.Res
import money_flow.shared.generated.resources.amount_not_empty_error
import kotlin.time.Clock
Expand All @@ -32,26 +28,22 @@ internal class AddTransactionViewModel(
private val errorMapper: MoneyFlowErrorMapper,
) : ViewModel() {

private val initialSelectedDateMillis: Long = Clock.System.now().toEpochMilliseconds()

private val _uiState = MutableStateFlow(
AddTransactionUiState(
selectedTransactionType = TransactionType.INCOME,
amountText = "",
descriptionText = null,
dateLabel = null,
dateLabel = initialSelectedDateMillis.formatDateDayMonthYear(),
addTransactionAction = null,
currencyConfig = null,
selectedDateMillis = initialSelectedDateMillis,
),
)
val uiState: StateFlow<AddTransactionUiState> = _uiState

// Private variables
private var selectedDateMillis: Long = Clock.System.now().toEpochMilliseconds()
private var yearNumber: Int = currentLocalDate().year
private var monthNumber: Int = currentLocalDate().month.ordinal
private var dayNumber: Int = currentLocalDate().day

init {
updateDateLabel()
observeCurrencyConfig()
}

Expand All @@ -65,27 +57,12 @@ internal class AddTransactionViewModel(
}
}

fun setYearNumber(yearNumber: Int) {
this.yearNumber = yearNumber
}

fun setMonthNumber(monthNumber: Int) {
this.monthNumber = monthNumber - 1
}

fun setDayNumber(dayNumber: Int) {
this.dayNumber = dayNumber
}

fun saveDate() {
val localDate = LocalDate(yearNumber, monthNumber + 1, dayNumber)
selectedDateMillis = localDate.atStartOfDayIn(TimeZone.currentSystemDefault()).toEpochMilliseconds()
updateDateLabel()
}

private fun updateDateLabel() {
fun updateSelectedDate(selectedDateMillis: Long) {
_uiState.update { state ->
state.copy(dateLabel = selectedDateMillis.formatDateDayMonthYear())
state.copy(
dateLabel = selectedDateMillis.formatDateDayMonthYear(),
selectedDateMillis = selectedDateMillis,
)
}
}

Expand All @@ -106,7 +83,7 @@ internal class AddTransactionViewModel(
val result = try {
moneyRepository.insertTransaction(
TransactionToSave(
dateMillis = selectedDateMillis,
dateMillis = uiState.value.selectedDateMillis,
amountCents = amountCents,
description = uiState.value.descriptionText,
categoryId = categoryId,
Expand Down Expand Up @@ -152,9 +129,6 @@ internal class AddTransactionViewModel(
state.copy(selectedTransactionType = transactionType)
}
}

private fun currentLocalDate(): LocalDate =
Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date
}

internal data class AddTransactionUiState(
Expand All @@ -164,4 +138,5 @@ internal data class AddTransactionUiState(
val dateLabel: String?,
val addTransactionAction: AddTransactionAction?,
val currencyConfig: CurrencyConfig?,
val selectedDateMillis: Long,
)
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,9 @@ private fun EntryProviderScope<AppRoute>.screens(
updateDescriptionText = viewModel::updateDescriptionText,
selectedTransactionType = uiState.selectedTransactionType,
updateTransactionType = viewModel::updateTransactionType,
updateYear = viewModel::setYearNumber,
updateMonth = viewModel::setMonthNumber,
updateDay = viewModel::setDayNumber,
saveDate = viewModel::saveDate,
updateSelectedDate = viewModel::updateSelectedDate,
dateLabel = uiState.dateLabel,
selectedDateMillis = uiState.selectedDateMillis,
addTransactionAction = uiState.addTransactionAction,
resetAction = viewModel::resetAction,
currencyConfig = uiState.currencyConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@ package com.prof18.moneyflow.presentation.addtransaction
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.DatePicker
import androidx.compose.material3.DatePickerDefaults
import androidx.compose.material3.DatePickerDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberDatePickerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
Expand All @@ -20,7 +27,6 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import com.prof18.moneyflow.database.model.TransactionType
import com.prof18.moneyflow.domain.entities.CurrencyConfig
import com.prof18.moneyflow.presentation.addtransaction.components.DatePickerDialog
import com.prof18.moneyflow.presentation.addtransaction.components.IconTextClickableRow
import com.prof18.moneyflow.presentation.addtransaction.components.MFTextInput
import com.prof18.moneyflow.presentation.addtransaction.components.TransactionTypeTabBar
Expand All @@ -32,6 +38,8 @@ import com.prof18.moneyflow.ui.style.Margins
import com.prof18.moneyflow.ui.style.MoneyFlowTheme
import money_flow.shared.generated.resources.Res
import money_flow.shared.generated.resources.add_transaction_screen
import money_flow.shared.generated.resources.cancel
import money_flow.shared.generated.resources.confirm
import money_flow.shared.generated.resources.description
import money_flow.shared.generated.resources.ic_calendar
import money_flow.shared.generated.resources.ic_edit
Expand All @@ -42,9 +50,11 @@ import money_flow.shared.generated.resources.select_category
import money_flow.shared.generated.resources.today
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
import kotlin.time.Clock

@Composable
@Suppress("LongMethod", "LongParameterList") // TODO: reduce method length
@OptIn(ExperimentalMaterial3Api::class)
internal fun AddTransactionScreen(
categoryState: State<CategoryUIData?>,
navigateUp: () -> Unit,
Expand All @@ -56,16 +66,19 @@ internal fun AddTransactionScreen(
updateDescriptionText: (String?) -> Unit,
selectedTransactionType: TransactionType,
updateTransactionType: (TransactionType) -> Unit,
updateYear: (Int) -> Unit,
updateMonth: (Int) -> Unit,
updateDay: (Int) -> Unit,
saveDate: () -> Unit,
updateSelectedDate: (Long) -> Unit,
dateLabel: String?,
selectedDateMillis: Long,
addTransactionAction: AddTransactionAction?,
resetAction: () -> Unit,
currencyConfig: CurrencyConfig?,
) {
val (showDatePickerDialog, setShowedDatePickerDialog) = remember { mutableStateOf(false) }
val datePickerState = rememberDatePickerState(initialSelectedDateMillis = selectedDateMillis)

LaunchedEffect(selectedDateMillis) {
datePickerState.selectedDateMillis = selectedDateMillis
}

val snackbarHostState = remember { SnackbarHostState() }
addTransactionAction?.let {
Expand Down Expand Up @@ -111,14 +124,37 @@ internal fun AddTransactionScreen(
},
content = { innerPadding ->
Column(modifier = Modifier.padding(innerPadding)) {
DatePickerDialog(
showDatePickerDialog,
setShowedDatePickerDialog,
onYearSelected = { updateYear(it) },
onMonthSelected = { updateMonth(it) },
onDaySelected = { updateDay(it) },
onSave = { saveDate() },
)
if (showDatePickerDialog) {
DatePickerDialog(
onDismissRequest = { setShowedDatePickerDialog(false) },
confirmButton = {
TextButton(
enabled = datePickerState.selectedDateMillis != null,
onClick = {
datePickerState.selectedDateMillis?.let { selectedDate ->
updateSelectedDate(selectedDate)
}
setShowedDatePickerDialog(false)
},
) {
Text(text = stringResource(Res.string.confirm))
}
},
dismissButton = {
TextButton(onClick = { setShowedDatePickerDialog(false) }) {
Text(text = stringResource(Res.string.cancel))
}
},
) {
DatePicker(
state = datePickerState,
showModeToggle = false,
colors = DatePickerDefaults.colors(
containerColor = MaterialTheme.colorScheme.surface,
),
)
}
}

TransactionTypeTabBar(
transactionType = selectedTransactionType,
Expand Down Expand Up @@ -225,11 +261,9 @@ private fun AddTransactionScreenPreview() {
updateDescriptionText = {},
selectedTransactionType = TransactionType.OUTCOME,
updateTransactionType = {},
updateYear = {},
updateMonth = {},
updateDay = {},
saveDate = {},
updateSelectedDate = {},
dateLabel = "11 July 2021",
selectedDateMillis = Clock.System.now().toEpochMilliseconds(),
addTransactionAction = null,
resetAction = {},
currencyConfig = CurrencyConfig(
Expand Down
Loading