Updated on 2026-08-14
This commit is contained in:
parent
3c5c53ce92
commit
537543636b
19 changed files with 431 additions and 145 deletions
10
core/ui/src/main/res/drawable/ic_no_token_44.xml
Normal file
10
core/ui/src/main/res/drawable/ic_no_token_44.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="44dp"
|
||||
android:height="44dp"
|
||||
android:viewportWidth="44"
|
||||
android:viewportHeight="44">
|
||||
<path
|
||||
android:pathData="M6.31,26.499C6.508,25.666 6.773,24.813 7.102,23.949C8.493,26.484 10.368,29 12.684,31.316C15.001,33.632 17.516,35.507 20.051,36.898C19.187,37.227 18.334,37.492 17.501,37.69C13.387,38.669 10.043,37.991 8.026,35.974C6.009,33.957 5.331,30.613 6.31,26.499ZM6.862,37.139C3.507,33.784 3.448,27.963 6.13,22C3.448,16.037 3.507,10.216 6.862,6.862C10.216,3.507 16.037,3.448 22,6.13C27.963,3.448 33.784,3.507 37.139,6.862C40.493,10.216 40.552,16.037 37.87,22C40.553,27.963 40.493,33.784 37.139,37.139C33.784,40.493 27.963,40.552 22,37.87C16.037,40.552 10.216,40.493 6.862,37.139ZM13.849,30.152C11.273,27.576 9.292,24.765 7.948,22C9.292,19.235 11.273,16.424 13.849,13.849C16.424,11.273 19.235,9.292 22,7.948C24.765,9.292 27.576,11.273 30.152,13.849C32.728,16.424 34.708,19.235 36.052,22C34.708,24.765 32.727,27.576 30.152,30.152C27.576,32.727 24.765,34.708 22,36.052C19.235,34.708 16.425,32.727 13.849,30.152ZM12.684,12.684C15,10.368 17.515,8.493 20.051,7.102C19.187,6.773 18.334,6.508 17.502,6.31C13.388,5.331 10.044,6.009 8.026,8.026C6.009,10.043 5.331,13.387 6.31,17.501C6.509,18.334 6.773,19.187 7.102,20.051C8.493,17.515 10.368,15 12.684,12.684ZM31.316,12.684C29,10.368 26.485,8.493 23.949,7.102C24.813,6.773 25.666,6.508 26.499,6.31C30.613,5.331 33.957,6.009 35.974,8.026C37.991,10.043 38.669,13.387 37.69,17.501C37.492,18.334 37.227,19.187 36.898,20.051C35.507,17.515 33.632,15 31.316,12.684ZM31.316,31.316C33.632,29 35.507,26.484 36.898,23.949C37.227,24.813 37.492,25.666 37.69,26.499C38.67,30.613 37.992,33.957 35.974,35.974C33.957,37.991 30.613,38.669 26.499,37.69C25.666,37.492 24.813,37.227 23.949,36.898C26.485,35.507 29,33.632 31.316,31.316ZM22.539,19.035L21.919,14.838L21.299,19.035C21.112,20.303 20.116,21.299 18.848,21.486L14.65,22.106L18.848,22.726C20.116,22.914 21.112,23.91 21.299,25.178L21.919,29.375L22.539,25.178C22.726,23.91 23.722,22.914 24.991,22.726L29.188,22.106L24.991,21.486C23.722,21.299 22.726,20.303 22.539,19.035Z"
|
||||
android:fillColor="#EBEBEB"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
|
|
@ -67,6 +67,14 @@ internal class DefaultQuotesRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getQuoteSync(currencyId: CryptoCurrency.ID): Quote {
|
||||
return withContext(dispatchers.io) {
|
||||
val quote = quotesStore.getSync(setOf(currencyId)).firstOrNull()
|
||||
requireNotNull(quote) { "Unable to get quote for $currencyId" }
|
||||
quotesConverter.convert(quote)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchExpiredQuotes(
|
||||
currenciesIds: Set<CryptoCurrency.ID>,
|
||||
appCurrencyId: String,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.error.mapper.mapToTokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
||||
class GetCryptoCurrencyStatusSyncUseCase(
|
||||
internal val currenciesRepository: CurrenciesRepository,
|
||||
internal val quotesRepository: QuotesRepository,
|
||||
internal val networksRepository: NetworksRepository,
|
||||
internal val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyId: CryptoCurrency.ID,
|
||||
): Either<TokenListError, CryptoCurrencyStatus> {
|
||||
val operations = CurrenciesStatusesOperations(
|
||||
userWalletId = userWalletId,
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
)
|
||||
|
||||
return operations.getCurrencyStatusSync(cryptoCurrencyId)
|
||||
.mapLeft { error -> error.mapToTokenListError() }
|
||||
}
|
||||
}
|
||||
|
|
@ -85,6 +85,28 @@ internal class CurrenciesStatusesOperations(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun getCurrencyStatusSync(cryptoCurrencyId: CryptoCurrency.ID): Either<Error, CryptoCurrencyStatus> {
|
||||
return either {
|
||||
catch(
|
||||
block = {
|
||||
val currency =
|
||||
currenciesRepository.getMultiCurrencyWalletCurrency(userWalletId, cryptoCurrencyId)
|
||||
val quotes = quotesRepository.getQuoteSync(cryptoCurrencyId).right()
|
||||
val networkStatuses =
|
||||
networksRepository.getNetworkStatusesSync(
|
||||
userWalletId,
|
||||
setOf(currency.network),
|
||||
false,
|
||||
).firstOrNull {
|
||||
it.network == currency.network
|
||||
}.right()
|
||||
return createCurrencyStatus(currency, quotes, networkStatuses)
|
||||
},
|
||||
catch = { raise(Error.DataError(it)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun getCardCurrenciesStatusesFlow(): Flow<Either<Error, List<CryptoCurrencyStatus>>> {
|
||||
return flow {
|
||||
val nonEmptyCurrencies = recover(
|
||||
|
|
|
|||
|
|
@ -29,4 +29,6 @@ interface QuotesRepository {
|
|||
* @return A [Flow] emitting a set of quotes corresponding to the specified cryptocurrencies.
|
||||
*/
|
||||
suspend fun getQuotesSync(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean): Set<Quote>
|
||||
|
||||
suspend fun getQuoteSync(currencyId: CryptoCurrency.ID): Quote
|
||||
}
|
||||
|
|
@ -1,12 +1,9 @@
|
|||
package com.tangem.feature.swap.domain
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.feature.swap.domain.models.domain.NetworkInfo
|
||||
|
||||
interface BlockchainInteractor {
|
||||
|
||||
fun getTokenDecimals(token: CryptoCurrency): Int
|
||||
|
||||
/**
|
||||
* In app blockchain id, actual in blockchain sdk, not the same as networkId
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.feature.swap.domain
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.feature.swap.domain.models.domain.NetworkInfo
|
||||
import com.tangem.lib.crypto.TransactionManager
|
||||
import javax.inject.Inject
|
||||
|
|
@ -22,12 +21,4 @@ internal class DefaultBlockchainInteractor @Inject constructor(
|
|||
override fun getExplorerTransactionLink(networkId: String, txAddress: String): String {
|
||||
return transactionManager.getExplorerTransactionLink(networkId, txAddress)
|
||||
}
|
||||
|
||||
override fun getTokenDecimals(token: CryptoCurrency): Int {
|
||||
return if (token is CryptoCurrency.Token) {
|
||||
token.decimals
|
||||
} else {
|
||||
transactionManager.getNativeTokenDecimals(token.network.backendId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.swap.domain
|
|||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.swap.domain.models.SwapAmount
|
||||
import com.tangem.feature.swap.domain.models.domain.*
|
||||
import com.tangem.feature.swap.domain.models.ui.*
|
||||
|
|
@ -110,4 +111,6 @@ interface SwapInteractor {
|
|||
networkId: String,
|
||||
fromToken: CryptoCurrency,
|
||||
): Boolean
|
||||
|
||||
fun getSelectedWallet(): UserWallet?
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
|||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
import com.tangem.feature.swap.domain.cache.SwapDataCache
|
||||
import com.tangem.feature.swap.domain.converters.SwapCurrencyConverter
|
||||
|
|
@ -93,6 +94,10 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
override fun getSelectedWallet(): UserWallet? {
|
||||
return getSelectedWalletSyncUseCase().getOrNull()
|
||||
}
|
||||
|
||||
private fun getToCurrenciesGroup(
|
||||
currency: CryptoCurrency,
|
||||
leastPairs: List<SwapPairLeast>,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.feature.swap.domain.di
|
||||
|
||||
import com.tangem.domain.tokens.GetCardTokensListUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyStatusesSyncUseCase
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
|
|
@ -66,13 +65,6 @@ class SwapDomainModule {
|
|||
return GetSelectedWalletSyncUseCase(walletsStateHolder = walletsStateHolder)
|
||||
}
|
||||
|
||||
@SwapScope
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesGetCryptoCurrenciesUseCase(currenciesRepository: CurrenciesRepository): GetCryptoCurrenciesUseCase {
|
||||
return GetCryptoCurrenciesUseCase(currenciesRepository = currenciesRepository)
|
||||
}
|
||||
|
||||
@SwapScope
|
||||
@Provides
|
||||
@Singleton
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@ dependencies {
|
|||
implementation(projects.domain.appCurrency.models)
|
||||
implementation(projects.domain.balanceHiding)
|
||||
implementation(projects.domain.balanceHiding.models)
|
||||
implementation(projects.domain.tokens)
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.wallets)
|
||||
implementation(projects.domain.wallets.models)
|
||||
|
||||
/** AndroidX */
|
||||
implementation(deps.androidx.activity.compose)
|
||||
|
|
|
|||
|
|
@ -32,13 +32,17 @@ class TokensDataConverter(
|
|||
availableTokens = value.available.map { tokenWithBalanceToTokenToSelect(it, true) }
|
||||
.toMutableList()
|
||||
.apply {
|
||||
this.add(0, availableTitle)
|
||||
if (this.isNotEmpty()) {
|
||||
this.add(0, availableTitle)
|
||||
}
|
||||
}
|
||||
.toImmutableList(),
|
||||
unavailableTokens = value.unavailable.map { tokenWithBalanceToTokenToSelect(it, false) }
|
||||
.toMutableList()
|
||||
.apply {
|
||||
this.add(0, unavailableTitle)
|
||||
if (this.isNotEmpty()) {
|
||||
this.add(0, unavailableTitle)
|
||||
}
|
||||
}
|
||||
.toImmutableList(),
|
||||
onSearchEntered = onSearchEntered,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
package com.tangem.feature.swap.di
|
||||
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.feature.swap.domain.*
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ViewModelComponent
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
|
||||
@Module
|
||||
@InstallIn(ViewModelComponent::class)
|
||||
class SwapPresentationModule {
|
||||
|
||||
@ViewModelScoped
|
||||
@Provides
|
||||
fun providesGetCryptoCurrenciesUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
dispatcherProvider: CoroutineDispatcherProvider,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
): GetCryptoCurrencyStatusSyncUseCase {
|
||||
return GetCryptoCurrencyStatusSyncUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
dispatchers = dispatcherProvider,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,8 +7,8 @@ import com.tangem.core.ui.components.states.SelectableItemsState
|
|||
import com.tangem.feature.swap.domain.models.ui.TxFee
|
||||
|
||||
data class SwapStateHolder(
|
||||
val sendCardData: SwapCardData,
|
||||
val receiveCardData: SwapCardData,
|
||||
val sendCardData: SwapCardState,
|
||||
val receiveCardData: SwapCardState,
|
||||
val networkCurrency: String,
|
||||
val networkId: String,
|
||||
val blockchainId: String, // not the same as networkId, its local id in app
|
||||
|
|
@ -33,18 +33,28 @@ data class SwapStateHolder(
|
|||
val onCancelPermissionBottomSheet: () -> Unit = {},
|
||||
)
|
||||
|
||||
data class SwapCardData(
|
||||
val type: TransactionCardType,
|
||||
val amountEquivalent: String?,
|
||||
val coinId: String?,
|
||||
val amountTextFieldValue: TextFieldValue?,
|
||||
val tokenIconUrl: String?,
|
||||
val tokenCurrency: String,
|
||||
val balance: String,
|
||||
val isBalanceHidden: Boolean,
|
||||
val isNotNativeToken: Boolean,
|
||||
val canSelectAnotherToken: Boolean = false,
|
||||
)
|
||||
sealed class SwapCardState {
|
||||
|
||||
data class SwapCardData(
|
||||
val type: TransactionCardType,
|
||||
val amountEquivalent: String?,
|
||||
val coinId: String?,
|
||||
val amountTextFieldValue: TextFieldValue?,
|
||||
val tokenIconUrl: String?,
|
||||
val tokenCurrency: String,
|
||||
val balance: String,
|
||||
val isBalanceHidden: Boolean,
|
||||
val isNotNativeToken: Boolean,
|
||||
val canSelectAnotherToken: Boolean = false,
|
||||
) : SwapCardState()
|
||||
|
||||
data class Empty(
|
||||
val type: TransactionCardType,
|
||||
val amountEquivalent: String?,
|
||||
val amountTextFieldValue: TextFieldValue?,
|
||||
val canSelectAnotherToken: Boolean = false,
|
||||
) : SwapCardState()
|
||||
}
|
||||
|
||||
data class SwapButton(
|
||||
val enabled: Boolean,
|
||||
|
|
|
|||
|
|
@ -8,9 +8,12 @@ import com.tangem.core.ui.components.states.Item
|
|||
import com.tangem.core.ui.components.states.SelectableItemsState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.feature.swap.converters.TokensDataConverter
|
||||
import com.tangem.feature.swap.domain.models.DataError
|
||||
import com.tangem.feature.swap.domain.models.domain.NetworkInfo
|
||||
|
|
@ -28,7 +31,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
internal class StateBuilder(
|
||||
private val actions: UiActions,
|
||||
private val isBalanceHiddenProvider: Provider<Boolean>,
|
||||
appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
) {
|
||||
|
||||
private val tokensDataConverter = TokensDataConverter(
|
||||
|
|
@ -42,7 +45,7 @@ internal class StateBuilder(
|
|||
return SwapStateHolder(
|
||||
networkId = initialCurrency.network.backendId,
|
||||
blockchainId = networkInfo.blockchainId,
|
||||
sendCardData = SwapCardData(
|
||||
sendCardData = SwapCardState.SwapCardData(
|
||||
type = TransactionCardType.SendCard(actions.onAmountChanged, actions.onAmountSelected),
|
||||
amountEquivalent = null,
|
||||
amountTextFieldValue = null,
|
||||
|
|
@ -54,7 +57,7 @@ internal class StateBuilder(
|
|||
balance = "",
|
||||
isBalanceHidden = true,
|
||||
),
|
||||
receiveCardData = SwapCardData(
|
||||
receiveCardData = SwapCardState.SwapCardData(
|
||||
type = TransactionCardType.ReceiveCard(),
|
||||
amountEquivalent = null,
|
||||
tokenIconUrl = "",
|
||||
|
|
@ -79,6 +82,53 @@ internal class StateBuilder(
|
|||
)
|
||||
}
|
||||
|
||||
fun createNoAvailableTokensToSwapState(
|
||||
uiStateHolder: SwapStateHolder,
|
||||
fromToken: CryptoCurrencyStatus,
|
||||
): SwapStateHolder {
|
||||
if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder
|
||||
return uiStateHolder.copy(
|
||||
sendCardData = SwapCardState.SwapCardData(
|
||||
type = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.SendCard),
|
||||
amountTextFieldValue = TextFieldValue(
|
||||
text = "0",
|
||||
),
|
||||
amountEquivalent = "0 ${appCurrencyProvider.invoke().symbol}",
|
||||
tokenIconUrl = uiStateHolder.sendCardData.tokenIconUrl,
|
||||
coinId = uiStateHolder.sendCardData.coinId,
|
||||
isNotNativeToken = uiStateHolder.sendCardData.isNotNativeToken,
|
||||
tokenCurrency = uiStateHolder.sendCardData.tokenCurrency,
|
||||
canSelectAnotherToken = uiStateHolder.sendCardData.canSelectAnotherToken,
|
||||
balance = fromToken.getFormattedAmount(),
|
||||
isBalanceHidden = isBalanceHiddenProvider(),
|
||||
),
|
||||
receiveCardData = SwapCardState.Empty(
|
||||
type = TransactionCardType.ReceiveCard(),
|
||||
amountEquivalent = "0 ${appCurrencyProvider.invoke().symbol}",
|
||||
amountTextFieldValue = TextFieldValue(
|
||||
text = "0",
|
||||
),
|
||||
canSelectAnotherToken = true,
|
||||
),
|
||||
warnings = listOf(
|
||||
SwapWarning.NoAvailableTokensToSwap(
|
||||
notificationConfig = NotificationConfig(
|
||||
title = stringReference("No tokens"),
|
||||
subtitle = stringReference("Swap tokens not available"),
|
||||
iconResId = R.drawable.ic_alert_24,
|
||||
),
|
||||
),
|
||||
),
|
||||
fee = FeeState.Empty,
|
||||
swapButton = SwapButton(
|
||||
enabled = false,
|
||||
loading = false,
|
||||
onClick = { },
|
||||
),
|
||||
updateInProgress = false,
|
||||
)
|
||||
}
|
||||
|
||||
fun createQuotesLoadingState(
|
||||
uiStateHolder: SwapStateHolder,
|
||||
fromToken: CryptoCurrency,
|
||||
|
|
@ -87,8 +137,10 @@ internal class StateBuilder(
|
|||
): SwapStateHolder {
|
||||
val canSelectSendToken = mainTokenId != fromToken.id.value // TODO look at id matching
|
||||
val canSelectReceiveToken = mainTokenId != toToken.id.value // TODO look at id matching
|
||||
if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder
|
||||
if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder
|
||||
return uiStateHolder.copy(
|
||||
sendCardData = SwapCardData(
|
||||
sendCardData = SwapCardState.SwapCardData(
|
||||
type = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.SendCard),
|
||||
amountTextFieldValue = uiStateHolder.sendCardData.amountTextFieldValue,
|
||||
amountEquivalent = null,
|
||||
|
|
@ -100,7 +152,7 @@ internal class StateBuilder(
|
|||
balance = if (!canSelectSendToken) uiStateHolder.sendCardData.balance else "",
|
||||
isBalanceHidden = isBalanceHiddenProvider(),
|
||||
),
|
||||
receiveCardData = SwapCardData(
|
||||
receiveCardData = SwapCardState.SwapCardData(
|
||||
type = TransactionCardType.ReceiveCard(),
|
||||
amountTextFieldValue = null,
|
||||
amountEquivalent = null,
|
||||
|
|
@ -128,12 +180,15 @@ internal class StateBuilder(
|
|||
* @param onFeeSetup callback for reset fee after auto update
|
||||
* @return updated whole screen state
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
fun createQuotesLoadedState(
|
||||
uiStateHolder: SwapStateHolder,
|
||||
quoteModel: SwapState.QuotesLoadedState,
|
||||
fromToken: CryptoCurrency,
|
||||
onFeeSetup: (TxFee) -> Unit,
|
||||
): SwapStateHolder {
|
||||
if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder
|
||||
if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder
|
||||
val warnings = mutableListOf<SwapWarning>()
|
||||
if (!quoteModel.preparedSwapConfigState.isAllowedToSpend &&
|
||||
quoteModel.preparedSwapConfigState.isFeeEnough &&
|
||||
|
|
@ -159,7 +214,7 @@ internal class StateBuilder(
|
|||
}
|
||||
val feeState = createFeeState(quoteModel, uiStateHolder, onFeeSetup)
|
||||
return uiStateHolder.copy(
|
||||
sendCardData = SwapCardData(
|
||||
sendCardData = SwapCardState.SwapCardData(
|
||||
type = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.SendCard),
|
||||
amountTextFieldValue = uiStateHolder.sendCardData.amountTextFieldValue,
|
||||
amountEquivalent = quoteModel.fromTokenInfo.tokenFiatBalance,
|
||||
|
|
@ -171,7 +226,7 @@ internal class StateBuilder(
|
|||
balance = quoteModel.fromTokenInfo.tokenWalletBalance,
|
||||
isBalanceHidden = isBalanceHiddenProvider(),
|
||||
),
|
||||
receiveCardData = SwapCardData(
|
||||
receiveCardData = SwapCardState.SwapCardData(
|
||||
type = TransactionCardType.ReceiveCard(),
|
||||
amountTextFieldValue = TextFieldValue(quoteModel.toTokenInfo.tokenAmount.formatToUIRepresentation()),
|
||||
amountEquivalent = quoteModel.toTokenInfo.tokenFiatBalance,
|
||||
|
|
@ -208,8 +263,10 @@ internal class StateBuilder(
|
|||
uiStateHolder: SwapStateHolder,
|
||||
emptyAmountState: SwapState.EmptyAmountState,
|
||||
): SwapStateHolder {
|
||||
if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder
|
||||
if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder
|
||||
return uiStateHolder.copy(
|
||||
sendCardData = SwapCardData(
|
||||
sendCardData = SwapCardState.SwapCardData(
|
||||
type = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.SendCard),
|
||||
amountTextFieldValue = uiStateHolder.sendCardData.amountTextFieldValue,
|
||||
amountEquivalent = emptyAmountState.zeroAmountEquivalent,
|
||||
|
|
@ -221,7 +278,7 @@ internal class StateBuilder(
|
|||
balance = emptyAmountState.fromTokenWalletBalance,
|
||||
isBalanceHidden = isBalanceHiddenProvider(),
|
||||
),
|
||||
receiveCardData = SwapCardData(
|
||||
receiveCardData = SwapCardState.SwapCardData(
|
||||
type = TransactionCardType.ReceiveCard(),
|
||||
amountTextFieldValue = TextFieldValue("0"),
|
||||
amountEquivalent = emptyAmountState.zeroAmountEquivalent,
|
||||
|
|
@ -268,6 +325,7 @@ internal class StateBuilder(
|
|||
}
|
||||
|
||||
fun updateSwapAmount(uiState: SwapStateHolder, amount: String): SwapStateHolder {
|
||||
if (uiState.sendCardData !is SwapCardState.SwapCardData) return uiState
|
||||
return uiState.copy(
|
||||
sendCardData = uiState.sendCardData.copy(
|
||||
amountTextFieldValue = TextFieldValue(
|
||||
|
|
@ -279,6 +337,8 @@ internal class StateBuilder(
|
|||
}
|
||||
|
||||
fun updateBalanceHiddenState(uiState: SwapStateHolder, isBalanceHidden: Boolean): SwapStateHolder {
|
||||
if (uiState.sendCardData !is SwapCardState.SwapCardData) return uiState
|
||||
if (uiState.receiveCardData !is SwapCardState.SwapCardData) return uiState
|
||||
val patchedSendCardData = uiState.sendCardData.copy(
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
)
|
||||
|
|
@ -666,11 +726,26 @@ internal class StateBuilder(
|
|||
return "$firstAddressPart...$secondAddressPart"
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getFormattedAmount(): String {
|
||||
val amount = value.amount ?: return UNKNOWN_AMOUNT_SIGN
|
||||
|
||||
return BigDecimalFormatter.formatCryptoAmount(amount, currency.symbol, currency.decimals)
|
||||
}
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
private fun CryptoCurrencyStatus.getFormattedFiatAmount(): String {
|
||||
val fiatAmount = value.fiatAmount ?: return UNKNOWN_AMOUNT_SIGN
|
||||
val appCurrency = appCurrencyProvider()
|
||||
|
||||
return BigDecimalFormatter.formatFiatAmount(fiatAmount, appCurrency.code, appCurrency.symbol)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val ADDRESS_MIN_LENGTH = 11
|
||||
const val ADDRESS_FIRST_PART_LENGTH = 7
|
||||
const val ADDRESS_SECOND_PART_LENGTH = 4
|
||||
private const val PRICE_IMPACT_THRESHOLD = 0.1
|
||||
private const val HUNDRED_PERCENTS = 100
|
||||
private const val UNKNOWN_AMOUNT_SIGN = "—"
|
||||
}
|
||||
}
|
||||
|
|
@ -145,48 +145,24 @@ private fun MainInfo(state: SwapStateHolder) {
|
|||
}
|
||||
val (topCard, bottomCard, button) = createRefs()
|
||||
val priceImpactWarning = state.warnings.filterIsInstance<SwapWarning.HighPriceImpact>().firstOrNull()
|
||||
TransactionCard(
|
||||
type = state.sendCardData.type,
|
||||
balance = if (state.sendCardData.isBalanceHidden) {
|
||||
STARS
|
||||
} else {
|
||||
state.sendCardData.balance
|
||||
},
|
||||
textFieldValue = state.sendCardData.amountTextFieldValue,
|
||||
amountEquivalent = state.sendCardData.amountEquivalent,
|
||||
tokenIconUrl = state.sendCardData.tokenIconUrl ?: "",
|
||||
tokenCurrency = state.sendCardData.tokenCurrency,
|
||||
priceImpact = priceImpactWarning,
|
||||
networkIconRes = if (state.sendCardData.isNotNativeToken) networkIconRes else null,
|
||||
iconPlaceholder = state.sendCardData.coinId?.let {
|
||||
getActiveIconResByCoinId(it)
|
||||
},
|
||||
onChangeTokenClick = if (state.sendCardData.canSelectAnotherToken) state.onSelectTokenClick else null,
|
||||
TransactionCardData(
|
||||
priceImpactWarning = priceImpactWarning,
|
||||
networkIconRes = networkIconRes,
|
||||
swapCardState = state.sendCardData,
|
||||
modifier = Modifier.constrainAs(topCard) {
|
||||
top.linkTo(parent.top)
|
||||
},
|
||||
onSelectTokenClick = state.onSelectTokenClick,
|
||||
)
|
||||
val marginCard = TangemTheme.dimens.spacing16
|
||||
TransactionCard(
|
||||
type = state.receiveCardData.type,
|
||||
balance = if (state.receiveCardData.isBalanceHidden) STARS else state.receiveCardData.balance,
|
||||
textFieldValue = state.receiveCardData.amountTextFieldValue,
|
||||
amountEquivalent = state.receiveCardData.amountEquivalent,
|
||||
tokenIconUrl = state.receiveCardData.tokenIconUrl ?: "",
|
||||
tokenCurrency = state.receiveCardData.tokenCurrency,
|
||||
priceImpact = priceImpactWarning,
|
||||
networkIconRes = if (state.receiveCardData.isNotNativeToken) networkIconRes else null,
|
||||
iconPlaceholder = state.receiveCardData.coinId?.let {
|
||||
getActiveIconResByCoinId(it)
|
||||
},
|
||||
onChangeTokenClick = if (state.receiveCardData.canSelectAnotherToken) {
|
||||
state.onSelectTokenClick
|
||||
} else {
|
||||
null
|
||||
},
|
||||
TransactionCardData(
|
||||
priceImpactWarning = priceImpactWarning,
|
||||
networkIconRes = networkIconRes,
|
||||
swapCardState = state.receiveCardData,
|
||||
modifier = Modifier.constrainAs(bottomCard) {
|
||||
top.linkTo(topCard.bottom, margin = marginCard)
|
||||
},
|
||||
onSelectTokenClick = state.onSelectTokenClick,
|
||||
)
|
||||
val marginButton = TangemTheme.dimens.spacing32
|
||||
SwapButton(
|
||||
|
|
@ -200,6 +176,48 @@ private fun MainInfo(state: SwapStateHolder) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TransactionCardData(
|
||||
priceImpactWarning: SwapWarning.HighPriceImpact?,
|
||||
networkIconRes: Int?,
|
||||
swapCardState: SwapCardState,
|
||||
onSelectTokenClick: (() -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (swapCardState) {
|
||||
is SwapCardState.Empty -> {
|
||||
TransactionCardEmpty(
|
||||
type = swapCardState.type,
|
||||
amountEquivalent = swapCardState.amountEquivalent,
|
||||
textFieldValue = swapCardState.amountTextFieldValue,
|
||||
onChangeTokenClick = if (swapCardState.canSelectAnotherToken) onSelectTokenClick else null,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
is SwapCardState.SwapCardData -> {
|
||||
TransactionCard(
|
||||
type = swapCardState.type,
|
||||
balance = if (swapCardState.isBalanceHidden) {
|
||||
STARS
|
||||
} else {
|
||||
swapCardState.balance
|
||||
},
|
||||
textFieldValue = swapCardState.amountTextFieldValue,
|
||||
amountEquivalent = swapCardState.amountEquivalent,
|
||||
tokenIconUrl = swapCardState.tokenIconUrl ?: "",
|
||||
tokenCurrency = swapCardState.tokenCurrency,
|
||||
priceImpact = priceImpactWarning,
|
||||
networkIconRes = if (swapCardState.isNotNativeToken) networkIconRes else null,
|
||||
iconPlaceholder = swapCardState.coinId?.let {
|
||||
getActiveIconResByCoinId(it)
|
||||
},
|
||||
onChangeTokenClick = if (swapCardState.canSelectAnotherToken) onSelectTokenClick else null,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun SwapButton(state: SwapStateHolder, modifier: Modifier = Modifier) {
|
||||
|
|
@ -268,7 +286,8 @@ private fun FeeItem(feeState: FeeState, currency: String) {
|
|||
}
|
||||
}
|
||||
is FeeState.Empty -> {
|
||||
SmallInfoCard(startText = titleString, endText = "")
|
||||
// show nothing
|
||||
// SmallInfoCard(startText = titleString, endText = "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -355,7 +374,7 @@ private fun MainButton(state: SwapStateHolder, onPermissionWarningClick: () -> U
|
|||
|
||||
// region preview
|
||||
|
||||
private val sendCard = SwapCardData(
|
||||
private val sendCard = SwapCardState.SwapCardData(
|
||||
type = TransactionCardType.SendCard({}) {},
|
||||
amountTextFieldValue = TextFieldValue(),
|
||||
amountEquivalent = "1 000 000",
|
||||
|
|
@ -368,7 +387,7 @@ private val sendCard = SwapCardData(
|
|||
isBalanceHidden = false,
|
||||
)
|
||||
|
||||
private val receiveCard = SwapCardData(
|
||||
private val receiveCard = SwapCardState.SwapCardData(
|
||||
type = TransactionCardType.ReceiveCard(),
|
||||
amountTextFieldValue = TextFieldValue(),
|
||||
amountEquivalent = "1 000 000",
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ fun TransactionCard(
|
|||
verticalArrangement = Arrangement.Top,
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
Header(balance = balance, type = type)
|
||||
Header(balance = stringResource(R.string.common_balance, balance), type = type)
|
||||
|
||||
Content(
|
||||
type = type,
|
||||
|
|
@ -106,6 +106,67 @@ fun TransactionCard(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TransactionCardEmpty(
|
||||
type: TransactionCardType,
|
||||
amountEquivalent: String?,
|
||||
textFieldValue: TextFieldValue?,
|
||||
modifier: Modifier = Modifier,
|
||||
onChangeTokenClick: (() -> Unit)? = null,
|
||||
) {
|
||||
Card(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius12),
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.Top,
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
Header(
|
||||
balance = stringResource(id = R.string.swapping_token_not_available),
|
||||
type = type,
|
||||
)
|
||||
|
||||
Content(
|
||||
type = type,
|
||||
amountEquivalent = amountEquivalent,
|
||||
textFieldValue = textFieldValue,
|
||||
priceImpact = null,
|
||||
)
|
||||
}
|
||||
|
||||
Box(modifier = Modifier.align(Alignment.BottomEnd)) {
|
||||
Token(
|
||||
tokenIconUrl = "",
|
||||
tokenCurrency = "",
|
||||
iconPlaceholder = R.drawable.ic_no_token_44,
|
||||
)
|
||||
}
|
||||
|
||||
if (onChangeTokenClick != null) {
|
||||
Box(modifier = Modifier.align(Alignment.CenterEnd)) {
|
||||
ChangeTokenSelector()
|
||||
}
|
||||
Box(
|
||||
Modifier
|
||||
.align(Alignment.CenterEnd)
|
||||
.height(TangemTheme.dimens.size116)
|
||||
.width(TangemTheme.dimens.size102)
|
||||
.clickable(
|
||||
indication = rememberRipple(bounded = false),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
) { onChangeTokenClick() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Header(type: TransactionCardType, balance: String, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
|
|
@ -134,7 +195,7 @@ private fun Header(type: TransactionCardType, balance: String, modifier: Modifie
|
|||
SpacerW16()
|
||||
if (balance.isNotBlank()) {
|
||||
Text(
|
||||
text = stringResource(R.string.common_balance, balance),
|
||||
text = balance,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = MaterialTheme.typography.body2,
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.feature.swap.viewmodels
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.feature.swap.domain.models.domain.Currency
|
||||
import com.tangem.feature.swap.domain.models.ui.RequestApproveStateData
|
||||
import com.tangem.feature.swap.domain.models.ui.SwapStateData
|
||||
|
|
@ -14,8 +14,8 @@ data class SwapProcessDataState(
|
|||
val fromCurrency: Currency? = null,
|
||||
@Deprecated("used in old swap mechanism")
|
||||
val toCurrency: Currency? = null,
|
||||
val fromCryptoCurrency: CryptoCurrency? = null,
|
||||
val toCryptoCurrency: CryptoCurrency? = null,
|
||||
val fromCryptoCurrency: CryptoCurrencyStatus? = null,
|
||||
val toCryptoCurrency: CryptoCurrencyStatus? = null,
|
||||
// Amount from input
|
||||
val amount: String? = null,
|
||||
val approveDataModel: RequestApproveStateData? = null,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ import com.tangem.core.ui.utils.InputNumberFormatter
|
|||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.feature.swap.analytics.SwapEvents
|
||||
import com.tangem.feature.swap.domain.BlockchainInteractor
|
||||
import com.tangem.feature.swap.domain.SwapInteractor
|
||||
|
|
@ -49,11 +51,13 @@ internal class SwapViewModel @Inject constructor(
|
|||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusSyncUseCase,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel(), DefaultLifecycleObserver {
|
||||
|
||||
private val initialCryptoCurrency: CryptoCurrency = savedStateHandle[SwapFragment.CURRENCY_BUNDLE_KEY]
|
||||
?: error("no expected parameter CryptoCurrency found`")
|
||||
private lateinit var initialCryptoCurrencyStatus: CryptoCurrencyStatus
|
||||
|
||||
private var isBalanceHidden = true
|
||||
|
||||
|
|
@ -88,11 +92,20 @@ internal class SwapViewModel @Inject constructor(
|
|||
get() = swapRouter.currentScreen
|
||||
|
||||
init {
|
||||
swapInteractor.initDerivationPathAndNetwork(
|
||||
derivationPath = initialCryptoCurrency.network.derivationPath.value,
|
||||
network = initialCryptoCurrency.network,
|
||||
)
|
||||
initTokens()
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
swapInteractor.getSelectedWallet()?.let {
|
||||
initialCryptoCurrencyStatus =
|
||||
requireNotNull(getCryptoCurrencyStatusUseCase(it.walletId, initialCryptoCurrency.id).getOrNull()) {
|
||||
"Failed to get initial crypto currency status"
|
||||
}
|
||||
|
||||
swapInteractor.initDerivationPathAndNetwork(
|
||||
derivationPath = initialCryptoCurrency.network.derivationPath.value,
|
||||
network = initialCryptoCurrency.network,
|
||||
)
|
||||
initTokens()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
|
|
@ -137,44 +150,38 @@ internal class SwapViewModel @Inject constructor(
|
|||
runCatching(dispatchers.io) {
|
||||
swapInteractor.getTokensDataState(initialCryptoCurrency)
|
||||
}.onSuccess { state ->
|
||||
dataState = dataState.copy(
|
||||
fromCryptoCurrency = initialCryptoCurrency,
|
||||
toCryptoCurrency = state.toGroup.available.first().currencyStatus.currency,
|
||||
tokensDataState = state,
|
||||
)
|
||||
|
||||
updateTokensState(state)
|
||||
startLoadingQuotes(
|
||||
fromToken = initialCryptoCurrency,
|
||||
toToken = state.toGroup.available.first().currencyStatus.currency,
|
||||
amount = lastAmount.value,
|
||||
)
|
||||
applyInitialTokenChoice(state, selectInitialCurrencyToSwap(state))
|
||||
}.onFailure {
|
||||
Timber.tag(loggingTag).e(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// old flow
|
||||
// viewModelScope.launch(dispatchers.main) {
|
||||
// runCatching(dispatchers.io) {
|
||||
// swapInteractor.initTokensToSwap(currency)
|
||||
// }
|
||||
// .onSuccess { state ->
|
||||
// // dataState = dataState.copy(
|
||||
// // fromCurrency = state.preselectTokens.fromToken,
|
||||
// // toCurrency = state.preselectTokens.toToken,
|
||||
// // )
|
||||
// // updateTokensState(dataState = state.foundTokensState)
|
||||
// // startLoadingQuotes(
|
||||
// // fromToken = state.preselectTokens.fromToken,
|
||||
// // toToken = state.preselectTokens.toToken,
|
||||
// // amount = lastAmount.value,
|
||||
// // )
|
||||
// }
|
||||
// .onFailure {
|
||||
// Timber.tag(loggingTag).e(it)
|
||||
// }
|
||||
// }
|
||||
private fun applyInitialTokenChoice(state: TokensDataStateExpress, selectedCurrency: CryptoCurrencyStatus?) {
|
||||
val fromCurrencyStatus = initialCryptoCurrencyStatus
|
||||
dataState = dataState.copy(
|
||||
fromCryptoCurrency = fromCurrencyStatus,
|
||||
toCryptoCurrency = selectedCurrency,
|
||||
tokensDataState = state,
|
||||
)
|
||||
if (selectedCurrency == null) {
|
||||
uiState = stateBuilder.createNoAvailableTokensToSwapState(
|
||||
uiStateHolder = uiState,
|
||||
fromToken = fromCurrencyStatus,
|
||||
)
|
||||
} else {
|
||||
startLoadingQuotes(
|
||||
fromToken = fromCurrencyStatus.currency,
|
||||
toToken = selectedCurrency.currency,
|
||||
amount = lastAmount.value,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectInitialCurrencyToSwap(state: TokensDataStateExpress): CryptoCurrencyStatus? {
|
||||
// todo add algorithm to select initial currency
|
||||
return state.toGroup.available.firstOrNull()?.currencyStatus
|
||||
}
|
||||
|
||||
private fun updateTokensState(dataState: TokensDataStateExpress) {
|
||||
|
|
@ -203,7 +210,7 @@ internal class SwapViewModel @Inject constructor(
|
|||
val toCurrency = dataState.toCryptoCurrency
|
||||
val amount = dataState.amount
|
||||
if (fromCurrency != null && toCurrency != null && amount != null) {
|
||||
startLoadingQuotes(fromCurrency, toCurrency, amount)
|
||||
startLoadingQuotes(fromCurrency.currency, toCurrency.currency, amount)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -285,8 +292,8 @@ internal class SwapViewModel @Inject constructor(
|
|||
swapInteractor.onSwap(
|
||||
networkId = dataState.networkId,
|
||||
swapStateData = requireNotNull(dataState.swapDataModel),
|
||||
currencyToSend = requireNotNull(dataState.fromCryptoCurrency),
|
||||
currencyToGet = requireNotNull(dataState.toCryptoCurrency),
|
||||
currencyToSend = requireNotNull(dataState.fromCryptoCurrency?.currency),
|
||||
currencyToGet = requireNotNull(dataState.toCryptoCurrency?.currency),
|
||||
amountToSwap = requireNotNull(dataState.amount),
|
||||
fee = requireNotNull(dataState.selectedFee),
|
||||
)
|
||||
|
|
@ -335,10 +342,10 @@ internal class SwapViewModel @Inject constructor(
|
|||
approveData = requireNotNull(dataState.approveDataModel) {
|
||||
"dataState.approveDataModel might not be null"
|
||||
},
|
||||
forTokenContractAddress = (dataState.fromCryptoCurrency as? CryptoCurrency.Token)
|
||||
forTokenContractAddress = (dataState.fromCryptoCurrency?.currency as? CryptoCurrency.Token)
|
||||
?.contractAddress
|
||||
?: "",
|
||||
fromToken = requireNotNull(dataState.fromCryptoCurrency) {
|
||||
fromToken = requireNotNull(dataState.fromCryptoCurrency?.currency) {
|
||||
"dataState.fromCurrency might not be null"
|
||||
},
|
||||
approveType = requireNotNull(uiState.permissionState as? SwapPermissionState.ReadyForRequest) {
|
||||
|
|
@ -384,28 +391,34 @@ internal class SwapViewModel @Inject constructor(
|
|||
private fun onTokenSelect(id: String) {
|
||||
val tokens = dataState.tokensDataState ?: return
|
||||
|
||||
val foundToken = tokens.toGroup.available.firstOrNull {
|
||||
it.currencyStatus.currency.id.value == id
|
||||
val foundToken = if (isOrderReversed) {
|
||||
tokens.fromGroup.available.firstOrNull {
|
||||
it.currencyStatus.currency.id.value == id
|
||||
}
|
||||
} else {
|
||||
tokens.toGroup.available.firstOrNull {
|
||||
it.currencyStatus.currency.id.value == id
|
||||
}
|
||||
}
|
||||
analyticsEventHandler.send(
|
||||
event = SwapEvents.SearchTokenClicked(currencySymbol = foundToken?.currencyStatus?.currency?.symbol),
|
||||
)
|
||||
|
||||
if (foundToken != null) {
|
||||
val fromToken: CryptoCurrency
|
||||
val toToken: CryptoCurrency
|
||||
val fromToken: CryptoCurrencyStatus
|
||||
val toToken: CryptoCurrencyStatus
|
||||
if (isOrderReversed) {
|
||||
fromToken = foundToken.currencyStatus.currency
|
||||
toToken = initialCryptoCurrency
|
||||
fromToken = foundToken.currencyStatus
|
||||
toToken = initialCryptoCurrencyStatus
|
||||
} else {
|
||||
fromToken = initialCryptoCurrency
|
||||
toToken = foundToken.currencyStatus.currency
|
||||
fromToken = initialCryptoCurrencyStatus
|
||||
toToken = foundToken.currencyStatus
|
||||
}
|
||||
dataState = dataState.copy(
|
||||
fromCryptoCurrency = fromToken,
|
||||
toCryptoCurrency = toToken,
|
||||
)
|
||||
startLoadingQuotes(fromToken, toToken, lastAmount.value)
|
||||
startLoadingQuotes(fromToken.currency, toToken.currency, lastAmount.value)
|
||||
swapRouter.openScreen(SwapNavScreen.Main)
|
||||
}
|
||||
}
|
||||
|
|
@ -419,13 +432,13 @@ internal class SwapViewModel @Inject constructor(
|
|||
toCryptoCurrency = newToToken,
|
||||
)
|
||||
isOrderReversed = !isOrderReversed
|
||||
val decimals = blockchainInteractor.getTokenDecimals(newFromToken)
|
||||
val decimals = newFromToken.currency.decimals
|
||||
lastAmount.value = cutAmountWithDecimals(decimals, lastAmount.value)
|
||||
uiState = stateBuilder.updateSwapAmount(
|
||||
uiState,
|
||||
inputNumberFormatter.formatWithThousands(lastAmount.value, decimals),
|
||||
)
|
||||
startLoadingQuotes(newFromToken, newToToken, lastAmount.value)
|
||||
startLoadingQuotes(newFromToken.currency, newToToken.currency, lastAmount.value)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -433,20 +446,20 @@ internal class SwapViewModel @Inject constructor(
|
|||
val fromToken = dataState.fromCryptoCurrency
|
||||
val toToken = dataState.toCryptoCurrency
|
||||
if (fromToken != null && toToken != null) {
|
||||
val decimals = blockchainInteractor.getTokenDecimals(fromToken)
|
||||
val decimals = fromToken.currency.decimals
|
||||
val cutValue = cutAmountWithDecimals(decimals, value)
|
||||
lastAmount.value = cutValue
|
||||
uiState =
|
||||
stateBuilder.updateSwapAmount(uiState, inputNumberFormatter.formatWithThousands(cutValue, decimals))
|
||||
amountDebouncer.debounce(viewModelScope, DEBOUNCE_AMOUNT_DELAY) {
|
||||
startLoadingQuotes(fromToken, toToken, lastAmount.value)
|
||||
startLoadingQuotes(fromToken.currency, toToken.currency, lastAmount.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onMaxAmountClicked() {
|
||||
dataState.fromCryptoCurrency?.let {
|
||||
val balance = swapInteractor.getTokenBalance(initialCryptoCurrency.network.id.value, it)
|
||||
val balance = swapInteractor.getTokenBalance(initialCryptoCurrency.network.id.value, it.currency)
|
||||
onAmountChanged(balance.formatToUIRepresentation())
|
||||
}
|
||||
}
|
||||
|
|
@ -522,7 +535,7 @@ internal class SwapViewModel @Inject constructor(
|
|||
dataState = dataState.copy(selectedFee = feeItem.data)
|
||||
val spendAmount = dataState.amount?.let { amount ->
|
||||
val fromToken = dataState.fromCryptoCurrency ?: return@let null
|
||||
swapInteractor.getSwapAmountForToken(amount, fromToken)
|
||||
swapInteractor.getSwapAmountForToken(amount, fromToken.currency)
|
||||
} ?: dataState.approveDataModel?.fromTokenAmount
|
||||
spendAmount ?: return@UiActions
|
||||
val fromToken = dataState.fromCryptoCurrency ?: return@UiActions
|
||||
|
|
@ -531,7 +544,7 @@ internal class SwapViewModel @Inject constructor(
|
|||
fee = feeItem.data.feeValue,
|
||||
spendAmount = spendAmount,
|
||||
networkId = dataState.networkId,
|
||||
fromToken = fromToken,
|
||||
fromToken = fromToken.currency,
|
||||
)
|
||||
uiState = stateBuilder.updateFeeSelectedItem(uiState, feeItem, isFeeEnough)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue