Skip to content
This repository was archived by the owner on Nov 5, 2024. It is now read-only.

refs #2620: Migrate screen-balance to ComposeViewModel #2801

Merged
merged 2 commits into from
Oct 22, 2023
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.ivy.balance

import com.ivy.legacy.data.model.TimePeriod

sealed interface BalanceEvent {
data class OnSetPeriod(val timePeriod: TimePeriod) : BalanceEvent
data object OnPreviousMonth : BalanceEvent
data object OnNextMonth : BalanceEvent
}
77 changes: 25 additions & 52 deletions screen-balance/src/main/java/com/ivy/balance/BalanceScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
Expand All @@ -35,7 +34,6 @@ import com.ivy.design.l0_system.style
import com.ivy.legacy.IvyWalletPreview
import com.ivy.legacy.data.model.TimePeriod
import com.ivy.legacy.utils.format
import com.ivy.legacy.utils.onScreenStart
import com.ivy.navigation.BalanceScreen
import com.ivy.navigation.navigation
import com.ivy.resources.R
Expand All @@ -52,46 +50,21 @@ import com.ivy.wallet.ui.theme.wallet.PeriodSelector

val FAB_BUTTON_SIZE = 56.dp


@Composable
fun BoxWithConstraintsScope.BalanceScreen(screen: BalanceScreen) {
val viewModel: BalanceViewModel = viewModel()

val period by viewModel.period.collectAsState()
val baseCurrencyCode by viewModel.baseCurrencyCode.collectAsState()
val currentBalance by viewModel.currentBalance.collectAsState()
val plannedPaymentsAmount by viewModel.plannedPaymentsAmount.collectAsState()
val balanceAfterPlannedPayments by viewModel.balanceAfterPlannedPayments.collectAsState()

onScreenStart {
viewModel.start()
}
val uiState = viewModel.uiState()

UI(
period = period,
baseCurrencyCode = baseCurrencyCode,
currentBalance = currentBalance,
plannedPaymentsAmount = plannedPaymentsAmount,
balanceAfterPlannedPayments = balanceAfterPlannedPayments,

onSetPeriod = viewModel::setPeriod,
onPreviousMonth = viewModel::previousMonth,
onNextMonth = viewModel::nextMonth
state = uiState,
onEvent = viewModel::onEvent
)
}

@Composable
private fun BoxWithConstraintsScope.UI(
period: TimePeriod,

baseCurrencyCode: String,
currentBalance: Double,
plannedPaymentsAmount: Double,
balanceAfterPlannedPayments: Double,

onSetPeriod: (TimePeriod) -> Unit = {},
onPreviousMonth: () -> Unit = {},
onNextMonth: () -> Unit = {}
state: BalanceState,
onEvent: (BalanceEvent) -> Unit = {}
) {
var choosePeriodModal: ChoosePeriodModalData? by remember { mutableStateOf(null) }

Expand All @@ -104,21 +77,21 @@ private fun BoxWithConstraintsScope.UI(
Spacer(Modifier.height(20.dp))

PeriodSelector(
period = period,
onPreviousMonth = onPreviousMonth,
onNextMonth = onNextMonth,
period = state.period,
onPreviousMonth = { onEvent(BalanceEvent.OnPreviousMonth) },
onNextMonth = { onEvent(BalanceEvent.OnNextMonth) },
onShowChoosePeriodModal = {
choosePeriodModal = ChoosePeriodModalData(
period = period
period = state.period
)
}
)

Spacer(Modifier.height(32.dp))

CurrentBalance(
currency = baseCurrencyCode,
currentBalance = currentBalance
currency = state.baseCurrencyCode,
currentBalance = state.currentBalance
)

Spacer(Modifier.height(32.dp))
Expand All @@ -131,10 +104,10 @@ private fun BoxWithConstraintsScope.UI(
Spacer(Modifier.height(40.dp))

BalanceAfterPlannedPayments(
currency = baseCurrencyCode,
currentBalance = currentBalance,
plannedPaymentsAmount = plannedPaymentsAmount,
balanceAfterPlannedPayments = balanceAfterPlannedPayments
currency = state.baseCurrencyCode,
currentBalance = state.currentBalance,
plannedPaymentsAmount = state.plannedPaymentsAmount,
balanceAfterPlannedPayments = state.balanceAfterPlannedPayments
)

Spacer(Modifier.weight(1f))
Expand All @@ -150,7 +123,7 @@ private fun BoxWithConstraintsScope.UI(
choosePeriodModal = null
}
) {
onSetPeriod(it)
onEvent(BalanceEvent.OnSetPeriod(it))
}
}

Expand Down Expand Up @@ -269,15 +242,15 @@ private fun ColumnScope.CloseButton() {
private fun Preview() {
IvyWalletPreview {
UI(
period = TimePeriod.currentMonth(
startDayOfMonth = 1
), // preview
baseCurrencyCode = "BGN",
currentBalance = 9326.55,
balanceAfterPlannedPayments = 8426.0,
plannedPaymentsAmount = -900.55,

onSetPeriod = {}
state = BalanceState(
period = TimePeriod.currentMonth(
startDayOfMonth = 1
),
baseCurrencyCode = "BGN",
currentBalance = 9326.55,
balanceAfterPlannedPayments = 8426.0,
plannedPaymentsAmount = -900.55,
)
)
}
}
13 changes: 13 additions & 0 deletions screen-balance/src/main/java/com/ivy/balance/BalanceState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.ivy.balance

import androidx.compose.runtime.Immutable
import com.ivy.legacy.data.model.TimePeriod

@Immutable
data class BalanceState(
val period: TimePeriod,
val baseCurrencyCode: String,
val currentBalance: Double,
val plannedPaymentsAmount: Double,
val balanceAfterPlannedPayments: Double
)
83 changes: 46 additions & 37 deletions screen-balance/src/main/java/com/ivy/balance/BalanceViewModel.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package com.ivy.balance

import androidx.lifecycle.ViewModel
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableDoubleStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.viewModelScope
import com.ivy.legacy.IvyWalletCtx
import com.ivy.domain.ComposeViewModel
import com.ivy.legacy.data.model.TimePeriod
import com.ivy.wallet.domain.action.settings.BaseCurrencyAct
import com.ivy.wallet.domain.action.wallet.CalcWalletBalanceAct
import com.ivy.wallet.domain.deprecated.logic.PlannedPaymentsLogic
import com.ivy.legacy.utils.dateNowUTC
import com.ivy.legacy.utils.ioThread
import com.ivy.legacy.utils.readOnly
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

Expand All @@ -21,66 +21,75 @@ class BalanceViewModel @Inject constructor(
private val ivyContext: com.ivy.legacy.IvyWalletCtx,
private val baseCurrencyAct: BaseCurrencyAct,
private val calcWalletBalanceAct: CalcWalletBalanceAct
) : ViewModel() {

private val _period = MutableStateFlow(ivyContext.selectedPeriod)
val period = _period.readOnly()

private val _baseCurrencyCode = MutableStateFlow("")
val baseCurrencyCode = _baseCurrencyCode.readOnly()

private val _currentBalance = MutableStateFlow(0.0)
val currentBalance = _currentBalance.readOnly()
) : ComposeViewModel<BalanceState, BalanceEvent>() {

private val period = mutableStateOf(ivyContext.selectedPeriod)
private val baseCurrencyCode = mutableStateOf("")
private val currentBalance = mutableDoubleStateOf(0.0)
private val plannedPaymentsAmount = mutableDoubleStateOf(0.0)
private val balanceAfterPlannedPayments = mutableDoubleStateOf(0.0)

@Composable
override fun uiState(): BalanceState {
LaunchedEffect(Unit) {
start()
}

private val _plannedPaymentsAmount = MutableStateFlow(0.0)
val plannedPaymentsAmount = _plannedPaymentsAmount.readOnly()
return BalanceState(
period = period.value,
balanceAfterPlannedPayments = balanceAfterPlannedPayments.doubleValue,
currentBalance = currentBalance.doubleValue,
baseCurrencyCode = baseCurrencyCode.value,
plannedPaymentsAmount = plannedPaymentsAmount.doubleValue
)
}

private val _balanceAfterPlannedPayments = MutableStateFlow(0.0)
val balanceAfterPlannedPayments = _balanceAfterPlannedPayments.readOnly()
override fun onEvent(event: BalanceEvent) {
when (event) {
is BalanceEvent.OnNextMonth -> nextMonth()
is BalanceEvent.OnSetPeriod -> setPeriod(event.timePeriod)
is BalanceEvent.OnPreviousMonth -> previousMonth()
}
}

fun start(period: com.ivy.legacy.data.model.TimePeriod = ivyContext.selectedPeriod) {
private fun start(timePeriod: TimePeriod = ivyContext.selectedPeriod) {
viewModelScope.launch {
_baseCurrencyCode.value = baseCurrencyAct(Unit)

_period.value = period
baseCurrencyCode.value = baseCurrencyAct(Unit)
period.value = timePeriod

val currentBalance = calcWalletBalanceAct(
currentBalance.doubleValue = calcWalletBalanceAct(
CalcWalletBalanceAct.Input(baseCurrencyCode.value)
).toDouble()

_currentBalance.value = currentBalance

val plannedPaymentsAmount = com.ivy.legacy.utils.ioThread {
plannedPaymentsAmount.doubleValue = ioThread {
plannedPaymentsLogic.plannedPaymentsAmountFor(
period.toRange(ivyContext.startDayOfMonth)
timePeriod.toRange(ivyContext.startDayOfMonth)
) // + positive if Income > Expenses else - negative
}
_plannedPaymentsAmount.value = plannedPaymentsAmount

_balanceAfterPlannedPayments.value = currentBalance + plannedPaymentsAmount
balanceAfterPlannedPayments.doubleValue = currentBalance.doubleValue + plannedPaymentsAmount.doubleValue
}
}

fun setPeriod(period: com.ivy.legacy.data.model.TimePeriod) {
start(period = period)
private fun setPeriod(timePeriod: com.ivy.legacy.data.model.TimePeriod) {
start(timePeriod = timePeriod)
}

fun nextMonth() {
private fun nextMonth() {
val month = period.value.month
val year = period.value.year ?: com.ivy.legacy.utils.dateNowUTC().year
if (month != null) {
start(
period = month.incrementMonthPeriod(ivyContext, 1L, year = year),
timePeriod = month.incrementMonthPeriod(ivyContext, 1L, year = year),
)
}
}

fun previousMonth() {
private fun previousMonth() {
val month = period.value.month
val year = period.value.year ?: com.ivy.legacy.utils.dateNowUTC().year
if (month != null) {
start(
period = month.incrementMonthPeriod(ivyContext, -1L, year = year),
timePeriod = month.incrementMonthPeriod(ivyContext, -1L, year = year),
)
}
}
Expand Down