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.models)
implementation(projects.domain.legacy)
implementation(projects.domain.models)
implementation(projects.domain.tokens)
implementation(projects.domain.tokens.models)
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.Stable
import androidx.compose.ui.Modifier
import arrow.core.getOrElse
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.impl.R
import com.tangem.features.onramp.selecttoken.OnrampSelectTokenComponent
import com.tangem.features.onramp.entity.OnrampOperation
import com.tangem.features.onramp.selecttoken.OnrampOperationComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.launch
@Stable
internal class DefaultBuyCryptoComponent @AssistedInject constructor(
onrampSelectTokenComponentFactory: OnrampSelectTokenComponent.Factory,
onrampOperationComponentFactory: OnrampOperationComponent.Factory,
@Assisted private val appComponentContext: AppComponentContext,
@Assisted private val params: BuyCryptoComponent.Params,
private val reduxStateHolder: ReduxStateHolder,
private val getWalletsUseCase: GetWalletsUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
) : BuyCryptoComponent {
private val selectTokenComponent: OnrampSelectTokenComponent = onrampSelectTokenComponentFactory.create(
private val selectTokenComponent: OnrampOperationComponent = onrampOperationComponentFactory.create(
context = appComponentContext,
params = OnrampSelectTokenComponent.Params(
params = OnrampOperationComponent.Params(
operation = OnrampOperation.BUY,
hasSearchBar = true,
userWalletId = params.userWalletId,
titleResId = R.string.common_buy,
onTokenClick = ::onTokenClick,
),
)
@ -44,19 +32,6 @@ internal class DefaultBuyCryptoComponent @AssistedInject constructor(
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
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
import androidx.annotation.StringRes
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.onramp.entity.OnrampOperation
/**
* Select token component
* Base operation component
*
[REDACTED_AUTHOR]
*/
interface OnrampSelectTokenComponent : ComposableContentComponent {
internal interface OnrampOperationComponent : ComposableContentComponent {
interface Factory : ComponentFactory<Params, OnrampSelectTokenComponent>
interface Factory : ComponentFactory<Params, OnrampOperationComponent>
/**
* Params
*
* @property operation operation
* @property hasSearchBar flag that indicates if search bar should be shown
* @property userWalletId id of multi-currency wallet
* @property titleResId resource id of title
* @property onTokenClick callback for token click
*/
data class Params(
val operation: OnrampOperation,
val hasSearchBar: Boolean,
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.Stable
import androidx.compose.ui.Modifier
import arrow.core.getOrElse
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.impl.R
import com.tangem.features.onramp.selecttoken.OnrampSelectTokenComponent
import com.tangem.features.onramp.entity.OnrampOperation
import com.tangem.features.onramp.selecttoken.OnrampOperationComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.launch
@Stable
internal class DefaultSellCryptoComponent @AssistedInject constructor(
onrampSelectTokenComponentFactory: OnrampSelectTokenComponent.Factory,
onrampOperationComponentFactory: OnrampOperationComponent.Factory,
@Assisted private val appComponentContext: AppComponentContext,
@Assisted private val params: SellCryptoComponent.Params,
private val reduxStateHolder: ReduxStateHolder,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
) : SellCryptoComponent {
private val selectTokenComponent: OnrampSelectTokenComponent = onrampSelectTokenComponentFactory.create(
private val selectTokenComponent: OnrampOperationComponent = onrampOperationComponentFactory.create(
context = appComponentContext,
params = OnrampSelectTokenComponent.Params(
params = OnrampOperationComponent.Params(
operation = OnrampOperation.SELL,
hasSearchBar = true,
userWalletId = params.userWalletId,
titleResId = R.string.common_sell,
onTokenClick = ::onTokenClick,
),
)
@ -42,18 +32,6 @@ internal class DefaultSellCryptoComponent @AssistedInject constructor(
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
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.domain.tokens.model.CryptoCurrencyStatus
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 */
@Stable
@ -21,11 +22,13 @@ internal interface OnrampTokenListComponent {
/**
* Params
*
* @property hasSearchBar flag that indicates if search bar should be shown
* @property userWalletId id of multi-currency wallet
* @property onTokenClick callback for token click
* @property filterOperation operation that is used to filter tokens by availability
* @property hasSearchBar flag that indicates if search bar should be shown
* @property userWalletId id of multi-currency wallet
* @property onTokenClick callback for token click
*/
data class Params(
val filterOperation: OnrampOperation,
val hasSearchBar: Boolean,
val userWalletId: UserWalletId,
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.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.core.utils.getOrElse
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.tokens.GetTokenListUseCase
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
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.entity.TokenListUM
import com.tangem.features.onramp.tokenlist.entity.TokenListUMController
@ -32,11 +35,16 @@ internal class OnrampTokenListModel @Inject constructor(
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val getTokenListUseCase: GetTokenListUseCase,
private val getWalletsUseCase: GetWalletsUseCase,
private val rampStateManager: RampStateManager,
) : Model() {
val state: StateFlow<TokenListUM> = tokenListUMController.state
private val params: OnrampTokenListComponent.Params = paramsContainer.require()
private val scanResponse by lazy {
getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }.scanResponse
}
init {
subscribeOnUpdateState()
@ -98,8 +106,14 @@ internal class OnrampTokenListModel @Inject constructor(
}
}
// TODO: [REDACTED_JIRA]
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)
}
}
}
}