Updated on 2026-08-14
This commit is contained in:
parent
f2bf06648f
commit
537f15517e
7 changed files with 201 additions and 86 deletions
|
|
@ -217,4 +217,4 @@ DEPENDENCIES
|
||||||
fastlane-plugin-firebase_app_distribution
|
fastlane-plugin-firebase_app_distribution
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.17.2
|
2.5.23
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ dependencies {
|
||||||
implementation(projects.domain.appCurrency.models)
|
implementation(projects.domain.appCurrency.models)
|
||||||
implementation(projects.domain.balanceHiding)
|
implementation(projects.domain.balanceHiding)
|
||||||
implementation(projects.domain.balanceHiding.models)
|
implementation(projects.domain.balanceHiding.models)
|
||||||
|
implementation(projects.domain.demo)
|
||||||
implementation(projects.domain.legacy)
|
implementation(projects.domain.legacy)
|
||||||
implementation(projects.domain.models)
|
implementation(projects.domain.models)
|
||||||
implementation(projects.domain.onramp)
|
implementation(projects.domain.onramp)
|
||||||
|
|
|
||||||
|
|
@ -2,39 +2,26 @@ package com.tangem.features.onramp.selecttoken
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import arrow.core.getOrElse
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|
||||||
import com.tangem.core.analytics.models.AnalyticsParam
|
|
||||||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
|
||||||
import com.tangem.core.decompose.context.AppComponentContext
|
import com.tangem.core.decompose.context.AppComponentContext
|
||||||
import com.tangem.core.decompose.context.child
|
import com.tangem.core.decompose.context.child
|
||||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
import com.tangem.core.decompose.model.getOrCreateModel
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.features.onramp.selecttoken.model.OnrampOperationModel
|
||||||
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.impl.R
|
|
||||||
import com.tangem.features.onramp.selecttoken.ui.OnrampSelectToken
|
import com.tangem.features.onramp.selecttoken.ui.OnrampSelectToken
|
||||||
import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent
|
import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent
|
||||||
import com.tangem.features.onramp.tokenlist.entity.OnrampOperation
|
import com.tangem.features.onramp.tokenlist.entity.OnrampOperation
|
||||||
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
|
|
||||||
|
|
||||||
// TODO: Add OnrampOperationModel
|
|
||||||
@Suppress("LongParameterList")
|
|
||||||
internal class DefaultOnrampOperationComponent @AssistedInject constructor(
|
internal class DefaultOnrampOperationComponent @AssistedInject constructor(
|
||||||
@Assisted appComponentContext: AppComponentContext,
|
@Assisted appComponentContext: AppComponentContext,
|
||||||
onrampTokenListComponentFactory: OnrampTokenListComponent.Factory,
|
onrampTokenListComponentFactory: OnrampTokenListComponent.Factory,
|
||||||
@Assisted private val params: OnrampOperationComponent.Params,
|
@Assisted private val params: OnrampOperationComponent.Params,
|
||||||
private val reduxStateHolder: ReduxStateHolder,
|
|
||||||
private val getWalletsUseCase: GetWalletsUseCase,
|
|
||||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
|
||||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
|
||||||
) : AppComponentContext by appComponentContext, OnrampOperationComponent {
|
) : AppComponentContext by appComponentContext, OnrampOperationComponent {
|
||||||
|
|
||||||
|
private val model: OnrampOperationModel = getOrCreateModel(params)
|
||||||
|
|
||||||
private val onrampTokenListComponent: OnrampTokenListComponent = onrampTokenListComponentFactory.create(
|
private val onrampTokenListComponent: OnrampTokenListComponent = onrampTokenListComponentFactory.create(
|
||||||
context = child(key = "token_list"),
|
context = child(key = "token_list"),
|
||||||
params = OnrampTokenListComponent.Params(
|
params = OnrampTokenListComponent.Params(
|
||||||
|
|
@ -44,78 +31,21 @@ internal class DefaultOnrampOperationComponent @AssistedInject constructor(
|
||||||
},
|
},
|
||||||
hasSearchBar = true,
|
hasSearchBar = true,
|
||||||
userWalletId = params.userWalletId,
|
userWalletId = params.userWalletId,
|
||||||
onTokenClick = { _, status -> onTokenClick(status) },
|
onTokenClick = { _, status -> model.onTokenClick(status) },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
init {
|
|
||||||
analyticsEventHandler.send(
|
|
||||||
event = when (params) {
|
|
||||||
is OnrampOperationComponent.Params.Buy -> MainScreenAnalyticsEvent.BuyScreenOpened
|
|
||||||
is OnrampOperationComponent.Params.Sell -> MainScreenAnalyticsEvent.SellScreenOpened
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content(modifier: Modifier) {
|
override fun Content(modifier: Modifier) {
|
||||||
|
val state = model.state.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
OnrampSelectToken(
|
OnrampSelectToken(
|
||||||
titleResId = when (params) {
|
state = state.value,
|
||||||
is OnrampOperationComponent.Params.Buy -> R.string.common_buy
|
|
||||||
is OnrampOperationComponent.Params.Sell -> R.string.common_sell
|
|
||||||
},
|
|
||||||
onBackClick = ::onBackClick,
|
|
||||||
onrampTokenListComponent = onrampTokenListComponent,
|
onrampTokenListComponent = onrampTokenListComponent,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onTokenClick(status: CryptoCurrencyStatus) {
|
|
||||||
val currencySymbol = status.currency.symbol
|
|
||||||
analyticsEventHandler.send(
|
|
||||||
event = when (params) {
|
|
||||||
is OnrampOperationComponent.Params.Buy -> {
|
|
||||||
MainScreenAnalyticsEvent.BuyTokenClicked(currencySymbol = currencySymbol)
|
|
||||||
}
|
|
||||||
is OnrampOperationComponent.Params.Sell -> {
|
|
||||||
MainScreenAnalyticsEvent.SellTokenClicked(currencySymbol = currencySymbol)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
componentScope.launch {
|
|
||||||
val appCurrencyCode = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default }.code
|
|
||||||
|
|
||||||
reduxStateHolder.dispatch(
|
|
||||||
action = when (params) {
|
|
||||||
is OnrampOperationComponent.Params.Buy -> getBuyAction(status, appCurrencyCode)
|
|
||||||
is OnrampOperationComponent.Params.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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onBackClick() {
|
|
||||||
analyticsEventHandler.send(
|
|
||||||
event = MainScreenAnalyticsEvent.ButtonClose(
|
|
||||||
source = when (params) {
|
|
||||||
is OnrampOperationComponent.Params.Buy -> AnalyticsParam.ScreensSources.Buy
|
|
||||||
is OnrampOperationComponent.Params.Sell -> AnalyticsParam.ScreensSources.Sell
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
router.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
interface Factory : OnrampOperationComponent.Factory {
|
interface Factory : OnrampOperationComponent.Factory {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.tangem.features.onramp.selecttoken.di
|
||||||
|
|
||||||
|
import com.tangem.core.decompose.di.DecomposeComponent
|
||||||
|
import com.tangem.core.decompose.model.Model
|
||||||
|
import com.tangem.features.onramp.selecttoken.model.OnrampOperationModel
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.multibindings.ClassKey
|
||||||
|
import dagger.multibindings.IntoMap
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(DecomposeComponent::class)
|
||||||
|
internal interface OnrampOperationModelModule {
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@ClassKey(OnrampOperationModel::class)
|
||||||
|
fun bindOnrampOperationModel(model: OnrampOperationModel): Model
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.tangem.features.onramp.selecttoken.entity
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
|
||||||
|
internal data class OnrampOperationUM(
|
||||||
|
@StringRes val titleResId: Int,
|
||||||
|
val onBackClick: () -> Unit,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,157 @@
|
||||||
|
package com.tangem.features.onramp.selecttoken.model
|
||||||
|
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import arrow.core.getOrElse
|
||||||
|
import com.tangem.common.routing.AppRouter
|
||||||
|
import com.tangem.common.ui.alerts.models.AlertDemoModeUM
|
||||||
|
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||||
|
import com.tangem.core.analytics.models.AnalyticsParam
|
||||||
|
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||||
|
import com.tangem.core.decompose.di.ComponentScoped
|
||||||
|
import com.tangem.core.decompose.model.Model
|
||||||
|
import com.tangem.core.decompose.model.ParamsContainer
|
||||||
|
import com.tangem.core.decompose.ui.UiMessageSender
|
||||||
|
import com.tangem.core.ui.components.BasicDialog
|
||||||
|
import com.tangem.core.ui.components.DialogButtonUM
|
||||||
|
import com.tangem.core.ui.extensions.resolveReference
|
||||||
|
import com.tangem.core.ui.message.ContentMessage
|
||||||
|
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||||
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
|
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||||
|
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.impl.R
|
||||||
|
import com.tangem.features.onramp.selecttoken.OnrampOperationComponent.Params
|
||||||
|
import com.tangem.features.onramp.selecttoken.entity.OnrampOperationUM
|
||||||
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@Suppress("LongParameterList")
|
||||||
|
@ComponentScoped
|
||||||
|
internal class OnrampOperationModel @Inject constructor(
|
||||||
|
paramsContainer: ParamsContainer,
|
||||||
|
getWalletsUseCase: GetWalletsUseCase,
|
||||||
|
override val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
private val router: AppRouter,
|
||||||
|
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||||
|
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||||
|
private val reduxStateHolder: ReduxStateHolder,
|
||||||
|
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||||
|
private val messageSender: UiMessageSender,
|
||||||
|
) : Model() {
|
||||||
|
|
||||||
|
val state: StateFlow<OnrampOperationUM> get() = _state
|
||||||
|
|
||||||
|
private val params: Params = paramsContainer.require()
|
||||||
|
|
||||||
|
private val _state = MutableStateFlow(value = getInitialState())
|
||||||
|
|
||||||
|
private val selectedUserWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
|
||||||
|
|
||||||
|
init {
|
||||||
|
analyticsEventHandler.send(
|
||||||
|
event = when (params) {
|
||||||
|
is Params.Buy -> MainScreenAnalyticsEvent.BuyScreenOpened
|
||||||
|
is Params.Sell -> MainScreenAnalyticsEvent.SellScreenOpened
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onTokenClick(status: CryptoCurrencyStatus) {
|
||||||
|
val currencySymbol = status.currency.symbol
|
||||||
|
analyticsEventHandler.send(
|
||||||
|
event = when (params) {
|
||||||
|
is Params.Buy -> MainScreenAnalyticsEvent.BuyTokenClicked(currencySymbol = currencySymbol)
|
||||||
|
is Params.Sell -> MainScreenAnalyticsEvent.SellTokenClicked(currencySymbol = currencySymbol)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if (params is Params.Sell) {
|
||||||
|
showErrorIfDemoModeOrElse { selectToken(status) }
|
||||||
|
} else {
|
||||||
|
selectToken(status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun selectToken(status: CryptoCurrencyStatus) {
|
||||||
|
modelScope.launch {
|
||||||
|
val appCurrencyCode = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default }.code
|
||||||
|
|
||||||
|
reduxStateHolder.dispatch(
|
||||||
|
action = when (params) {
|
||||||
|
is Params.Buy -> getBuyAction(status, appCurrencyCode)
|
||||||
|
is Params.Sell -> TradeCryptoAction.Sell(status, appCurrencyCode)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getInitialState(): OnrampOperationUM {
|
||||||
|
return OnrampOperationUM(
|
||||||
|
titleResId = when (params) {
|
||||||
|
is Params.Buy -> R.string.common_buy
|
||||||
|
is Params.Sell -> R.string.common_sell
|
||||||
|
},
|
||||||
|
onBackClick = ::onBackClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onBackClick() {
|
||||||
|
analyticsEventHandler.send(
|
||||||
|
event = MainScreenAnalyticsEvent.ButtonClose(
|
||||||
|
source = when (params) {
|
||||||
|
is Params.Buy -> AnalyticsParam.ScreensSources.Buy
|
||||||
|
is Params.Sell -> AnalyticsParam.ScreensSources.Sell
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
router.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBuyAction(status: CryptoCurrencyStatus, appCurrencyCode: String): TradeCryptoAction {
|
||||||
|
return TradeCryptoAction.Buy(
|
||||||
|
userWallet = selectedUserWallet,
|
||||||
|
cryptoCurrencyStatus = status,
|
||||||
|
appCurrencyCode = appCurrencyCode,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showErrorIfDemoModeOrElse(action: () -> Unit) {
|
||||||
|
if (isDemoCardUseCase(cardId = selectedUserWallet.cardId)) {
|
||||||
|
val alertUM = AlertDemoModeUM(onConfirmClick = {})
|
||||||
|
|
||||||
|
messageSender.send(
|
||||||
|
message = ContentMessage { onDismiss ->
|
||||||
|
val confirmButton = DialogButtonUM(
|
||||||
|
title = alertUM.confirmButtonText.resolveReference(),
|
||||||
|
onClick = {
|
||||||
|
alertUM.onConfirmClick()
|
||||||
|
onDismiss()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
val dismissButton = DialogButtonUM(
|
||||||
|
title = stringResource(id = R.string.common_cancel),
|
||||||
|
onClick = onDismiss,
|
||||||
|
)
|
||||||
|
|
||||||
|
BasicDialog(
|
||||||
|
message = alertUM.message.resolveReference(),
|
||||||
|
confirmButton = confirmButton,
|
||||||
|
onDismissDialog = onDismiss,
|
||||||
|
title = alertUM.title.resolveReference(),
|
||||||
|
dismissButton = dismissButton,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package com.tangem.features.onramp.selecttoken.ui
|
package com.tangem.features.onramp.selecttoken.ui
|
||||||
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.imePadding
|
import androidx.compose.foundation.layout.imePadding
|
||||||
|
|
@ -15,17 +14,17 @@ import androidx.compose.ui.unit.dp
|
||||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.features.onramp.impl.R
|
import com.tangem.features.onramp.impl.R
|
||||||
|
import com.tangem.features.onramp.selecttoken.entity.OnrampOperationUM
|
||||||
import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent
|
import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
internal fun OnrampSelectToken(
|
internal fun OnrampSelectToken(
|
||||||
@StringRes titleResId: Int,
|
state: OnrampOperationUM,
|
||||||
onBackClick: () -> Unit,
|
|
||||||
onrampTokenListComponent: OnrampTokenListComponent,
|
onrampTokenListComponent: OnrampTokenListComponent,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
BackHandler(onBack = onBackClick)
|
BackHandler(onBack = state.onBackClick)
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
|
@ -35,8 +34,8 @@ internal fun OnrampSelectToken(
|
||||||
) {
|
) {
|
||||||
stickyHeader(key = "header") {
|
stickyHeader(key = "header") {
|
||||||
AppBarWithBackButton(
|
AppBarWithBackButton(
|
||||||
onBackClick = onBackClick,
|
onBackClick = state.onBackClick,
|
||||||
text = stringResource(id = titleResId),
|
text = stringResource(id = state.titleResId),
|
||||||
iconRes = R.drawable.ic_close_24,
|
iconRes = R.drawable.ic_close_24,
|
||||||
containerColor = TangemTheme.colors.background.secondary,
|
containerColor = TangemTheme.colors.background.secondary,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue