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
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.2
|
||||
2.5.23
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ dependencies {
|
|||
implementation(projects.domain.appCurrency.models)
|
||||
implementation(projects.domain.balanceHiding)
|
||||
implementation(projects.domain.balanceHiding.models)
|
||||
implementation(projects.domain.demo)
|
||||
implementation(projects.domain.legacy)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.onramp)
|
||||
|
|
|
|||
|
|
@ -2,39 +2,26 @@ package com.tangem.features.onramp.selecttoken
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
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.impl.R
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.features.onramp.selecttoken.model.OnrampOperationModel
|
||||
import com.tangem.features.onramp.selecttoken.ui.OnrampSelectToken
|
||||
import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent
|
||||
import com.tangem.features.onramp.tokenlist.entity.OnrampOperation
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
// TODO: Add OnrampOperationModel
|
||||
@Suppress("LongParameterList")
|
||||
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,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : AppComponentContext by appComponentContext, OnrampOperationComponent {
|
||||
|
||||
private val model: OnrampOperationModel = getOrCreateModel(params)
|
||||
|
||||
private val onrampTokenListComponent: OnrampTokenListComponent = onrampTokenListComponentFactory.create(
|
||||
context = child(key = "token_list"),
|
||||
params = OnrampTokenListComponent.Params(
|
||||
|
|
@ -44,78 +31,21 @@ internal class DefaultOnrampOperationComponent @AssistedInject constructor(
|
|||
},
|
||||
hasSearchBar = true,
|
||||
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
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state = model.state.collectAsStateWithLifecycle()
|
||||
|
||||
OnrampSelectToken(
|
||||
titleResId = when (params) {
|
||||
is OnrampOperationComponent.Params.Buy -> R.string.common_buy
|
||||
is OnrampOperationComponent.Params.Sell -> R.string.common_sell
|
||||
},
|
||||
onBackClick = ::onBackClick,
|
||||
state = state.value,
|
||||
onrampTokenListComponent = onrampTokenListComponent,
|
||||
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
|
||||
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
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
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.res.TangemTheme
|
||||
import com.tangem.features.onramp.impl.R
|
||||
import com.tangem.features.onramp.selecttoken.entity.OnrampOperationUM
|
||||
import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
internal fun OnrampSelectToken(
|
||||
@StringRes titleResId: Int,
|
||||
onBackClick: () -> Unit,
|
||||
state: OnrampOperationUM,
|
||||
onrampTokenListComponent: OnrampTokenListComponent,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BackHandler(onBack = onBackClick)
|
||||
BackHandler(onBack = state.onBackClick)
|
||||
|
||||
LazyColumn(
|
||||
modifier = modifier
|
||||
|
|
@ -35,8 +34,8 @@ internal fun OnrampSelectToken(
|
|||
) {
|
||||
stickyHeader(key = "header") {
|
||||
AppBarWithBackButton(
|
||||
onBackClick = onBackClick,
|
||||
text = stringResource(id = titleResId),
|
||||
onBackClick = state.onBackClick,
|
||||
text = stringResource(id = state.titleResId),
|
||||
iconRes = R.drawable.ic_close_24,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue