Updated on 2026-08-14

This commit is contained in:
Tangem 2024-10-30 13:13:53 +04:00
parent 488d1e2815
commit 101e609dec
11 changed files with 157 additions and 139 deletions

View file

@ -29,6 +29,7 @@ dependencies {
implementation(projects.domain.balanceHiding) implementation(projects.domain.balanceHiding)
implementation(projects.domain.balanceHiding.models) implementation(projects.domain.balanceHiding.models)
implementation(projects.domain.legacy) implementation(projects.domain.legacy)
implementation(projects.domain.models)
implementation(projects.domain.tokens) implementation(projects.domain.tokens)
implementation(projects.domain.tokens.models) implementation(projects.domain.tokens.models)
implementation(projects.domain.wallets) implementation(projects.domain.wallets)

View file

@ -3,39 +3,27 @@ package com.tangem.features.onramp.buy
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import arrow.core.getOrElse
import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.tokens.legacy.TradeCryptoAction
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.onramp.component.BuyCryptoComponent import com.tangem.features.onramp.component.BuyCryptoComponent
import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.entity.OnrampOperation
import com.tangem.features.onramp.selecttoken.OnrampSelectTokenComponent import com.tangem.features.onramp.selecttoken.OnrampOperationComponent
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import kotlinx.coroutines.launch
@Stable @Stable
internal class DefaultBuyCryptoComponent @AssistedInject constructor( internal class DefaultBuyCryptoComponent @AssistedInject constructor(
onrampSelectTokenComponentFactory: OnrampSelectTokenComponent.Factory, onrampOperationComponentFactory: OnrampOperationComponent.Factory,
@Assisted private val appComponentContext: AppComponentContext, @Assisted private val appComponentContext: AppComponentContext,
@Assisted private val params: BuyCryptoComponent.Params, @Assisted private val params: BuyCryptoComponent.Params,
private val reduxStateHolder: ReduxStateHolder,
private val getWalletsUseCase: GetWalletsUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
) : BuyCryptoComponent { ) : BuyCryptoComponent {
private val selectTokenComponent: OnrampSelectTokenComponent = onrampSelectTokenComponentFactory.create( private val selectTokenComponent: OnrampOperationComponent = onrampOperationComponentFactory.create(
context = appComponentContext, context = appComponentContext,
params = OnrampSelectTokenComponent.Params( params = OnrampOperationComponent.Params(
operation = OnrampOperation.BUY,
hasSearchBar = true, hasSearchBar = true,
userWalletId = params.userWalletId, userWalletId = params.userWalletId,
titleResId = R.string.common_buy,
onTokenClick = ::onTokenClick,
), ),
) )
@ -44,19 +32,6 @@ internal class DefaultBuyCryptoComponent @AssistedInject constructor(
selectTokenComponent.Content(modifier = modifier) selectTokenComponent.Content(modifier = modifier)
} }
private fun onTokenClick(status: CryptoCurrencyStatus) {
appComponentContext.componentScope.launch {
reduxStateHolder.dispatch(
TradeCryptoAction.Buy(
userWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId },
cryptoCurrencyStatus = status,
appCurrencyCode = getSelectedAppCurrencyUseCase.invokeSync()
.getOrElse { AppCurrency.Default }.code,
),
)
}
}
@AssistedFactory @AssistedFactory
interface Factory : BuyCryptoComponent.Factory { interface Factory : BuyCryptoComponent.Factory {

View file

@ -0,0 +1,12 @@
package com.tangem.features.onramp.entity
/**
* Onramp operation
*
[REDACTED_AUTHOR]
*/
internal enum class OnrampOperation {
BUY,
SELL,
;
}

View file

@ -0,0 +1,84 @@
package com.tangem.features.onramp.selecttoken
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import arrow.core.getOrElse
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.tokens.legacy.TradeCryptoAction
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.onramp.entity.OnrampOperation
import com.tangem.features.onramp.impl.R
import com.tangem.features.onramp.selecttoken.ui.OnrampSelectToken
import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.launch
internal class DefaultOnrampOperationComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
onrampTokenListComponentFactory: OnrampTokenListComponent.Factory,
@Assisted private val params: OnrampOperationComponent.Params,
private val reduxStateHolder: ReduxStateHolder,
private val getWalletsUseCase: GetWalletsUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
) : AppComponentContext by appComponentContext, OnrampOperationComponent {
private val onrampTokenListComponent: OnrampTokenListComponent = onrampTokenListComponentFactory.create(
context = child(key = "token_list"),
params = OnrampTokenListComponent.Params(
filterOperation = params.operation,
hasSearchBar = params.hasSearchBar,
userWalletId = params.userWalletId,
onTokenClick = ::onTokenClick,
),
)
@Composable
override fun Content(modifier: Modifier) {
OnrampSelectToken(
titleResId = when (params.operation) {
OnrampOperation.BUY -> R.string.common_buy
OnrampOperation.SELL -> R.string.common_sell
},
onBackClick = router::pop,
onrampTokenListComponent = onrampTokenListComponent,
modifier = modifier,
)
}
private fun onTokenClick(status: CryptoCurrencyStatus) {
componentScope.launch {
val appCurrencyCode = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default }.code
reduxStateHolder.dispatch(
when (params.operation) {
OnrampOperation.BUY -> getBuyAction(status, appCurrencyCode)
OnrampOperation.SELL -> TradeCryptoAction.Sell(status, appCurrencyCode)
},
)
}
}
private fun getBuyAction(status: CryptoCurrencyStatus, appCurrencyCode: String): TradeCryptoAction {
return TradeCryptoAction.Buy(
userWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId },
cryptoCurrencyStatus = status,
appCurrencyCode = appCurrencyCode,
)
}
@AssistedFactory
interface Factory : OnrampOperationComponent.Factory {
override fun create(
context: AppComponentContext,
params: OnrampOperationComponent.Params,
): DefaultOnrampOperationComponent
}
}

View file

@ -1,46 +0,0 @@
package com.tangem.features.onramp.selecttoken
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
import com.tangem.features.onramp.selecttoken.ui.OnrampSelectToken
import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultOnrampSelectTokenComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
onrampTokenListComponentFactory: OnrampTokenListComponent.Factory,
@Assisted private val params: OnrampSelectTokenComponent.Params,
) : AppComponentContext by appComponentContext, OnrampSelectTokenComponent {
private val onrampTokenListComponent: OnrampTokenListComponent = onrampTokenListComponentFactory.create(
context = child(key = "token_list"),
params = OnrampTokenListComponent.Params(
hasSearchBar = params.hasSearchBar,
userWalletId = params.userWalletId,
onTokenClick = params.onTokenClick,
),
)
@Composable
override fun Content(modifier: Modifier) {
OnrampSelectToken(
titleResId = params.titleResId,
onBackClick = router::pop,
onrampTokenListComponent = onrampTokenListComponent,
modifier = modifier,
)
}
@AssistedFactory
interface Factory : OnrampSelectTokenComponent.Factory {
override fun create(
context: AppComponentContext,
params: OnrampSelectTokenComponent.Params,
): DefaultOnrampSelectTokenComponent
}
}

View file

@ -1,32 +1,29 @@
package com.tangem.features.onramp.selecttoken package com.tangem.features.onramp.selecttoken
import androidx.annotation.StringRes
import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.onramp.entity.OnrampOperation
/** /**
* Select token component * Base operation component
* *
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
interface OnrampSelectTokenComponent : ComposableContentComponent { internal interface OnrampOperationComponent : ComposableContentComponent {
interface Factory : ComponentFactory<Params, OnrampSelectTokenComponent> interface Factory : ComponentFactory<Params, OnrampOperationComponent>
/** /**
* Params * Params
* *
* @property operation operation
* @property hasSearchBar flag that indicates if search bar should be shown * @property hasSearchBar flag that indicates if search bar should be shown
* @property userWalletId id of multi-currency wallet * @property userWalletId id of multi-currency wallet
* @property titleResId resource id of title
* @property onTokenClick callback for token click
*/ */
data class Params( data class Params(
val operation: OnrampOperation,
val hasSearchBar: Boolean, val hasSearchBar: Boolean,
val userWalletId: UserWalletId, val userWalletId: UserWalletId,
@StringRes val titleResId: Int,
val onTokenClick: (CryptoCurrencyStatus) -> Unit,
) )
} }

View file

@ -0,0 +1,20 @@
package com.tangem.features.onramp.selecttoken.di
import com.tangem.features.onramp.selecttoken.DefaultOnrampOperationComponent
import com.tangem.features.onramp.selecttoken.OnrampOperationComponent
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal interface OnrampOperationComponentModule {
@Binds
@Singleton
fun bindOnrampOperationComponentFactory(
factory: DefaultOnrampOperationComponent.Factory,
): OnrampOperationComponent.Factory
}

View file

@ -1,20 +0,0 @@
package com.tangem.features.onramp.selecttoken.di
import com.tangem.features.onramp.selecttoken.DefaultOnrampSelectTokenComponent
import com.tangem.features.onramp.selecttoken.OnrampSelectTokenComponent
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal interface OnrampSelectTokenComponentModule {
@Binds
@Singleton
fun bindOnrampSelectTokenComponentFactory(
factory: DefaultOnrampSelectTokenComponent.Factory,
): OnrampSelectTokenComponent.Factory
}

View file

@ -3,37 +3,27 @@ package com.tangem.features.onramp.sell
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import arrow.core.getOrElse
import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.tokens.legacy.TradeCryptoAction
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.onramp.component.SellCryptoComponent import com.tangem.features.onramp.component.SellCryptoComponent
import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.entity.OnrampOperation
import com.tangem.features.onramp.selecttoken.OnrampSelectTokenComponent import com.tangem.features.onramp.selecttoken.OnrampOperationComponent
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import kotlinx.coroutines.launch
@Stable @Stable
internal class DefaultSellCryptoComponent @AssistedInject constructor( internal class DefaultSellCryptoComponent @AssistedInject constructor(
onrampSelectTokenComponentFactory: OnrampSelectTokenComponent.Factory, onrampOperationComponentFactory: OnrampOperationComponent.Factory,
@Assisted private val appComponentContext: AppComponentContext, @Assisted private val appComponentContext: AppComponentContext,
@Assisted private val params: SellCryptoComponent.Params, @Assisted private val params: SellCryptoComponent.Params,
private val reduxStateHolder: ReduxStateHolder,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
) : SellCryptoComponent { ) : SellCryptoComponent {
private val selectTokenComponent: OnrampSelectTokenComponent = onrampSelectTokenComponentFactory.create( private val selectTokenComponent: OnrampOperationComponent = onrampOperationComponentFactory.create(
context = appComponentContext, context = appComponentContext,
params = OnrampSelectTokenComponent.Params( params = OnrampOperationComponent.Params(
operation = OnrampOperation.SELL,
hasSearchBar = true, hasSearchBar = true,
userWalletId = params.userWalletId, userWalletId = params.userWalletId,
titleResId = R.string.common_sell,
onTokenClick = ::onTokenClick,
), ),
) )
@ -42,18 +32,6 @@ internal class DefaultSellCryptoComponent @AssistedInject constructor(
selectTokenComponent.Content(modifier = modifier) selectTokenComponent.Content(modifier = modifier)
} }
private fun onTokenClick(status: CryptoCurrencyStatus) {
appComponentContext.componentScope.launch {
reduxStateHolder.dispatch(
TradeCryptoAction.Sell(
cryptoCurrencyStatus = status,
appCurrencyCode = getSelectedAppCurrencyUseCase.invokeSync()
.getOrElse { AppCurrency.Default }.code,
),
)
}
}
@AssistedFactory @AssistedFactory
interface Factory : SellCryptoComponent.Factory { interface Factory : SellCryptoComponent.Factory {

View file

@ -7,6 +7,7 @@ import androidx.compose.ui.Modifier
import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.onramp.entity.OnrampOperation
/** Token list component that present list of token for multi-currency wallet */ /** Token list component that present list of token for multi-currency wallet */
@Stable @Stable
@ -21,11 +22,13 @@ internal interface OnrampTokenListComponent {
/** /**
* Params * Params
* *
* @property hasSearchBar flag that indicates if search bar should be shown * @property filterOperation operation that is used to filter tokens by availability
* @property userWalletId id of multi-currency wallet * @property hasSearchBar flag that indicates if search bar should be shown
* @property onTokenClick callback for token click * @property userWalletId id of multi-currency wallet
* @property onTokenClick callback for token click
*/ */
data class Params( data class Params(
val filterOperation: OnrampOperation,
val hasSearchBar: Boolean, val hasSearchBar: Boolean,
val userWalletId: UserWalletId, val userWalletId: UserWalletId,
val onTokenClick: (CryptoCurrencyStatus) -> Unit, val onTokenClick: (CryptoCurrencyStatus) -> Unit,

View file

@ -8,9 +8,12 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.core.utils.getOrElse import com.tangem.domain.core.utils.getOrElse
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.GetTokenListUseCase
import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.onramp.entity.OnrampOperation
import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent
import com.tangem.features.onramp.tokenlist.entity.TokenListUM import com.tangem.features.onramp.tokenlist.entity.TokenListUM
import com.tangem.features.onramp.tokenlist.entity.TokenListUMController import com.tangem.features.onramp.tokenlist.entity.TokenListUMController
@ -32,11 +35,16 @@ internal class OnrampTokenListModel @Inject constructor(
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val getTokenListUseCase: GetTokenListUseCase, private val getTokenListUseCase: GetTokenListUseCase,
private val getWalletsUseCase: GetWalletsUseCase,
private val rampStateManager: RampStateManager,
) : Model() { ) : Model() {
val state: StateFlow<TokenListUM> = tokenListUMController.state val state: StateFlow<TokenListUM> = tokenListUMController.state
private val params: OnrampTokenListComponent.Params = paramsContainer.require() private val params: OnrampTokenListComponent.Params = paramsContainer.require()
private val scanResponse by lazy {
getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }.scanResponse
}
init { init {
subscribeOnUpdateState() subscribeOnUpdateState()
@ -98,8 +106,14 @@ internal class OnrampTokenListModel @Inject constructor(
} }
} }
// TODO: [REDACTED_JIRA]
private fun List<CryptoCurrencyStatus>.filterByAvailability(): List<CryptoCurrencyStatus> { private fun List<CryptoCurrencyStatus>.filterByAvailability(): List<CryptoCurrencyStatus> {
return this return filter {
when (params.filterOperation) {
OnrampOperation.BUY -> {
rampStateManager.availableForBuy(scanResponse = scanResponse, cryptoCurrency = it.currency)
}
OnrampOperation.SELL -> rampStateManager.availableForSell(cryptoCurrency = it.currency)
}
}
} }
} }