Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-15 11:22:40 +03:00
commit c2cd7e6b4c
675 changed files with 9577 additions and 4375 deletions

View file

@ -48,6 +48,8 @@ dependencies {
implementation(projects.domain.settings)
implementation(projects.domain.transaction.models)
implementation(projects.domain.account.status)
implementation(projects.domain.appTheme)
implementation(projects.domain.appTheme.models)
/** DI */
implementation(deps.hilt.android)

View file

@ -1,13 +1,5 @@
<?xml version="1.0" ?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>NonBooleanPropertyPrefixedWithIs:AvailableSwapPairsModel.kt$AvailableSwapPairsModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
<ID>NonBooleanPropertyPrefixedWithIs:HotCryptoModel.kt$HotCryptoModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
<ID>NonBooleanPropertyPrefixedWithIs:OnrampAddTokenUiBuilder.kt$OnrampAddTokenUiBuilder$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
<ID>NonBooleanPropertyPrefixedWithIs:OnrampMainComponentModel.kt$OnrampMainComponentModel$private val isDemoCardUseCase: IsDemoCardUseCase</ID>
<ID>NonBooleanPropertyPrefixedWithIs:OnrampOperationModel.kt$OnrampOperationModel$private val isDemoCardUseCase: IsDemoCardUseCase</ID>
<ID>NonBooleanPropertyPrefixedWithIs:OnrampTokenListModel.kt$OnrampTokenListModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
<ID>NonBooleanPropertyPrefixedWithIs:SwapSelectTokensModel.kt$SwapSelectTokensModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
</CurrentIssues>
<CurrentIssues/>
</SmellBaseline>

View file

@ -86,7 +86,7 @@ internal class AllOffersStateFactory(
fun getPaymentsState(): AllOffersStateUM {
return when (val currentState = currentStateProvider.invoke()) {
is AllOffersStateUM.Content -> {
analyticsEventHandler.send(OnrampAnalyticsEvent.PaymentMethodsScreenOpened)
analyticsEventHandler.send(OnrampAnalyticsEvent.PaymentMethodsScreenOpened())
currentState.copy(currentMethod = null)
}
AllOffersStateUM.Loading,

View file

@ -48,8 +48,8 @@ internal class AllOffersModel @Inject constructor(
init {
subscribeOnAllOffers()
analyticsEventHandler.send(OnrampAnalyticsEvent.PaymentMethodsScreenOpened)
analyticsEventHandler.send(OnrampAnalyticsEvent.AllOffersClicked)
analyticsEventHandler.send(OnrampAnalyticsEvent.PaymentMethodsScreenOpened())
analyticsEventHandler.send(OnrampAnalyticsEvent.AllOffersClicked())
}
fun dismiss() {

View file

@ -69,7 +69,7 @@ internal class ConfirmResidencyModel @Inject constructor(
} else {
ConfirmResidencyUM.ActionButtonConfig(
onClick = {
analyticsEventHandler.send(OnrampAnalyticsEvent.CloseOnramp)
analyticsEventHandler.send(OnrampAnalyticsEvent.CloseOnramp())
router.pop()
},
text = resourceReference(R.string.common_close),
@ -77,7 +77,7 @@ internal class ConfirmResidencyModel @Inject constructor(
}
private fun onChangeClick() {
analyticsEventHandler.send(OnrampAnalyticsEvent.OnResidenceChange)
analyticsEventHandler.send(OnrampAnalyticsEvent.OnResidenceChange())
bottomSheetNavigation.activate(ConfirmResidencyBottomSheetConfig.SelectCountry(params.onDismiss))
}
}

View file

@ -240,11 +240,11 @@ internal class OnrampAmountStateFactory(
val errorTextRes = when (error) {
is OnrampError.AmountError.TooBigError -> {
analyticsEventHandler.send(OnrampAnalyticsEvent.MaxAmountError)
analyticsEventHandler.send(OnrampAnalyticsEvent.MaxAmountError())
R.string.onramp_max_amount_restriction
}
is OnrampError.AmountError.TooSmallError -> {
analyticsEventHandler.send(OnrampAnalyticsEvent.MinAmountError)
analyticsEventHandler.send(OnrampAnalyticsEvent.MinAmountError())
R.string.onramp_min_amount_restriction
}
}

View file

@ -288,7 +288,7 @@ internal class OnrampMainComponentModel @Inject constructor(
}
override fun openCurrenciesList() {
analyticsEventHandler.send(OnrampAnalyticsEvent.SelectCurrencyScreenOpened)
analyticsEventHandler.send(OnrampAnalyticsEvent.SelectCurrencyScreenOpened())
bottomSheetNavigation.activate(OnrampMainBottomSheetConfig.CurrenciesList)
}
@ -333,7 +333,7 @@ internal class OnrampMainComponentModel @Inject constructor(
}
private fun onCloseClick() {
analyticsEventHandler.send(OnrampAnalyticsEvent.CloseOnramp)
analyticsEventHandler.send(OnrampAnalyticsEvent.CloseOnramp())
router.pop()
}

View file

@ -118,11 +118,11 @@ internal class OnrampV2AmountStateFactory(
val errorTextRes = when (error) {
is OnrampError.AmountError.TooBigError -> {
analyticsEventHandler.send(OnrampAnalyticsEvent.MaxAmountError)
analyticsEventHandler.send(OnrampAnalyticsEvent.MaxAmountError())
R.string.onramp_max_amount_restriction
}
is OnrampError.AmountError.TooSmallError -> {
analyticsEventHandler.send(OnrampAnalyticsEvent.MinAmountError)
analyticsEventHandler.send(OnrampAnalyticsEvent.MinAmountError())
R.string.onramp_min_amount_restriction
}
}

View file

@ -124,7 +124,7 @@ internal class OnrampV2MainComponentModel @Inject constructor(
}
override fun openCurrenciesList() {
analyticsEventHandler.send(OnrampAnalyticsEvent.SelectCurrencyScreenOpened)
analyticsEventHandler.send(OnrampAnalyticsEvent.SelectCurrencyScreenOpened())
bottomSheetNavigation.activate(OnrampV2MainBottomSheetConfig.CurrenciesList)
}
@ -225,7 +225,7 @@ internal class OnrampV2MainComponentModel @Inject constructor(
}
private fun onCloseClick() {
analyticsEventHandler.send(OnrampAnalyticsEvent.CloseOnramp)
analyticsEventHandler.send(OnrampAnalyticsEvent.CloseOnramp())
router.pop()
}

View file

@ -66,7 +66,7 @@ internal class SelectProviderModel @Inject constructor(
userCountry = getUserCountryUseCase.invokeSync().getOrNull()
?: UserCountry.Other(Locale.getDefault().country)
analyticsEventHandler.send(OnrampAnalyticsEvent.ProvidersScreenOpened)
analyticsEventHandler.send(OnrampAnalyticsEvent.ProvidersScreenOpened())
getPaymentMethods()
getProviders(params.selectedPaymentMethod)
}
@ -140,7 +140,7 @@ internal class SelectProviderModel @Inject constructor(
}
private fun openPaymentMethods() {
analyticsEventHandler.send(OnrampAnalyticsEvent.PaymentMethodsScreenOpened)
analyticsEventHandler.send(OnrampAnalyticsEvent.PaymentMethodsScreenOpened())
bottomSheetNavigation.activate(
ProviderListBottomSheetConfig.PaymentMethods(
selectedMethodId = state.value.selectedPaymentMethod.paymentMethod.id,

View file

@ -42,9 +42,9 @@ internal class DefaultOnrampRedirectComponent @AssistedInject constructor(
BackHandler(onBack = params.onBack)
OnrampRedirectContent(modifier = modifier, state = model.state)
val isDarkTheme = isSystemInDarkTheme()
val isSystemInDarkTheme = isSystemInDarkTheme()
LaunchedEffect(model.state) {
model.getRedirectUrl(isDarkTheme = isDarkTheme)
model.getRedirectUrl(isSystemInDarkTheme = isSystemInDarkTheme)
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.features.onramp.redirect.model
import arrow.core.getOrElse
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.api.AnalyticsEventHandler
@ -13,6 +14,8 @@ 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.message.DialogMessage
import com.tangem.domain.apptheme.GetAppThemeModeUseCase
import com.tangem.domain.apptheme.model.AppThemeMode
import com.tangem.domain.onramp.GetOnrampRedirectUrlUseCase
import com.tangem.domain.onramp.model.cache.OnrampTransaction
import com.tangem.domain.onramp.model.error.OnrampError
@ -24,7 +27,9 @@ import com.tangem.features.onramp.redirect.entity.OnrampRedirectUM
import com.tangem.features.onramp.success.OnrampSuccessScreenListener
import com.tangem.features.onramp.utils.sendOnrampErrorEvent
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import timber.log.Timber
@ -38,6 +43,7 @@ internal class OnrampRedirectModel @Inject constructor(
private val messageSender: UiMessageSender,
private val analyticsEventHandler: AnalyticsEventHandler,
private val onrampSuccessScreenListener: OnrampSuccessScreenListener,
private val getAppThemeModeUseCase: GetAppThemeModeUseCase,
private val appRouter: AppRouter,
paramsContainer: ParamsContainer,
getWalletsUseCase: GetWalletsUseCase,
@ -74,8 +80,17 @@ internal class OnrampRedirectModel @Inject constructor(
subscribeToOnrampSuccessListener()
}
fun getRedirectUrl(isDarkTheme: Boolean) {
fun getRedirectUrl(isSystemInDarkTheme: Boolean) {
modelScope.launch {
val isDarkTheme = getAppThemeModeUseCase()
.map { result ->
when (result.getOrElse { AppThemeMode.FOLLOW_SYSTEM }) {
AppThemeMode.FORCE_DARK -> true
AppThemeMode.FORCE_LIGHT -> false
AppThemeMode.FOLLOW_SYSTEM -> isSystemInDarkTheme
}
}.firstOrNull() ?: isSystemInDarkTheme
getOnrampRedirectUrlUseCase.invoke(
userWallet = selectedUserWallet,
quote = params.onrampProviderWithQuote,

View file

@ -60,7 +60,7 @@ internal class OnrampSelectCountryModel @Inject constructor(
val state: StateFlow<CountryListUM> get() = controller.state
init {
analyticsEventHandler.send(OnrampAnalyticsEvent.SelectResidenceOpened)
analyticsEventHandler.send(OnrampAnalyticsEvent.SelectResidenceOpened())
updateCountriesList()
modelScope.launch { subscribeOnUpdateState() }
}

View file

@ -48,19 +48,20 @@ internal class OnrampOperationModel @Inject constructor(
private val messageSender: UiMessageSender,
private val rampStateManager: RampStateManager,
) : Model() {
private val params: Params = paramsContainer.require()
private val selectedUserWallet = getWalletsUseCase.invokeSync()
.first { it.walletId == params.userWalletId }
private val params: Params = paramsContainer.require()
val state: StateFlow<OnrampOperationUM>
field = 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
is Params.Buy -> MainScreenAnalyticsEvent.BuyScreenOpened()
is Params.Sell -> MainScreenAnalyticsEvent.SellScreenOpened()
},
)
}

View file

@ -37,7 +37,7 @@ internal class OnrampSettingsModel @Inject constructor(
val bottomSheetNavigation: SlotNavigation<OnrampSettingsConfig> = SlotNavigation()
init {
analyticsEventHandler.send(OnrampAnalyticsEvent.SettingsOpened)
analyticsEventHandler.send(OnrampAnalyticsEvent.SettingsOpened())
subscribeOnUpdateState()
}

View file

@ -74,7 +74,7 @@ internal class OnrampSuccessComponentModel @Inject constructor(
}
override fun goToProviderClick(providerLink: String) {
analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider)
analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider())
urlOpener.openUrl(providerLink)
}

View file

@ -50,7 +50,7 @@ internal class DefaultSwapSelectTokensComponent @AssistedInject constructor(
)
init {
analyticsEventHandler.send(event = MainScreenAnalyticsEvent.SwapScreenOpened)
analyticsEventHandler.send(event = MainScreenAnalyticsEvent.SwapScreenOpened())
}
@Composable