Updated on 2026-08-14

This commit is contained in:
Tangem 2023-09-13 13:34:38 +03:00
parent 3fbddfa8c7
commit 70d2f5ee06
14 changed files with 120 additions and 84 deletions

View file

@ -1,10 +0,0 @@
package com.tangem.core.ui.components.transactions.intents
interface TxHistoryClickIntents {
fun onBuyClick()
fun onReloadClick()
fun onExploreClick()
}

View file

@ -1,6 +1,7 @@
package com.tangem.domain.tokens
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.domain.tokens.models.CryptoCurrency
import com.tangem.domain.tokens.repository.MarketCryptoCurrencyRepository
@ -21,21 +22,24 @@ class GetCryptoCurrencyActionsUseCase(
private val dispatchers: CoroutineDispatcherProvider,
) {
operator fun invoke(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Flow<TokenActionsState> {
operator fun invoke(
userWalletId: UserWalletId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
): Flow<TokenActionsState> {
return flow {
val actionStates = createTokenActionsState(userWalletId, cryptoCurrency)
val actionStates = createTokenActionsState(userWalletId, cryptoCurrencyStatus)
emit(actionStates)
}.flowOn(dispatchers.io)
}
private suspend fun createTokenActionsState(
userWalletId: UserWalletId,
cryptoCurrency: CryptoCurrency,
cryptoCurrencyStatus: CryptoCurrencyStatus,
): TokenActionsState {
return TokenActionsState(
walletId = userWalletId,
cryptoCurrencyId = cryptoCurrency.id,
states = createListOfActions(userWalletId, cryptoCurrency),
cryptoCurrencyStatus = cryptoCurrencyStatus,
states = createListOfActions(userWalletId, cryptoCurrencyStatus.currency),
)
}

View file

@ -1,11 +1,10 @@
package com.tangem.domain.tokens.model
import com.tangem.domain.tokens.models.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
data class TokenActionsState(
val walletId: UserWalletId,
val cryptoCurrencyId: CryptoCurrency.ID,
val cryptoCurrencyStatus: CryptoCurrencyStatus,
val states: List<ActionState>,
) {

View file

@ -3,17 +3,17 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.
import androidx.paging.PagingData
import arrow.core.Either
import com.tangem.common.Provider
import com.tangem.core.ui.components.transactions.intents.TxHistoryClickIntents
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.txhistory.models.TxHistoryListError
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.utils.converter.Converter
import kotlinx.coroutines.flow.Flow
internal class TokenDetailsLoadedTxHistoryConverter(
private val currentStateProvider: Provider<TokenDetailsState>,
private val clickIntents: TxHistoryClickIntents,
private val clickIntents: TokenDetailsClickIntents,
symbol: String,
decimals: Int,
) : Converter<Either<TxHistoryListError, Flow<PagingData<TxHistoryItem>>>, TokenDetailsState> {

View file

@ -3,17 +3,17 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.
import androidx.paging.PagingData
import arrow.core.Either
import com.tangem.common.Provider
import com.tangem.core.ui.components.transactions.intents.TxHistoryClickIntents
import com.tangem.core.ui.components.transactions.state.TransactionState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.domain.txhistory.models.TxHistoryStateError
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.utils.converter.Converter
import kotlinx.coroutines.flow.update
internal class TokenDetailsLoadingTxHistoryConverter(
private val currentStateProvider: Provider<TokenDetailsState>,
private val clickIntents: TxHistoryClickIntents,
private val clickIntents: TokenDetailsClickIntents,
) : Converter<Either<TxHistoryStateError, Int>, TokenDetailsState> {
override fun convert(value: Either<TxHistoryStateError, Int>): TokenDetailsState {

View file

@ -3,13 +3,13 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.
import android.text.format.DateUtils
import androidx.paging.*
import com.tangem.common.Provider
import com.tangem.core.ui.components.transactions.intents.TxHistoryClickIntents
import com.tangem.core.ui.components.transactions.state.TransactionState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistoryItemState
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.features.tokendetails.impl.R
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.isToday
@ -18,7 +18,10 @@ import com.tangem.utils.toBriefAddressFormat
import com.tangem.utils.toFormattedCurrencyString
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
import org.joda.time.DateTime
import org.joda.time.DateTimeZone
import org.joda.time.format.DateTimeFormatterBuilder
@ -29,7 +32,7 @@ internal class TokenDetailsTxHistoryItemFlowConverter(
private val currentStateProvider: Provider<TokenDetailsState>,
private val symbol: String,
private val decimals: Int,
private val clickIntents: TxHistoryClickIntents,
private val clickIntents: TokenDetailsClickIntents,
) : Converter<Flow<PagingData<TxHistoryItem>>, TxHistoryState> {
/** Example, 2 Aug, 2023 */

View file

@ -1,8 +1,6 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels
import com.tangem.core.ui.components.transactions.intents.TxHistoryClickIntents
interface TokenDetailsClickIntents : TxHistoryClickIntents {
interface TokenDetailsClickIntents {
fun onBackClick()
@ -21,4 +19,10 @@ interface TokenDetailsClickIntents : TxHistoryClickIntents {
fun onHideConfirmed()
fun onRefreshSwipe()
fun onBuyClick()
fun onReloadClick()
fun onExploreClick()
}

View file

@ -88,12 +88,11 @@ internal class TokenDetailsViewModel @Inject constructor(
private fun updateContent(selectedWallet: UserWallet) {
updateMarketPrice(selectedWallet = selectedWallet)
updateButtons(userWalletId = selectedWallet.walletId, currency = cryptoCurrency)
updateTxHistory()
}
private fun updateButtons(userWalletId: UserWalletId, currency: CryptoCurrency) {
getCryptoCurrencyActionsUseCase(userWalletId = userWalletId, cryptoCurrency = currency)
private fun updateButtons(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus) {
getCryptoCurrencyActionsUseCase(userWalletId = userWalletId, cryptoCurrencyStatus = currencyStatus)
.distinctUntilChanged()
.onEach { uiState = stateFactory.getManageButtonsState(actions = it.states) }
.flowOn(dispatchers.io)
@ -108,7 +107,10 @@ internal class TokenDetailsViewModel @Inject constructor(
.distinctUntilChanged()
.onEach { either ->
uiState = stateFactory.getCurrencyLoadedBalanceState(either)
either.onRight { status -> cryptoCurrencyStatus = status }
either.onRight { status ->
cryptoCurrencyStatus = status
updateButtons(userWalletId = selectedWallet.walletId, currencyStatus = status)
}
}
.flowOn(dispatchers.io)
.launchIn(viewModelScope)

View file

@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
@ -20,18 +21,15 @@ import kotlinx.collections.immutable.toImmutableList
internal class TokenActionsProvider(private val clickIntents: WalletClickIntents) {
fun provideActions(tokenActions: TokenActionsState): ImmutableList<TokenActionButtonConfig> {
return convertTokenActionsState(tokenActions)
}
private fun convertTokenActionsState(
tokenActionsState: TokenActionsState,
): ImmutableList<TokenActionButtonConfig> {
return tokenActionsState.states
.map(::tokenActionStateMapper)
return tokenActions.states
.map { mapTokenActionState(it, tokenActions.cryptoCurrencyStatus) }
.toImmutableList()
}
private fun tokenActionStateMapper(actionsState: TokenActionsState.ActionState): TokenActionButtonConfig {
private fun mapTokenActionState(
actionsState: TokenActionsState.ActionState,
cryptoCurrencyStatus: CryptoCurrencyStatus,
): TokenActionButtonConfig {
val title: TextReference
val icon: Int
val action: () -> Unit
@ -39,27 +37,27 @@ internal class TokenActionsProvider(private val clickIntents: WalletClickIntents
is TokenActionsState.ActionState.Buy -> {
title = resourceReference(R.string.common_buy)
icon = R.drawable.ic_plus_24
action = { }
action = { clickIntents.onBuyClick(cryptoCurrencyStatus) }
}
is TokenActionsState.ActionState.Receive -> {
title = resourceReference(R.string.common_receive)
icon = R.drawable.ic_arrow_down_24
action = { }
action = { clickIntents.onReceiveClick(cryptoCurrencyStatus) }
}
is TokenActionsState.ActionState.Sell -> {
title = resourceReference(R.string.common_sell)
icon = R.drawable.ic_currency_24
action = { }
action = { clickIntents.onSellClick(cryptoCurrencyStatus) }
}
is TokenActionsState.ActionState.Send -> {
title = resourceReference(R.string.common_send)
icon = R.drawable.ic_arrow_up_24
action = { }
action = { clickIntents.onMultiCurrencySendClick(cryptoCurrencyStatus) }
}
is TokenActionsState.ActionState.Swap -> {
title = resourceReference(R.string.common_swap)
icon = R.drawable.ic_exchange_horizontal_24
action = { }
action = { clickIntents.onSwapClick(cryptoCurrencyStatus) }
}
}
return TokenActionButtonConfig(

View file

@ -14,9 +14,9 @@ import kotlinx.collections.immutable.toPersistentList
internal class WalletCryptoCurrencyActionsConverter(
private val currentStateProvider: Provider<WalletState>,
private val clickIntents: WalletClickIntents,
) : Converter<List<TokenActionsState.ActionState>, WalletState> {
) : Converter<TokenActionsState, WalletState> {
override fun convert(value: List<TokenActionsState.ActionState>): WalletState {
override fun convert(value: TokenActionsState): WalletState {
return when (val state = currentStateProvider()) {
is WalletSingleCurrencyState.Content -> state.copy(buttons = value.mapToManageButtons())
is WalletSingleCurrencyState.Locked,
@ -26,23 +26,31 @@ internal class WalletCryptoCurrencyActionsConverter(
}
}
private fun List<TokenActionsState.ActionState>.mapToManageButtons(): PersistentList<WalletManageButton> {
return this
private fun TokenActionsState.mapToManageButtons(): PersistentList<WalletManageButton> {
return this.states
.mapNotNull { action ->
when (action) {
is TokenActionsState.ActionState.Buy -> {
WalletManageButton.Buy(enabled = action.enabled, onClick = clickIntents::onBuyClick)
WalletManageButton.Buy(
enabled = action.enabled,
onClick = { clickIntents.onBuyClick(cryptoCurrencyStatus) },
)
}
is TokenActionsState.ActionState.Receive -> {
WalletManageButton.Receive(onClick = clickIntents::onReceiveClick)
WalletManageButton.Receive(
onClick = { clickIntents.onReceiveClick(cryptoCurrencyStatus) },
)
}
is TokenActionsState.ActionState.Sell -> {
WalletManageButton.Sell(enabled = action.enabled, onClick = clickIntents::onSellClick)
WalletManageButton.Sell(
enabled = action.enabled,
onClick = { clickIntents.onSellClick(cryptoCurrencyStatus) },
)
}
is TokenActionsState.ActionState.Send -> {
WalletManageButton.Send(
enabled = action.enabled,
onClick = clickIntents::onSingleCurrencySendClick,
onClick = { clickIntents.onSingleCurrencySendClick(cryptoCurrencyStatus) },
)
}
is TokenActionsState.ActionState.Swap -> null

View file

@ -215,8 +215,16 @@ internal class WalletStateFactory(
}
}
fun getLoadingTxHistoryState(itemsCountEither: Either<TxHistoryStateError, Int>): WalletState {
return loadingTransactionsStateConverter.convert(value = itemsCountEither)
fun getLoadingTxHistoryState(
itemsCountEither: Either<TxHistoryStateError, Int>,
cryptoCurrencyStatus: CryptoCurrencyStatus,
): WalletState {
return loadingTransactionsStateConverter.convert(
WalletLoadingTxHistoryConverter.WalletLoadingTxHistoryModel(
historyLoadingState = itemsCountEither,
cryptoCurrencyStatus = cryptoCurrencyStatus,
),
)
}
fun getLoadedTxHistoryState(
@ -233,8 +241,8 @@ internal class WalletStateFactory(
return singleCurrencyLoadedBalanceConverter.convert(maybeCryptoCurrencyStatus)
}
fun getSingleCurrencyManageButtonsState(actions: List<TokenActionsState.ActionState>): WalletState {
return cryptoCurrencyActionsConverter.convert(value = actions)
fun getSingleCurrencyManageButtonsState(actionsState: TokenActionsState): WalletState {
return cryptoCurrencyActionsConverter.convert(value = actionsState)
}
fun getStateByCurrencyStatusError(error: CurrencyStatusError): WalletState {

View file

@ -2,41 +2,45 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory
import androidx.paging.PagingData
import arrow.core.Either
import com.tangem.common.Converter
import com.tangem.common.Provider
import com.tangem.core.ui.components.transactions.state.TransactionState
import com.tangem.core.ui.components.transactions.state.TxHistoryState.*
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.txhistory.models.TxHistoryStateError
import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState
import com.tangem.feature.wallet.presentation.wallet.state.WalletState
import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents
import com.tangem.utils.converter.Converter
import kotlinx.coroutines.flow.update
/**
* Converter from loading tx history state to [WalletSingleCurrencyState.Content]
*
* @property currentStateProvider current state provider
* @property clickIntents screen click intents
* @property currentStateProvider current state provider
* @property clickIntents screen click intents
*
[REDACTED_AUTHOR]
*/
internal class WalletLoadingTxHistoryConverter(
private val currentStateProvider: Provider<WalletState>,
private val clickIntents: WalletClickIntents,
) : Converter<Either<TxHistoryStateError, Int>, WalletState> {
) : Converter<WalletLoadingTxHistoryConverter.WalletLoadingTxHistoryModel, WalletState> {
override fun convert(value: Either<TxHistoryStateError, Int>): WalletState {
return value.fold(ifLeft = ::convertError, ifRight = ::convert)
override fun convert(value: WalletLoadingTxHistoryModel): WalletState {
return value.historyLoadingState.fold(
ifLeft = { convertError(it, value.cryptoCurrencyStatus) },
ifRight = ::convertRight,
)
}
private fun convertError(error: TxHistoryStateError): WalletState {
private fun convertError(error: TxHistoryStateError, cryptoCurrencyStatus: CryptoCurrencyStatus): WalletState {
val state = currentStateProvider()
return if (state is WalletSingleCurrencyState.Content) {
state.copy(
txHistoryState = when (error) {
is TxHistoryStateError.EmptyTxHistories -> {
Empty(onBuyClick = clickIntents::onBuyClick)
Empty(onBuyClick = { clickIntents.onBuyClick(cryptoCurrencyStatus) })
}
is TxHistoryStateError.DataError -> {
Error(onReloadClick = clickIntents::onReloadClick)
@ -51,7 +55,7 @@ internal class WalletLoadingTxHistoryConverter(
}
}
private fun convert(value: Int): WalletState {
private fun convertRight(value: Int): WalletState {
val state = currentStateProvider()
val txHistoryContent = (state as? WalletSingleCurrencyState.Content)?.txHistoryState as? Content
@ -71,4 +75,9 @@ internal class WalletLoadingTxHistoryConverter(
return state
}
data class WalletLoadingTxHistoryModel(
val historyLoadingState: Either<TxHistoryStateError, Int>,
val cryptoCurrencyStatus: CryptoCurrencyStatus,
)
}

View file

@ -1,12 +1,11 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels
import com.tangem.core.ui.components.transactions.intents.TxHistoryClickIntents
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.models.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
@Suppress("TooManyFunctions")
internal interface WalletClickIntents : TxHistoryClickIntents {
internal interface WalletClickIntents {
fun onBackClick()
@ -56,9 +55,17 @@ internal interface WalletClickIntents : TxHistoryClickIntents {
fun onMultiCurrencySendClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onReceiveClick()
fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onSellClick()
fun onSellClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onManageTokensClick()
fun onBuyClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onSwapClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onReloadClick()
fun onExploreClick()
}

View file

@ -366,20 +366,23 @@ internal class WalletViewModel @Inject constructor(
router.openOrganizeTokensScreen(walletId)
}
override fun onBuyClick() {
override fun onBuyClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
val state = uiState as? WalletState.ContentState ?: return
val status = singleWalletCryptoCurrencyStatus ?: return
val wallet = getWallet(index = state.walletsListConfig.selectedWalletIndex)
reduxStateHolder.dispatch(
TradeCryptoAction.New.Buy(
userWallet = wallet,
cryptoCurrencyStatus = status,
cryptoCurrencyStatus = cryptoCurrencyStatus,
appCurrencyCode = selectedAppCurrencyFlow.value.code,
),
)
}
override fun onSwapClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
// todo implement onSwapClick [REDACTED_JIRA]
}
override fun onSingleCurrencySendClick(cryptoCurrencyStatus: CryptoCurrencyStatus?) {
val state = uiState as? WalletState.ContentState ?: return
@ -424,16 +427,14 @@ internal class WalletViewModel @Inject constructor(
}
}
override fun onReceiveClick() {
override fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
// TODO: [REDACTED_JIRA]
}
override fun onSellClick() {
val status = singleWalletCryptoCurrencyStatus ?: return
override fun onSellClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
reduxStateHolder.dispatch(
TradeCryptoAction.New.Sell(
cryptoCurrencyStatus = status,
cryptoCurrencyStatus = cryptoCurrencyStatus,
appCurrencyCode = selectedAppCurrencyFlow.value.code,
),
)
@ -501,7 +502,7 @@ internal class WalletViewModel @Inject constructor(
val userWallet = getWallet(state.walletsListConfig.selectedWalletIndex)
viewModelScope.launch(dispatchers.io) {
getCryptoCurrencyActionsUseCase
.invoke(userWallet.walletId, cryptoCurrencyStatus.currency)
.invoke(userWallet.walletId, cryptoCurrencyStatus)
.take(count = 1)
.collectLatest {
uiState = stateFactory.getStateWithTokenActionBottomSheet(it)
@ -591,7 +592,10 @@ internal class WalletViewModel @Inject constructor(
viewModelScope.launch(dispatchers.io) {
val txHistoryItemsCountEither = txHistoryItemsCountUseCase(network)
uiState = stateFactory.getLoadingTxHistoryState(itemsCountEither = txHistoryItemsCountEither)
uiState = stateFactory.getLoadingTxHistoryState(
itemsCountEither = txHistoryItemsCountEither,
cryptoCurrencyStatus = singleWalletCryptoCurrencyStatus ?: return@launch,
)
txHistoryItemsCountEither.onRight {
uiState = stateFactory.getLoadedTxHistoryState(
@ -613,7 +617,7 @@ internal class WalletViewModel @Inject constructor(
maybeCryptoCurrencyStatus.onRight { status ->
singleWalletCryptoCurrencyStatus = status
updateButtons(userWalletId = userWalletId, currency = status.currency)
updateButtons(userWalletId = userWalletId, currencyStatus = status)
updateTxHistory(status.currency.network)
}
}
@ -622,10 +626,10 @@ internal class WalletViewModel @Inject constructor(
.saveIn(marketPriceJobHolder)
}
private fun updateButtons(userWalletId: UserWalletId, currency: CryptoCurrency) {
getCryptoCurrencyActionsUseCase(userWalletId = userWalletId, cryptoCurrency = currency)
private fun updateButtons(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus) {
getCryptoCurrencyActionsUseCase(userWalletId = userWalletId, cryptoCurrencyStatus = currencyStatus)
.distinctUntilChanged()
.onEach { uiState = stateFactory.getSingleCurrencyManageButtonsState(actions = it.states) }
.onEach { uiState = stateFactory.getSingleCurrencyManageButtonsState(actionsState = it) }
.flowOn(dispatchers.io)
.launchIn(viewModelScope)
.saveIn(buttonsJobHolder)