Updated on 2026-08-14

This commit is contained in:
Tangem 2023-10-25 19:44:25 +03:00
parent cd28b90873
commit d6370a1f59
15 changed files with 592 additions and 24 deletions

View file

@ -366,7 +366,10 @@ class TradeCryptoMiddleware {
)
}
}
val bundle = bundleOf(SendRouter.CRYPTO_CURRENCY_KEY to currency)
val bundle = bundleOf(
SendRouter.CRYPTO_CURRENCY_KEY to currency,
SendRouter.USER_WALLET_ID_KEY to action.userWallet.walletId.stringValue,
)
store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Send, bundle = bundle))
}
}
@ -416,7 +419,10 @@ class TradeCryptoMiddleware {
is CryptoCurrency.Token -> error("Action.tokenStatus.currency is Token")
}
val bundle = bundleOf(SendRouter.CRYPTO_CURRENCY_KEY to currency)
val bundle = bundleOf(
SendRouter.CRYPTO_CURRENCY_KEY to currency,
SendRouter.USER_WALLET_ID_KEY to action.userWallet.walletId.stringValue,
)
store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Send, bundle = bundle))
}
}

View file

@ -12,7 +12,9 @@ object BigDecimalFormatter {
private const val TEMP_CURRENCY_CODE = "USD"
fun formatCryptoAmount(cryptoAmount: BigDecimal, cryptoCurrency: String, decimals: Int): String {
fun formatCryptoAmount(cryptoAmount: BigDecimal?, cryptoCurrency: String, decimals: Int): String {
if (cryptoAmount == null) return EMPTY_BALANCE_SIGN
val formatter = NumberFormat.getNumberInstance().apply {
maximumFractionDigits = decimals.coerceAtMost(maximumValue = 8)
minimumFractionDigits = 2
@ -23,11 +25,13 @@ object BigDecimalFormatter {
}
fun formatFiatAmount(
fiatAmount: BigDecimal,
fiatAmount: BigDecimal?,
fiatCurrencyCode: String,
fiatCurrencySymbol: String,
locale: Locale = Locale.getDefault(),
): String {
if (fiatAmount == null) return EMPTY_BALANCE_SIGN
val formatterCurrency = getCurrency(fiatCurrencyCode)
val formatter = NumberFormat.getCurrencyInstance(locale).apply {
currency = formatterCurrency

View file

@ -8,5 +8,6 @@ interface SendRouter {
companion object {
const val CRYPTO_CURRENCY_KEY = "send_crypto_currency"
const val USER_WALLET_ID_KEY = "send_user_wallet_id"
}
}

View file

@ -19,6 +19,7 @@ dependencies {
implementation(deps.kotlin.immutable.collections)
implementation(deps.material)
implementation(deps.arrow.core)
implementation(deps.tangem.card.core)
/** Compose */
implementation(deps.compose.accompanist.systemUiController)
@ -38,6 +39,7 @@ dependencies {
implementation(projects.core.utils)
/** Domain modules */
implementation(projects.domain.models)
implementation(projects.domain.tokens)
implementation(projects.domain.tokens.models)
implementation(projects.domain.wallets)

View file

@ -4,10 +4,14 @@ import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.hilt.navigation.compose.hiltViewModel
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.screen.ComposeBottomSheetFragment
import com.tangem.core.ui.theme.AppThemeModeHolder
import com.tangem.features.send.impl.presentation.send.state.SendUiState
import com.tangem.features.send.impl.presentation.send.ui.SendScreen
import com.tangem.features.send.impl.presentation.send.viewmodel.SendViewModel
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@ -24,13 +28,16 @@ internal class SendFragment : ComposeBottomSheetFragment() {
@Composable
override fun ScreenContent(modifier: Modifier) {
SystemBarsEffect {
setSystemBarsColor(color = Color.Transparent)
val viewModel = hiltViewModel<SendViewModel>()
LocalLifecycleOwner.current.lifecycle.addObserver(viewModel)
SystemBarsEffect { setSystemBarsColor(color = Color.Transparent) }
BackHandler { dismiss() }
when (val state = viewModel.uiState) {
is SendUiState.Content -> SendScreen(state)
SendUiState.Dismiss -> dismiss()
}
BackHandler {
dismiss()
}
SendScreen()
}
companion object {

View file

@ -0,0 +1,63 @@
package com.tangem.features.send.impl.presentation.send.state
import arrow.core.Either
import com.tangem.common.Provider
import com.tangem.core.ui.components.currency.tokenicon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.utils.BigDecimalFormatter.formatCryptoAmount
import com.tangem.core.ui.utils.BigDecimalFormatter.formatFiatAmount
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.send.impl.presentation.send.state.fields.SendAmountFieldConverter
import com.tangem.features.send.impl.presentation.send.viewmodel.SendClickIntents
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.persistentListOf
internal class SendAmountStateConverter(
private val currentStateProvider: Provider<SendUiState>,
private val appCurrencyProvider: Provider<AppCurrency>,
private val userWalletProvider: Provider<UserWallet?>,
private val clickIntents: SendClickIntents,
private val iconStateConverter: CryptoCurrencyToIconStateConverter,
private val sendAmountFieldConverter: SendAmountFieldConverter,
) : Converter<Either<CurrencyStatusError, CryptoCurrencyStatus>, SendUiState> {
override fun convert(value: Either<CurrencyStatusError, CryptoCurrencyStatus>): SendUiState {
val userWallet = userWalletProvider() ?: return currentStateProvider()
val appCurrency = appCurrencyProvider()
return value.fold(
ifLeft = {
// TODO add error handling
currentStateProvider()
},
ifRight = {
val fiat = formatFiatAmount(it.value.fiatAmount, appCurrency.code, appCurrency.symbol)
val crypto = formatCryptoAmount(it.value.amount, it.currency.symbol, it.currency.decimals)
SendUiState.Content.AmountState(
cryptoCurrencyStatus = it,
walletName = userWallet.name,
walletBalance = "$crypto ($fiat)",
tokenIconState = iconStateConverter.convert(it),
appCurrency = appCurrency,
amountTextField = sendAmountFieldConverter.convert(Unit),
isFiatValue = false,
clickIntents = clickIntents,
segmentedButtonConfig = persistentListOf(
SendAmountSegmentedButtonsConfig(
title = stringReference(it.currency.symbol),
iconState = iconStateConverter.convert(it),
isFiat = false,
),
SendAmountSegmentedButtonsConfig(
title = stringReference(appCurrency.code),
iconState = iconStateConverter.convert(it),
isFiat = true,
),
),
)
},
)
}
}

View file

@ -0,0 +1,58 @@
package com.tangem.features.send.impl.presentation.send.state
import arrow.core.Either
import com.tangem.common.Provider
import com.tangem.core.ui.components.currency.tokenicon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.send.impl.presentation.send.state.fields.SendAmountFieldChangeConverter
import com.tangem.features.send.impl.presentation.send.state.fields.SendAmountFieldConverter
import com.tangem.features.send.impl.presentation.send.viewmodel.SendClickIntents
internal class SendStateFactory(
private val clickIntents: SendClickIntents,
private val currentStateProvider: Provider<SendUiState>,
private val appCurrencyProvider: Provider<AppCurrency>,
private val userWalletProvider: Provider<UserWallet?>,
) {
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
private val amountFieldConverter by lazy { SendAmountFieldConverter(clickIntents) }
private val amountFieldChangeConverter by lazy { SendAmountFieldChangeConverter(currentStateProvider) }
private val amountStateConverter by lazy {
SendAmountStateConverter(
currentStateProvider = currentStateProvider,
appCurrencyProvider = appCurrencyProvider,
clickIntents = clickIntents,
iconStateConverter = iconStateConverter,
userWalletProvider = userWalletProvider,
sendAmountFieldConverter = amountFieldConverter,
)
}
fun getInitialState(): SendUiState = SendUiState.Content.Initial(clickIntents = clickIntents)
fun getAmountState(cryptoCurrencyStatus: Either<CurrencyStatusError, CryptoCurrencyStatus>): SendUiState {
return amountStateConverter.convert(cryptoCurrencyStatus)
}
fun getOnReceiveState(): SendUiState = SendUiState.Content.Initial(clickIntents = clickIntents)
fun getOnAmountValueChange(value: String) = amountFieldChangeConverter.convert(value)
fun getOnCurrencyChangedState(isFiat: Boolean): SendUiState {
val state = currentStateProvider()
val amountState = state as? SendUiState.Content.AmountState ?: return state
return if (amountState.isFiatValue == isFiat) {
state
} else {
return state.copy(isFiatValue = isFiat)
}
}
}

View file

@ -5,6 +5,7 @@ import com.tangem.core.ui.components.currency.tokenicon.TokenIconState
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.impl.presentation.send.state.fields.SendTextField
import com.tangem.features.send.impl.presentation.send.viewmodel.SendClickIntents
import kotlinx.collections.immutable.PersistentList
/**
@ -16,42 +17,54 @@ internal sealed class SendUiState {
/** States with content */
sealed class Content : SendUiState() {
abstract val nextButtonEnabled: Boolean
/** Is primary button enabled */
abstract val isPrimaryButtonEnabled: Boolean
/** Click intents */
abstract val clickIntents: SendClickIntents
/** Initial state */
data class Initial(
override val nextButtonEnabled: Boolean = false,
override val isPrimaryButtonEnabled: Boolean = false,
override val clickIntents: SendClickIntents,
) : Content()
/** Amount state */
data class AmountState(
override val nextButtonEnabled: Boolean = false,
override val isPrimaryButtonEnabled: Boolean = false,
override val clickIntents: SendClickIntents,
val walletName: String,
val walletBalance: String,
val tokenIconState: TokenIconState,
val cryptoCurrencyStatus: CryptoCurrencyStatus,
val appCurrency: AppCurrency,
val isFiatValue: Boolean,
val segmentedButtonConfig: PersistentList<SendAmountSegmentedButtonConfig>,
val segmentedButtonConfig: PersistentList<SendAmountSegmentedButtonsConfig>,
val amountTextField: SendTextField.Amount,
) : Content()
// todo [REDACTED_JIRA]
/** Recipient state */
data class RecipientState(
override val nextButtonEnabled: Boolean = false,
override val isPrimaryButtonEnabled: Boolean = false,
override val clickIntents: SendClickIntents,
) : Content()
// todo [REDACTED_JIRA]
/** Fee and speed state */
data class FeeState(
override val nextButtonEnabled: Boolean = false,
override val isPrimaryButtonEnabled: Boolean = false,
override val clickIntents: SendClickIntents,
) : Content()
// todo [REDACTED_JIRA]
/** Send state */
data class SendState(
override val nextButtonEnabled: Boolean = true,
override val isPrimaryButtonEnabled: Boolean = true,
override val clickIntents: SendClickIntents,
) : Content()
}
/** Dismiss screen */
object Dismiss : SendUiState()
}

View file

@ -0,0 +1,97 @@
package com.tangem.features.send.impl.presentation.send.state.fields
import com.tangem.common.Provider
import com.tangem.features.send.impl.presentation.send.state.SendUiState
import com.tangem.utils.converter.Converter
import java.text.DecimalFormatSymbols
import java.text.NumberFormat
internal class SendAmountFieldChangeConverter(
private val currentStateProvider: Provider<SendUiState>,
) : Converter<String, SendUiState> {
override fun convert(value: String): SendUiState {
val state = currentStateProvider()
if (
state !is SendUiState.Content.AmountState ||
value.checkDecimalSeparatorDuplicate()
) {
return state
}
if (value.isEmpty()) return state.emptyState()
val fiatRate = state.cryptoCurrencyStatus.value.fiatRate
val trimmedValue = value.trim()
val cryptoValue = if (state.isFiatValue) {
if (value.isNotBlank()) {
trimmedValue.toBigDecimal().divide(fiatRate)?.stripTrailingZeros()?.toPlainString().orEmpty()
} else {
DEFAULT_VALUE
}
} else {
trimmedValue
}
val fiatValue = if (!state.isFiatValue) {
if (value.isNotBlank()) {
trimmedValue.toBigDecimal().multiply(fiatRate)?.stripTrailingZeros()?.toPlainString().orEmpty()
} else {
DEFAULT_VALUE
}
} else {
trimmedValue
}
val isExceedBalance = value.checkExceedBalance(state)
return state.copy(
amountTextField = state.amountTextField.copy(
value = cryptoValue,
fiatValue = fiatValue,
isError = isExceedBalance,
),
isPrimaryButtonEnabled = !isExceedBalance,
)
}
private fun SendUiState.Content.AmountState.emptyState(): SendUiState {
return copy(
amountTextField = amountTextField.copy(
value = if (!isFiatValue) "" else DEFAULT_VALUE,
fiatValue = if (isFiatValue) "" else DEFAULT_VALUE,
isError = false,
),
isPrimaryButtonEnabled = false,
)
}
private fun String.checkDecimalSeparatorDuplicate(): Boolean {
val regex = "[\\.\\,]".toRegex()
val decimalSeparatorCount = regex.findAll(this).count()
return decimalSeparatorCount > 1
}
private fun String.checkExceedBalance(state: SendUiState.Content.AmountState): Boolean {
val currencyStatus = state.cryptoCurrencyStatus.value
return if (state.isFiatValue) {
toBigDecimal() > currencyStatus.fiatAmount
} else {
toBigDecimal() > currencyStatus.amount
}
}
private fun String.trim(): String {
var trimmedValue = this
if (length > 1 && firstOrNull() == '0' && get(1).isDigit()) trimmedValue = drop(1)
val separatorChar = DecimalFormatSymbols.getInstance().decimalSeparator.toString()
return trimmedValue.replace("[\\.\\,]".toRegex(), separatorChar)
}
companion object {
private val DEFAULT_VALUE = NumberFormat.getInstance().format(0.00)
}
}

View file

@ -0,0 +1,35 @@
package com.tangem.features.send.impl.presentation.send.state.fields
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import com.tangem.core.ui.extensions.TextReference
import com.tangem.features.send.impl.R
import com.tangem.features.send.impl.presentation.send.viewmodel.SendClickIntents
import com.tangem.utils.converter.Converter
import java.text.NumberFormat
internal class SendAmountFieldConverter(
private val clickIntents: SendClickIntents,
) : Converter<Unit, SendTextField.Amount> {
override fun convert(value: Unit): SendTextField.Amount {
return SendTextField.Amount(
value = "",
fiatValue = DEFAULT_VALUE,
onValueChange = clickIntents::onAmountValueChange,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Next,
keyboardType = KeyboardType.Number,
),
label = TextReference.Str(""),
placeholder = TextReference.Str(DEFAULT_VALUE),
isError = false,
error = TextReference.Res(R.string.send_insufficient_funds),
)
}
companion object {
private val DEFAULT_VALUE = NumberFormat.getInstance().format(0.00)
}
}

View file

@ -0,0 +1,109 @@
package com.tangem.features.send.impl.presentation.send.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.buttons.common.TangemButtonSize
import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons
import com.tangem.core.ui.components.currency.fiaticon.FiatIcon
import com.tangem.core.ui.components.currency.tokenicon.TokenIcon
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.send.impl.R
import com.tangem.features.send.impl.presentation.send.state.SendAmountSegmentedButtonsConfig
import com.tangem.features.send.impl.presentation.send.state.SendUiState
import com.tangem.features.send.impl.presentation.send.ui.amount.AmountFieldContainer
@Composable
internal fun SendAmountContent(amountState: SendUiState.Content.AmountState) {
Column(
modifier = Modifier
.background(TangemTheme.colors.background.tertiary),
) {
Text(
text = stringResource(R.string.common_send),
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
modifier = Modifier
.padding(vertical = TangemTheme.dimens.spacing16)
.align(CenterHorizontally),
)
AmountFieldContainer(amountState = amountState)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(
top = TangemTheme.dimens.spacing12,
start = TangemTheme.dimens.spacing16,
end = TangemTheme.dimens.spacing16,
),
) {
SegmentedButtons(
modifier = Modifier
.height(TangemTheme.dimens.size40)
.weight(1f),
config = amountState.segmentedButtonConfig,
onClick = { amountState.clickIntents.onCurrencyChangeClick(it.isFiat) },
) {
SendAmountCurrencyButton(it)
}
SecondaryButton(
text = stringResource(R.string.send_max_amount),
onClick = amountState.clickIntents::onMaxValueClick,
size = TangemButtonSize.Text,
shape = RoundedCornerShape(TangemTheme.dimens.radius26),
modifier = Modifier
.padding(start = TangemTheme.dimens.spacing8)
.height(TangemTheme.dimens.size40),
)
}
}
}
@Composable
private fun SendAmountCurrencyButton(button: SendAmountSegmentedButtonsConfig) {
Row(
modifier = Modifier
.fillMaxSize()
.padding(
horizontal = TangemTheme.dimens.spacing10,
),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
if (button.isFiat) {
FiatIcon(
url = button.iconUrl,
modifier = Modifier
.size(TangemTheme.dimens.size18),
)
} else {
button.iconState?.let {
TokenIcon(
state = it,
shouldDisplayNetwork = false,
modifier = Modifier
.size(TangemTheme.dimens.size18),
)
}
}
Text(
text = button.title.resolveReference(),
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.button,
modifier = Modifier
.padding(
start = TangemTheme.dimens.spacing8,
),
)
}
}

View file

@ -76,7 +76,7 @@ private fun SendPrimaryNavigationButton(uiState: SendUiState.Content, modifier:
PrimaryButtonIconEnd(
text = stringResource(textId),
iconResId = R.drawable.ic_tangem_24,
enabled = uiState.nextButtonEnabled,
enabled = uiState.isPrimaryButtonEnabled,
onClick = {
// todo add next click
},
@ -84,7 +84,7 @@ private fun SendPrimaryNavigationButton(uiState: SendUiState.Content, modifier:
} else {
PrimaryButton(
text = stringResource(textId),
enabled = uiState.nextButtonEnabled,
enabled = uiState.isPrimaryButtonEnabled,
onClick = {
// todo add next click
},

View file

@ -16,7 +16,7 @@ import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.send.impl.presentation.send.state.SendUiState
@Composable
fun SendScreen() {
internal fun SendScreen(uiState: SendUiState.Content) {
Column(
modifier = Modifier
.imePadding()
@ -37,13 +37,17 @@ fun SendScreen() {
.weight(1f)
.scrollable(state = rememberScrollState(), orientation = Orientation.Vertical),
) {
SendScreenContent()
SendScreenContent(uiState)
}
SendNavigationButtons(uiState = SendUiState.Content.Initial())
SendNavigationButtons(uiState)
}
}
@Composable
fun SendScreenContent() {
// todo work in progress
private fun SendScreenContent(uiState: SendUiState.Content) {
when (uiState) {
is SendUiState.Content.AmountState -> SendAmountContent(uiState)
else -> { /* [REDACTED_TODO_COMMENT]*/
}
}
}

View file

@ -0,0 +1,14 @@
package com.tangem.features.send.impl.presentation.send.viewmodel
interface SendClickIntents {
fun onNextClick()
fun onPrevClick()
fun onAmountValueChange(value: String)
fun onCurrencyChangeClick(isFiat: Boolean)
fun onMaxValueClick()
}

View file

@ -0,0 +1,155 @@
package com.tangem.features.send.impl.presentation.send.viewmodel
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.*
import arrow.core.getOrElse
import com.tangem.common.Provider
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.GetCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.send.api.navigation.SendRouter
import com.tangem.features.send.impl.presentation.send.state.SendStateFactory
import com.tangem.features.send.impl.presentation.send.state.SendUiState
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveIn
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
internal class SendViewModel @Inject constructor(
private val dispatchers: CoroutineDispatcherProvider,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
savedStateHandle: SavedStateHandle,
) : ViewModel(), DefaultLifecycleObserver, SendClickIntents {
private val userWalletId: UserWalletId = savedStateHandle.get<String>(SendRouter.USER_WALLET_ID_KEY)
?.let { stringValue -> UserWalletId(stringValue) }
?: error("This screen can't open without `UserWalletId`")
private val cryptoCurrency: CryptoCurrency = savedStateHandle[SendRouter.CRYPTO_CURRENCY_KEY]
?: error("This screen can't open without `CryptoCurrency`")
private val selectedAppCurrencyFlow: StateFlow<AppCurrency> = createSelectedAppCurrencyFlow()
private val stateFactory = SendStateFactory(
clickIntents = this,
currentStateProvider = Provider { uiState },
userWalletProvider = Provider { userWallet },
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
)
var uiState: SendUiState by mutableStateOf(stateFactory.getInitialState())
private set
private var userWallet: UserWallet? = null
private var balanceJobHolder = JobHolder()
override fun onCreate(owner: LifecycleOwner) {
subscribeOnCurrencyStatusUpdates(owner)
}
private fun subscribeOnCurrencyStatusUpdates(owner: LifecycleOwner) {
viewModelScope.launch(dispatchers.io) {
getUserWalletUseCase(userWalletId).fold(
ifRight = { wallet ->
userWallet = wallet
getCurrencyStatusUpdates(owner, wallet)
},
ifLeft = {
// TODO add error handling
return@launch
},
)
}
}
private fun getCurrencyStatusUpdates(owner: LifecycleOwner, wallet: UserWallet) {
val isSingleWallet = wallet.scanResponse.walletData?.token != null && !wallet.isMultiCurrency
getCurrencyStatusUpdatesUseCase(
userWalletId = userWalletId,
currencyId = cryptoCurrency.id,
derivationPath = cryptoCurrency.network.derivationPath,
isSingleWalletWithTokens = isSingleWallet,
)
.flowWithLifecycle(owner.lifecycle)
.conflate()
.distinctUntilChanged()
.onEach { either ->
uiState = stateFactory.getAmountState(
cryptoCurrencyStatus = either,
)
}
.flowOn(dispatchers.io)
.launchIn(viewModelScope)
.saveIn(balanceJobHolder)
}
private fun createSelectedAppCurrencyFlow(): StateFlow<AppCurrency> {
return getSelectedAppCurrencyUseCase()
.map { maybeAppCurrency ->
maybeAppCurrency.getOrElse { AppCurrency.Default }
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.Eagerly,
initialValue = AppCurrency.Default,
)
}
// region screen state navigation
override fun onNextClick() {
when (uiState) {
is SendUiState.Content.AmountState -> onRecipientStateClick()
is SendUiState.Content.RecipientState -> onFeeStateClick()
else -> {
// todo implement
}
}
}
override fun onPrevClick() {
// todo implement
}
private fun onRecipientStateClick() {
stateFactory.getOnReceiveState()
}
private fun onFeeStateClick() {
// todo implement
}
// endregion
// region amount state clicks
override fun onCurrencyChangeClick(isFiat: Boolean) {
uiState = stateFactory.getOnCurrencyChangedState(isFiat)
}
override fun onAmountValueChange(value: String) {
uiState = stateFactory.getOnAmountValueChange(value)
}
override fun onMaxValueClick() {
val amountState = uiState as? SendUiState.Content.AmountState ?: return
val amount = if (amountState.isFiatValue) {
amountState.cryptoCurrencyStatus.value.fiatAmount
} else {
amountState.cryptoCurrencyStatus.value.amount
}
onAmountValueChange(amount?.toPlainString() ?: "0.00")
}
// endregion
}