From 2f767f566a8fd87c0028d568b068688917565cfd Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 28 Jul 2025 19:29:43 +0500 Subject: [PATCH 01/53] Updated on 2026-08-14 --- .../src/main/res/drawable/ic_stack_new_24.xml | 16 ++++++++++++ .../impl/amount/SwapAmountBlockComponent.kt | 4 +++ .../v2/impl/amount/model/SwapAmountModel.kt | 7 ++++++ .../SwapChooseProviderComponent.kt | 4 ++- .../SwapChooseProviderBottomSheetContent.kt | 1 + .../model/SwapChooseProviderModel.kt | 6 +++++ .../SwapProviderListItemConverter.kt | 10 +++++++- .../ui/SwapChooseProviderBottomSheet.kt | 25 +++++++++++++++---- .../SwapChooseProviderContentPreview.kt | 3 ++- .../swap/v2/impl/common/SwapProviderUtils.kt | 13 ++++++++++ 10 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 core/ui/src/main/res/drawable/ic_stack_new_24.xml create mode 100644 features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/SwapProviderUtils.kt diff --git a/core/ui/src/main/res/drawable/ic_stack_new_24.xml b/core/ui/src/main/res/drawable/ic_stack_new_24.xml new file mode 100644 index 0000000000..a0a35fc823 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_stack_new_24.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt index dbbf511732..5917c12bf3 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt @@ -17,6 +17,7 @@ import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.settings.usercountry.models.UserCountry import com.tangem.features.swap.v2.impl.amount.SwapAmountComponentParams.AmountBlockParams import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM import com.tangem.features.swap.v2.impl.amount.model.SwapAmountModel @@ -77,6 +78,7 @@ internal class SwapAmountBlockComponent( providers = amountUM.swapQuotes, cryptoCurrency = cryptoCurrency, selectedProvider = selectedProvider, + userCountry = model.userCountry, ), ) }, @@ -95,6 +97,7 @@ internal class SwapAmountBlockComponent( providers = config.providers, cryptoCurrency = config.cryptoCurrency, selectedProvider = config.selectedProvider, + userCountry = config.userCountry, callback = model, onDismiss = { model.bottomSheetNavigation.dismiss() }, ), @@ -105,5 +108,6 @@ internal class SwapAmountBlockComponent( val providers: ImmutableList, val cryptoCurrency: CryptoCurrency, val selectedProvider: ExpressProvider, + val userCountry: UserCountry, ) } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt index dfc08a58f3..23c478c056 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt @@ -17,6 +17,8 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.express.models.ExpressError import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.settings.usercountry.GetUserCountryUseCase +import com.tangem.domain.settings.usercountry.models.UserCountry import com.tangem.domain.swap.models.SwapCurrencies import com.tangem.domain.swap.models.SwapDirection import com.tangem.domain.swap.models.SwapDirection.Companion.withSwapDirection @@ -54,6 +56,7 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import timber.log.Timber import java.math.BigDecimal +import java.util.Locale import javax.inject.Inject import kotlin.properties.Delegates import com.tangem.utils.transformer.update as transformerUpdate @@ -69,6 +72,7 @@ internal class SwapAmountModel @Inject constructor( private val swapChooseTokenNetworkListener: SwapChooseTokenNetworkListener, private val getAllowanceUseCase: GetAllowanceUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, + private val getUserCountryUseCase: GetUserCountryUseCase, private val appRouter: AppRouter, private val swapAmountAlertFactory: SwapAmountAlertFactory, private val swapAlertFactory: SwapAlertFactory, @@ -89,6 +93,7 @@ internal class SwapAmountModel @Inject constructor( private var secondaryMaximumAmountBoundary: EnterAmountBoundary? = null private var secondaryMinimumAmountBoundary: EnterAmountBoundary? = null + var userCountry: UserCountry = UserCountry.Other(Locale.getDefault().country) val bottomSheetNavigation: SlotNavigation = SlotNavigation() val uiState: StateFlow @@ -100,6 +105,8 @@ internal class SwapAmountModel @Inject constructor( init { modelScope.launch { appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default } + userCountry = getUserCountryUseCase.invokeSync().getOrNull() + ?: UserCountry.Other(Locale.getDefault().country) } configAmountNavigation() subscribeOnCryptoCurrencyStatusFlow() diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/SwapChooseProviderComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/SwapChooseProviderComponent.kt index 744a8a3c91..c04a88654c 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/SwapChooseProviderComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/SwapChooseProviderComponent.kt @@ -10,6 +10,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.settings.usercountry.models.UserCountry import com.tangem.features.swap.v2.impl.chooseprovider.model.SwapChooseProviderModel import com.tangem.features.swap.v2.impl.chooseprovider.ui.SwapChooseProviderBottomSheet import com.tangem.features.swap.v2.impl.chooseprovider.ui.SwapChooseProviderContent @@ -41,7 +42,7 @@ internal class SwapChooseProviderComponent( SwapChooseProviderBottomSheet(config = bottomSheetConfig) { SwapChooseProviderContent( - providerList = state.value.providerList, + contentUM = state.value, onProviderClick = model::onProviderClick, ) } @@ -51,6 +52,7 @@ internal class SwapChooseProviderComponent( val cryptoCurrency: CryptoCurrency, val selectedProvider: ExpressProvider, val providers: ImmutableList, + val userCountry: UserCountry, val callback: ModelCallback, val onDismiss: () -> Unit, ) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/entity/SwapChooseProviderBottomSheetContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/entity/SwapChooseProviderBottomSheetContent.kt index ad5eb47e18..289afe2a26 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/entity/SwapChooseProviderBottomSheetContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/entity/SwapChooseProviderBottomSheetContent.kt @@ -7,6 +7,7 @@ import kotlinx.collections.immutable.ImmutableList internal data class SwapChooseProviderBottomSheetContent( val providerList: ImmutableList, + val isApplyFCARestrictions: Boolean, val selectedProvider: ExpressProvider, ) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/SwapChooseProviderModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/SwapChooseProviderModel.kt index 5b9ce17796..c4bf3d574e 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/SwapChooseProviderModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/SwapChooseProviderModel.kt @@ -3,10 +3,12 @@ package com.tangem.features.swap.v2.impl.chooseprovider.model import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions import com.tangem.features.swap.v2.impl.chooseprovider.SwapChooseProviderComponent import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapChooseProviderBottomSheetContent import com.tangem.features.swap.v2.impl.chooseprovider.model.converter.SwapProviderListItemConverter import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM +import com.tangem.features.swap.v2.impl.common.isRestrictedByFCA import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.flow.MutableStateFlow @@ -21,10 +23,13 @@ internal class SwapChooseProviderModel @Inject constructor( private val params: SwapChooseProviderComponent.Params = paramsContainer.require() + private val needApplyFCARestrictions = params.userCountry.needApplyFCARestrictions() + private val swapProviderListItemConverter by lazy(LazyThreadSafetyMode.NONE) { SwapProviderListItemConverter( cryptoCurrency = params.cryptoCurrency, selectedProvider = params.selectedProvider, + needApplyFCARestrictions = needApplyFCARestrictions, ) } @@ -38,6 +43,7 @@ internal class SwapChooseProviderModel @Inject constructor( private fun getInitialState(): SwapChooseProviderBottomSheetContent { return SwapChooseProviderBottomSheetContent( + isApplyFCARestrictions = needApplyFCARestrictions && params.selectedProvider.isRestrictedByFCA(), providerList = swapProviderListItemConverter.convertList(params.providers) .filterNotNull() .toPersistentList(), diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverter.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverter.kt index 75234f816b..e7f45e0726 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverter.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverter.kt @@ -15,11 +15,13 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.features.swap.v2.impl.R import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderListItem import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM +import com.tangem.features.swap.v2.impl.common.isRestrictedByFCA import com.tangem.utils.converter.Converter internal class SwapProviderListItemConverter( private val cryptoCurrency: CryptoCurrency, private val selectedProvider: ExpressProvider, + private val needApplyFCARestrictions: Boolean, ) : Converter { override fun convert(value: SwapQuoteUM): SwapProviderListItem? { val provider = value.provider ?: return null @@ -66,9 +68,15 @@ internal class SwapProviderListItemConverter( }, ) } + is SwapQuoteUM.Content -> if (needApplyFCARestrictions && value.provider.isRestrictedByFCA()) { + ProviderChooseUM.ExtraUM.Action( + text = resourceReference(R.string.express_provider_fca_warning_list), + ) + } else { + ProviderChooseUM.ExtraUM.Empty + } SwapQuoteUM.Empty, SwapQuoteUM.Loading, - is SwapQuoteUM.Content, -> ProviderChooseUM.ExtraUM.Empty } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderBottomSheet.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderBottomSheet.kt index 46ab92931b..11d0e32879 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderBottomSheet.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderBottomSheet.kt @@ -1,6 +1,7 @@ package com.tangem.features.swap.v2.impl.chooseprovider.ui import android.content.res.Configuration +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.material3.Icon @@ -19,6 +20,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle +import com.tangem.core.ui.components.notifications.Notification import com.tangem.core.ui.components.provider.ProviderChooseCrypto import com.tangem.core.ui.extensions.conditional import com.tangem.core.ui.extensions.resourceReference @@ -27,10 +29,9 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.features.swap.v2.impl.R import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapChooseProviderBottomSheetContent -import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderListItem import com.tangem.features.swap.v2.impl.chooseprovider.ui.preview.SwapChooseProviderContentPreview import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM -import kotlinx.collections.immutable.ImmutableList +import com.tangem.features.swap.v2.impl.notifications.entity.SwapNotificationUM @Composable internal fun SwapChooseProviderBottomSheet(config: TangemBottomSheetConfig, content: @Composable () -> Unit) { @@ -50,7 +51,7 @@ internal fun SwapChooseProviderBottomSheet(config: TangemBottomSheetConfig, cont @Composable internal fun SwapChooseProviderContent( - providerList: ImmutableList, + contentUM: SwapChooseProviderBottomSheetContent, onProviderClick: (SwapQuoteUM) -> Unit, modifier: Modifier = Modifier, ) { @@ -64,7 +65,17 @@ internal fun SwapChooseProviderContent( color = TangemTheme.colors.text.secondary, textAlign = TextAlign.Center, ) - providerList.fastForEachIndexed { index, provider -> + AnimatedVisibility( + modifier = Modifier.padding(top = 12.dp), + visible = contentUM.isApplyFCARestrictions, + ) { + Notification( + config = SwapNotificationUM.Error.FCAWarningList.config, + containerColor = TangemTheme.colors.button.disabled, + iconTint = TangemTheme.colors.icon.warning, + ) + } + contentUM.providerList.fastForEachIndexed { index, provider -> ProviderChooseCrypto( providerChooseUM = provider.providerUM, onClick = { onProviderClick(provider.quote) }, @@ -114,7 +125,11 @@ private fun SwapChooseProviderContent_Preview( ), ) { SwapChooseProviderContent( - providerList = params.providerList, + contentUM = SwapChooseProviderBottomSheetContent( + providerList = params.providerList, + isApplyFCARestrictions = true, + selectedProvider = SwapChooseProviderContentPreview.provider1, + ), onProviderClick = {}, ) } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/preview/SwapChooseProviderContentPreview.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/preview/SwapChooseProviderContentPreview.kt index b96c7acb4b..dd75bbf62c 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/preview/SwapChooseProviderContentPreview.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/preview/SwapChooseProviderContentPreview.kt @@ -16,7 +16,7 @@ import java.math.BigDecimal internal object SwapChooseProviderContentPreview { - private val provider1 = ExpressProvider( + val provider1 = ExpressProvider( providerId = "changenow", rateTypes = listOf(ExpressRateType.Float), name = "ChangeNow", @@ -88,5 +88,6 @@ internal object SwapChooseProviderContentPreview { ), ), selectedProvider = provider1, + isApplyFCARestrictions = false, ) } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/SwapProviderUtils.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/SwapProviderUtils.kt new file mode 100644 index 0000000000..6c9e8ece21 --- /dev/null +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/SwapProviderUtils.kt @@ -0,0 +1,13 @@ +package com.tangem.features.swap.v2.impl.common + +import com.tangem.domain.express.models.ExpressProvider + +private val FCA_RESTRICTED_PROVIDER_IDS = setOf( + "changelly", + "changenow", + "okx-cross-chain", + "okx-on-chain", + "simpleswap", +) + +fun ExpressProvider.isRestrictedByFCA() = FCA_RESTRICTED_PROVIDER_IDS.contains(providerId) \ No newline at end of file From cf559c1a11aa1ae17ba231e9ea88c3bd2df5602d Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 28 Jul 2025 20:46:15 +0500 Subject: [PATCH 02/53] Updated on 2026-08-14 --- .../provider/ProviderChooseCrypto.kt | 16 +- .../SwapAmountSetQuotesTransformer.kt | 1 + .../SwapChooseProviderBottomSheetContent.kt | 1 + .../entity/SwapProviderState.kt | 34 +++ .../model/SwapChooseProviderModel.kt | 10 +- .../SwapProviderListItemConverter.kt | 10 + .../converter/SwapProviderStateConverter.kt | 92 +++++++ .../ui/SwapChooseProviderBottomSheet.kt | 26 +- .../ui/SwapChooseProviderContent.kt | 21 +- .../chooseprovider/ui/SwapProviderItem.kt | 232 ++++++++++++++++++ .../SwapChooseProviderContentPreview.kt | 19 ++ .../swap/v2/impl/common/entity/SwapQuoteUM.kt | 1 + 12 files changed, 443 insertions(+), 20 deletions(-) create mode 100644 features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/entity/SwapProviderState.kt create mode 100644 features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderStateConverter.kt create mode 100644 features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapProviderItem.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/provider/ProviderChooseCrypto.kt b/core/ui/src/main/java/com/tangem/core/ui/components/provider/ProviderChooseCrypto.kt index aa7f0f5d08..0370272dcc 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/provider/ProviderChooseCrypto.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/provider/ProviderChooseCrypto.kt @@ -23,6 +23,7 @@ import androidx.constraintlayout.compose.Visibility import coil.compose.SubcomposeAsyncImage import coil.request.ImageRequest import com.tangem.core.ui.R +import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.components.audits.AuditLabel import com.tangem.core.ui.components.audits.AuditLabelUM import com.tangem.core.ui.components.badge.Badge @@ -50,7 +51,6 @@ private const val DISABLED_ICON_ALPHA = 0.4f fun ProviderChooseCrypto(providerChooseUM: ProviderChooseUM, onClick: () -> Unit, modifier: Modifier = Modifier) { ConstraintLayout( modifier = modifier - .background(TangemTheme.colors.background.action) .clip(RoundedCornerShape(14.dp)) .selectedBorder(isSelected = providerChooseUM.isSelected) .clickable( @@ -133,13 +133,23 @@ private fun IconContent(iconUrl: String, modifier: Modifier = Modifier) { SubcomposeAsyncImage( modifier = modifier .size(40.dp) - .clip(RoundedCornerShape(8.dp)) - .background(TangemColorPalette.Light1), + .clip(RoundedCornerShape(8.dp)), model = ImageRequest.Builder(context = LocalContext.current) .data(iconUrl) .crossfade(enable = true) .allowHardware(false) .build(), + loading = { + RectangleShimmer(radius = 8.dp) + }, + error = { + Box( + modifier = Modifier.background( + color = TangemColorPalette.Light1, + shape = RoundedCornerShape(8.dp), + ), + ) + }, contentDescription = null, ) } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt index e6b6c96434..614f1d8b53 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt @@ -58,6 +58,7 @@ internal class SwapAmountSetQuotesTransformer( val percent = quote.quoteAmount / bestQuote.quoteAmount - BigDecimal.ONE quote.copy( diffPercent = DifferencePercent.Diff( + isPositive = percent.isPositive(), percent = stringReference( if (percent.isPositive()) { "${StringsSigns.PLUS}${percent.format { percent() }}" diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/entity/SwapChooseProviderBottomSheetContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/entity/SwapChooseProviderBottomSheetContent.kt index 289afe2a26..7be1b3c0c7 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/entity/SwapChooseProviderBottomSheetContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/entity/SwapChooseProviderBottomSheetContent.kt @@ -13,5 +13,6 @@ internal data class SwapChooseProviderBottomSheetContent( internal data class SwapProviderListItem( val providerUM: ProviderChooseUM, + val swapProviderState: SwapProviderState, val quote: SwapQuoteUM, ) \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/entity/SwapProviderState.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/entity/SwapProviderState.kt new file mode 100644 index 0000000000..24e18c0fb1 --- /dev/null +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/entity/SwapProviderState.kt @@ -0,0 +1,34 @@ +package com.tangem.features.swap.v2.impl.chooseprovider.entity + +import androidx.compose.runtime.Immutable +import com.tangem.core.ui.extensions.TextReference +import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM.Content.DifferencePercent + +@Deprecated("Use ProviderChooseUM with new design") +@Immutable +internal sealed class SwapProviderState { + + abstract val isSelected: Boolean + + data object Empty : SwapProviderState() { + override val isSelected = false + } + + data class Content( + override val isSelected: Boolean, + val name: String, + val type: String, + val iconUrl: String, + val subtitle: TextReference, + val additionalBadge: AdditionalBadge, + val diffPercent: DifferencePercent, + ) : SwapProviderState() + + @Immutable + sealed class AdditionalBadge { + data object FCAWarningList : AdditionalBadge() + data object BestTrade : AdditionalBadge() + data object Empty : AdditionalBadge() + data object PermissionRequired : AdditionalBadge() + } +} \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/SwapChooseProviderModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/SwapChooseProviderModel.kt index c4bf3d574e..08511759a0 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/SwapChooseProviderModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/SwapChooseProviderModel.kt @@ -3,6 +3,7 @@ package com.tangem.features.swap.v2.impl.chooseprovider.model import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.domain.express.models.ExpressError import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions import com.tangem.features.swap.v2.impl.chooseprovider.SwapChooseProviderComponent import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapChooseProviderBottomSheetContent @@ -10,6 +11,7 @@ import com.tangem.features.swap.v2.impl.chooseprovider.model.converter.SwapProvi import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM import com.tangem.features.swap.v2.impl.common.isRestrictedByFCA import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.extensions.isSingleItem import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -30,6 +32,7 @@ internal class SwapChooseProviderModel @Inject constructor( cryptoCurrency = params.cryptoCurrency, selectedProvider = params.selectedProvider, needApplyFCARestrictions = needApplyFCARestrictions, + needBestRateBadge = params.providers.filterIsInstance().isSingleItem().not(), ) } @@ -42,9 +45,14 @@ internal class SwapChooseProviderModel @Inject constructor( } private fun getInitialState(): SwapChooseProviderBottomSheetContent { + val filteredProviderList = params.providers.filter { + it is SwapQuoteUM.Content || + it is SwapQuoteUM.Allowance || + (it as? SwapQuoteUM.Error)?.expressError is ExpressError.AmountError + } return SwapChooseProviderBottomSheetContent( isApplyFCARestrictions = needApplyFCARestrictions && params.selectedProvider.isRestrictedByFCA(), - providerList = swapProviderListItemConverter.convertList(params.providers) + providerList = swapProviderListItemConverter.convertList(filteredProviderList) .filterNotNull() .toPersistentList(), selectedProvider = params.selectedProvider, diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverter.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverter.kt index e7f45e0726..045d882f8f 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverter.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverter.kt @@ -22,11 +22,21 @@ internal class SwapProviderListItemConverter( private val cryptoCurrency: CryptoCurrency, private val selectedProvider: ExpressProvider, private val needApplyFCARestrictions: Boolean, + needBestRateBadge: Boolean, ) : Converter { + + private val providerStateConverter = SwapProviderStateConverter( + cryptoCurrency = cryptoCurrency, + selectedProvider = selectedProvider, + needApplyFCARestrictions = needApplyFCARestrictions, + isNeedBestRateBadge = needBestRateBadge, + ) + override fun convert(value: SwapQuoteUM): SwapProviderListItem? { val provider = value.provider ?: return null return SwapProviderListItem( + swapProviderState = providerStateConverter.convert(value), providerUM = ProviderChooseUM( title = stringReference(provider.name), subtitle = stringReference(provider.type.typeName), diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderStateConverter.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderStateConverter.kt new file mode 100644 index 0000000000..4a7a127735 --- /dev/null +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderStateConverter.kt @@ -0,0 +1,92 @@ +package com.tangem.features.swap.v2.impl.chooseprovider.model.converter + +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.format.bigdecimal.crypto +import com.tangem.core.ui.format.bigdecimal.format +import com.tangem.domain.express.models.ExpressError +import com.tangem.domain.express.models.ExpressProvider +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.features.swap.v2.impl.R +import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderState +import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderState.AdditionalBadge +import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM +import com.tangem.features.swap.v2.impl.common.isRestrictedByFCA +import com.tangem.utils.converter.Converter + +@Deprecated("Remove with new design") +internal class SwapProviderStateConverter( + private val cryptoCurrency: CryptoCurrency, + private val selectedProvider: ExpressProvider, + private val isNeedBestRateBadge: Boolean, + private val needApplyFCARestrictions: Boolean, +) : Converter { + + override fun convert(value: SwapQuoteUM): SwapProviderState { + return when (value) { + is SwapQuoteUM.Content -> value.convertToContent() + is SwapQuoteUM.Error -> value.convertToErrorContent() + is SwapQuoteUM.Allowance, + SwapQuoteUM.Empty, + SwapQuoteUM.Loading, + -> SwapProviderState.Empty + } + } + + private fun SwapQuoteUM.Content.convertToContent(): SwapProviderState { + val isBestRate = when (diffPercent) { + SwapQuoteUM.Content.DifferencePercent.Best -> true + else -> false + } + + val additionalBadge = when { + needApplyFCARestrictions && provider.isRestrictedByFCA() -> AdditionalBadge.FCAWarningList + isNeedBestRateBadge && isBestRate && !needApplyFCARestrictions -> AdditionalBadge.BestTrade + else -> AdditionalBadge.Empty + } + + return SwapProviderState.Content( + name = provider.name, + iconUrl = provider.imageLarge, + type = provider.type.typeName, + subtitle = quoteAmountValue, + additionalBadge = additionalBadge, + diffPercent = diffPercent, + isSelected = provider == selectedProvider, + ) + } + + private fun SwapQuoteUM.Error.convertToErrorContent(): SwapProviderState { + val additionalBadge = when { + needApplyFCARestrictions && provider.isRestrictedByFCA() -> AdditionalBadge.FCAWarningList + else -> AdditionalBadge.Empty + } + + return SwapProviderState.Content( + name = provider.name, + iconUrl = provider.imageLarge, + type = provider.type.typeName, + subtitle = when (val error = expressError) { + is ExpressError.AmountError.TooSmallError -> resourceReference( + id = R.string.express_provider_min_amount, + formatArgs = wrappedList( + error.amount.format { crypto(cryptoCurrency) }, + ), + ) + is ExpressError.AmountError.NotEnoughAllowanceError, + is ExpressError.AmountError.TooBigError, + -> resourceReference( + id = R.string.express_provider_max_amount, + formatArgs = wrappedList( + error.amount.format { crypto(cryptoCurrency) }, + ), + ) + else -> TextReference.EMPTY + }, + additionalBadge = additionalBadge, + diffPercent = SwapQuoteUM.Content.DifferencePercent.Empty, + isSelected = false, + ) + } +} \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderBottomSheet.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderBottomSheet.kt index 11d0e32879..dcd529b3f9 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderBottomSheet.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderBottomSheet.kt @@ -2,13 +2,16 @@ package com.tangem.features.swap.v2.impl.chooseprovider.ui import android.content.res.Configuration import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -16,14 +19,14 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastForEachIndexed +import com.tangem.core.ui.components.SpacerH12 import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle import com.tangem.core.ui.components.notifications.Notification -import com.tangem.core.ui.components.provider.ProviderChooseCrypto -import com.tangem.core.ui.extensions.conditional import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.selectedBorder import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview @@ -57,13 +60,14 @@ internal fun SwapChooseProviderContent( ) { Column( horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(horizontal = 13.dp), + modifier = modifier.padding(horizontal = 13.dp), ) { Text( text = stringResourceSafe(id = R.string.onramp_choose_provider_title_hint), style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.secondary, textAlign = TextAlign.Center, + modifier = Modifier.padding(bottom = 4.dp), ) AnimatedVisibility( modifier = Modifier.padding(top = 12.dp), @@ -75,12 +79,18 @@ internal fun SwapChooseProviderContent( iconTint = TangemTheme.colors.icon.warning, ) } + SpacerH12() contentUM.providerList.fastForEachIndexed { index, provider -> - ProviderChooseCrypto( - providerChooseUM = provider.providerUM, - onClick = { onProviderClick(provider.quote) }, - modifier = modifier - .conditional(index == 0) { padding(top = 24.dp) }, + SwapProviderItem( + state = provider.swapProviderState, + modifier = Modifier + .clip(RoundedCornerShape(14.dp)) + .selectedBorder(isSelected = provider.swapProviderState.isSelected) + .clickable( + enabled = provider.quote !is SwapQuoteUM.Error, + onClick = { onProviderClick(provider.quote) }, + ) + .padding(12.dp), ) } Icon( diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderContent.kt index 43c2d3c8f0..38a3b4718c 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderContent.kt @@ -4,11 +4,7 @@ import android.content.res.Configuration import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon @@ -27,6 +23,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import coil.compose.SubcomposeAsyncImage import coil.request.ImageRequest +import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.components.SpacerWMax import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemColorPalette @@ -57,7 +54,7 @@ fun SwapChooseProviderContent(expressProvider: ExpressProvider?, onClick: () -> ) { Icon( painter = rememberVectorPainter( - ImageVector.vectorResource(R.drawable.ic_exchange_horizontal_24), + ImageVector.vectorResource(R.drawable.ic_stack_new_24), ), tint = TangemTheme.colors.icon.accent, contentDescription = null, @@ -72,13 +69,21 @@ fun SwapChooseProviderContent(expressProvider: ExpressProvider?, onClick: () -> SubcomposeAsyncImage( modifier = modifier .size(20.dp) - .clip(RoundedCornerShape(4.dp)) - .background(TangemColorPalette.Light1), + .clip(RoundedCornerShape(4.dp)), model = ImageRequest.Builder(context = LocalContext.current) .data(expressProvider?.imageLarge) .crossfade(enable = true) .allowHardware(false) .build(), + loading = { RectangleShimmer(radius = 4.dp) }, + error = { + Box( + modifier = Modifier.background( + color = TangemColorPalette.Light1, + shape = RoundedCornerShape(4.dp), + ), + ) + }, contentDescription = null, ) Text( diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapProviderItem.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapProviderItem.kt new file mode 100644 index 0000000000..4b8df6d2b6 --- /dev/null +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapProviderItem.kt @@ -0,0 +1,232 @@ +package com.tangem.features.swap.v2.impl.chooseprovider.ui + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import androidx.compose.ui.unit.dp +import coil.compose.SubcomposeAsyncImage +import coil.request.ImageRequest +import com.tangem.core.ui.R +import com.tangem.core.ui.components.RectangleShimmer +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderState +import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM + +@Deprecated("Use ProviderChooseCrypto with new design") +@Composable +internal fun SwapProviderItem(state: SwapProviderState, modifier: Modifier = Modifier) { + when (state) { + is SwapProviderState.Content -> ProviderContentState( + state = state, + modifier = modifier, + ) + is SwapProviderState.Empty -> { /* no-op */ + } + } +} + +@Suppress("LongMethod") +@Composable +private fun ProviderContentState(state: SwapProviderState.Content, modifier: Modifier = Modifier) { + Row( + modifier = modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + SubcomposeAsyncImage( + modifier = Modifier + .size(size = 40.dp) + .clip(TangemTheme.shapes.roundedCorners8), + model = ImageRequest.Builder(context = LocalContext.current).data(state.iconUrl) + .crossfade(enable = true).allowHardware(false).build(), + loading = { RectangleShimmer(radius = 8.dp) }, + error = { + ErrorProviderIcon(Modifier.size(size = 40.dp)) + }, + contentDescription = null, + ) + + Column(modifier = Modifier.padding(start = 12.dp)) { + Row { + Text( + text = state.name, + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.text.primary1, + ) + Text( + text = state.type, + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier.padding(start = 4.dp), + ) + val badgeModifier = Modifier.padding(start = 4.dp) + when (state.additionalBadge) { + SwapProviderState.AdditionalBadge.FCAWarningList -> FCABadgeItem(badgeModifier) + SwapProviderState.AdditionalBadge.BestTrade -> BestTradeItem(badgeModifier) + SwapProviderState.AdditionalBadge.PermissionRequired -> PermissionBadgeItem(badgeModifier) + SwapProviderState.AdditionalBadge.Empty -> Unit + } + } + Row( + modifier = Modifier.padding(top = 2.dp), + ) { + Text( + text = state.subtitle.resolveReference(), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.tertiary, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + ) + + if (state.diffPercent is SwapQuoteUM.Content.DifferencePercent.Diff) { + val textColor = if (state.diffPercent.isPositive) { + TangemTheme.colors.icon.accent + } else { + TangemTheme.colors.text.warning + } + + Text( + text = state.diffPercent.percent.resolveReference(), + style = TangemTheme.typography.body2, + color = textColor, + modifier = Modifier.padding(start = 4.dp), + overflow = TextOverflow.Ellipsis, + maxLines = 1, + ) + } + } + } + } +} + +@Composable +private fun ErrorProviderIcon(modifier: Modifier = Modifier) { + Box( + modifier = modifier.background( + color = TangemTheme.colors.background.secondary, + shape = TangemTheme.shapes.roundedCorners8, + ), + contentAlignment = Alignment.Center, + ) { + Icon( + modifier = Modifier.matchParentSize(), + painter = painterResource(id = R.drawable.ic_custom_token_44), + contentDescription = null, + ) + } +} + +@Composable +private fun BestTradeItem(modifier: Modifier = Modifier) { + Box( + modifier = modifier.background( + color = TangemTheme.colors.icon.accent.copy(alpha = 0.1f), + shape = TangemTheme.shapes.roundedCornersLarge, + ), + ) { + Text( + text = stringResourceSafe(R.string.express_provider_best_rate), + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.icon.accent, + modifier = Modifier.padding(horizontal = 6.dp), + maxLines = 1, + ) + } +} + +@Composable +private fun PermissionBadgeItem(modifier: Modifier = Modifier) { + Box( + modifier = modifier.background( + color = TangemTheme.colors.background.secondary, + shape = TangemTheme.shapes.roundedCornersLarge, + ), + ) { + Text( + text = stringResourceSafe(id = R.string.express_provider_permission_needed), + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier.padding(horizontal = 6.dp), + maxLines = 1, + ) + } +} + +@Composable +private fun FCABadgeItem(modifier: Modifier = Modifier) { + Box( + modifier = modifier.background( + color = TangemTheme.colors.background.secondary, + shape = TangemTheme.shapes.roundedCornersLarge, + ), + ) { + Text( + text = stringResourceSafe(id = R.string.express_provider_fca_warning_list), + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier.padding(horizontal = 6.dp), + maxLines = 1, + ) + } +} + +// region Preview +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun ProviderItemPreview( + @PreviewParameter(ProviderItemParameterProvider::class) state: Pair, +) { + TangemThemePreview { + SwapProviderItem( + modifier = Modifier.background(TangemTheme.colors.background.action), + state = state.first, + ) + } +} + +private class ProviderItemParameterProvider : CollectionPreviewParameterProvider>( + collection = buildList { + val contentState = SwapProviderState.Content( + name = "1inch", + type = "DEX", + iconUrl = "", + subtitle = stringReference(value = "0,64554846 DAI ≈ 1 MATIC"), + additionalBadge = SwapProviderState.AdditionalBadge.Empty, + diffPercent = SwapQuoteUM.Content.DifferencePercent.Diff( + isPositive = false, + percent = stringReference("-10%"), + ), + isSelected = true, + ) + val contentState2 = contentState.copy( + subtitle = stringReference(value = "1 132,46 MATIC"), + additionalBadge = SwapProviderState.AdditionalBadge.PermissionRequired, + diffPercent = SwapQuoteUM.Content.DifferencePercent.Diff( + isPositive = true, + percent = stringReference("+10%"), + ), + ) + add(contentState to true) + add(contentState to false) + + add(contentState2 to true) + add(contentState2 to false) + }, +) +// endregion Preview \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/preview/SwapChooseProviderContentPreview.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/preview/SwapChooseProviderContentPreview.kt index dd75bbf62c..0975e02bdf 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/preview/SwapChooseProviderContentPreview.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/preview/SwapChooseProviderContentPreview.kt @@ -10,6 +10,7 @@ import com.tangem.domain.express.models.ExpressRateType import com.tangem.features.swap.v2.impl.R import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapChooseProviderBottomSheetContent import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderListItem +import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderState import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM import kotlinx.collections.immutable.persistentListOf import java.math.BigDecimal @@ -65,6 +66,15 @@ internal object SwapChooseProviderContentPreview { ), ), ), + swapProviderState = SwapProviderState.Content( + name = provider1.name, + type = provider1.type.typeName, + iconUrl = "", + subtitle = stringReference("1800 POL"), + additionalBadge = SwapProviderState.AdditionalBadge.BestTrade, + diffPercent = SwapQuoteUM.Content.DifferencePercent.Best, + isSelected = true, + ), quote = quote1, ), SwapProviderListItem( @@ -85,6 +95,15 @@ internal object SwapChooseProviderContentPreview { ), ), quote = quote2, + swapProviderState = SwapProviderState.Content( + name = provider1.name, + type = provider1.type.typeName, + iconUrl = "", + subtitle = stringReference("1800 POL"), + additionalBadge = SwapProviderState.AdditionalBadge.BestTrade, + diffPercent = SwapQuoteUM.Content.DifferencePercent.Best, + isSelected = true, + ), ), ), selectedProvider = provider1, diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/entity/SwapQuoteUM.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/entity/SwapQuoteUM.kt index ec6aaf74e7..ff661ba3ee 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/entity/SwapQuoteUM.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/entity/SwapQuoteUM.kt @@ -40,6 +40,7 @@ internal sealed class SwapQuoteUM { data object Empty : DifferencePercent() data object Best : DifferencePercent() data class Diff( + val isPositive: Boolean, val percent: TextReference, ) : DifferencePercent() } From 496072829c204c13d1fc630490dff3ccb2902b43 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 25 Jul 2025 13:44:57 +0400 Subject: [PATCH 03/53] Updated on 2026-08-14 --- .../tap/di/domain/StakingDomainModule.kt | 24 +----------- .../data/staking/DefaultStakingRepository.kt | 31 ++-------------- .../com/tangem/data/swap/di/SwapDataModule.kt | 4 +- .../GetActionRequirementAmountUseCase.kt | 18 +++++---- .../staking/GetStakingIntegrationIdUseCase.kt | 12 ------ .../domain/staking/IsApproveNeededUseCase.kt | 19 ---------- .../staking/repositories/StakingRepository.kt | 6 --- .../BaseCurrencyStatusOperations.kt | 2 +- .../CachedCurrenciesStatusesOperations.kt | 8 ++-- .../utils/CurrencyStatusProxyCreator.kt | 13 ++----- .../impl/presentation/model/StakingModel.kt | 37 +++++++++---------- .../tokendetails/model/TokenDetailsModel.kt | 7 +--- .../TokenDetailsSkeletonStateConverter.kt | 5 +-- .../state/factory/TokenDetailsStateFactory.kt | 7 +--- 14 files changed, 47 insertions(+), 146 deletions(-) delete mode 100644 domain/staking/src/main/java/com/tangem/domain/staking/GetStakingIntegrationIdUseCase.kt delete mode 100644 domain/staking/src/main/java/com/tangem/domain/staking/IsApproveNeededUseCase.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt index b6152265bf..213e3753c7 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt @@ -173,18 +173,6 @@ internal object StakingDomainModule { ) } - @Provides - @Singleton - fun provideIsApproveNeededUseCase( - stakingRepository: StakingRepository, - stakingErrorResolver: StakingErrorResolver, - ): IsApproveNeededUseCase { - return IsApproveNeededUseCase( - stakingRepository = stakingRepository, - stakingErrorResolver = stakingErrorResolver, - ) - } - @Provides @Singleton fun provideGetConstructedStakingTransactionUseCase( @@ -209,12 +197,6 @@ internal object StakingDomainModule { ) } - @Provides - @Singleton - fun provideGetStakingIntegrationIdUseCase(stakingRepository: StakingRepository): GetStakingIntegrationIdUseCase { - return GetStakingIntegrationIdUseCase(stakingRepository) - } - @Provides @Singleton fun provideCheckAccountInitializedUseCase( @@ -225,9 +207,7 @@ internal object StakingDomainModule { @Provides @Singleton - fun provideGetActionRequirementAmountUseCase( - stakingRepository: StakingRepository, - ): GetActionRequirementAmountUseCase { - return GetActionRequirementAmountUseCase(stakingRepository) + fun provideGetActionRequirementAmountUseCase(): GetActionRequirementAmountUseCase { + return GetActionRequirementAmountUseCase() } } \ No newline at end of file diff --git a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt index fbdea80804..15787789bf 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt @@ -10,8 +10,6 @@ import com.tangem.blockchain.common.TransactionStatus import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.blockchainsdk.utils.toBlockchain -import com.tangem.blockchainsdk.utils.toCoinId -import com.tangem.blockchainsdk.utils.toMigratedCoinId import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toCompressedPublicKey import com.tangem.data.staking.converters.YieldConverter @@ -38,9 +36,9 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.staking.model.StakingApproval import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.staking.model.StakingEntryInfo +import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.staking.model.stakekit.NetworkType import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.YieldBalance @@ -63,7 +61,6 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.withContext import timber.log.Timber import java.math.BigDecimal -import kotlin.time.Duration.Companion.seconds @Suppress("LargeClass", "LongParameterList", "TooManyFunctions") internal class DefaultStakingRepository( @@ -95,10 +92,6 @@ internal class DefaultStakingRepository( private val networkTypeAdapter by lazy { moshi.adapter(NetworkTypeDTO::class.java) } private val stakingActionStatusAdapter by lazy { moshi.adapter(StakingActionStatusDTO::class.java) } - override fun getSupportedIntegrationId(cryptoCurrencyId: CryptoCurrency.ID): String? { - return stakingIdFactory.createIntegrationId(currencyId = cryptoCurrencyId) - } - override suspend fun fetchEnabledYields() { withContext(dispatchers.io) { when (val stakingTokensWithYields = stakeKitApi.getEnabledYields(preferredValidatorsOnly = false)) { @@ -202,7 +195,7 @@ internal class DefaultStakingRepository( return@channelFlow } - val isSupportedInMobileApp = getSupportedIntegrationId(cryptoCurrency.id).isNullOrEmpty().not() + val isSupportedInMobileApp = StakingIntegrationID.create(currencyId = cryptoCurrency.id) != null getEnabledYields() .distinctUntilChanged() @@ -248,7 +241,7 @@ internal class DefaultStakingRepository( return StakingAvailability.Unavailable } - val isSupportedInMobileApp = getSupportedIntegrationId(cryptoCurrency.id).isNullOrEmpty().not() + val isSupportedInMobileApp = StakingIntegrationID.create(currencyId = cryptoCurrency.id) != null val yields = getEnabledYieldsSync() if (yields.isEmpty()) { @@ -481,17 +474,6 @@ internal class DefaultStakingRepository( } } - override fun getStakingApproval(cryptoCurrency: CryptoCurrency): StakingApproval { - val integrationId = stakingIdFactory.createIntegrationId(currencyId = cryptoCurrency.id) - - return when (integrationId) { - Blockchain.Ethereum.id + Blockchain.Polygon.toCoinId(), - Blockchain.Ethereum.id + Blockchain.Polygon.toMigratedCoinId(), - -> StakingApproval.Needed(ETHEREUM_POLYGON_APPROVE_SPENDER) - else -> StakingApproval.Empty - } - } - private fun getTransactionDataType(networkId: String, unsignedTransaction: String): TransactionData.Compiled.Data { return when (Blockchain.fromId(networkId)) { Blockchain.Solana, @@ -543,14 +525,7 @@ internal class DefaultStakingRepository( } } - @Suppress("unused") companion object { - private const val YIELDS_STORE_KEY = "yields" - - private const val ETHEREUM_POLYGON_APPROVE_SPENDER = "0x5e3Ef299fDDf15eAa0432E6e66473ace8c13D908" - - internal val YIELDS_WATITING_TIMEOUT = 15.seconds - private val INVALID_BATCHES_FOR_SOLANA = listOf("AC01", "CB79") } } \ No newline at end of file diff --git a/data/swap/src/main/java/com/tangem/data/swap/di/SwapDataModule.kt b/data/swap/src/main/java/com/tangem/data/swap/di/SwapDataModule.kt index b7df9273ac..5edf45f002 100644 --- a/data/swap/src/main/java/com/tangem/data/swap/di/SwapDataModule.kt +++ b/data/swap/src/main/java/com/tangem/data/swap/di/SwapDataModule.kt @@ -14,7 +14,6 @@ import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.domain.express.ExpressRepository import com.tangem.domain.quotes.single.SingleQuoteStatusFetcher import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier -import com.tangem.domain.staking.repositories.StakingRepository import com.tangem.domain.swap.SwapErrorResolver import com.tangem.domain.swap.SwapRepositoryV2 import com.tangem.domain.swap.SwapTransactionRepository @@ -49,7 +48,6 @@ internal object SwapDataModule { dataSignatureVerifier: DataSignatureVerifier, singleQuoteStatusSupplier: SingleQuoteStatusSupplier, singleQuoteStatusFetcher: SingleQuoteStatusFetcher, - stakingRepository: StakingRepository, @NetworkMoshi moshi: Moshi, ): SwapRepositoryV2 { return DefaultSwapRepositoryV2( @@ -61,7 +59,7 @@ internal object SwapDataModule { moshi = moshi, singleQuoteStatusSupplier = singleQuoteStatusSupplier, singleQuoteStatusFetcher = singleQuoteStatusFetcher, - currencyStatusProxyCreator = CurrencyStatusProxyCreator(stakingRepository), + currencyStatusProxyCreator = CurrencyStatusProxyCreator(), ) } diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/GetActionRequirementAmountUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/GetActionRequirementAmountUseCase.kt index 34d0c03b27..ccbb011f35 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/GetActionRequirementAmountUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/GetActionRequirementAmountUseCase.kt @@ -1,16 +1,18 @@ package com.tangem.domain.staking -import arrow.core.Either +import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.staking.model.stakekit.action.StakingActionType -import com.tangem.domain.staking.repositories.StakingRepository import java.math.BigDecimal -class GetActionRequirementAmountUseCase( - private val stakingRepository: StakingRepository, -) { +class GetActionRequirementAmountUseCase { - operator fun invoke(integrationId: String, actionType: StakingActionType): Either = - Either.catch { - stakingRepository.getActionRequirementAmount(integrationId, actionType) + operator fun invoke(integrationId: String, actionType: StakingActionType): BigDecimal? { + return if (StakingIntegrationID.EthereumToken.Polygon.value == integrationId && + actionType == StakingActionType.CLAIM_REWARDS + ) { + BigDecimal.ONE + } else { + null } + } } \ No newline at end of file diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingIntegrationIdUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingIntegrationIdUseCase.kt deleted file mode 100644 index 2c9d213e72..0000000000 --- a/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingIntegrationIdUseCase.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.tangem.domain.staking - -import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.staking.repositories.StakingRepository - -class GetStakingIntegrationIdUseCase( - private val stakingRepository: StakingRepository, -) { - - operator fun invoke(cryptoCurrencyId: CryptoCurrency.ID) = - stakingRepository.getSupportedIntegrationId(cryptoCurrencyId) -} \ No newline at end of file diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/IsApproveNeededUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/IsApproveNeededUseCase.kt deleted file mode 100644 index 549dbb614c..0000000000 --- a/domain/staking/src/main/java/com/tangem/domain/staking/IsApproveNeededUseCase.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tangem.domain.staking - -import arrow.core.Either -import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.staking.model.StakingApproval -import com.tangem.domain.staking.model.stakekit.StakingError -import com.tangem.domain.staking.repositories.StakingErrorResolver -import com.tangem.domain.staking.repositories.StakingRepository - -class IsApproveNeededUseCase( - private val stakingRepository: StakingRepository, - private val stakingErrorResolver: StakingErrorResolver, -) { - operator fun invoke(cryptoCurrency: CryptoCurrency): Either { - return Either - .catch { stakingRepository.getStakingApproval(cryptoCurrency) } - .mapLeft { stakingErrorResolver.resolve(it) } - } -} \ No newline at end of file diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt index dce26456cb..dcb09a9010 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt @@ -6,7 +6,6 @@ import com.tangem.blockchain.common.transaction.Fee import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.staking.model.StakingApproval import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.staking.model.StakingEntryInfo import com.tangem.domain.staking.model.stakekit.NetworkType @@ -24,8 +23,6 @@ import java.math.BigDecimal @Suppress("TooManyFunctions") interface StakingRepository { - fun getSupportedIntegrationId(cryptoCurrencyId: CryptoCurrency.ID): String? - suspend fun fetchEnabledYields() suspend fun getEntryInfo(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): StakingEntryInfo @@ -66,9 +63,6 @@ interface StakingRepository { transactionId: String, ): Pair - /** Returns staking approval */ - fun getStakingApproval(cryptoCurrency: CryptoCurrency): StakingApproval - suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean /** diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt index 80744461c7..351c2e0f89 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt @@ -52,7 +52,7 @@ abstract class BaseCurrencyStatusOperations( private val tokensFeatureToggles: TokensFeatureToggles, ) { - protected val currencyStatusProxyCreator = CurrencyStatusProxyCreator(stakingRepository) + protected val currencyStatusProxyCreator = CurrencyStatusProxyCreator() protected abstract fun getQuotes(id: CryptoCurrency.RawID): Flow>> diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt index 2861f1eb19..6bcb07b46b 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt @@ -24,8 +24,8 @@ import com.tangem.domain.quotes.QuotesRepository import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher import com.tangem.domain.quotes.single.SingleQuoteStatusProducer import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier +import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.staking.model.stakekit.YieldBalance -import com.tangem.domain.staking.model.stakekit.YieldBalance.Unsupported.integrationId import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.staking.repositories.StakingRepository import com.tangem.domain.staking.single.SingleYieldBalanceProducer @@ -46,7 +46,7 @@ import kotlinx.coroutines.flow.* class CachedCurrenciesStatusesOperations( private val currenciesRepository: CurrenciesRepository, quotesRepository: QuotesRepository, - private val stakingRepository: StakingRepository, + stakingRepository: StakingRepository, private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier, multiNetworkStatusSupplier: MultiNetworkStatusSupplier, private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher, @@ -270,9 +270,7 @@ class CachedCurrenciesStatusesOperations( ): YieldBalance? { if (yieldBalances.isNullOrEmpty()) return null - val supportedIntegration = stakingRepository.getSupportedIntegrationId(currency.id) - - if (supportedIntegration.isNullOrBlank()) return null + val supportedIntegration = StakingIntegrationID.create(currencyId = currency.id)?.value ?: return null val address = extractAddress(networkStatus) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt index f6e74db9ca..c1778e6af1 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt @@ -7,9 +7,8 @@ import arrow.core.toNonEmptySetOrNull import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.quote.QuoteStatus +import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.staking.model.stakekit.YieldBalance -import com.tangem.domain.staking.model.stakekit.YieldBalance.Unsupported.integrationId -import com.tangem.domain.staking.repositories.StakingRepository import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error import com.tangem.domain.tokens.operations.CurrencyStatusOperations @@ -17,13 +16,9 @@ import com.tangem.domain.tokens.operations.CurrencyStatusOperations /** * Proxy creator of [CryptoCurrencyStatus]. Used [CurrencyStatusOperations] to create statuses. * - * @property stakingRepository staking repository - * [REDACTED_AUTHOR] */ -class CurrencyStatusProxyCreator( - private val stakingRepository: StakingRepository, -) { +class CurrencyStatusProxyCreator { fun createCurrencyStatus( currency: CryptoCurrency, @@ -78,8 +73,8 @@ class CurrencyStatusProxyCreator( val networkStatus = networksStatuses?.firstOrNull { it.network == currency.network } val address = extractAddress(networkStatus) - val supportedIntegration = stakingRepository.getSupportedIntegrationId(currency.id) - val yieldBalance = if (supportedIntegration.isNullOrEmpty().not()) { + val supportedIntegration = StakingIntegrationID.create(currencyId = currency.id)?.value + val yieldBalance = if (supportedIntegration != null) { yieldBalances?.firstOrNull { it.integrationId == supportedIntegration && it.address == address } ?: YieldBalance.Error(integrationId = supportedIntegration, address = address) } else { diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt index 8b99cc5560..96e5279cb3 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt @@ -29,10 +29,14 @@ import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.BlockchainErrorInfo import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.staking.* import com.tangem.domain.staking.analytics.StakeScreenSource import com.tangem.domain.staking.analytics.StakingAnalyticsEvent import com.tangem.domain.staking.model.StakingApproval +import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.staking.model.stakekit.* import com.tangem.domain.staking.model.stakekit.action.StakingAction import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType @@ -45,9 +49,6 @@ import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.CreateApprovalTransactionUseCase import com.tangem.domain.transaction.usecase.GetAllowanceUseCase import com.tangem.domain.transaction.usecase.SendTransactionUseCase -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.features.staking.api.StakingComponent import com.tangem.features.staking.impl.analytics.StakingParamsInterceptor @@ -103,7 +104,6 @@ internal class StakingModel @Inject constructor( private val sendTransactionUseCase: SendTransactionUseCase, private val createApprovalTransactionUseCase: CreateApprovalTransactionUseCase, private val getAllowanceUseCase: GetAllowanceUseCase, - private val isApproveNeededUseCase: IsApproveNeededUseCase, private val vibratorHapticManager: VibratorHapticManager, private val getCardInfoUseCase: GetCardInfoUseCase, private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase, @@ -534,7 +534,7 @@ internal class StakingModel @Inject constructor( getActionRequirementAmountUseCase.invoke( integrationId = yieldBalance.integrationId, actionType = StakingActionType.CLAIM_REWARDS, - ).getOrNull() + ) } else { minimumAmount } @@ -904,21 +904,18 @@ internal class StakingModel @Inject constructor( } private suspend fun setupApprovalNeeded() { - stakingApproval = isApproveNeededUseCase(cryptoCurrencyStatus.currency).fold( - ifRight = { approval -> - if (approval is StakingApproval.Needed) { - stakingAllowance = getAllowanceUseCase( - userWalletId = userWalletId, - cryptoCurrency = cryptoCurrencyStatus.currency, - spenderAddress = approval.spenderAddress, - ).getOrElse { BigDecimal.ZERO } - } - approval - }, - ifLeft = { - StakingApproval.Empty - }, - ) + val approval = StakingIntegrationID.create(currencyId = cryptoCurrencyStatus.currency.id)?.approval + ?: StakingApproval.Empty + + stakingApproval = approval + + if (approval is StakingApproval.Needed) { + stakingAllowance = getAllowanceUseCase( + userWalletId = userWalletId, + cryptoCurrency = cryptoCurrencyStatus.currency, + spenderAddress = approval.spenderAddress, + ).getOrElse { BigDecimal.ZERO } + } } private suspend fun setupIsAnyTokenStaked() { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt index 186939ab0a..cb0d326470 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt @@ -38,13 +38,14 @@ import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.NetworkAddress +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.onramp.model.OnrampSource import com.tangem.domain.promo.ShouldShowPromoTokenUseCase import com.tangem.domain.promo.models.PromoId import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.staking.GetStakingAvailabilityUseCase import com.tangem.domain.staking.GetStakingEntryInfoUseCase -import com.tangem.domain.staking.GetStakingIntegrationIdUseCase import com.tangem.domain.staking.GetYieldUseCase import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.tokens.* @@ -67,8 +68,6 @@ import com.tangem.domain.transaction.usecase.RetryIncompleteTransactionUseCase import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase @@ -133,7 +132,6 @@ internal class TokenDetailsModel @Inject constructor( paramsContainer: ParamsContainer, expressStatusFactory: ExpressStatusFactory.Factory, getUserWalletUseCase: GetUserWalletUseCase, - getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase, private val appRouter: AppRouter, private val router: InnerTokenDetailsRouter, private val tokenDetailsDeepLinkActionListener: TokenDetailsDeepLinkActionListener, @@ -169,7 +167,6 @@ internal class TokenDetailsModel @Inject constructor( networkHasDerivationUseCase = networkHasDerivationUseCase, getUserWalletUseCase = getUserWalletUseCase, userWalletId = userWalletId, - getStakingIntegrationIdUseCase = getStakingIntegrationIdUseCase, symbol = cryptoCurrency.symbol, decimals = cryptoCurrency.decimals, ) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt index ac4a41d6a3..3b3acb4d24 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt @@ -11,8 +11,8 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.res.TangemTheme import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network -import com.tangem.domain.staking.GetStakingIntegrationIdUseCase import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents @@ -29,7 +29,6 @@ import kotlinx.coroutines.flow.MutableStateFlow internal class TokenDetailsSkeletonStateConverter( private val clickIntents: TokenDetailsClickIntents, private val networkHasDerivationUseCase: NetworkHasDerivationUseCase, - private val getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase, private val getUserWalletUseCase: GetUserWalletUseCase, private val userWalletId: UserWalletId, ) : Converter { @@ -38,7 +37,7 @@ internal class TokenDetailsSkeletonStateConverter( override fun convert(value: CryptoCurrency): TokenDetailsState { val iconState = iconStateConverter.convert(value) - val isSupportedInMobileApp = getStakingIntegrationIdUseCase(value.id).isNullOrBlank().not() + val isSupportedInMobileApp = StakingIntegrationID.create(currencyId = value.id) != null return TokenDetailsState( topAppBarConfig = TokenDetailsTopAppBarConfig( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt index 16726e8a98..251bfb5dfd 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt @@ -18,7 +18,8 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.NetworkAddress import com.tangem.domain.models.network.TxInfo -import com.tangem.domain.staking.GetStakingIntegrationIdUseCase +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.staking.model.StakingEntryInfo import com.tangem.domain.tokens.error.CurrencyStatusError @@ -28,8 +29,6 @@ import com.tangem.domain.tokens.model.TokenActionsState import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.domain.txhistory.models.TxHistoryStateError -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents @@ -55,7 +54,6 @@ internal class TokenDetailsStateFactory( private val networkHasDerivationUseCase: NetworkHasDerivationUseCase, private val getUserWalletUseCase: GetUserWalletUseCase, private val userWalletId: UserWalletId, - getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase, symbol: String, decimals: Int, ) { @@ -64,7 +62,6 @@ internal class TokenDetailsStateFactory( TokenDetailsSkeletonStateConverter( clickIntents = clickIntents, networkHasDerivationUseCase = networkHasDerivationUseCase, - getStakingIntegrationIdUseCase = getStakingIntegrationIdUseCase, getUserWalletUseCase = getUserWalletUseCase, userWalletId = userWalletId, ) From 8ec30bd373330ac811f212e6eaa74a779ae455c8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 29 Jul 2025 11:26:48 +0300 Subject: [PATCH 04/53] Updated on 2026-08-14 --- .../com/tangem/common/utils/WireMockUtils.kt | 96 ++++ .../screens/BuyTokenDetailsPageObject.kt | 101 ++++ .../screens/BuyTokenFiatListPageObject.kt | 38 ++ .../com/tangem/screens/BuyTokenPageObject.kt | 54 ++ .../com/tangem/screens/DialogPageObject.kt | 10 +- .../tangem/screens/MainScreenPageObject.kt | 5 + .../screens/ResidenceSettingsPageObject.kt | 46 ++ .../tangem/screens/SelectCountryPageObject.kt | 59 +++ .../screens/SelectPaymentMethodPageObject.kt | 50 ++ .../screens/SelectProviderPageObject.kt | 91 ++++ .../kotlin/com/tangem/tests/BuyTokenTest.kt | 469 ++++++++++++++++++ .../kotlin/com/tangem/tests/HideTokenTest.kt | 2 +- .../ui/components/appbar/TangemTopAppBar.kt | 5 + .../components/buttons/common/TangemButton.kt | 4 +- .../core/ui/components/fields/SearchBar.kt | 5 +- .../components/notifications/Notification.kt | 4 + .../tangem/core/ui/test/BaseButtonTestTags.kt | 5 + .../ui/test/BuyTokenDetailsScreenTestTags.kt | 16 + .../core/ui/test/BuyTokenFiatListTestTags.kt | 6 + .../core/ui/test/BuyTokenScreenTestTags.kt | 6 + .../com/tangem/core/ui/test/DialogTestTags.kt | 1 - .../tangem/core/ui/test/MainScreenTestTags.kt | 1 + .../core/ui/test/NotificationTestTags.kt | 6 + .../test/ResidenceSettingsScreenTestTags.kt | 5 + .../test/SelectCountryBottomSheetTestTags.kt | 12 + .../SelectPaymentMethodBottomSheetTestTags.kt | 7 + .../test/SelectProviderBottomSheetTestTags.kt | 18 + .../tangem/core/ui/test/TopAppBarTestTags.kt | 7 + .../onramp/main/ui/OnrampAmountContent.kt | 15 +- .../onramp/main/ui/OnrampButtonComponent.kt | 3 + .../onramp/main/ui/OnrampProviderContent.kt | 6 + .../paymentmethod/ui/PaymentMethodIcon.kt | 5 +- .../ui/SelectPaymentMethodBottomSheet.kt | 9 +- .../providers/ui/SelectProviderBottomSheet.kt | 32 +- .../ui/SelectCountryBottomSheet.kt | 21 +- .../ui/SelectCurrencyBottomSheet.kt | 7 +- .../selecttoken/ui/OnrampSelectToken.kt | 5 +- .../settings/ui/OnrampSettingsContent.kt | 3 + .../onramp/tokenlist/ui/OnrampTokenList.kt | 8 +- .../multicurrency/MultiCurrencyAction.kt | 4 +- 40 files changed, 1214 insertions(+), 33 deletions(-) create mode 100644 app/src/androidTest/kotlin/com/tangem/common/utils/WireMockUtils.kt create mode 100644 app/src/androidTest/kotlin/com/tangem/screens/BuyTokenDetailsPageObject.kt create mode 100644 app/src/androidTest/kotlin/com/tangem/screens/BuyTokenFiatListPageObject.kt create mode 100644 app/src/androidTest/kotlin/com/tangem/screens/BuyTokenPageObject.kt create mode 100644 app/src/androidTest/kotlin/com/tangem/screens/ResidenceSettingsPageObject.kt create mode 100644 app/src/androidTest/kotlin/com/tangem/screens/SelectCountryPageObject.kt create mode 100644 app/src/androidTest/kotlin/com/tangem/screens/SelectPaymentMethodPageObject.kt create mode 100644 app/src/androidTest/kotlin/com/tangem/screens/SelectProviderPageObject.kt create mode 100644 app/src/androidTest/kotlin/com/tangem/tests/BuyTokenTest.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/test/BaseButtonTestTags.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/test/BuyTokenDetailsScreenTestTags.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/test/BuyTokenFiatListTestTags.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/test/BuyTokenScreenTestTags.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/test/NotificationTestTags.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/test/ResidenceSettingsScreenTestTags.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/test/SelectCountryBottomSheetTestTags.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/test/SelectPaymentMethodBottomSheetTestTags.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/test/SelectProviderBottomSheetTestTags.kt create mode 100644 core/ui/src/main/java/com/tangem/core/ui/test/TopAppBarTestTags.kt diff --git a/app/src/androidTest/kotlin/com/tangem/common/utils/WireMockUtils.kt b/app/src/androidTest/kotlin/com/tangem/common/utils/WireMockUtils.kt new file mode 100644 index 0000000000..eb4bc13f9e --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/common/utils/WireMockUtils.kt @@ -0,0 +1,96 @@ +package com.tangem.common.utils + +import okhttp3.* +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.RequestBody.Companion.toRequestBody +import timber.log.Timber +import java.io.IOException + +/** + * Method uses to set WireMock scenario state + */ +fun setWireMockScenarioState( + scenarioName: String, + state: String, + baseUrl: String = "[REDACTED_ENV_URL]" +): Boolean { + val client = OkHttpClient() + val json = """{"state": "$state"}""" + val mediaType = "application/json".toMediaType() + + val request = Request.Builder() + .url("$baseUrl/__admin/scenarios/$scenarioName/state") + .put(json.toRequestBody(mediaType)) + .build() + + return try { + client.newCall(request).execute().use { response -> + val body = response.body?.string() ?: "" + Timber.d("WireMock scenario request URL: ${request.url}") + Timber.d("WireMock scenario request body: $json") + Timber.d("WireMock scenario response: ${response.code} - ${response.message}") + Timber.d("WireMock scenario response body: $body") + response.isSuccessful + } + } catch (e: IOException) { + Timber.e(e, "WireMock scenario error") + false + } +} + +/** + * Method checks accessibility of WireMock + */ +fun checkWireMockStatus(baseUrl: String = "[REDACTED_ENV_URL]"): Boolean { + val client = OkHttpClient() + val request = Request.Builder() + .url("$baseUrl/__admin/scenarios") + .get() + .build() + + return try { + client.newCall(request).execute().use { response -> + val body = response.body?.string() ?: "" + Timber.d("WireMock status check: ${response.code}") + Timber.d("Available scenarios: $body") + response.isSuccessful + } + } catch (e: IOException) { + Timber.e(e, "WireMock not accessible") + false + } +} + +/** + * Method to reset all WireMock scenarios + */ +fun resetWireMockScenarios(baseUrl: String = "[REDACTED_ENV_URL]"): Boolean { + Timber.i("=== WireMock Scenarios Reset ===") + Timber.i("Base URL: $baseUrl") + + val client = OkHttpClient() + val url = "$baseUrl/__admin/scenarios/reset" + Timber.i("Request URL: $url") + + val request = Request.Builder() + .url(url) + .post("".toRequestBody()) + .build() + + return try { + Timber.d("Sending reset request...") + client.newCall(request).execute().use { response -> + Timber.d("Response code: ${response.code}") + Timber.d("Response message: ${response.message}") + val responseBody = response.body?.string() ?: "" + Timber.d("Response body: $responseBody") + + val isSuccessful = response.isSuccessful + Timber.d("Is successful: $isSuccessful") + isSuccessful + } + } catch (e: IOException) { + Timber.e(e, "Exception during reset") + false + } +} \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/BuyTokenDetailsPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/BuyTokenDetailsPageObject.kt new file mode 100644 index 0000000000..4193f3ab8b --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/BuyTokenDetailsPageObject.kt @@ -0,0 +1,101 @@ +package com.tangem.screens + +import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import com.tangem.common.BaseTestCase +import com.tangem.core.ui.R +import com.tangem.core.ui.test.BaseButtonTestTags +import com.tangem.core.ui.test.BuyTokenDetailsScreenTestTags +import com.tangem.core.ui.test.NotificationTestTags +import com.tangem.core.ui.test.TopAppBarTestTags +import io.github.kakaocup.compose.node.element.ComposeScreen +import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen +import io.github.kakaocup.compose.node.element.KNode +import io.github.kakaocup.kakao.common.utilities.getResourceString +import androidx.compose.ui.test.hasTestTag as withTestTag + +class BuyTokenDetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + val topBarTitle: KNode = child { + hasTestTag(TopAppBarTestTags.TITLE) + useUnmergedTree = true + } + + val topBarMoreButton: KNode = child { + hasTestTag(TopAppBarTestTags.MORE_BUTTON) + useUnmergedTree = true + } + + val topBarCloseButton: KNode = child { + hasTestTag(TopAppBarTestTags.CLOSE_BUTTON) + useUnmergedTree = true + } + + val errorNotificationTitle: KNode = child { + hasTestTag(NotificationTestTags.TITLE) + hasText(getResourceString(R.string.common_error)) + useUnmergedTree = true + } + + val errorNotificationText: KNode = child { + hasTestTag(NotificationTestTags.TEXT) + hasText(getResourceString(R.string.common_unknown_error)) + useUnmergedTree = true + } + + val refreshButton: KNode = child { + hasTestTag(BaseButtonTestTags.BUTTON) + hasText(getResourceString(R.string.warning_button_refresh)) + } + + val fiatCurrencyIcon: KNode = child { + hasTestTag(BuyTokenDetailsScreenTestTags.FIAT_CURRENCY_ICON) + useUnmergedTree = true + } + + val expandFiatListButton: KNode = child { + hasTestTag(BuyTokenDetailsScreenTestTags.EXPAND_FIAT_LIST_BUTTON) + useUnmergedTree = true + } + + val fiatAmountTextField: KNode = child { + hasParent(withTestTag(BuyTokenDetailsScreenTestTags.FIAT_AMOUNT_TEXT_FIELD)) + useUnmergedTree = true + } + + val tokenAmountField: KNode = child { + hasParent(withTestTag(BuyTokenDetailsScreenTestTags.TOKEN_AMOUNT)) + useUnmergedTree = true + } + + val providerLoadingTitle: KNode = child { + hasTestTag(BuyTokenDetailsScreenTestTags.PROVIDER_LOADING_TITLE) + } + + val providerLoadingText: KNode = child { + hasTestTag(BuyTokenDetailsScreenTestTags.PROVIDER_LOADING_TEXT) + } + + val providerTitle: KNode = child { + hasTestTag(BuyTokenDetailsScreenTestTags.PROVIDER_TITLE) + useUnmergedTree = true + } + + val providerText: KNode = child { + hasTestTag(BuyTokenDetailsScreenTestTags.PROVIDER_TEXT) + useUnmergedTree = true + } + + val buyButton: KNode = child { + hasTestTag(BaseButtonTestTags.BUTTON) + hasText(getResourceString(R.string.common_buy)) + } + + val toSBlock: KNode = child { + hasTestTag(BuyTokenDetailsScreenTestTags.TOS_BLOCK) + useUnmergedTree = true + } +} + +internal fun BaseTestCase.onBuyTokenDetailsScreen(function: BuyTokenDetailsPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/BuyTokenFiatListPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/BuyTokenFiatListPageObject.kt new file mode 100644 index 0000000000..9e453330b3 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/BuyTokenFiatListPageObject.kt @@ -0,0 +1,38 @@ +package com.tangem.screens + +import androidx.compose.ui.test.SemanticsMatcher +import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import com.tangem.common.BaseTestCase +import com.tangem.common.utils.LazyListItemNode +import com.tangem.core.ui.test.BuyTokenFiatListTestTags +import com.tangem.core.ui.utils.LazyListItemPositionSemantics +import io.github.kakaocup.compose.node.element.ComposeScreen +import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen +import io.github.kakaocup.compose.node.element.KNode +import io.github.kakaocup.compose.node.element.lazylist.KLazyListNode + +class BuyTokenFiatListPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + + private val lazyList = KLazyListNode( + semanticsProvider = semanticsProvider, + viewBuilderAction = { hasTestTag(BuyTokenFiatListTestTags.LAZY_LIST) }, + itemTypeBuilder = { itemType(::LazyListItemNode) }, + positionMatcher = { position -> + SemanticsMatcher.expectValue( + LazyListItemPositionSemantics, + position + ) + } + ) + + fun fiatListItemWithTitle(title: String): KNode { + return lazyList.child { + hasText(title) + } + } +} + +internal fun BaseTestCase.onBuyTokenFiatListBottomSheet(function: BuyTokenFiatListPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/BuyTokenPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/BuyTokenPageObject.kt new file mode 100644 index 0000000000..1dbe1d3ba3 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/BuyTokenPageObject.kt @@ -0,0 +1,54 @@ +package com.tangem.screens + +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.SemanticsMatcher +import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import com.tangem.common.BaseTestCase +import com.tangem.common.utils.LazyListItemNode +import com.tangem.core.ui.R +import com.tangem.core.ui.test.BuyTokenScreenTestTags +import com.tangem.core.ui.test.TokenElementsTestTags +import com.tangem.core.ui.test.TopAppBarTestTags +import com.tangem.core.ui.utils.LazyListItemPositionSemantics +import io.github.kakaocup.compose.node.element.ComposeScreen +import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen +import io.github.kakaocup.compose.node.element.KNode +import io.github.kakaocup.compose.node.element.lazylist.KLazyListNode +import io.github.kakaocup.kakao.common.utilities.getResourceString + +class BuyTokenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + val topAppBarTitle: KNode = child { + hasTestTag(TopAppBarTestTags.TITLE) + hasText(getResourceString(R.string.common_buy)) + useUnmergedTree = true + } + + private val lazyList = KLazyListNode( + semanticsProvider = semanticsProvider, + viewBuilderAction = { hasTestTag(BuyTokenScreenTestTags.LAZY_LIST) }, + itemTypeBuilder = { itemType(::LazyListItemNode) }, + positionMatcher = { position -> + SemanticsMatcher.expectValue( + LazyListItemPositionSemantics, + position + ) + } + ) + + @OptIn(ExperimentalTestApi::class) + fun tokenWithTitleAndFiatAmount(tokenTitle: String): KNode { + return lazyList.childWith { + hasTestTag(BuyTokenScreenTestTags.LAZY_LIST_ITEM) + hasText(tokenTitle) + useUnmergedTree = true + }.child { + hasTestTag(TokenElementsTestTags.TOKEN_FIAT_AMOUNT) + useUnmergedTree = true + } + } +} + +internal fun BaseTestCase.onBuyTokenScreen(function: BuyTokenPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/DialogPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/DialogPageObject.kt index 960b371248..5bac035afc 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/DialogPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/DialogPageObject.kt @@ -3,6 +3,7 @@ package com.tangem.screens import androidx.compose.ui.test.SemanticsNodeInteractionsProvider import com.tangem.common.BaseTestCase import com.tangem.core.ui.R +import com.tangem.core.ui.test.BaseButtonTestTags import com.tangem.core.ui.test.DialogTestTags import io.github.kakaocup.compose.node.element.ComposeScreen import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen @@ -17,14 +18,19 @@ class DialogPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : } val cancelButton: KNode = child { - hasTestTag(DialogTestTags.BUTTON) + hasTestTag(BaseButtonTestTags.BUTTON) hasText(getResourceString(R.string.common_cancel)) } val hideButton: KNode = child { - hasTestTag(DialogTestTags.BUTTON) + hasTestTag(BaseButtonTestTags.BUTTON) hasText(getResourceString(R.string.token_details_hide_alert_hide)) } + + val confirmButton: KNode = child { + hasTestTag(BaseButtonTestTags.BUTTON) + hasText(getResourceString(R.string.common_confirm)) + } } internal fun BaseTestCase.onDialog(function: DialogPageObject.() -> Unit) = diff --git a/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt index c9a08a06f8..ce3a3181b3 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/MainScreenPageObject.kt @@ -39,6 +39,11 @@ class MainScreenPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) hasText(getResourceString(R.string.common_generate_addresses)) } + val buyButton: KNode = child { + hasTestTag(MainScreenTestTags.MULTI_CURRENCY_ACTION_BUTTON) + hasText(getResourceString(R.string.common_buy)) + } + /** * Find token list item with title and address */ diff --git a/app/src/androidTest/kotlin/com/tangem/screens/ResidenceSettingsPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/ResidenceSettingsPageObject.kt new file mode 100644 index 0000000000..c25bad4120 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/ResidenceSettingsPageObject.kt @@ -0,0 +1,46 @@ +package com.tangem.screens + +import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import com.tangem.common.BaseTestCase +import com.tangem.core.ui.R +import com.tangem.core.ui.test.ResidenceSettingsScreenTestTags +import com.tangem.core.ui.test.TopAppBarTestTags +import io.github.kakaocup.compose.node.element.ComposeScreen +import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen +import io.github.kakaocup.compose.node.element.KNode +import io.github.kakaocup.kakao.common.utilities.getResourceString +import com.tangem.features.onramp.impl.R as OnrampImplR + +class ResidenceSettingsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + val topBarTitle: KNode = child { + hasTestTag(TopAppBarTestTags.TITLE) + hasText(getResourceString(R.string.onramp_settings_title)) + useUnmergedTree = true + } + + val topBarCloseButton: KNode = child { + hasTestTag(TopAppBarTestTags.CLOSE_BUTTON) + useUnmergedTree = true + } + + val residenceButton: KNode = child { + hasText(getResourceString(OnrampImplR.string.onramp_settings_residence)) + useUnmergedTree = true + } + + val countryName: KNode = child { + hasTestTag(ResidenceSettingsScreenTestTags.COUNTRY_NAME) + useUnmergedTree = true + } + + val residenceSettingsDescription: KNode = child { + hasText(getResourceString(OnrampImplR.string.onramp_settings_residence_description)) + useUnmergedTree = true + } + +} + +internal fun BaseTestCase.onResidenceSettingsScreen(function: ResidenceSettingsPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/SelectCountryPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/SelectCountryPageObject.kt new file mode 100644 index 0000000000..b3a6a9d573 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/SelectCountryPageObject.kt @@ -0,0 +1,59 @@ +package com.tangem.screens + +import androidx.compose.ui.test.SemanticsMatcher +import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import com.tangem.common.BaseTestCase +import com.tangem.common.utils.LazyListItemNode +import com.tangem.core.ui.test.SelectCountryBottomSheetTestTags +import com.tangem.core.ui.utils.LazyListItemPositionSemantics +import com.tangem.features.onramp.impl.R +import io.github.kakaocup.compose.node.element.ComposeScreen +import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen +import io.github.kakaocup.compose.node.element.KNode +import io.github.kakaocup.compose.node.element.lazylist.KLazyListNode +import io.github.kakaocup.kakao.common.utilities.getResourceString +import androidx.compose.ui.test.hasText as withText +import androidx.compose.ui.test.hasTestTag as withTestTag + + +class SelectCountryPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + + private val lazyList = KLazyListNode( + semanticsProvider = semanticsProvider, + viewBuilderAction = { hasTestTag(SelectCountryBottomSheetTestTags.LAZY_LIST) }, + itemTypeBuilder = { itemType(::LazyListItemNode) }, + positionMatcher = { position -> + SemanticsMatcher.expectValue( + LazyListItemPositionSemantics, + position + ) + } + ) + + val searchBar: KNode = child { + hasTestTag(SelectCountryBottomSheetTestTags.SEARCH_BAR) + useUnmergedTree = true + } + + fun countryWithNameAndIcon(name: String): KNode { + return lazyList.child { + hasText(name) + hasAnySibling(withTestTag(SelectCountryBottomSheetTestTags.COUNTRY_ICON)) + useUnmergedTree = true + } + } + + fun unavailableCountryWithNameAndIcon(name: String): KNode { + return lazyList.child { + hasText(name) + hasAnySibling(withText(getResourceString(R.string.onramp_country_unavailable))) + hasAnySibling(withTestTag(SelectCountryBottomSheetTestTags.UNAVAILABLE_COUNTRY_ICON)) + useUnmergedTree = true + } + } +} + +internal fun BaseTestCase.onSelectCountryBottomSheet(function: SelectCountryPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/SelectPaymentMethodPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/SelectPaymentMethodPageObject.kt new file mode 100644 index 0000000000..0739883572 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/SelectPaymentMethodPageObject.kt @@ -0,0 +1,50 @@ +package com.tangem.screens + +import androidx.compose.ui.test.SemanticsMatcher +import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import com.tangem.common.BaseTestCase +import com.tangem.common.utils.LazyListItemNode +import com.tangem.core.ui.test.SelectPaymentMethodBottomSheetTestTags +import com.tangem.core.ui.test.TopAppBarTestTags +import com.tangem.core.ui.utils.LazyListItemPositionSemantics +import com.tangem.features.onramp.impl.R +import io.github.kakaocup.compose.node.element.ComposeScreen +import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen +import io.github.kakaocup.compose.node.element.KNode +import io.github.kakaocup.compose.node.element.lazylist.KLazyListNode +import io.github.kakaocup.kakao.common.utilities.getResourceString +import androidx.compose.ui.test.hasTestTag as withTestTag +import androidx.compose.ui.test.hasText as withText + +class SelectPaymentMethodPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + + private val lazyList = KLazyListNode( + semanticsProvider = semanticsProvider, + viewBuilderAction = { hasTestTag(SelectPaymentMethodBottomSheetTestTags.LAZY_LIST) }, + itemTypeBuilder = { itemType(::LazyListItemNode) }, + positionMatcher = { position -> + SemanticsMatcher.expectValue( + LazyListItemPositionSemantics, + position + ) + } + ) + + val title: KNode = child { + hasTestTag(TopAppBarTestTags.TITLE) + hasText(getResourceString(R.string.onramp_pay_with)) + } + + fun paymentMethodWithNameAndIcon(name: String): KNode { + return lazyList.child { + hasAnyDescendant(withText(name)) + hasAnyDescendant(withTestTag(SelectPaymentMethodBottomSheetTestTags.PAYMENT_METHOD_ICON)) + useUnmergedTree = true + } + } +} + +internal fun BaseTestCase.onSelectPaymentMethodBottomSheet(function: SelectPaymentMethodPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/SelectProviderPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/SelectProviderPageObject.kt new file mode 100644 index 0000000000..2c8a050019 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/SelectProviderPageObject.kt @@ -0,0 +1,91 @@ +package com.tangem.screens + +import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import com.tangem.common.BaseTestCase +import com.tangem.core.ui.test.SelectProviderBottomSheetTestTags +import com.tangem.features.onramp.impl.R +import io.github.kakaocup.compose.node.element.ComposeScreen +import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen +import io.github.kakaocup.compose.node.element.KNode +import io.github.kakaocup.kakao.common.utilities.getResourceString +import androidx.compose.ui.test.hasText as withText + +class SelectProviderPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + val title: KNode = child { + hasText(getResourceString(R.string.onramp_choose_provider_title_hint)) + useUnmergedTree = true + } + + val paymentMethodIcon: KNode = child { + hasTestTag(SelectProviderBottomSheetTestTags.PAYMENT_METHOD_ICON) + useUnmergedTree = true + } + + val paymentMethodTitle: KNode = child { + hasTestTag(SelectProviderBottomSheetTestTags.PAYMENT_METHOD_NAME) + useUnmergedTree = true + } + + val paymentMethodName: KNode = child { + hasTestTag(SelectProviderBottomSheetTestTags.PAYMENT_METHOD_NAME) + useUnmergedTree = true + } + + val paymentMethodExpandButton: KNode = child { + hasTestTag(SelectProviderBottomSheetTestTags.PAYMENT_METHOD_EXPAND_BUTTON) + useUnmergedTree = true + } + + val availableProviderItem: KNode = child { + hasTestTag(SelectProviderBottomSheetTestTags.AVAILABLE_PROVIDER_ITEM) + useUnmergedTree = true + } + + val availableProviderName: KNode = child { + hasTestTag(SelectProviderBottomSheetTestTags.AVAILABLE_PROVIDER_NAME) + useUnmergedTree = true + } + + val tokenAmount: KNode = child { + hasTestTag(SelectProviderBottomSheetTestTags.TOKEN_AMOUNT) + useUnmergedTree = true + } + + val unavailableProviderItem: KNode = child { + hasTestTag(SelectProviderBottomSheetTestTags.UNAVAILABLE_PROVIDER_ITEM) + useUnmergedTree = true + } + + val unavailableProviderName: KNode = child { + hasTestTag(SelectProviderBottomSheetTestTags.UNAVAILABLE_PROVIDER_NAME) + useUnmergedTree = true + } + + val moreProvidersIcon: KNode = child { + hasTestTag(SelectProviderBottomSheetTestTags.MORE_PROVIDERS_ICON) + useUnmergedTree = true + } + + val moreProvidersText: KNode = child { + hasTestTag(SelectProviderBottomSheetTestTags.MORE_PROVIDERS_TEXT) + useUnmergedTree = true + } + + val bestRateLabel: KNode = child { + hasTestTag(SelectProviderBottomSheetTestTags.BEST_RATE_LABEL) + useUnmergedTree = true + } + + fun availableProviderWithName(name: String, tokenAmount: String, rate: String): KNode = child { + hasTestTag(SelectProviderBottomSheetTestTags.AVAILABLE_PROVIDER_ITEM) + hasAnyChild(withText(name)) + hasAnyChild(withText(tokenAmount)) + hasAnyChild(withText(rate)) + useUnmergedTree = true + } +} + +internal fun BaseTestCase.onSelectProviderBottomSheet(function: SelectProviderPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/tests/BuyTokenTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/BuyTokenTest.kt new file mode 100644 index 0000000000..d65c64ed2b --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/tests/BuyTokenTest.kt @@ -0,0 +1,469 @@ +package com.tangem.tests + +import com.tangem.common.BaseTestCase +import com.tangem.common.extensions.clickWithAssertion +import com.tangem.common.utils.resetWireMockScenarios +import com.tangem.common.utils.setWireMockScenarioState +import com.tangem.scenarios.OpenMainScreenScenario +import com.tangem.screens.* +import dagger.hilt.android.testing.HiltAndroidTest +import io.qameta.allure.kotlin.AllureId +import io.qameta.allure.kotlin.junit4.DisplayName +import org.junit.Test + +@HiltAndroidTest +class BuyTokenTest : BaseTestCase() { + + @AllureId("3478") + @DisplayName("Onramp: error in providers loading") + @Test + fun errorInProvidersLoadingTest() { + setupHooks( + additionalAfterSection = { + resetWireMockScenarios() + } + ).run { + val tokenTitle = "Bitcoin" + val balance = "$184.85" + + resetWireMockScenarios() + + step("Setup WireMock scenario for 'Error' state") { + setWireMockScenarioState("payment_methods", "Error") + } + step("Open 'Main Screen'") { + scenario(OpenMainScreenScenario(composeTestRule)) + } + step("Click on 'Synchronize addresses' button") { + onMainScreen { synchronizeAddressesButton.clickWithAssertion() } + } + step("Assert wallet balance = $balance") { + onMainScreen { walletBalance().assertTextContains(balance) } + } + step("Click on 'Buy' button") { + onMainScreen { buyButton.clickWithAssertion() } + } + step("Click on token with name: '$tokenTitle'") { + onBuyTokenScreen { + topAppBarTitle.assertIsDisplayed() + tokenWithTitleAndFiatAmount(tokenTitle).clickWithAssertion() + } + } + step("Assert error notification title is displayed") { + onBuyTokenDetailsScreen { errorNotificationTitle.assertIsDisplayed() } + } + step("Assert error notification text is displayed") { + onBuyTokenDetailsScreen { errorNotificationText.assertIsDisplayed() } + } + step("Assert 'Refresh' button is displayed and clickable") { + onBuyTokenDetailsScreen { refreshButton.clickWithAssertion() } + } + } + } + + @AllureId("2565") + @DisplayName("Onramp: validate currency selector") + @Test + fun validateCurrencySelectorTest() { + setupHooks().run { + val tokenTitle = "Polygon" + val balance = "$184.85" + val popularFiatsTitle = "Popular Fiats" + val otherCurrenciesTitle = "Other currencies" + val australianDollar = "AUD" + val fiatAmount = "1" + val tokenAmount = "POL 488.24938338" + + step("Open 'Main Screen'") { + scenario(OpenMainScreenScenario(composeTestRule)) + } + step("Click on 'Synchronize addresses' button") { + onMainScreen { synchronizeAddressesButton.clickWithAssertion() } + } + step("Assert wallet balance = '$balance'") { + onMainScreen { walletBalance().assertTextContains(balance) } + } + step("Click on 'Buy' button") { + onMainScreen { buyButton.clickWithAssertion() } + } + step("Click on token with name: '$tokenTitle'") { + onBuyTokenScreen { + topAppBarTitle.assertIsDisplayed() + tokenWithTitleAndFiatAmount(tokenTitle).clickWithAssertion() + } + } + step("Click on 'Confirm' button in 'Dialog'") { + onDialog { confirmButton.clickWithAssertion() } + } + step("Write fiat amount = '$fiatAmount'") { + onBuyTokenDetailsScreen { fiatAmountTextField.performTextInput(fiatAmount) } + } + step("Assert 'Provider loading block' is displayed") { + onBuyTokenDetailsScreen { + providerLoadingTitle.assertIsDisplayed() + providerLoadingText.assertIsDisplayed() + } + } + step("Assert 'Provider block' is displayed") { + onBuyTokenDetailsScreen { + providerTitle.assertIsDisplayed() + providerText.assertIsDisplayed() + } + } + step("Assert token amount = '$tokenAmount'") { + onBuyTokenDetailsScreen { + tokenAmountField.assertTextContains(tokenAmount) + } + } + step("Fiat currency icon is displayed") { + onBuyTokenDetailsScreen { fiatCurrencyIcon.assertIsDisplayed() } + } + step("Click on 'Expand fiat list' button") { + onBuyTokenDetailsScreen { expandFiatListButton.clickWithAssertion() } + } + step("Assert '$popularFiatsTitle' is displayed") { + onBuyTokenFiatListBottomSheet { + fiatListItemWithTitle(popularFiatsTitle).assertIsDisplayed() + } + } + step("Assert '$otherCurrenciesTitle' is displayed") { + onBuyTokenFiatListBottomSheet { + fiatListItemWithTitle(otherCurrenciesTitle).assertIsDisplayed() + } + } + step("Click on fiat with title: '$australianDollar'") { + onBuyTokenFiatListBottomSheet { + fiatListItemWithTitle(australianDollar).performClick() + } + } + step("Assert new fiat currency: '$australianDollar' is displayed") { + onBuyTokenDetailsScreen { + fiatAmountTextField.assertTextContains(australianDollar + fiatAmount) + } + } + step("Assert token amount = '$tokenAmount'") { + onBuyTokenDetailsScreen { + tokenAmountField.assertTextContains(tokenAmount) + } + } + } + } + + @AllureId("2566") + @DisplayName("Onramp: validate 'Buy token' screen") + @Test + fun validateBuyTokenScreenTest() { + setupHooks().run { + val tokenTitle = "Polygon" + val balance = "$184.85" + val euro = "EUR" + val fiatAmount = "1" + val tokenAmount = "POL 488.24938338" + + step("Open 'Main Screen'") { + scenario(OpenMainScreenScenario(composeTestRule)) + } + step("Click on 'Synchronize addresses' button") { + onMainScreen { synchronizeAddressesButton.clickWithAssertion() } + } + step("Assert wallet balance = '$balance'") { + onMainScreen { walletBalance().assertTextContains(balance) } + } + step("Click on 'Buy' button") { + onMainScreen { buyButton.clickWithAssertion() } + } + step("Click on token with name: '$tokenTitle'") { + onBuyTokenScreen { + topAppBarTitle.assertIsDisplayed() + tokenWithTitleAndFiatAmount(tokenTitle).clickWithAssertion() + } + } + step("Click on 'Confirm' button in 'Dialog'") { + onDialog { confirmButton.clickWithAssertion() } + } + step("Assert 'Buy Token' title is displayed") { + onBuyTokenDetailsScreen { topBarTitle.assertTextContains("Buy $tokenTitle") } + } + step("Assert 'More button' in top bar is displayed") { + onBuyTokenDetailsScreen { topBarMoreButton.assertIsDisplayed() } + } + step("Write fiat amount = '$fiatAmount'") { + onBuyTokenDetailsScreen { fiatAmountTextField.performTextInput(fiatAmount) } + } + step("Assert fiat amount = '$fiatAmount'") { + onBuyTokenDetailsScreen { fiatAmountTextField.assertTextContains(euro + fiatAmount) } + } + step("Assert 'Provider loading block' is displayed") { + onBuyTokenDetailsScreen { + providerLoadingTitle.assertIsDisplayed() + providerLoadingText.assertIsDisplayed() + } + } + step("Assert 'Provider block' is displayed") { + onBuyTokenDetailsScreen { + providerTitle.assertIsDisplayed() + providerText.assertIsDisplayed() + } + } + step("Assert token amount = '$tokenAmount'") { + onBuyTokenDetailsScreen { tokenAmountField.assertTextContains(tokenAmount) } + } + step("Assert 'ToS' block is displayed") { + onBuyTokenDetailsScreen { toSBlock.assertIsDisplayed()} + } + step("Assert 'Buy' button is displayed") { + onBuyTokenDetailsScreen { buyButton.assertIsDisplayed()} + } + step("Assert 'Close' button in top bar is displayed") { + onBuyTokenDetailsScreen { topBarCloseButton.assertIsDisplayed() } + } + } + } + + @AllureId("2563") + @DisplayName("Onramp: validate 'Residence' settings screen") + @Test + fun validateResidenceSettingsScreenTest() { + setupHooks().run { + val tokenTitle = "Polygon" + val balance = "$184.85" + val country = "Albania" + val unavailableCountry = "Lebanon" + + step("Open 'Main Screen'") { + scenario(OpenMainScreenScenario(composeTestRule)) + } + step("Click on 'Synchronize addresses' button") { + onMainScreen { synchronizeAddressesButton.clickWithAssertion() } + } + step("Assert wallet balance = '$balance'") { + onMainScreen { walletBalance().assertTextContains(balance) } + } + step("Click on 'Buy' button") { + onMainScreen { buyButton.clickWithAssertion() } + } + step("Click on token with name: '$tokenTitle'") { + onBuyTokenScreen { + topAppBarTitle.assertIsDisplayed() + tokenWithTitleAndFiatAmount(tokenTitle).clickWithAssertion() + } + } + step("Click on 'Confirm' button in 'Dialog'") { + onDialog { confirmButton.clickWithAssertion() } + } + step("Assert 'Buy $tokenTitle' title is displayed") { + onBuyTokenDetailsScreen { topBarTitle.assertTextContains("Buy $tokenTitle") } + } + step("Click 'More' button in tab bar") { + onBuyTokenDetailsScreen { topBarMoreButton.clickWithAssertion() } + } + step("Assert 'Residence Settings' screen top bar title is displayed") { + onResidenceSettingsScreen { topBarTitle.assertIsDisplayed() } + } + step("Assert 'Residence Settings' screen top bar 'Close' button is displayed") { + onResidenceSettingsScreen { topBarCloseButton.assertIsDisplayed() } + } + step("Assert 'Residence' button is displayed on 'Residence Settings' screen") { + onResidenceSettingsScreen { residenceButton.assertIsDisplayed() } + } + step("Assert country name is displayed on 'Residence Settings' screen") { + onResidenceSettingsScreen { countryName.assertIsDisplayed() } + } + step("Assert residence settings description is displayed on 'Residence Settings' screen") { + onResidenceSettingsScreen { residenceSettingsDescription.assertIsDisplayed() } + } + step("Click 'Residence button'") { + onResidenceSettingsScreen { residenceButton.clickWithAssertion() } + } + step("Assert 'Search bar' is displayed") { + onSelectCountryBottomSheet { searchBar.assertIsDisplayed() } + } + step("Type unavailable country name: '$unavailableCountry' in 'Search bar'") { + onSelectCountryBottomSheet { searchBar.performTextReplacement(unavailableCountry) } + } + step("Unavailable country: '$unavailableCountry' is displayed") { + onSelectCountryBottomSheet { unavailableCountryWithNameAndIcon(unavailableCountry).assertIsDisplayed() } + } + step("Type country name: '$country' in 'Search bar'") { + onSelectCountryBottomSheet { searchBar.performTextReplacement(country) } + } + step("Available country: '$country' is displayed") { + onSelectCountryBottomSheet { countryWithNameAndIcon(country).assertIsDisplayed() } + } + step("Click on country: '$country'") { + onSelectCountryBottomSheet { countryWithNameAndIcon(country).clickWithAssertion() } + } + step("Assert country: '$country' is displayed on 'Residence Settings' screen") { + onResidenceSettingsScreen { countryName.assertTextContains(country) } + } + } + } + + @AllureId("2570") + @DisplayName("Onramp: validate 'Select provider' bottom sheet") + @Test + fun validateProvidersScreenTest() { + setupHooks().run { + val tokenTitle = "Polygon" + val balance = "$184.85" + val paymentMethod = "Card" + val fiatAmount = "1" + val providerNameMercuryo = "Mercuryo" + val providerNameSimplex = "Simplex" + val tokenAmount = "POL 488.24938338" + val bestRate = "Best rate" + val rate = "-0.00%" + + step("Open 'Main Screen'") { + scenario(OpenMainScreenScenario(composeTestRule)) + } + step("Click on 'Synchronize addresses' button") { + onMainScreen { synchronizeAddressesButton.clickWithAssertion() } + } + step("Assert wallet balance = '$balance'") { + onMainScreen { walletBalance().assertTextContains(balance) } + } + step("Click on 'Buy' button") { + onMainScreen { buyButton.clickWithAssertion() } + } + step("Click on token with name: '$tokenTitle'") { + onBuyTokenScreen { + topAppBarTitle.assertIsDisplayed() + tokenWithTitleAndFiatAmount(tokenTitle).clickWithAssertion() + } + } + step("Click on 'Confirm' button in 'Dialog'") { + onDialog { confirmButton.clickWithAssertion() } + } + step("Write fiat amount = '$fiatAmount'") { + onBuyTokenDetailsScreen { fiatAmountTextField.performTextInput(fiatAmount) } + } + step("Assert 'Provider block' is displayed") { + onBuyTokenDetailsScreen { + providerTitle.assertIsDisplayed() + providerText.assertIsDisplayed() + } + } + step("Open 'Select Provider' bottom sheet") { + onBuyTokenDetailsScreen { providerTitle.performClick() } + } + step("Assert available provider name is displayed") { + onSelectProviderBottomSheet { availableProviderItem.assertIsDisplayed() } + } + step("Assert unavailable provider name is displayed") { + onSelectProviderBottomSheet { unavailableProviderItem.assertIsDisplayed() } + } + step("Click on 'Expand payment methods' button") { + onSelectProviderBottomSheet { paymentMethodExpandButton.clickWithAssertion() } + } + step("Click on payment method: '$paymentMethod'") { + onSelectPaymentMethodBottomSheet { paymentMethodWithNameAndIcon(paymentMethod).clickWithAssertion() } + } + step("Assert 'Select Provider' bottom sheet title is displayed") { + onSelectProviderBottomSheet { title.assertIsDisplayed() } + } + step("Assert payment method icon is displayed") { + onSelectProviderBottomSheet { paymentMethodIcon.assertIsDisplayed() } + } + step("Assert payment method title is displayed") { + onSelectProviderBottomSheet { paymentMethodTitle.assertIsDisplayed() } + } + step("Assert payment method name is displayed") { + onSelectProviderBottomSheet { paymentMethodName.assertIsDisplayed() } + } + step("Assert provider with name: '$providerNameMercuryo' and rate: '$bestRate' is displayed") { + onSelectProviderBottomSheet { + availableProviderWithName(providerNameMercuryo, tokenAmount, bestRate).assertIsDisplayed() + } + } + step("Assert provider with name: '$providerNameSimplex' and rate: '$rate' is displayed") { + onSelectProviderBottomSheet { + availableProviderWithName(providerNameSimplex, tokenAmount, rate).assertIsDisplayed() + } + } + step("Assert 'More providers' icon is displayed") { + onSelectProviderBottomSheet { moreProvidersIcon.assertIsDisplayed() } + } + step("Assert 'More providers' text is displayed") { + onSelectProviderBottomSheet { moreProvidersText.assertIsDisplayed() } + } + step("Assert 'Best rate' label is displayed") { + onSelectProviderBottomSheet { bestRateLabel.assertIsDisplayed() } + } + } + } + + @AllureId("2570") + @DisplayName("Onramp: validate 'Select payment method' bottom sheet") + @Test + fun validatePaymentMethodScreenTest() { + setupHooks().run { + val tokenTitle = "Polygon" + val balance = "$184.85" + val card = "Card" + val googlePay = "Google Pay" + val invoiceRevolutPay = "Invoice Revolut Pay" + val sepa = "Sepa" + val fiatAmount = "1" + + step("Open 'Main Screen'") { + scenario(OpenMainScreenScenario(composeTestRule)) + } + step("Click on 'Synchronize addresses' button") { + onMainScreen { synchronizeAddressesButton.clickWithAssertion() } + } + step("Assert wallet balance = '$balance'") { + onMainScreen { walletBalance().assertTextContains(balance) } + } + step("Click on 'Buy' button") { + onMainScreen { buyButton.clickWithAssertion() } + } + step("Click on token with name: '$tokenTitle'") { + onBuyTokenScreen { + topAppBarTitle.assertIsDisplayed() + tokenWithTitleAndFiatAmount(tokenTitle).clickWithAssertion() + } + } + step("Click on 'Confirm' button in 'Dialog'") { + onDialog { confirmButton.clickWithAssertion() } + } + step("Write fiat amount = '$fiatAmount'") { + onBuyTokenDetailsScreen { fiatAmountTextField.performTextInput(fiatAmount) } + } + step("Assert 'Provider block' is displayed") { + onBuyTokenDetailsScreen { + providerTitle.assertIsDisplayed() + providerText.assertIsDisplayed() + } + } + step("Open 'Select Provider' bottom sheet") { + onBuyTokenDetailsScreen { providerTitle.performClick() } + } + step("Click on 'Expand payment methods' button") { + onSelectProviderBottomSheet { paymentMethodExpandButton.clickWithAssertion() } + } + step("Assert 'Select Payment Method' bottom sheet title is displayed") { + onSelectPaymentMethodBottomSheet { title.assertIsDisplayed() } + } + step("Assert payment method: '$card' is displayed") { + onSelectPaymentMethodBottomSheet { paymentMethodWithNameAndIcon(card).assertIsDisplayed() } + } + step("Assert payment method: '$googlePay' is displayed") { + onSelectPaymentMethodBottomSheet { paymentMethodWithNameAndIcon(googlePay).assertIsDisplayed() } + } + step("Assert payment method: '$invoiceRevolutPay' is displayed") { + onSelectPaymentMethodBottomSheet { paymentMethodWithNameAndIcon(invoiceRevolutPay).assertIsDisplayed() } + } + step("Assert payment method: '$sepa' is displayed") { + onSelectPaymentMethodBottomSheet { paymentMethodWithNameAndIcon(sepa).assertIsDisplayed() } + } + step("Press 'Back' button") { + onSelectPaymentMethodBottomSheet { device.uiDevice.pressBack() } + } + step("Assert 'Select Provider' bottom sheet title is displayed") { + onSelectProviderBottomSheet { title.assertIsDisplayed() } + } + } + } + +} \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/tests/HideTokenTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/HideTokenTest.kt index dd0dee7e9e..6bbf318ec7 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/HideTokenTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/HideTokenTest.kt @@ -17,7 +17,7 @@ class HideTokenTest : BaseTestCase() { @Test fun hideWalletTokenByHideButtonTest() { val tokenTitle = "Polygon" - val balance = "<$0.01" + val balance = "$184.85" setupHooks().run { step("Open 'Main Screen'") { scenario(OpenMainScreenScenario(composeTestRule)) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/TangemTopAppBar.kt b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/TangemTopAppBar.kt index 716d79978f..319beeb81f 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/TangemTopAppBar.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/TangemTopAppBar.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -20,6 +21,7 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.TopAppBarTestTags /** * [TangemTopAppBar] height options. @@ -127,6 +129,7 @@ fun TangemTopAppBar( TopAppBarButton( button = endButton, tint = iconTint, + modifier = modifier.testTag(TopAppBarTestTags.MORE_BUTTON), ) } }, @@ -172,6 +175,7 @@ fun TangemTopAppBar( TopAppBarButton( button = startButton, tint = iconTint, + modifier = modifier.testTag(TopAppBarTestTags.CLOSE_BUTTON), ) } } @@ -222,6 +226,7 @@ private fun TopAppBarTitle( color = textColor, maxLines = 1, overflow = TextOverflow.Ellipsis, + modifier = Modifier.testTag(TopAppBarTestTags.TITLE), ) AnimatedVisibility( diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt index 3a2dfe9d3d..90dbd2db78 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt @@ -25,7 +25,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.tangem.core.ui.components.ResizableText import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.test.DialogTestTags +import com.tangem.core.ui.test.BaseButtonTestTags import com.tangem.core.ui.utils.MultipleClickPreventer @Suppress("LongParameterList") @@ -51,7 +51,7 @@ fun TangemButton( Button( modifier = modifier .heightIn(min = size.toHeightDp()) - .testTag(DialogTestTags.BUTTON), + .testTag(BaseButtonTestTags.BUTTON), onClick = { multipleClickPreventer.processEvent { if (!showProgress) onClick() } }, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SearchBar.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SearchBar.kt index a302980668..4366b157be 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SearchBar.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SearchBar.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.SoftwareKeyboardController +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType @@ -35,6 +36,7 @@ import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.SelectCountryBottomSheetTestTags @Composable fun SearchBar( @@ -57,7 +59,8 @@ fun SearchBar( } else { state.onActiveChange(false) } - }, + } + .testTag(SelectCountryBottomSheetTestTags.SEARCH_BAR), enabled = enabled, value = state.query, onValueChange = state.onQueryChange, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt index 10bfdbba1d..1e8c2f342d 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.Preview @@ -35,6 +36,7 @@ import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.NotificationTestTags import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState as NotificationButtonsState /** @@ -206,6 +208,7 @@ internal fun TextsBlock( text = titleText, color = titleColor, style = TangemTheme.typography.button, + modifier = modifier.testTag(NotificationTestTags.TITLE), ) SpacerH(height = TangemTheme.dimens.spacing2) @@ -215,6 +218,7 @@ internal fun TextsBlock( text = subtitle.resolveReference(), color = subtitleColor, style = TangemTheme.typography.caption2, + modifier = modifier.testTag(NotificationTestTags.TEXT), ) } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/BaseButtonTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/BaseButtonTestTags.kt new file mode 100644 index 0000000000..9d4954ba04 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/BaseButtonTestTags.kt @@ -0,0 +1,5 @@ +package com.tangem.core.ui.test + +object BaseButtonTestTags { + const val BUTTON = "BASE_BUTTON" +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/BuyTokenDetailsScreenTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/BuyTokenDetailsScreenTestTags.kt new file mode 100644 index 0000000000..0f045c5da5 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/BuyTokenDetailsScreenTestTags.kt @@ -0,0 +1,16 @@ +package com.tangem.core.ui.test + +object BuyTokenDetailsScreenTestTags { + const val EXPAND_FIAT_LIST_BUTTON = "BUY_TOKEN_DETAILS_SCREEN_EXPAND_FIAT_LIST_BUTTON" + const val FIAT_CURRENCY_ICON = "BUY_TOKEN_DETAILS_SCREEN_FIAT_CURRENCY_ICON" + const val FIAT_AMOUNT_TEXT_FIELD = "BUY_TOKEN_DETAILS_SCREEN_FIAT_AMOUNT_TEXT_FIELD" + const val TOKEN_AMOUNT = "BUY_TOKEN_DETAILS_SCREEN_TOKEN_AMOUNT" + + const val PROVIDER_LOADING_TITLE = "BUY_TOKEN_DETAILS_SCREEN_PROVIDER_LOADING_TITLE" + const val PROVIDER_LOADING_TEXT = "BUY_TOKEN_DETAILS_SCREEN_PROVIDER_LOADING_TITLE" + + const val PROVIDER_TITLE = "BUY_TOKEN_DETAILS_SCREEN_PROVIDER_TITLE" + const val PROVIDER_TEXT = "BUY_TOKEN_DETAILS_SCREEN_PROVIDER_TEXT" + + const val TOS_BLOCK = "BUY_TOKEN_DETAILS_SCREEN_TOS_BLOCK" +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/BuyTokenFiatListTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/BuyTokenFiatListTestTags.kt new file mode 100644 index 0000000000..a656caf7f0 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/BuyTokenFiatListTestTags.kt @@ -0,0 +1,6 @@ +package com.tangem.core.ui.test + +object BuyTokenFiatListTestTags { + const val LAZY_LIST = "BUY_TOKEN_FIAT_LIST_LAZY_LIST" + const val LAZY_LIST_ITEM = "BUY_TOKEN_FIAT_LIST_LAZY_LIST_ITEM" +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/BuyTokenScreenTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/BuyTokenScreenTestTags.kt new file mode 100644 index 0000000000..af41f67814 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/BuyTokenScreenTestTags.kt @@ -0,0 +1,6 @@ +package com.tangem.core.ui.test + +object BuyTokenScreenTestTags { + const val LAZY_LIST = "BUY_TOKEN_SCREEN_LAZY_LIST" + const val LAZY_LIST_ITEM = "BUY_TOKEN_SCREEN_LAZY_LIST_ITEM" +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/DialogTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/DialogTestTags.kt index 8f911d8632..0c5eae6e75 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/test/DialogTestTags.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/test/DialogTestTags.kt @@ -2,5 +2,4 @@ package com.tangem.core.ui.test object DialogTestTags { const val DIALOG_CONTAINER = "DIALOG_CONTAINER" - const val BUTTON = "DIALOG_BUTTON" } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/MainScreenTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/MainScreenTestTags.kt index f635ce4544..dfef1326a7 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/test/MainScreenTestTags.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/test/MainScreenTestTags.kt @@ -8,4 +8,5 @@ object MainScreenTestTags { const val WALLET_BALANCE = "MAIN_SCREEN_WALLET_BALANCE" const val WALLET_LIST_ITEM = "MAIN_SCREEN_WALLET_LIST_ITEM" const val ORGANIZE_TOKENS_BUTTON = "MAIN_SCREEN_ORGANIZE_TOKENS_BUTTON" + const val MULTI_CURRENCY_ACTION_BUTTON = "MAIN_SCREEN_MULTI_CURRENCY_ACTION_BUTTON" } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/NotificationTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/NotificationTestTags.kt new file mode 100644 index 0000000000..15a63e2186 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/NotificationTestTags.kt @@ -0,0 +1,6 @@ +package com.tangem.core.ui.test + +object NotificationTestTags { + const val TITLE = "NOTIFICATION_TITLE" + const val TEXT = "NOTIFICATION_TEXT" +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/ResidenceSettingsScreenTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/ResidenceSettingsScreenTestTags.kt new file mode 100644 index 0000000000..9ebf2b26e0 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/ResidenceSettingsScreenTestTags.kt @@ -0,0 +1,5 @@ +package com.tangem.core.ui.test + +object ResidenceSettingsScreenTestTags { + const val COUNTRY_NAME = "RESIDENCE_SETTINGS_SCREEN_COUNTRY_NAME" +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/SelectCountryBottomSheetTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/SelectCountryBottomSheetTestTags.kt new file mode 100644 index 0000000000..6a2d859e19 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/SelectCountryBottomSheetTestTags.kt @@ -0,0 +1,12 @@ +package com.tangem.core.ui.test + +object SelectCountryBottomSheetTestTags { + + const val LAZY_LIST = "SELECT_COUNTRY_BOTTOM_SHEET_LAZY_LIST" + const val COUNTRY_ITEM = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_ITEM" + const val UNAVAILABLE_COUNTRY_ITEM = "SELECT_COUNTRY_BOTTOM_SHEET_UNAVAILABLE_COUNTRY_ITEM" + const val SEARCH_BAR = "SELECT_COUNTRY_BOTTOM_SHEET_SEARCH_BAR" + const val COUNTRY_ICON = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_ICON" + const val UNAVAILABLE_COUNTRY_ICON = "SELECT_COUNTRY_BOTTOM_SHEET_UNAVAILABLE_COUNTRY_ICON" + const val COUNTRY_NAME = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_NAME" +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/SelectPaymentMethodBottomSheetTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/SelectPaymentMethodBottomSheetTestTags.kt new file mode 100644 index 0000000000..ebb601e9a5 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/SelectPaymentMethodBottomSheetTestTags.kt @@ -0,0 +1,7 @@ +package com.tangem.core.ui.test + +object SelectPaymentMethodBottomSheetTestTags { + + const val LAZY_LIST = "SELECT_PAYMENT_METHOD_LAZY_LIST" + const val PAYMENT_METHOD_ICON = "PAYMENT_METHOD_NAME" +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/SelectProviderBottomSheetTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/SelectProviderBottomSheetTestTags.kt new file mode 100644 index 0000000000..d457d18189 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/SelectProviderBottomSheetTestTags.kt @@ -0,0 +1,18 @@ +package com.tangem.core.ui.test + +object SelectProviderBottomSheetTestTags { + + const val PAYMENT_METHOD_ICON = "SELECT_PROVIDER_BOTTOM_SHEET_PAYMENT_METHOD_ICON" + const val PAYMENT_METHOD_TITLE = "SELECT_PROVIDER_BOTTOM_SHEET_PAYMENT_METHOD_TITLE" + const val PAYMENT_METHOD_NAME = "SELECT_PROVIDER_BOTTOM_SHEET_PAYMENT_METHOD_NAME" + const val PAYMENT_METHOD_EXPAND_BUTTON = "SELECT_PROVIDER_BOTTOM_SHEET_PAYMENT_METHOD_EXPAND_BUTTON" + const val TOKEN_AMOUNT = "SELECT_PROVIDER_BOTTOM_SHEET_TOKEN_AMOUNT" + const val AVAILABLE_PROVIDER_NAME = "SELECT_PROVIDER_BOTTOM_SHEET_AVAILABLE_PROVIDER_NAME" + const val AVAILABLE_PROVIDER_ITEM = "SELECT_PROVIDER_BOTTOM_SHEET_AVAILABLE_PROVIDER_ITEM" + const val UNAVAILABLE_PROVIDER_ITEM = "SELECT_PROVIDER_BOTTOM_SHEET_UNAVAILABLE_PROVIDER_ITEM" + const val UNAVAILABLE_PROVIDER_NAME = "SELECT_PROVIDER_BOTTOM_SHEET_UNAVAILABLE_PROVIDER_NAME" + const val UNAVAILABLE_PROVIDER_SUBTITLE = "SELECT_PROVIDER_BOTTOM_SHEET_UNAVAILABLE_PROVIDER_SUBTITLE" + const val MORE_PROVIDERS_ICON = "SELECT_PROVIDER_BOTTOM_SHEET_MORE_PROVIDERS_ICON" + const val MORE_PROVIDERS_TEXT = "SELECT_PROVIDER_BOTTOM_SHEET_MORE_PROVIDERS_TEXT" + const val BEST_RATE_LABEL = "SELECT_PROVIDER_BOTTOM_SHEET_BEST_RATE_LABEL" +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/TopAppBarTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/TopAppBarTestTags.kt new file mode 100644 index 0000000000..1c2c1b09bf --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/test/TopAppBarTestTags.kt @@ -0,0 +1,7 @@ +package com.tangem.core.ui.test + +object TopAppBarTestTags { + const val TITLE = "TOP_APP_BAR_TITLE" + const val MORE_BUTTON = "TOP_APP_BAR_MORE_BUTTON" + const val CLOSE_BUTTON = "TOP_APP_BAR_CLOSE_BUTTON" +} \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/ui/OnrampAmountContent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/ui/OnrampAmountContent.kt index 07d5b17e3c..a493e1a7c9 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/ui/OnrampAmountContent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/ui/OnrampAmountContent.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDirection @@ -26,6 +27,7 @@ import com.tangem.core.ui.components.fields.AmountTextField import com.tangem.core.ui.components.fields.visualtransformations.AmountVisualTransformation import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.test.BuyTokenDetailsScreenTestTags import com.tangem.core.ui.utils.rememberDecimalFormat import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.main.entity.OnrampAmountBlockUM @@ -84,7 +86,8 @@ private fun OnrampAmountField(amountField: AmountFieldModel) { start = TangemTheme.dimens.spacing12, end = TangemTheme.dimens.spacing12, ) - .requiredHeightIn(min = TangemTheme.dimens.size32), + .requiredHeightIn(min = TangemTheme.dimens.size32) + .testTag(BuyTokenDetailsScreenTestTags.FIAT_AMOUNT_TEXT_FIELD), ) LaunchedEffect(key1 = Unit) { @@ -101,7 +104,8 @@ private fun OnrampAmountSecondary(state: OnrampAmountSecondaryFieldUM) { top = TangemTheme.dimens.spacing8, start = TangemTheme.dimens.spacing12, end = TangemTheme.dimens.spacing12, - ), + ) + .testTag(BuyTokenDetailsScreenTestTags.TOKEN_AMOUNT), contentAlignment = Alignment.Center, ) { when (state) { @@ -138,12 +142,15 @@ private fun OnrampCurrencyIcon(currencyUM: OnrampCurrencyUM, modifier: Modifier AsyncImage( modifier = Modifier .size(TangemTheme.dimens.size40) - .clip(CircleShape), + .clip(CircleShape) + .testTag(BuyTokenDetailsScreenTestTags.FIAT_CURRENCY_ICON), model = currencyUM.iconUrl, contentDescription = null, ) Icon( - modifier = Modifier.size(TangemTheme.dimens.size16), + modifier = Modifier + .size(TangemTheme.dimens.size16) + .testTag(BuyTokenDetailsScreenTestTags.EXPAND_FIAT_LIST_BUTTON), painter = painterResource(id = R.drawable.ic_chevron_24), tint = TangemTheme.colors.icon.informative, contentDescription = null, diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/ui/OnrampButtonComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/ui/OnrampButtonComponent.kt index d63b3aa736..fe7e8c0075 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/ui/OnrampButtonComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/ui/OnrampButtonComponent.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.ClickableText import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -15,6 +16,7 @@ import com.tangem.core.ui.components.PrimaryButton import com.tangem.core.ui.extensions.appendColored import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.test.BuyTokenDetailsScreenTestTags import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.main.entity.OnrampMainComponentUM import com.tangem.features.onramp.main.entity.OnrampProviderBlockUM @@ -77,6 +79,7 @@ private fun OnrampTosText(provider: OnrampProviderBlockUM.Content?) { color = TangemTheme.colors.text.tertiary, textAlign = TextAlign.Center, ), + modifier = Modifier.testTag(BuyTokenDetailsScreenTestTags.TOS_BLOCK), onClick = { offset -> clickableAnnotation.getStringAnnotations( tag = TERMS_OF_USE_KEY, diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/ui/OnrampProviderContent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/ui/OnrampProviderContent.kt index e13fd96310..8d2bf768e6 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/ui/OnrampProviderContent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/ui/OnrampProviderContent.kt @@ -13,12 +13,14 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.withStyle import com.tangem.core.ui.extensions.appendSpace import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.test.BuyTokenDetailsScreenTestTags import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.main.entity.OnrampProviderBlockUM import com.tangem.features.onramp.paymentmethod.ui.PaymentMethodIcon @@ -60,6 +62,7 @@ private fun OnrampProviderBlock(state: OnrampProviderBlockUM.Content, modifier: }, color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.body2, + modifier = modifier.testTag(BuyTokenDetailsScreenTestTags.PROVIDER_TITLE), ) Text( text = buildAnnotatedString { @@ -69,6 +72,7 @@ private fun OnrampProviderBlock(state: OnrampProviderBlockUM.Content, modifier: }, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, + modifier = modifier.testTag(BuyTokenDetailsScreenTestTags.PROVIDER_TEXT), ) } AnimatedVisibility( @@ -108,6 +112,7 @@ private fun OnrampProviderLoading(modifier: Modifier = Modifier) { text = stringResourceSafe(id = R.string.express_provider), style = TangemTheme.typography.subtitle2, color = TangemTheme.colors.text.tertiary, + modifier = Modifier.testTag(BuyTokenDetailsScreenTestTags.PROVIDER_LOADING_TITLE), ) Row( verticalAlignment = Alignment.CenterVertically, @@ -122,6 +127,7 @@ private fun OnrampProviderLoading(modifier: Modifier = Modifier) { text = stringResourceSafe(id = R.string.express_fetch_best_rates), style = TangemTheme.typography.body2, color = TangemTheme.colors.text.tertiary, + modifier = Modifier.testTag(BuyTokenDetailsScreenTestTags.PROVIDER_LOADING_TEXT), ) } } diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/paymentmethod/ui/PaymentMethodIcon.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/paymentmethod/ui/PaymentMethodIcon.kt index 18e7c56005..8c6125e33d 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/paymentmethod/ui/PaymentMethodIcon.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/paymentmethod/ui/PaymentMethodIcon.kt @@ -7,10 +7,12 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.testTag import coil.compose.SubcomposeAsyncImage import coil.request.ImageRequest import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.test.SelectProviderBottomSheetTestTags @Composable internal fun PaymentMethodIcon(imageUrl: String, modifier: Modifier = Modifier) { @@ -19,7 +21,8 @@ internal fun PaymentMethodIcon(imageUrl: String, modifier: Modifier = Modifier) .size(TangemTheme.dimens.size40) .clip(TangemTheme.shapes.roundedCorners8) .background(TangemColorPalette.Light1) - .padding(TangemTheme.dimens.spacing6), + .padding(TangemTheme.dimens.spacing6) + .testTag(SelectProviderBottomSheetTestTags.PAYMENT_METHOD_ICON), model = ImageRequest.Builder(context = LocalContext.current) .data(imageUrl) .crossfade(enable = true) diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/paymentmethod/ui/SelectPaymentMethodBottomSheet.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/paymentmethod/ui/SelectPaymentMethodBottomSheet.kt index fb80254c3c..ee929788ee 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/paymentmethod/ui/SelectPaymentMethodBottomSheet.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/paymentmethod/ui/SelectPaymentMethodBottomSheet.kt @@ -11,11 +11,13 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.testTag import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.selectedBorder import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.test.SelectPaymentMethodBottomSheetTestTags import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.paymentmethod.entity.PaymentMethodUM import com.tangem.features.onramp.paymentmethod.entity.PaymentMethodsBottomSheetConfig @@ -49,7 +51,7 @@ private fun SelectPaymentMethodBottomSheetContent( modifier: Modifier = Modifier, ) { LazyColumn( - modifier = modifier, + modifier = modifier.testTag(SelectPaymentMethodBottomSheetTestTags.LAZY_LIST), ) { items( items = methods, @@ -86,7 +88,10 @@ private fun PaymentMethodItem(paymentMethod: PaymentMethodUM, isSelected: Boolea verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), ) { - PaymentMethodIcon(paymentMethod.imageUrl) + PaymentMethodIcon( + imageUrl = paymentMethod.imageUrl, + modifier = Modifier.testTag(SelectPaymentMethodBottomSheetTestTags.PAYMENT_METHOD_ICON), + ) Text( text = paymentMethod.name, style = TangemTheme.typography.subtitle2, diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/ui/SelectProviderBottomSheet.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/ui/SelectProviderBottomSheet.kt index 59cb8cb0b0..116ca08de5 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/ui/SelectProviderBottomSheet.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/ui/SelectProviderBottomSheet.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextAlign @@ -32,6 +33,7 @@ import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet import com.tangem.core.ui.extensions.* import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.SelectProviderBottomSheetTestTags import com.tangem.domain.onramp.model.OnrampPaymentMethod import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.paymentmethod.ui.PaymentMethodIcon @@ -101,15 +103,19 @@ private fun PaymentMethodBlock( text = stringResourceSafe(id = R.string.onramp_pay_with), style = TangemTheme.typography.body2, color = TangemTheme.colors.text.tertiary, + modifier = Modifier.testTag(SelectProviderBottomSheetTestTags.PAYMENT_METHOD_TITLE), ) Text( text = state.name, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.primary1, + modifier = Modifier.testTag(SelectProviderBottomSheetTestTags.PAYMENT_METHOD_NAME), ) } Icon( - modifier = Modifier.size(TangemTheme.dimens.size24), + modifier = Modifier + .size(TangemTheme.dimens.size24) + .testTag(SelectProviderBottomSheetTestTags.PAYMENT_METHOD_EXPAND_BUTTON), painter = painterResource(id = R.drawable.ic_chevron_24), contentDescription = null, tint = TangemTheme.colors.icon.informative, @@ -149,14 +155,16 @@ private fun AvailableProviderItem(state: ProviderListItemUM.Available.Content, m modifier = modifier .selectedBorder(isSelected = state.isSelected) .clickable(onClick = state.onClick) - .padding(all = TangemTheme.dimens.spacing12), + .padding(all = TangemTheme.dimens.spacing12) + .testTag(SelectProviderBottomSheetTestTags.AVAILABLE_PROVIDER_ITEM), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), ) { SubcomposeAsyncImage( modifier = Modifier .size(size = TangemTheme.dimens.size40) - .clip(TangemTheme.shapes.roundedCorners8), + .clip(TangemTheme.shapes.roundedCorners8) + .testTag(SelectProviderBottomSheetTestTags.AVAILABLE_PROVIDER_NAME), model = ImageRequest.Builder(context = LocalContext.current) .data(state.imageUrl) .crossfade(enable = true) @@ -166,7 +174,9 @@ private fun AvailableProviderItem(state: ProviderListItemUM.Available.Content, m contentDescription = null, ) Text( - modifier = Modifier.weight(1F), + modifier = Modifier + .weight(1F) + .testTag(SelectProviderBottomSheetTestTags.TOKEN_AMOUNT), text = state.name, style = TangemTheme.typography.subtitle2, color = if (state.isSelected) TangemTheme.colors.text.primary1 else TangemTheme.colors.text.secondary, @@ -189,7 +199,8 @@ private fun AvailableProviderItem(state: ProviderListItemUM.Available.Content, m modifier = Modifier .clip(RoundedCornerShape(4.dp)) .background(TangemTheme.colors.icon.accent) - .padding(vertical = 1.dp, horizontal = 6.dp), + .padding(vertical = 1.dp, horizontal = 6.dp) + .testTag(SelectProviderBottomSheetTestTags.BEST_RATE_LABEL), ) } state.diffRate != null -> { @@ -218,7 +229,8 @@ private fun UnavailableProviderItem( Row( modifier = modifier .selectedBorder(isSelected = isSelected) - .padding(all = TangemTheme.dimens.spacing12), + .padding(all = TangemTheme.dimens.spacing12) + .testTag(SelectProviderBottomSheetTestTags.UNAVAILABLE_PROVIDER_ITEM), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), ) { @@ -240,11 +252,13 @@ private fun UnavailableProviderItem( text = providerName, style = TangemTheme.typography.subtitle2, color = TangemTheme.colors.text.secondary, + modifier = Modifier.testTag(SelectProviderBottomSheetTestTags.UNAVAILABLE_PROVIDER_NAME), ) Text( text = subtitle.resolveReference(), style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, + modifier = Modifier.testTag(SelectProviderBottomSheetTestTags.UNAVAILABLE_PROVIDER_SUBTITLE), ) } } @@ -259,7 +273,8 @@ private fun OnrampMoreProviders() { contentDescription = null, tint = TangemTheme.colors.icon.informative, modifier = Modifier - .padding(top = 16.dp), + .padding(top = 16.dp) + .testTag(SelectProviderBottomSheetTestTags.MORE_PROVIDERS_ICON), ) Text( text = stringResourceSafe(R.string.express_more_providers_soon), @@ -267,7 +282,8 @@ private fun OnrampMoreProviders() { color = TangemTheme.colors.icon.informative, modifier = Modifier .padding(top = 4.dp, bottom = 24.dp) - .padding(horizontal = TangemTheme.dimens.spacing56), + .padding(horizontal = TangemTheme.dimens.spacing56) + .testTag(SelectProviderBottomSheetTestTags.MORE_PROVIDERS_TEXT), textAlign = TextAlign.Center, ) } diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcountry/ui/SelectCountryBottomSheet.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcountry/ui/SelectCountryBottomSheet.kt index aaea4ccb7e..741e13aaa6 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcountry/ui/SelectCountryBottomSheet.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcountry/ui/SelectCountryBottomSheet.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import coil.compose.AsyncImage @@ -25,6 +26,7 @@ import com.tangem.core.ui.components.fields.SearchBar import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.test.SelectCountryBottomSheetTestTags import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.selectcountry.entity.CountryItemState import com.tangem.features.onramp.selectcountry.entity.CountryListUM @@ -40,7 +42,7 @@ internal fun SelectCountryBottomSheet(config: TangemBottomSheetConfig, content: @Composable internal fun OnrampCountryList(state: CountryListUM, modifier: Modifier = Modifier) { LazyColumn( - modifier = modifier, + modifier = modifier.testTag(SelectCountryBottomSheetTestTags.LAZY_LIST), contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16), ) { item(key = "search_bar") { @@ -102,13 +104,15 @@ private fun LazyListScope.countryListWithContent(state: CountryListUM.Content) { modifier = Modifier .fillMaxWidth() .clickable(onClick = item.onClick) - .padding(vertical = TangemTheme.dimens.spacing16), + .padding(vertical = TangemTheme.dimens.spacing16) + .testTag(SelectCountryBottomSheetTestTags.COUNTRY_ITEM), state = item, ) is CountryItemState.WithContent.Unavailable -> UnavailableCountryItem( modifier = Modifier .fillMaxWidth() - .padding(vertical = TangemTheme.dimens.spacing16), + .padding(vertical = TangemTheme.dimens.spacing16) + .testTag(SelectCountryBottomSheetTestTags.UNAVAILABLE_COUNTRY_ITEM), state = item, ) } @@ -138,13 +142,17 @@ private fun ContentCountryItem(state: CountryItemState.WithContent.Content, modi verticalAlignment = Alignment.CenterVertically, ) { AsyncImage( - modifier = Modifier.size(TangemTheme.dimens.size36), + modifier = Modifier + .size(TangemTheme.dimens.size36) + .testTag(SelectCountryBottomSheetTestTags.COUNTRY_ICON), model = state.flagUrl, contentDescription = null, ) Text( text = state.countryName, - modifier = Modifier.weight(1F), + modifier = Modifier + .weight(1F) + .testTag(SelectCountryBottomSheetTestTags.COUNTRY_NAME), overflow = TextOverflow.Ellipsis, maxLines = 1, style = TangemTheme.typography.subtitle2, @@ -172,7 +180,8 @@ private fun UnavailableCountryItem(state: CountryItemState.WithContent.Unavailab AsyncImage( modifier = Modifier .size(TangemTheme.dimens.size36) - .alpha(0.4F), + .alpha(0.4F) + .testTag(SelectCountryBottomSheetTestTags.UNAVAILABLE_COUNTRY_ICON), model = state.flagUrl, contentDescription = null, ) diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcurrency/ui/SelectCurrencyBottomSheet.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcurrency/ui/SelectCurrencyBottomSheet.kt index 8bcf48db89..cbc2cb8075 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcurrency/ui/SelectCurrencyBottomSheet.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcurrency/ui/SelectCurrencyBottomSheet.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.testTag import androidx.compose.ui.util.fastForEach import coil.compose.AsyncImage import com.tangem.core.ui.components.CircleShimmer @@ -25,6 +26,7 @@ import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.test.BuyTokenFiatListTestTags import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.selectcurrency.entity.CurrenciesListUM import com.tangem.features.onramp.selectcurrency.entity.CurrencyItemState @@ -40,7 +42,7 @@ internal fun SelectCurrencyBottomSheet(config: TangemBottomSheetConfig, content: @Composable internal fun OnrampCurrencyList(state: CurrenciesListUM, modifier: Modifier = Modifier) { LazyColumn( - modifier = modifier, + modifier = modifier.testTag(BuyTokenFiatListTestTags.LAZY_LIST), contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16), ) { item(key = "search_bar") { @@ -106,7 +108,8 @@ private fun LazyListScope.currencyListContent(state: CurrenciesListUM.Content) { modifier = Modifier .fillMaxWidth() .clickable(onClick = item.onClick) - .padding(vertical = TangemTheme.dimens.spacing16), + .padding(vertical = TangemTheme.dimens.spacing16) + .testTag(BuyTokenFiatListTestTags.LAZY_LIST_ITEM), currency = item, ) }, diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/ui/OnrampSelectToken.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/ui/OnrampSelectToken.kt index d2c298c8f6..689cee2068 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/ui/OnrampSelectToken.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/ui/OnrampSelectToken.kt @@ -10,10 +10,12 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.dp import com.tangem.core.ui.components.appbar.AppBarWithBackButton import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.test.BuyTokenScreenTestTags import com.tangem.core.ui.utils.rememberHideKeyboardNestedScrollConnection import com.tangem.features.onramp.hottokens.HotCryptoComponent import com.tangem.features.onramp.impl.R @@ -36,7 +38,8 @@ internal fun OnrampSelectToken( .nestedScroll(nestedScrollConnection) .background(TangemTheme.colors.background.secondary) .imePadding() - .systemBarsPadding(), + .systemBarsPadding() + .testTag(BuyTokenScreenTestTags.LAZY_LIST), ) { stickyHeader(key = "header") { AppBarWithBackButton( diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/settings/ui/OnrampSettingsContent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/settings/ui/OnrampSettingsContent.kt index 8a0d062131..33ba297a0e 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/settings/ui/OnrampSettingsContent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/settings/ui/OnrampSettingsContent.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.key import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.util.fastForEach import coil.compose.AsyncImage @@ -19,6 +20,7 @@ import com.tangem.core.ui.components.SpacerWMax import com.tangem.core.ui.components.appbar.AppBarWithBackButton import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.test.ResidenceSettingsScreenTestTags import com.tangem.core.ui.utils.WindowInsetsZero import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.settings.entity.OnrampSettingsItemUM @@ -95,6 +97,7 @@ private fun ResidenceSection(state: OnrampSettingsItemUM.Residence, modifier: Mo text = state.countryName, color = TangemTheme.colors.text.primary1, style = TangemTheme.typography.body2, + modifier = Modifier.testTag(ResidenceSettingsScreenTestTags.COUNTRY_NAME), ) Icon( painter = painterResource(id = R.drawable.ic_chevron_right_24), diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/ui/OnrampTokenList.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/ui/OnrampTokenList.kt index c45da297d2..27943a21c4 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/ui/OnrampTokenList.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/ui/OnrampTokenList.kt @@ -9,6 +9,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.key import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp @@ -24,6 +26,8 @@ import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM import com.tangem.core.ui.decorations.roundedShapeItemDecoration import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.BuyTokenScreenTestTags +import com.tangem.core.ui.utils.lazyListItemPosition import com.tangem.features.onramp.tokenlist.entity.TokenListUM import com.tangem.features.onramp.tokenlist.ui.preview.PreviewTokenListUMProvider import kotlinx.collections.immutable.ImmutableList @@ -90,7 +94,9 @@ private fun ItemsBlock(items: ImmutableList, isBalanceHidden: lastIndex = items.lastIndex, addDefaultPadding = false, backgroundColor = TangemTheme.colors.background.primary, - ), + ) + .testTag(BuyTokenScreenTestTags.LAZY_LIST_ITEM) + .semantics { lazyListItemPosition = index }, ) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyAction.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyAction.kt index e82c67b4fe..8465d3fca3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyAction.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyAction.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp @@ -14,6 +15,7 @@ import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.buttons.actions.ActionButtonContent import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.test.MainScreenTestTags /** [REDACTED_AUTHOR] @@ -48,7 +50,7 @@ internal fun MultiCurrencyAction( paddingBetweenIconAndText = 4.dp, ) }, - modifier = modifier, + modifier = modifier.testTag(MainScreenTestTags.MULTI_CURRENCY_ACTION_BUTTON), color = TangemTheme.colors.button.secondary, ) } \ No newline at end of file From b2a199a56800b1873ab395c1f496a0a8c221ed38 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 25 Jul 2025 16:16:39 +0400 Subject: [PATCH 05/53] Updated on 2026-08-14 --- .../tap/di/domain/StakingDomainModule.kt | 6 + .../tap/di/domain/TokensDomainModule.kt | 5 + .../staking/di/YieldBalanceSupplierModule.kt | 5 +- .../DefaultSingleYieldBalanceProducer.kt | 42 +--- .../DefaultSingleYieldBalanceProducerTest.kt | 207 ++++++++---------- .../tangem/domain/staking/StakingIdFactory.kt | 27 ++- .../single/SingleYieldBalanceProducer.kt | 11 +- domain/tokens/build.gradle.kts | 7 +- .../BaseCurrencyStatusOperations.kt | 34 ++- .../CachedCurrenciesStatusesOperations.kt | 20 +- 10 files changed, 177 insertions(+), 187 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt index 213e3753c7..7f6857ddf0 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt @@ -210,4 +210,10 @@ internal object StakingDomainModule { fun provideGetActionRequirementAmountUseCase(): GetActionRequirementAmountUseCase { return GetActionRequirementAmountUseCase() } + + @Provides + @Singleton + fun provideStakingIdFactory(walletManagersFacade: WalletManagersFacade): StakingIdFactory { + return StakingIdFactory(walletManagersFacade = walletManagersFacade) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index eddbbc0fd4..b1d8469ec4 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -11,6 +11,7 @@ import com.tangem.domain.promo.PromoRepository import com.tangem.domain.quotes.QuotesRepository import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier +import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.staking.repositories.StakingRepository import com.tangem.domain.staking.single.SingleYieldBalanceFetcher @@ -405,6 +406,7 @@ internal object TokensDomainModule { singleYieldBalanceSupplier: SingleYieldBalanceSupplier, multiYieldBalanceFetcher: MultiYieldBalanceFetcher, multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, + stakingIdFactory: StakingIdFactory, ): BaseCurrenciesStatusesOperations { return CachedCurrenciesStatusesOperations( currenciesRepository = currenciesRepository, @@ -420,6 +422,7 @@ internal object TokensDomainModule { multiYieldBalanceFetcher = multiYieldBalanceFetcher, tokensFeatureToggles = tokensFeatureToggles, multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, + stakingIdFactory = stakingIdFactory, ) } @@ -439,6 +442,7 @@ internal object TokensDomainModule { singleYieldBalanceSupplier: SingleYieldBalanceSupplier, multiYieldBalanceFetcher: MultiYieldBalanceFetcher, multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, + stakingIdFactory: StakingIdFactory, ): BaseCurrencyStatusOperations { return CachedCurrenciesStatusesOperations( currenciesRepository = currenciesRepository, @@ -454,6 +458,7 @@ internal object TokensDomainModule { multiYieldBalanceFetcher = multiYieldBalanceFetcher, multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, tokensFeatureToggles = tokensFeatureToggles, + stakingIdFactory = stakingIdFactory, ) } diff --git a/data/staking/src/main/java/com/tangem/data/staking/di/YieldBalanceSupplierModule.kt b/data/staking/src/main/java/com/tangem/data/staking/di/YieldBalanceSupplierModule.kt index 3407a3d99b..3ef90f5b74 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/di/YieldBalanceSupplierModule.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/di/YieldBalanceSupplierModule.kt @@ -42,9 +42,8 @@ internal object YieldBalanceSupplierModule { listOf( "single_yield_balance", params.userWalletId.stringValue, - params.currencyId.value, - params.network.id.rawId, - params.network.id.derivationPath, + params.stakingId.integrationId, + params.stakingId.address, ) .joinToString(separator = "_") }, diff --git a/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducer.kt b/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducer.kt index bd38f407a4..cac9b3c1a3 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducer.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducer.kt @@ -2,8 +2,6 @@ package com.tangem.data.staking.single import com.tangem.core.analytics.api.AnalyticsExceptionHandler import com.tangem.core.analytics.models.ExceptionAnalyticsEvent -import com.tangem.data.staking.utils.StakingIdFactory -import com.tangem.domain.staking.model.StakingID import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.staking.multi.MultiYieldBalanceProducer import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier @@ -24,7 +22,7 @@ import timber.log.Timber * * @property params params * @property multiYieldBalanceSupplier multi yield balance supplier - * @property stakingIdFactory factory for creating [StakingID] + * @property analyticsExceptionHandler analytics exception handler * @property dispatchers dispatchers * [REDACTED_AUTHOR] @@ -32,20 +30,17 @@ import timber.log.Timber internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor( @Assisted private val params: SingleYieldBalanceProducer.Params, private val multiYieldBalanceSupplier: MultiYieldBalanceSupplier, - private val stakingIdFactory: StakingIdFactory, private val analyticsExceptionHandler: AnalyticsExceptionHandler, private val dispatchers: CoroutineDispatcherProvider, ) : SingleYieldBalanceProducer { override val fallback: YieldBalance by lazy { YieldBalance.Error( - integrationId = stakingIdFactory.createIntegrationId(currencyId = params.currencyId), - address = null, + integrationId = params.stakingId.integrationId, + address = params.stakingId.address, ) } - private var stakingId: StakingID? = null - override fun produce(): Flow { Timber.i("Producing yield balance for params:\n$params") @@ -53,12 +48,7 @@ internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor( params = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId), ) .mapNotNull { balances -> - val currentStakingId = getStakingId() - - if (currentStakingId == null) { - Timber.i("Staking ID is null for params: $params") - return@mapNotNull YieldBalance.Unsupported - } + val currentStakingId = params.stakingId val currentBalances = balances.filter { it.getStakingId() == currentStakingId } @@ -86,34 +76,16 @@ internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor( currentBalances.first() } } else { - val balance = currentBalances.firstOrNull() + val balance = currentBalances.firstOrNull() ?: return@mapNotNull null - if (balance != null) { - Timber.i("Yield balance found for $currentStakingId:\n$balance") - balance - } else { - Timber.i("No yield balance found for $currentStakingId:\n${YieldBalance.Unsupported}") - YieldBalance.Unsupported - } + Timber.i("Yield balance found for $currentStakingId:\n$balance") + balance } } .distinctUntilChanged() .flowOn(dispatchers.default) } - private suspend fun getStakingId(): StakingID? { - val saved = stakingId - - if (saved != null) return saved - - return stakingIdFactory.create( - userWalletId = params.userWalletId, - currencyId = params.currencyId, - network = params.network, - ) - .also { stakingId = it } - } - @AssistedFactory interface Factory : SingleYieldBalanceProducer.Factory { override fun create(params: SingleYieldBalanceProducer.Params): DefaultSingleYieldBalanceProducer diff --git a/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducerTest.kt b/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducerTest.kt index 1d7e5af029..b6f14b7e6b 100644 --- a/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducerTest.kt +++ b/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducerTest.kt @@ -1,13 +1,10 @@ package com.tangem.data.staking.single import com.google.common.truth.Truth -import com.tangem.blockchain.common.Blockchain import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory -import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory import com.tangem.common.test.utils.getEmittedValues import com.tangem.core.analytics.api.AnalyticsExceptionHandler import com.tangem.data.staking.toDomain -import com.tangem.data.staking.utils.StakingIdFactory import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.staking.model.StakingID import com.tangem.domain.staking.model.stakekit.YieldBalance @@ -15,40 +12,49 @@ import com.tangem.domain.staking.multi.MultiYieldBalanceProducer import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier import com.tangem.domain.staking.single.SingleYieldBalanceProducer import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider -import io.mockk.* +import io.mockk.clearMocks +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify import kotlinx.coroutines.flow.* import kotlinx.coroutines.test.runTest -import org.junit.Test +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance /** [REDACTED_AUTHOR] */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) internal class DefaultSingleYieldBalanceProducerTest { private val params = SingleYieldBalanceProducer.Params( userWalletId = UserWalletId(stringValue = "011"), - currencyId = ton.id, - network = ton.network, + stakingId = tonId, ) private val multiNetworkStatusSupplier = mockk() - private val stakingIdFactory = mockk() private val analyticsExceptionHandler = mockk(relaxUnitFun = true) private val dispatchers = TestingCoroutineDispatcherProvider() private val producer = DefaultSingleYieldBalanceProducer( params = params, - stakingIdFactory = stakingIdFactory, multiYieldBalanceSupplier = multiNetworkStatusSupplier, analyticsExceptionHandler = analyticsExceptionHandler, dispatchers = dispatchers, ) + @BeforeEach + fun resetMocks() { + clearMocks(multiNetworkStatusSupplier, analyticsExceptionHandler) + } + @Test - fun `test that flow is mapped for data from params`() = runTest { + fun `flow is mapped for data from params`() = runTest { + // Arrange val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain() - val expected = flowOf( + val multiFlow = flowOf( setOf( balance, MockYieldBalanceWrapperDTOFactory.createWithBalance(solanaId).toDomain(), @@ -56,97 +62,89 @@ internal class DefaultSingleYieldBalanceProducerTest { ) val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId) - every { multiNetworkStatusSupplier(multiParams) } returns expected - coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns tonId + every { multiNetworkStatusSupplier(multiParams) } returns multiFlow - val actual = producer.produce() + // Act + val actual = getEmittedValues(flow = producer.produce()) - verify { multiNetworkStatusSupplier(multiParams) } + Truth.assertThat(actual).hasSize(1) + Truth.assertThat(actual).containsExactly(balance) - val values = getEmittedValues(flow = actual) - - coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } - - Truth.assertThat(values.size).isEqualTo(1) - Truth.assertThat(values).isEqualTo(listOf(balance)) + verify(exactly = 1) { multiNetworkStatusSupplier(multiParams) } } @Test - fun `test that flow is updated if yield balance is updated`() = runTest { - val expected = MutableSharedFlow>(replay = 2, extraBufferCapacity = 1) + fun `flow is updated if yield balance is updated`() = runTest { + // Arrange + val multiFlow = MutableSharedFlow>(replay = 2, extraBufferCapacity = 1) val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId) - every { multiNetworkStatusSupplier(multiParams) } returns expected - coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns tonId + every { multiNetworkStatusSupplier(multiParams) } returns multiFlow - val actual = producer.produceWithFallback() + val producerFlow = producer.produceWithFallback() - verify { multiNetworkStatusSupplier(multiParams) } - - // first emit val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain() - expected.emit(value = setOf(balance)) + val updatedBalance = YieldBalance.Error(integrationId = tonId.integrationId, address = tonId.address) - val values1 = getEmittedValues(flow = actual) + // Act (first emit) + multiFlow.emit(value = setOf(balance)) + val actual1 = getEmittedValues(flow = producerFlow) - coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } + // Assert (first emit) + Truth.assertThat(actual1).hasSize(1) + Truth.assertThat(actual1).containsExactly(balance) - Truth.assertThat(values1.size).isEqualTo(1) - Truth.assertThat(values1).isEqualTo(listOf(balance)) + // Act (second emit) + multiFlow.emit(value = setOf(updatedBalance)) + val actual2 = getEmittedValues(flow = producerFlow) - // second emit - val updatedStatus = YieldBalance.Error(integrationId = tonId.integrationId, address = tonId.address) - expected.emit(value = setOf(updatedStatus)) + // Assert (second emit) + Truth.assertThat(actual2).hasSize(2) + Truth.assertThat(actual2).containsExactly(balance, updatedBalance) - val values2 = getEmittedValues(flow = actual) - - coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } - - Truth.assertThat(values2.size).isEqualTo(2) - Truth.assertThat(values2).isEqualTo(listOf(balance, updatedStatus)) + verify(exactly = 1) { multiNetworkStatusSupplier(multiParams) } } @Test - fun `test that flow is filtered the same status`() = runTest { - val expected = MutableSharedFlow>(replay = 2, extraBufferCapacity = 1) + fun `flow is filtered the same status`() = runTest { + // Arrange + val multiFlow = MutableSharedFlow>(replay = 2, extraBufferCapacity = 1) val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId) - every { multiNetworkStatusSupplier(multiParams) } returns expected - coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns tonId + every { multiNetworkStatusSupplier(multiParams) } returns multiFlow - val actual = producer.produceWithFallback() + val producerFlow = producer.produceWithFallback() - verify { multiNetworkStatusSupplier(multiParams) } - - // first emit val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain() - expected.emit(value = setOf(balance)) - val values1 = getEmittedValues(flow = actual) + // Act (first emit) + multiFlow.emit(value = setOf(balance)) + val actual1 = getEmittedValues(flow = producerFlow) - coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } + // Assert (first emit) + Truth.assertThat(actual1).hasSize(1) + Truth.assertThat(actual1).containsExactly(balance) - Truth.assertThat(values1.size).isEqualTo(1) - Truth.assertThat(values1).isEqualTo(listOf(balance)) + // Act (second emit) + multiFlow.emit(value = setOf(balance)) + val actual2 = getEmittedValues(flow = producerFlow) - // second emit - expected.emit(value = setOf(balance)) + // Assert (second emit) + Truth.assertThat(actual2).hasSize(1) + Truth.assertThat(actual2).containsExactly(balance) - val values2 = getEmittedValues(flow = actual) - - coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } - - Truth.assertThat(values2.size).isEqualTo(1) - Truth.assertThat(values2).isEqualTo(listOf(balance)) + verify(exactly = 1) { multiNetworkStatusSupplier(multiParams) } } @Test - fun `test if flow throws exception`() = runTest { + fun `flow throws exception`() = runTest { + // Arrange val exception = IllegalStateException() + val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain() val innerFlow = MutableStateFlow(value = false) - val expected = flow { + val multiFlow = flow { if (innerFlow.value) { emit(setOf(balance)) } else { @@ -156,83 +154,52 @@ internal class DefaultSingleYieldBalanceProducerTest { .buffer(capacity = 5) val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId) - every { multiNetworkStatusSupplier(multiParams) } returns expected - every { stakingIdFactory.createIntegrationId(currencyId = params.currencyId) } returns tonId.integrationId + every { multiNetworkStatusSupplier(multiParams) } returns multiFlow - val actual = producer.produceWithFallback() + val producerFlow = producer.produceWithFallback() - verify { multiNetworkStatusSupplier(multiParams) } + // Act (first emit) + val actual1 = getEmittedValues(flow = producerFlow) - val values1 = getEmittedValues(flow = actual) + // Assert (first emit) + val fallbackStatus = YieldBalance.Error(integrationId = tonId.integrationId, address = "0x1") - coVerify(inverse = true) { stakingIdFactory.create(any(), any(), any()) } - - Truth.assertThat(values1.size).isEqualTo(1) - val fallbackStatus = YieldBalance.Error(integrationId = tonId.integrationId, address = null) - Truth.assertThat(values1).isEqualTo(listOf(fallbackStatus)) - - coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns tonId + Truth.assertThat(actual1).hasSize(1) + Truth.assertThat(actual1).containsExactly(fallbackStatus) + // Act (second emit) innerFlow.emit(value = true) + val actual2 = getEmittedValues(flow = producerFlow) - val values2 = getEmittedValues(flow = actual) + Truth.assertThat(actual2).hasSize(1) + Truth.assertThat(actual2).containsExactly(balance) - coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } - - Truth.assertThat(values2.size).isEqualTo(1) - Truth.assertThat(values2).isEqualTo(listOf(balance)) + verify(exactly = 1) { multiNetworkStatusSupplier(multiParams) } } @Test - fun `test if flow doesn't contain network from params`() = runTest { + fun `flow doesn't contain network from params`() = runTest { + // Arrange val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(solanaId).toDomain() - val yieldBalancesFlow = flowOf(setOf(balance)) + val multiFlow = flowOf(setOf(balance)) val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId) - every { multiNetworkStatusSupplier(multiParams) } returns yieldBalancesFlow - coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns tonId + every { multiNetworkStatusSupplier(multiParams) } returns multiFlow - val actual = producer.produce() + val producerFlow = producer.produce() - verify { multiNetworkStatusSupplier(multiParams) } + // Act + val actual = getEmittedValues(flow = producerFlow) - val values = getEmittedValues(flow = actual) + // Assert + Truth.assertThat(actual).isEmpty() - coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } - - val expected = YieldBalance.Unsupported - Truth.assertThat(values.first()).isEqualTo(expected) - } - - @Test - fun `test if wallet manager facade returns null`() = runTest { - val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain() - - val yieldBalancesFlow = flowOf(setOf(balance)) - - val multiParams = MultiYieldBalanceProducer.Params(userWalletId = params.userWalletId) - every { multiNetworkStatusSupplier(multiParams) } returns yieldBalancesFlow - coEvery { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } returns null - - val actual = producer.produce() - - verify { multiNetworkStatusSupplier(multiParams) } - - val values = getEmittedValues(flow = actual) - - coVerify { stakingIdFactory.create(params.userWalletId, params.currencyId, params.network) } - - val expected = YieldBalance.Unsupported - Truth.assertThat(values.first()).isEqualTo(expected) + verify(exactly = 1) { multiNetworkStatusSupplier(multiParams) } } private companion object { - val mocks = MockCryptoCurrencyFactory() - - val ton = mocks.createCoin(Blockchain.TON) - val tonId = MockYieldBalanceWrapperDTOFactory.defaultStakingId val solanaId = StakingID( integrationId = "solana-sol-native-multivalidator-staking", diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/StakingIdFactory.kt b/domain/staking/src/main/java/com/tangem/domain/staking/StakingIdFactory.kt index 0da69ebb56..a830af4484 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/StakingIdFactory.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/StakingIdFactory.kt @@ -42,12 +42,37 @@ class StakingIdFactory( userWalletId: UserWalletId, currencyId: CryptoCurrency.ID, network: Network, + ): Either { + return createInternal( + currencyId = currencyId, + defaultAddressProvider = { + walletManagersFacade.getDefaultAddress(userWalletId = userWalletId, network = network) + }, + ) + } + + /** + * Creates a [StakingID] for the given cryptocurrency and default address + * + * @param currencyId the identifier of the cryptocurrency + * @param defaultAddress the default address for staking, can be null + */ + fun create(currencyId: CryptoCurrency.ID, defaultAddress: String?): Either { + return createInternal( + currencyId = currencyId, + defaultAddressProvider = { defaultAddress }, + ) + } + + private inline fun createInternal( + currencyId: CryptoCurrency.ID, + defaultAddressProvider: () -> String?, ): Either = either { val integrationId = StakingIntegrationID.create(currencyId = currencyId) ensureNotNull(integrationId) { Error.UnsupportedCurrency } - val address = walletManagersFacade.getDefaultAddress(userWalletId = userWalletId, network = network) + val address = defaultAddressProvider().takeUnless { it.isNullOrEmpty() } ensureNotNull(address) { Error.UnableToGetAddress(integrationId = integrationId) } diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceProducer.kt b/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceProducer.kt index aae567c354..7fea7e7014 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceProducer.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceProducer.kt @@ -1,10 +1,9 @@ package com.tangem.domain.staking.single import com.tangem.domain.core.flow.FlowProducer -import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.models.network.Network -import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.staking.model.StakingID +import com.tangem.domain.staking.model.stakekit.YieldBalance /** * Producer of yield balance for selected wallet [UserWalletId] @@ -15,16 +14,14 @@ interface SingleYieldBalanceProducer : FlowProducer { data class Params( val userWalletId: UserWalletId, - val currencyId: CryptoCurrency.ID, - val network: Network, + val stakingId: StakingID, ) { override fun toString(): String { return """ SingleYieldBalanceProducer.Params( userWalletId = $userWalletId, - currencyId = $currencyId, - network = $network + stakingId = $stakingId, ) """.trimIndent() } diff --git a/domain/tokens/build.gradle.kts b/domain/tokens/build.gradle.kts index 2bcbd75a88..a1d0b5b08f 100644 --- a/domain/tokens/build.gradle.kts +++ b/domain/tokens/build.gradle.kts @@ -54,6 +54,10 @@ dependencies { implementation(deps.jodatime) implementation(deps.reKotlin) + implementation(tangemDeps.blockchain) { + exclude(module = "joda-time") + } + /** Tests */ testImplementation(deps.test.coroutine) testImplementation(deps.test.junit5) @@ -61,7 +65,4 @@ dependencies { testImplementation(deps.test.mockk) testImplementation(deps.test.truth) testImplementation(projects.common.test) - testImplementation(tangemDeps.blockchain) { - exclude(module = "joda-time") - } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt index 351c2e0f89..d3c6c507a6 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt @@ -5,6 +5,7 @@ import arrow.core.raise.Raise import arrow.core.raise.catch import arrow.core.raise.either import arrow.core.raise.recover +import com.tangem.blockchainsdk.utils.toBlockchain import com.tangem.domain.core.utils.EitherFlow import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network @@ -18,6 +19,9 @@ import com.tangem.domain.networks.single.SingleNetworkStatusSupplier import com.tangem.domain.quotes.QuotesRepository import com.tangem.domain.quotes.single.SingleQuoteStatusProducer import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier +import com.tangem.domain.staking.StakingIdFactory +import com.tangem.domain.staking.model.StakingID +import com.tangem.domain.staking.model.isStakingSupported import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.staking.repositories.StakingRepository import com.tangem.domain.staking.single.SingleYieldBalanceProducer @@ -49,6 +53,7 @@ abstract class BaseCurrencyStatusOperations( private val singleQuoteStatusSupplier: SingleQuoteStatusSupplier, private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier, private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, + private val stakingIdFactory: StakingIdFactory, private val tokensFeatureToggles: TokensFeatureToggles, ) { @@ -82,7 +87,7 @@ abstract class BaseCurrencyStatusOperations( return getCurrencyStatusFlow(userWalletId = userWalletId, currency = currency) } - fun getCurrencyStatusFlow( + suspend fun getCurrencyStatusFlow( userWalletId: UserWalletId, currency: CryptoCurrency, includeQuotes: Boolean = true, @@ -105,9 +110,24 @@ abstract class BaseCurrencyStatusOperations( val statusFlow = getNetworkStatus(userWalletId = userWalletId, network = currency.network) - val yieldBalanceFlow = getYieldBalance(userWalletId = userWalletId, cryptoCurrency = currency) + val isStakingSupported = currency.network.toBlockchain().isStakingSupported - return if (subscribeOnYieldBalance) { + val yieldBalanceFlow = if (isStakingSupported) { + val stakingId = stakingIdFactory.create( + userWalletId = userWalletId, + currencyId = currency.id, + network = currency.network, + ) + .getOrNull() + + stakingId?.let { + getYieldBalance(userWalletId = userWalletId, stakingId = it) + } + } else { + null + } + + return if (subscribeOnYieldBalance && yieldBalanceFlow != null) { combine(quoteFlow, statusFlow, yieldBalanceFlow) { maybeQuote, maybeNetworkStatus, maybeYieldBalance -> currencyStatusProxyCreator.createCurrencyStatus( currency = currency, @@ -334,15 +354,11 @@ abstract class BaseCurrencyStatusOperations( .bind() } - private fun getYieldBalance( - userWalletId: UserWalletId, - cryptoCurrency: CryptoCurrency, - ): EitherFlow { + private fun getYieldBalance(userWalletId: UserWalletId, stakingId: StakingID): EitherFlow { return singleYieldBalanceSupplier( params = SingleYieldBalanceProducer.Params( userWalletId = userWalletId, - currencyId = cryptoCurrency.id, - network = cryptoCurrency.network, + stakingId = stakingId, ), ) .map> { it.right() } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt index 6bcb07b46b..5c5c5d91b2 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt @@ -24,6 +24,7 @@ import com.tangem.domain.quotes.QuotesRepository import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher import com.tangem.domain.quotes.single.SingleQuoteStatusProducer import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier +import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher @@ -56,6 +57,7 @@ class CachedCurrenciesStatusesOperations( private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier, private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher, multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, + private val stakingIdFactory: StakingIdFactory, private val tokensFeatureToggles: TokensFeatureToggles, ) : BaseCurrenciesStatusesOperations, BaseCurrencyStatusOperations( @@ -67,6 +69,7 @@ class CachedCurrenciesStatusesOperations( singleQuoteStatusSupplier = singleQuoteStatusSupplier, singleYieldBalanceSupplier = singleYieldBalanceSupplier, multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, + stakingIdFactory = stakingIdFactory, tokensFeatureToggles = tokensFeatureToggles, ) { @@ -371,20 +374,19 @@ class CachedCurrenciesStatusesOperations( return channelFlow { val state = MutableStateFlow(emptyList()) - cryptoCurrencies.onEach { + val stakingIds = cryptoCurrencies.mapNotNullTo(hashSetOf()) { + stakingIdFactory.create(userWalletId = userWalletId, currencyId = it.id, network = it.network) + .getOrNull() + } + + stakingIds.onEach { launch { singleYieldBalanceSupplier( - params = SingleYieldBalanceProducer.Params( - userWalletId = userWalletId, - currencyId = it.id, - network = it.network, - ), + params = SingleYieldBalanceProducer.Params(userWalletId = userWalletId, stakingId = it), ) .onEach { balance -> state.update { loadedBalances -> - loadedBalances.addOrReplace(balance) { - it.integrationId == balance.integrationId && it.address == balance.address - } + loadedBalances.addOrReplace(balance) { balance.getStakingId() == it } } } .launchIn(scope = this) From 3f60d158200590e0dbbec5c78f4117cf02bc53d8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 25 Jul 2025 18:58:54 +0400 Subject: [PATCH 06/53] Updated on 2026-08-14 --- .../tap/di/domain/ManageTokensDomainModule.kt | 3 + .../tap/di/domain/MarketsDomainModule.kt | 3 + .../tap/di/domain/StakingDomainModule.kt | 4 +- .../tap/di/domain/TokensDomainModule.kt | 10 + .../domain/token/MockCryptoCurrencyFactory.kt | 1 + .../com/tangem/common/test/utils/TruthExt.kt | 19 +- .../multi/DefaultMultiYieldBalanceFetcher.kt | 39 +--- .../DefaultSingleYieldBalanceFetcher.kt | 4 +- .../DefaultMultiYieldBalanceFetcherTest.kt | 192 ++++-------------- .../DefaultSingleYieldBalanceFetcherTest.kt | 24 +-- .../managetokens/SaveManagedTokensUseCase.kt | 11 +- .../domain/markets/SaveMarketTokensUseCase.kt | 11 +- .../FetchStakingYieldBalanceUseCase.kt | 45 ++-- .../staking/multi/MultiYieldBalanceFetcher.kt | 15 +- .../single/SingleYieldBalanceFetcher.kt | 9 +- .../tokens/AddCryptoCurrenciesUseCase.kt | 32 ++- .../tokens/FetchCardTokenListUseCase.kt | 13 +- .../tokens/FetchCurrencyStatusUseCase.kt | 30 ++- .../domain/tokens/FetchTokenListUseCase.kt | 13 +- .../CachedCurrenciesStatusesOperations.kt | 6 +- .../tokens/wallet/WalletBalanceFetcher.kt | 35 +++- .../tokens/wallet/WalletBalanceFetcherTest.kt | 174 +++++++++++++++- .../intents/WalletWarningsClickIntents.kt | 15 +- 23 files changed, 405 insertions(+), 303 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt index 55d82f0af5..0db474821f 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt @@ -6,6 +6,7 @@ import com.tangem.domain.managetokens.repository.CustomTokensRepository import com.tangem.domain.managetokens.repository.ManageTokensRepository import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher +import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.walletmanager.WalletManagersFacade @@ -72,6 +73,7 @@ internal object ManageTokensDomainModule { multiNetworkStatusFetcher: MultiNetworkStatusFetcher, multiQuoteStatusFetcher: MultiQuoteStatusFetcher, multiYieldBalanceFetcher: MultiYieldBalanceFetcher, + stakingIdFactory: StakingIdFactory, ): SaveManagedTokensUseCase { return SaveManagedTokensUseCase( customTokensRepository = customTokensRepository, @@ -81,6 +83,7 @@ internal object ManageTokensDomainModule { multiNetworkStatusFetcher = multiNetworkStatusFetcher, multiQuoteStatusFetcher = multiQuoteStatusFetcher, multiYieldBalanceFetcher = multiYieldBalanceFetcher, + stakingIdFactory = stakingIdFactory, ) } diff --git a/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt index 1dab6fc6f5..71f6985831 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt @@ -9,6 +9,7 @@ import com.tangem.domain.promo.PromoRepository import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier import com.tangem.domain.settings.repositories.SettingsRepository +import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.wallets.legacy.UserWalletsListManager @@ -63,6 +64,7 @@ object MarketsDomainModule { multiNetworkStatusFetcher: MultiNetworkStatusFetcher, multiQuoteStatusFetcher: MultiQuoteStatusFetcher, multiYieldBalanceFetcher: MultiYieldBalanceFetcher, + stakingIdFactory: StakingIdFactory, ): SaveMarketTokensUseCase { return SaveMarketTokensUseCase( derivationsRepository = derivationsRepository, @@ -71,6 +73,7 @@ object MarketsDomainModule { multiNetworkStatusFetcher = multiNetworkStatusFetcher, multiQuoteStatusFetcher = multiQuoteStatusFetcher, multiYieldBalanceFetcher = multiYieldBalanceFetcher, + stakingIdFactory = stakingIdFactory, ) } diff --git a/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt index 7f6857ddf0..f98c7d2bd2 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt @@ -94,12 +94,12 @@ internal object StakingDomainModule { @Provides @Singleton fun provideFetchStakingYieldBalanceUseCase( - stakingErrorResolver: StakingErrorResolver, singleYieldBalanceFetcher: SingleYieldBalanceFetcher, + stakingIdFactory: StakingIdFactory, ): FetchStakingYieldBalanceUseCase { return FetchStakingYieldBalanceUseCase( - stakingErrorResolver = stakingErrorResolver, singleYieldBalanceFetcher = singleYieldBalanceFetcher, + stakingIdFactory = stakingIdFactory, ) } diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index b1d8469ec4..70d26f85a2 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -47,6 +47,7 @@ internal object TokensDomainModule { singleYieldBalanceFetcher: SingleYieldBalanceFetcher, multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, tokensFeatureToggles: TokensFeatureToggles, + stakingIdFactory: StakingIdFactory, ): AddCryptoCurrenciesUseCase { return AddCryptoCurrenciesUseCase( currenciesRepository = currenciesRepository, @@ -55,6 +56,7 @@ internal object TokensDomainModule { singleYieldBalanceFetcher = singleYieldBalanceFetcher, multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, tokensFeatureToggles = tokensFeatureToggles, + stakingIdFactory = stakingIdFactory, ) } @@ -65,12 +67,14 @@ internal object TokensDomainModule { multiNetworkStatusFetcher: MultiNetworkStatusFetcher, multiQuoteStatusFetcher: MultiQuoteStatusFetcher, multiYieldBalanceFetcher: MultiYieldBalanceFetcher, + stakingIdFactory: StakingIdFactory, ): FetchTokenListUseCase { return FetchTokenListUseCase( currenciesRepository = currenciesRepository, multiNetworkStatusFetcher = multiNetworkStatusFetcher, multiQuoteStatusFetcher = multiQuoteStatusFetcher, multiYieldBalanceFetcher = multiYieldBalanceFetcher, + stakingIdFactory = stakingIdFactory, ) } @@ -173,6 +177,7 @@ internal object TokensDomainModule { singleYieldBalanceFetcher: SingleYieldBalanceFetcher, multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, tokensFeatureToggles: TokensFeatureToggles, + stakingIdFactory: StakingIdFactory, ): FetchCurrencyStatusUseCase { return FetchCurrencyStatusUseCase( currenciesRepository = currenciesRepository, @@ -181,6 +186,7 @@ internal object TokensDomainModule { singleYieldBalanceFetcher = singleYieldBalanceFetcher, multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, tokensFeatureToggles = tokensFeatureToggles, + stakingIdFactory = stakingIdFactory, ) } @@ -191,12 +197,14 @@ internal object TokensDomainModule { multiNetworkStatusFetcher: MultiNetworkStatusFetcher, multiQuoteStatusFetcher: MultiQuoteStatusFetcher, multiYieldBalanceFetcher: MultiYieldBalanceFetcher, + stakingIdFactory: StakingIdFactory, ): FetchCardTokenListUseCase { return FetchCardTokenListUseCase( currenciesRepository = currenciesRepository, multiNetworkStatusFetcher = multiNetworkStatusFetcher, multiQuoteStatusFetcher = multiQuoteStatusFetcher, multiYieldBalanceFetcher = multiYieldBalanceFetcher, + stakingIdFactory = stakingIdFactory, ) } @@ -477,6 +485,7 @@ internal object TokensDomainModule { multiNetworkStatusFetcher: MultiNetworkStatusFetcher, multiQuoteStatusFetcher: MultiQuoteStatusFetcher, multiYieldBalanceFetcher: MultiYieldBalanceFetcher, + stakingIdFactory: StakingIdFactory, dispatchers: CoroutineDispatcherProvider, ): WalletBalanceFetcher { return WalletBalanceFetcher( @@ -486,6 +495,7 @@ internal object TokensDomainModule { multiNetworkStatusFetcher = multiNetworkStatusFetcher, multiQuoteStatusFetcher = multiQuoteStatusFetcher, multiYieldBalanceFetcher = multiYieldBalanceFetcher, + stakingIdFactory = stakingIdFactory, dispatchers = dispatchers, ) } diff --git a/common/test/src/main/java/com/tangem/common/test/domain/token/MockCryptoCurrencyFactory.kt b/common/test/src/main/java/com/tangem/common/test/domain/token/MockCryptoCurrencyFactory.kt index 6592071c92..efba92dc00 100644 --- a/common/test/src/main/java/com/tangem/common/test/domain/token/MockCryptoCurrencyFactory.kt +++ b/common/test/src/main/java/com/tangem/common/test/domain/token/MockCryptoCurrencyFactory.kt @@ -25,6 +25,7 @@ class MockCryptoCurrencyFactory(private val userWallet: UserWallet.Cold = defaul val cardano by lazy { createCoin(blockchain = Blockchain.Cardano) } val chia by lazy { createCoin(Blockchain.Chia) } val ethereum by lazy { createCoin(Blockchain.Ethereum) } + val stellar by lazy { createCoin(Blockchain.Stellar) } val chiaAndEthereum by lazy { listOf( diff --git a/common/test/src/main/java/com/tangem/common/test/utils/TruthExt.kt b/common/test/src/main/java/com/tangem/common/test/utils/TruthExt.kt index c8a88ac0ff..67cd37711b 100644 --- a/common/test/src/main/java/com/tangem/common/test/utils/TruthExt.kt +++ b/common/test/src/main/java/com/tangem/common/test/utils/TruthExt.kt @@ -9,7 +9,24 @@ fun assertEither(actual: Either, expected: Either) { + actual + .onRight { Truth.assertThat(actual).isEqualTo(Either.Right(Unit)) } + .onLeft { + error("Actual is Either.Left: $it") + } +} + +fun assertEitherLeft(actual: Either, expected: Throwable) { + actual + .onRight { error("Actual is Either.Right: $it") } + .onLeft { + Truth.assertThat(it::class.java).isEqualTo(expected::class.java) + Truth.assertThat(it).hasMessageThat().isEqualTo(expected.message) + } } \ No newline at end of file diff --git a/data/staking/src/main/java/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcher.kt b/data/staking/src/main/java/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcher.kt index abf8a0a3ef..2e21a957da 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcher.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcher.kt @@ -1,15 +1,11 @@ package com.tangem.data.staking.multi import arrow.core.Either -import arrow.core.getOrElse import arrow.core.left -import arrow.core.raise.catch -import arrow.core.raise.either -import arrow.core.raise.ensure +import arrow.core.right import arrow.core.toOption import com.tangem.data.common.api.safeApiCall import com.tangem.data.staking.store.YieldsBalancesStore -import com.tangem.data.staking.utils.StakingIdFactory import com.tangem.data.staking.utils.YieldBalanceRequestBodyFactory import com.tangem.datasource.api.stakekit.StakeKitApi import com.tangem.datasource.api.stakekit.models.request.YieldBalanceRequestBody @@ -36,7 +32,6 @@ import javax.inject.Inject * @property userWalletsStore user wallets store * @property stakingYieldsStore staking yields store * @property yieldsBalancesStore yields balances store - * @property stakingIdFactory factory for creating StakingID * @property stakeKitApi stake kit API * @property dispatchers dispatchers * @@ -46,7 +41,6 @@ internal class DefaultMultiYieldBalanceFetcher @Inject constructor( private val userWalletsStore: UserWalletsStore, private val stakingYieldsStore: StakingYieldsStore, private val yieldsBalancesStore: YieldsBalancesStore, - private val stakingIdFactory: StakingIdFactory, private val stakeKitApi: StakeKitApi, private val dispatchers: CoroutineDispatcherProvider, ) : MultiYieldBalanceFetcher { @@ -54,11 +48,12 @@ internal class DefaultMultiYieldBalanceFetcher @Inject constructor( override suspend fun invoke(params: MultiYieldBalanceFetcher.Params): Either { Timber.i("Start fetching yield balances for params:\n$params") - checkIsSupportedByWalletOrElse(userWalletId = params.userWalletId) { - return it.left() + val stakingIds = params.stakingIds.ifEmpty { + Timber.i("Nothing to fetch, empty stakingIds for ${params.userWalletId}") + return Unit.right() } - val stakingIds = getStakingIds(params).getOrElse { + checkIsSupportedByWalletOrElse(userWalletId = params.userWalletId) { return it.left() } @@ -94,30 +89,6 @@ internal class DefaultMultiYieldBalanceFetcher @Inject constructor( } } - private suspend fun getStakingIds(params: MultiYieldBalanceFetcher.Params) = either { - val stakingIds = catch( - block = { - params.currencyIdWithNetworkMap.mapNotNullTo(hashSetOf()) { (currencyId, network) -> - stakingIdFactory.create( - userWalletId = params.userWalletId, - currencyId = currencyId, - network = network, - ) - } - }, - catch = ::raise, - ) - - ensure(stakingIds.isNotEmpty()) { - val exception = IllegalStateException("Unable to create staking ids for $params: list is empty") - Timber.e(exception) - - raise(exception) - } - - stakingIds - } - private suspend fun getAvailableStakingIds(userWalletId: UserWalletId, stakingIds: Set): Set { val yieldIds = getYieldsIds(userWalletId = userWalletId) diff --git a/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcher.kt b/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcher.kt index 4f507e4739..1666b20e35 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcher.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcher.kt @@ -20,9 +20,7 @@ internal class DefaultSingleYieldBalanceFetcher @Inject constructor( return multiYieldBalanceFetcher( params = MultiYieldBalanceFetcher.Params( userWalletId = params.userWalletId, - currencyIdWithNetworkMap = mapOf( - params.currencyId to params.network, - ), + stakingIds = setOf(params.stakingId), ), ) } diff --git a/data/staking/src/test/kotlin/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcherTest.kt b/data/staking/src/test/kotlin/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcherTest.kt index d83b1481ee..0f02079987 100644 --- a/data/staking/src/test/kotlin/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcherTest.kt +++ b/data/staking/src/test/kotlin/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcherTest.kt @@ -1,14 +1,12 @@ package com.tangem.data.staking.multi import arrow.core.toOption -import com.google.common.truth.Truth -import com.tangem.blockchain.common.Blockchain import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory import com.tangem.common.test.data.staking.MockYieldDTOFactory -import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory import com.tangem.common.test.domain.wallet.MockUserWalletFactory +import com.tangem.common.test.utils.assertEitherLeft +import com.tangem.common.test.utils.assertEitherRight import com.tangem.data.staking.store.YieldsBalancesStore -import com.tangem.data.staking.utils.StakingIdFactory import com.tangem.data.staking.utils.YieldBalanceRequestBodyFactory import com.tangem.datasource.api.common.response.ApiResponse import com.tangem.datasource.api.common.response.ApiResponseError @@ -34,55 +32,46 @@ internal class DefaultMultiYieldBalanceFetcherTest { private val userWalletsStore: UserWalletsStore = mockk() private val stakingYieldsStore: StakingYieldsStore = mockk() - private val yieldsBalancesStore: YieldsBalancesStore = mockk() - private val stakingIdFactory: StakingIdFactory = mockk() + private val yieldsBalancesStore: YieldsBalancesStore = mockk(relaxUnitFun = true) private val stakeKitApi: StakeKitApi = mockk() private val fetcher = DefaultMultiYieldBalanceFetcher( userWalletsStore = userWalletsStore, stakingYieldsStore = stakingYieldsStore, yieldsBalancesStore = yieldsBalancesStore, - stakingIdFactory = stakingIdFactory, stakeKitApi = stakeKitApi, dispatchers = TestingCoroutineDispatcherProvider(), ) @BeforeEach fun resetMocks() { - clearMocks(userWalletsStore, stakingYieldsStore, yieldsBalancesStore, stakingIdFactory, stakeKitApi) + clearMocks(userWalletsStore, stakingYieldsStore, yieldsBalancesStore, stakeKitApi) } @Test fun `fetch yields balances successfully`() = runTest { // Arrange - val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network) - - val params = MultiYieldBalanceFetcher.Params(userWalletId, currencyIdWithNetworkMap) + val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds) coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet - coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns tonId - coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns solanaId - coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs val yields = listOf(MockYieldDTOFactory.create(tonId), MockYieldDTOFactory.create(solanaId)) coEvery { stakingYieldsStore.getSyncWithTimeout() } returns yields - val requests = tonAndSolanaIds.map(YieldBalanceRequestBodyFactory::create).sortedBy { it.integrationId } + val requests = tonAndSolanaIds.map(YieldBalanceRequestBodyFactory::create) val result = setOf( MockYieldBalanceWrapperDTOFactory.createWithBalance(solanaId), MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId), ) + coEvery { stakeKitApi.getMultipleYieldBalances(requests) } returns ApiResponse.Success(result) - coEvery { yieldsBalancesStore.storeActual(userWalletId = userWalletId, values = result) } just Runs // Actual val actual = fetcher.invoke(params) // Assert - coVerify { + coVerifyOrder { userWalletsStore.getSyncOrNull(params.userWalletId) - stakingIdFactory.create(params.userWalletId, ton.id, ton.network) - stakingIdFactory.create(params.userWalletId, solana.id, solana.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds) stakingYieldsStore.getSyncWithTimeout() stakeKitApi.getMultipleYieldBalances(requests) @@ -91,40 +80,30 @@ internal class DefaultMultiYieldBalanceFetcherTest { coVerify(inverse = true) { yieldsBalancesStore.storeError(any(), any()) } - Truth.assertThat(actual.isRight()).isTrue() + assertEitherRight(actual) } @Test fun `fetch yields balances successfully if one of stakingIds is unavailable`() = runTest { // Arrange - val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network) - - val params = MultiYieldBalanceFetcher.Params(userWalletId, currencyIdWithNetworkMap) + val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds) coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet - coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns tonId - coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns solanaId - coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs val yields = listOf(MockYieldDTOFactory.create(tonId)) coEvery { stakingYieldsStore.getSyncWithTimeout() } returns yields - coEvery { yieldsBalancesStore.storeError(userWalletId = userWalletId, stakingIds = setOf(solanaId)) } just Runs - val requests = listOf(YieldBalanceRequestBodyFactory.create(tonId)) val result = setOf(MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId)) coEvery { stakeKitApi.getMultipleYieldBalances(requests) } returns ApiResponse.Success(result) - coEvery { yieldsBalancesStore.storeActual(userWalletId = userWalletId, values = result) } just Runs // Actual val actual = fetcher.invoke(params) // Assert - coVerify { + coVerifyOrder { userWalletsStore.getSyncOrNull(params.userWalletId) - stakingIdFactory.create(params.userWalletId, ton.id, ton.network) - stakingIdFactory.create(params.userWalletId, solana.id, solana.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds) stakingYieldsStore.getSyncWithTimeout() yieldsBalancesStore.storeError(userWalletId = userWalletId, stakingIds = setOf(solanaId)) @@ -132,15 +111,13 @@ internal class DefaultMultiYieldBalanceFetcherTest { yieldsBalancesStore.storeActual(userWalletId = userWalletId, values = result) } - Truth.assertThat(actual.isRight()).isTrue() + assertEitherRight(actual) } @Test fun `fetch yields balances failure if user wallet is not supported`() = runTest { // Arrange - val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network) - - val params = MultiYieldBalanceFetcher.Params(userWalletId, currencyIdWithNetworkMap) + val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds) val userWallet = MockUserWalletFactory.create().copy(isMultiCurrency = false) coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet @@ -149,10 +126,9 @@ internal class DefaultMultiYieldBalanceFetcherTest { val actual = fetcher.invoke(params) // Assert - coVerify { userWalletsStore.getSyncOrNull(params.userWalletId) } + coVerifyOrder { userWalletsStore.getSyncOrNull(params.userWalletId) } coVerify(inverse = true) { - stakingIdFactory.create(params.userWalletId, ton.id, ton.network) yieldsBalancesStore.refresh(userWalletId = any(), stakingIds = any()) stakingYieldsStore.getSyncWithTimeout() stakeKitApi.getSingleYieldBalance(integrationId = any(), body = any()) @@ -162,17 +138,13 @@ internal class DefaultMultiYieldBalanceFetcherTest { val expected = IllegalStateException("Wallet ${params.userWalletId} is not supported: ${userWallet.toOption()}") - Truth.assertThat(actual.isLeft()).isTrue() - Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java) - Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message) + assertEitherLeft(actual, expected) } @Test fun `fetch yields balances failure if userWalletsStore returns null`() = runTest { // Arrange - val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network) - - val params = MultiYieldBalanceFetcher.Params(userWalletId, currencyIdWithNetworkMap) + val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds) coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns null @@ -180,10 +152,9 @@ internal class DefaultMultiYieldBalanceFetcherTest { val actual = fetcher.invoke(params) // Assert - coVerify { userWalletsStore.getSyncOrNull(params.userWalletId) } + coVerifyOrder { userWalletsStore.getSyncOrNull(params.userWalletId) } coVerify(inverse = true) { - stakingIdFactory.create(params.userWalletId, ton.id, ton.network) yieldsBalancesStore.refresh(userWalletId = any(), stakingIds = any()) stakingYieldsStore.getSyncWithTimeout() stakeKitApi.getSingleYieldBalance(integrationId = any(), body = any()) @@ -193,69 +164,23 @@ internal class DefaultMultiYieldBalanceFetcherTest { val expected = IllegalStateException("Wallet ${params.userWalletId} is not supported: ${null.toOption()}") - Truth.assertThat(actual.isLeft()).isTrue() - Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java) - Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message) - } - - @Test - fun `fetch yields balances failure if stakingIdFactory returns null`() = runTest { - // Arrange - val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network) - - val params = MultiYieldBalanceFetcher.Params(userWalletId, currencyIdWithNetworkMap) - - coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet - coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns null - coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns null - - // Actual - val actual = fetcher.invoke(params) - - // Assert - coVerify { - userWalletsStore.getSyncOrNull(params.userWalletId) - stakingIdFactory.create(params.userWalletId, ton.id, ton.network) - stakingIdFactory.create(params.userWalletId, solana.id, solana.network) - } - - coVerify(inverse = true) { - yieldsBalancesStore.refresh(any(), any>()) - stakingYieldsStore.getSyncWithTimeout() - stakeKitApi.getMultipleYieldBalances(any()) - yieldsBalancesStore.storeActual(any(), any()) - yieldsBalancesStore.storeError(any(), any()) - } - - val expected = IllegalStateException("Unable to create staking ids for $params: list is empty") - - Truth.assertThat(actual.isLeft()).isTrue() - Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java) - Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message) + assertEitherLeft(actual, expected) } @Test fun `fetch yields balances failure if stakingYieldsStore getSyncWithTimeout returns null`() = runTest { // Arrange - val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network) - - val params = MultiYieldBalanceFetcher.Params(userWalletId, currencyIdWithNetworkMap) + val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds) coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet - coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns tonId - coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns solanaId - coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs coEvery { stakingYieldsStore.getSyncWithTimeout() } returns null - coEvery { yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds) } just Runs // Actual val actual = fetcher.invoke(params) // Assert - coVerify { + coVerifyOrder { userWalletsStore.getSyncOrNull(params.userWalletId) - stakingIdFactory.create(params.userWalletId, ton.id, ton.network) - stakingIdFactory.create(params.userWalletId, solana.id, solana.network) yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) stakingYieldsStore.getSyncWithTimeout() yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds) @@ -268,33 +193,23 @@ internal class DefaultMultiYieldBalanceFetcherTest { val expected = IllegalStateException("No enabled yields for ${params.userWalletId}") - Truth.assertThat(actual.isLeft()).isTrue() - Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java) - Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message) + assertEitherLeft(actual, expected) } @Test fun `fetch yields balances failure if stakingYieldsStore getSyncWithTimeout returns empty list`() = runTest { // Arrange - val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network) - - val params = MultiYieldBalanceFetcher.Params(userWalletId, currencyIdWithNetworkMap) + val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds) coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet - coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns tonId - coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns solanaId - coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs coEvery { stakingYieldsStore.getSyncWithTimeout() } returns emptyList() - coEvery { yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds) } just Runs // Actual val actual = fetcher.invoke(params) // Assert - coVerify { + coVerifyOrder { userWalletsStore.getSyncOrNull(params.userWalletId) - stakingIdFactory.create(params.userWalletId, ton.id, ton.network) - stakingIdFactory.create(params.userWalletId, solana.id, solana.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds) stakingYieldsStore.getSyncWithTimeout() yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds) @@ -307,38 +222,28 @@ internal class DefaultMultiYieldBalanceFetcherTest { val expected = IllegalStateException("No enabled yields for ${params.userWalletId}") - Truth.assertThat(actual.isLeft()).isTrue() - Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java) - Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message) + assertEitherLeft(actual, expected) } @Test fun `fetch yields balances failure if yields converting is failed`() = runTest { // Arrange - val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network) - - val params = MultiYieldBalanceFetcher.Params(userWalletId, currencyIdWithNetworkMap) + val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds) coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet - coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns tonId - coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns solanaId - coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs val yields = listOf( MockYieldDTOFactory.create(tonId).copy(id = null), MockYieldDTOFactory.create(solanaId).copy(id = null), ) coEvery { stakingYieldsStore.getSyncWithTimeout() } returns yields - coEvery { yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds) } just Runs // Actual val actual = fetcher.invoke(params) // Assert - coVerify { + coVerifyOrder { userWalletsStore.getSyncOrNull(params.userWalletId) - stakingIdFactory.create(params.userWalletId, ton.id, ton.network) - stakingIdFactory.create(params.userWalletId, solana.id, solana.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds) stakingYieldsStore.getSyncWithTimeout() yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds) @@ -351,35 +256,25 @@ internal class DefaultMultiYieldBalanceFetcherTest { val expected = IllegalStateException("No enabled yields for ${params.userWalletId}") - Truth.assertThat(actual.isLeft()).isTrue() - Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java) - Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message) + assertEitherLeft(actual, expected) } @Test fun `fetch yields balances failure if available yields does not contain ids from params`() = runTest { // Arrange - val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network) - - val params = MultiYieldBalanceFetcher.Params(userWalletId, currencyIdWithNetworkMap) + val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds) coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet - coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns tonId - coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns solanaId - coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs val yields = listOf(MockYieldDTOFactory.create(StakingID(integrationId = "polygon", address = "0x1"))) coEvery { stakingYieldsStore.getSyncWithTimeout() } returns yields - coEvery { yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds) } just Runs // Actual val actual = fetcher.invoke(params) // Assert - coVerify { + coVerifyOrder { userWalletsStore.getSyncOrNull(params.userWalletId) - stakingIdFactory.create(params.userWalletId, ton.id, ton.network) - stakingIdFactory.create(params.userWalletId, solana.id, solana.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds) stakingYieldsStore.getSyncWithTimeout() yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds) @@ -394,47 +289,37 @@ internal class DefaultMultiYieldBalanceFetcherTest { """ No available yields to fetch yield balances: – userWalletId: $userWalletId - – stakingIds: ${setOf(solanaId, tonId).joinToString()} + – stakingIds: ${tonAndSolanaIds.joinToString()} """.trimIndent(), ) - Truth.assertThat(actual.isLeft()).isTrue() - Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java) - Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message) + assertEitherLeft(actual, expected) } @Test fun `fetch yields balances failure if stakeKitApi getMultipleYieldBalances is failed`() = runTest { // Arrange - val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network) - - val params = MultiYieldBalanceFetcher.Params(userWalletId, currencyIdWithNetworkMap) + val params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = tonAndSolanaIds) coEvery { userWalletsStore.getSyncOrNull(params.userWalletId) } returns userWallet - coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns tonId - coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns solanaId - coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs val yields = listOf(MockYieldDTOFactory.create(tonId), MockYieldDTOFactory.create(solanaId)) coEvery { stakingYieldsStore.getSyncWithTimeout() } returns yields - val requests = setOf(solanaId, tonId).map(YieldBalanceRequestBodyFactory::create) + val requests = setOf(tonId, solanaId).map(YieldBalanceRequestBodyFactory::create) @Suppress("UNCHECKED_CAST") val errorResponse = ApiResponse.Error(ApiResponseError.NetworkException) as ApiResponse> coEvery { stakeKitApi.getMultipleYieldBalances(requests) } returns errorResponse - coEvery { yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds) } just Runs // Actual val actual = fetcher.invoke(params) // Assert - coVerify { + coVerifyOrder { userWalletsStore.getSyncOrNull(params.userWalletId) - stakingIdFactory.create(params.userWalletId, ton.id, ton.network) - stakingIdFactory.create(params.userWalletId, solana.id, solana.network) yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds) stakingYieldsStore.getSyncWithTimeout() stakeKitApi.getMultipleYieldBalances(requests) @@ -445,20 +330,13 @@ internal class DefaultMultiYieldBalanceFetcherTest { val expected = ApiResponseError.NetworkException - Truth.assertThat(actual.isLeft()).isTrue() - Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java) - Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message) + assertEitherLeft(actual, expected) } private companion object { val userWalletId = UserWalletId("011") val userWallet = MockUserWalletFactory.create() - val mocks = MockCryptoCurrencyFactory() - - val ton = mocks.createCoin(Blockchain.TON) - val solana = mocks.createCoin(Blockchain.Solana) - val tonId = MockYieldBalanceWrapperDTOFactory.defaultStakingId val solanaId = StakingID( integrationId = "solana-sol-native-multivalidator-staking", diff --git a/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcherTest.kt b/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcherTest.kt index 7bf536b73d..6722fc8d5f 100644 --- a/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcherTest.kt +++ b/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcherTest.kt @@ -3,8 +3,7 @@ package com.tangem.data.staking.single import arrow.core.left import arrow.core.right import com.google.common.truth.Truth -import com.tangem.blockchain.common.Blockchain -import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory +import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.staking.single.SingleYieldBalanceFetcher @@ -37,15 +36,11 @@ internal class DefaultSingleYieldBalanceFetcherTest { @Test fun `fetch yield balance successfully`() = runTest { // Arrange - val params = SingleYieldBalanceFetcher.Params( - userWalletId = userWalletId, - currencyId = ton.id, - network = ton.network, - ) + val params = SingleYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingId = tonId) val multiParams = MultiYieldBalanceFetcher.Params( userWalletId = userWalletId, - currencyIdWithNetworkMap = mapOf(ton.id to ton.network), + stakingIds = setOf(tonId), ) val multiResult = Unit.right() @@ -64,16 +59,9 @@ internal class DefaultSingleYieldBalanceFetcherTest { @Test fun `fetch yield balance failure`() = runTest { // Arrange - val params = SingleYieldBalanceFetcher.Params( - userWalletId = userWalletId, - currencyId = ton.id, - network = ton.network, - ) + val params = SingleYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingId = tonId) - val multiParams = MultiYieldBalanceFetcher.Params( - userWalletId = userWalletId, - currencyIdWithNetworkMap = mapOf(ton.id to ton.network), - ) + val multiParams = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = setOf(tonId)) val multiResult = IllegalStateException().left() @@ -89,6 +77,6 @@ internal class DefaultSingleYieldBalanceFetcherTest { private companion object { val userWalletId = UserWalletId("011") - val ton = MockCryptoCurrencyFactory().createCoin(Blockchain.TON) + val tonId = MockYieldBalanceWrapperDTOFactory.defaultStakingId } } \ No newline at end of file diff --git a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt index 1c78e3949d..065f27f412 100644 --- a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt +++ b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt @@ -10,6 +10,7 @@ import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher +import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.walletmanager.WalletManagersFacade @@ -23,6 +24,7 @@ class SaveManagedTokensUseCase( private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher, private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher, private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher, + private val stakingIdFactory: StakingIdFactory, ) { suspend operator fun invoke( @@ -91,11 +93,12 @@ class SaveManagedTokensUseCase( userWalletId: UserWalletId, addedCurrencies: List, ) { + val stakingIds = addedCurrencies.mapNotNullTo(hashSetOf()) { + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it).getOrNull() + } + multiYieldBalanceFetcher( - params = MultiYieldBalanceFetcher.Params( - userWalletId = userWalletId, - currencyIdWithNetworkMap = addedCurrencies.associateTo(hashMapOf()) { it.id to it.network }, - ), + params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds), ) } diff --git a/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt b/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt index d5bbb3f689..47e769d11f 100644 --- a/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt +++ b/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt @@ -8,6 +8,7 @@ import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher +import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.tokens.repository.CurrenciesRepository @@ -28,6 +29,7 @@ class SaveMarketTokensUseCase( private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher, private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher, private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher, + private val stakingIdFactory: StakingIdFactory, ) { suspend operator fun invoke( @@ -89,11 +91,12 @@ class SaveMarketTokensUseCase( userWalletId: UserWalletId, existingCurrencies: List, ) { + val stakingIds = existingCurrencies.mapNotNullTo(hashSetOf()) { + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it).getOrNull() + } + multiYieldBalanceFetcher( - params = MultiYieldBalanceFetcher.Params( - userWalletId = userWalletId, - currencyIdWithNetworkMap = existingCurrencies.associateTo(hashMapOf()) { it.id to it.network }, - ), + params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds), ) } diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/FetchStakingYieldBalanceUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/FetchStakingYieldBalanceUseCase.kt index 24b94b0ff2..0308e969e5 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/FetchStakingYieldBalanceUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/FetchStakingYieldBalanceUseCase.kt @@ -1,36 +1,41 @@ package com.tangem.domain.staking import arrow.core.Either -import arrow.core.raise.catch +import arrow.core.getOrElse import arrow.core.raise.either +import arrow.core.right import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.staking.model.stakekit.StakingError -import com.tangem.domain.staking.repositories.StakingErrorResolver -import com.tangem.domain.staking.single.SingleYieldBalanceFetcher import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.staking.model.stakekit.StakingError +import com.tangem.domain.staking.single.SingleYieldBalanceFetcher class FetchStakingYieldBalanceUseCase( - private val stakingErrorResolver: StakingErrorResolver, private val singleYieldBalanceFetcher: SingleYieldBalanceFetcher, + private val stakingIdFactory: StakingIdFactory, ) { suspend operator fun invoke( userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, - ): Either { - return either { - catch( - block = { - singleYieldBalanceFetcher( - params = SingleYieldBalanceFetcher.Params( - userWalletId = userWalletId, - currencyId = cryptoCurrency.id, - network = cryptoCurrency.network, - ), - ) - }, - catch = { stakingErrorResolver.resolve(it) }, - ) - } + ): Either = either { + val stakingId = stakingIdFactory.create( + userWalletId = userWalletId, + currencyId = cryptoCurrency.id, + network = cryptoCurrency.network, + ) + .getOrElse { + when (it) { + is StakingIdFactory.Error.UnableToGetAddress -> raise(StakingError.DomainError("$it")) + StakingIdFactory.Error.UnsupportedCurrency -> Unit.right() + } + + return@either + } + + singleYieldBalanceFetcher( + params = SingleYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingId = stakingId), + ) + .mapLeft { StakingError.DomainError("$it") } + .bind() } } \ No newline at end of file diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/multi/MultiYieldBalanceFetcher.kt b/domain/staking/src/main/java/com/tangem/domain/staking/multi/MultiYieldBalanceFetcher.kt index 397dd9f7de..17330db5b0 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/multi/MultiYieldBalanceFetcher.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/multi/MultiYieldBalanceFetcher.kt @@ -1,9 +1,8 @@ package com.tangem.domain.staking.multi import com.tangem.domain.core.flow.FlowFetcher -import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.staking.model.StakingID /** * Fetcher of yields balances @@ -15,23 +14,19 @@ interface MultiYieldBalanceFetcher : FlowFetcher, + val stakingIds: Set, ) { override fun toString(): String { - val currencyIdWithNetworkMap = currencyIdWithNetworkMap.entries.joinToString { - "${it.key.value} - ${it.value}" - } - return """ MultiYieldBalanceFetcher.Params( userWalletId = $userWalletId, - currencyIdWithNetworkMap: $currencyIdWithNetworkMap + stakingIds: ${stakingIds.joinToString()} ) """.trimIndent() } diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceFetcher.kt b/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceFetcher.kt index ffff57fcff..789c93584a 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceFetcher.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceFetcher.kt @@ -1,9 +1,8 @@ package com.tangem.domain.staking.single import com.tangem.domain.core.flow.FlowFetcher -import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.staking.model.StakingID /** * Fetcher of yield balance @@ -16,12 +15,10 @@ interface SingleYieldBalanceFetcher : FlowFetcher = either { + val stakingId = stakingIdFactory.create( + userWalletId = userWalletId, + currencyId = addedCurrency.id, + network = addedCurrency.network, ) + .getOrElse { + when (it) { + is StakingIdFactory.Error.UnableToGetAddress -> raise(IllegalStateException("$it")) + StakingIdFactory.Error.UnsupportedCurrency -> Unit.right() + } + + return@either + } + + singleYieldBalanceFetcher( + params = SingleYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingId = stakingId), + ) + .bind() } private suspend fun refreshUpdatedQuotes(currencyToAdd: CryptoCurrency) { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchCardTokenListUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchCardTokenListUseCase.kt index 82b0862695..2c491ffdee 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchCardTokenListUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchCardTokenListUseCase.kt @@ -6,12 +6,13 @@ import arrow.core.raise.catch import arrow.core.raise.either import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher +import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.repository.CurrenciesRepository -import com.tangem.domain.models.wallet.UserWalletId import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope @@ -21,6 +22,7 @@ class FetchCardTokenListUseCase( private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher, private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher, private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher, + private val stakingIdFactory: StakingIdFactory, ) { suspend operator fun invoke(userWalletId: UserWalletId, refresh: Boolean = false): Either { @@ -83,11 +85,12 @@ class FetchCardTokenListUseCase( } private suspend fun fetchYieldBalances(userWalletId: UserWalletId, currencies: List) { + val stakingIds = currencies.mapNotNullTo(hashSetOf()) { + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it).getOrNull() + } + multiYieldBalanceFetcher( - params = MultiYieldBalanceFetcher.Params( - userWalletId = userWalletId, - currencyIdWithNetworkMap = currencies.associateTo(hashMapOf()) { it.id to it.network }, - ), + params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds), ) } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchCurrencyStatusUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchCurrencyStatusUseCase.kt index 6cb6d54c1d..e500c445fb 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchCurrencyStatusUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchCurrencyStatusUseCase.kt @@ -1,18 +1,20 @@ package com.tangem.domain.tokens import arrow.core.Either +import arrow.core.getOrElse import arrow.core.raise.Raise import arrow.core.raise.catch import arrow.core.raise.either import arrow.core.right import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.networks.single.SingleNetworkStatusFetcher import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher +import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.single.SingleYieldBalanceFetcher import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.tokens.repository.CurrenciesRepository -import com.tangem.domain.models.wallet.UserWalletId import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope @@ -32,6 +34,7 @@ class FetchCurrencyStatusUseCase( private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher, private val singleYieldBalanceFetcher: SingleYieldBalanceFetcher, private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, + private val stakingIdFactory: StakingIdFactory, private val tokensFeatureToggles: TokensFeatureToggles, ) { @@ -136,14 +139,25 @@ class FetchCurrencyStatusUseCase( private suspend fun fetchStakingBalance( userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, - ): Either { - return singleYieldBalanceFetcher( - params = SingleYieldBalanceFetcher.Params( - userWalletId = userWalletId, - currencyId = cryptoCurrency.id, - network = cryptoCurrency.network, - ), + ): Either = either { + val stakingId = stakingIdFactory.create( + userWalletId = userWalletId, + currencyId = cryptoCurrency.id, + network = cryptoCurrency.network, ) + .getOrElse { + when (it) { + is StakingIdFactory.Error.UnableToGetAddress -> raise(IllegalStateException("$it")) + StakingIdFactory.Error.UnsupportedCurrency -> Unit.right() + } + + return@either + } + + singleYieldBalanceFetcher( + params = SingleYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingId = stakingId), + ) + .bind() } private fun List>.summarizeResult(): Either { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchTokenListUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchTokenListUseCase.kt index 6206a7203b..d4df6acfcd 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchTokenListUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchTokenListUseCase.kt @@ -8,12 +8,13 @@ import arrow.core.raise.ensureNotNull import arrow.core.toNonEmptyListOrNull import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher +import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.repository.CurrenciesRepository -import com.tangem.domain.models.wallet.UserWalletId import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope @@ -29,6 +30,7 @@ class FetchTokenListUseCase( private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher, private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher, private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher, + private val stakingIdFactory: StakingIdFactory, ) { /** @@ -104,11 +106,12 @@ class FetchTokenListUseCase( } private suspend fun fetchYieldBalances(userWalletId: UserWalletId, currencies: List) { + val stakingIds = currencies.mapNotNullTo(hashSetOf()) { + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it).getOrNull() + } + multiYieldBalanceFetcher( - params = MultiYieldBalanceFetcher.Params( - userWalletId = userWalletId, - currencyIdWithNetworkMap = currencies.associateTo(hashMapOf()) { it.id to it.network }, - ), + params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds), ) } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt index 5c5c5d91b2..6302bef5ef 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt @@ -216,10 +216,14 @@ class CachedCurrenciesStatusesOperations( ) }, async { + val stakingIds = currencies.mapNotNullTo(hashSetOf()) { + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it).getOrNull() + } + multiYieldBalanceFetcher( params = MultiYieldBalanceFetcher.Params( userWalletId = userWalletId, - currencyIdWithNetworkMap = currencies.associateTo(hashMapOf()) { it.id to it.network }, + stakingIds = stakingIds, ), ) }, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/wallet/WalletBalanceFetcher.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/wallet/WalletBalanceFetcher.kt index 4e6677d2c0..bceb16c296 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/wallet/WalletBalanceFetcher.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/wallet/WalletBalanceFetcher.kt @@ -1,11 +1,14 @@ package com.tangem.domain.tokens.wallet import arrow.core.Either +import arrow.core.raise.either import com.tangem.domain.core.flow.FlowFetcher import com.tangem.domain.core.utils.catchOn import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher +import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier @@ -13,7 +16,6 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.wallet.implementor.MultiWalletBalanceFetcher import com.tangem.domain.tokens.wallet.implementor.SingleWalletBalanceFetcher import com.tangem.domain.tokens.wallet.implementor.SingleWalletWithTokenBalanceFetcher -import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -43,6 +45,7 @@ class WalletBalanceFetcher internal constructor( private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher, private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher, private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher, + private val stakingIdFactory: StakingIdFactory, private val dispatchers: CoroutineDispatcherProvider, ) : FlowFetcher { @@ -54,6 +57,7 @@ class WalletBalanceFetcher internal constructor( multiNetworkStatusFetcher: MultiNetworkStatusFetcher, multiQuoteStatusFetcher: MultiQuoteStatusFetcher, multiYieldBalanceFetcher: MultiYieldBalanceFetcher, + stakingIdFactory: StakingIdFactory, dispatchers: CoroutineDispatcherProvider, ) : this( currenciesRepository = currenciesRepository, @@ -68,6 +72,7 @@ class WalletBalanceFetcher internal constructor( multiNetworkStatusFetcher = multiNetworkStatusFetcher, multiQuoteStatusFetcher = multiQuoteStatusFetcher, multiYieldBalanceFetcher = multiYieldBalanceFetcher, + stakingIdFactory = stakingIdFactory, dispatchers = dispatchers, ) @@ -145,13 +150,27 @@ class WalletBalanceFetcher internal constructor( private suspend fun fetchStaking( userWalletId: UserWalletId, currencies: Set, - ): Either { - return multiYieldBalanceFetcher( - params = MultiYieldBalanceFetcher.Params( - userWalletId = userWalletId, - currencyIdWithNetworkMap = currencies.associateTo(hashMapOf()) { it.id to it.network }, - ), - ) + ): Either = either { + val maybeStakingIds = currencies.map { + val stakingId = stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it) + + if (stakingId.isLeft { it is StakingIdFactory.Error.UnableToGetAddress }) { + Timber.e("Unable to get staking ID for user wallet $userWalletId and currency ${it.id}") + } + + stakingId + } + + val stakingIds = maybeStakingIds.mapNotNullTo(hashSetOf()) { it.getOrNull() } + + if (stakingIds.isNotEmpty()) { + multiYieldBalanceFetcher( + params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds), + ) + .bind() + } else { + Timber.i("No staking IDs found for user wallet $userWalletId with currencies: $currencies") + } } /** diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/wallet/WalletBalanceFetcherTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/wallet/WalletBalanceFetcherTest.kt index d5d5f14816..f7cc8a5633 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/wallet/WalletBalanceFetcherTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/wallet/WalletBalanceFetcherTest.kt @@ -1,20 +1,25 @@ package com.tangem.domain.tokens.wallet +import arrow.core.Either import arrow.core.left import arrow.core.right import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory import com.tangem.common.test.utils.assertEither +import com.tangem.common.test.utils.assertEitherRight import com.tangem.domain.card.CardTypesResolver import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher +import com.tangem.domain.staking.StakingIdFactory +import com.tangem.domain.staking.model.StakingID +import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.wallet.FetchingSource.* import com.tangem.domain.tokens.wallet.implementor.MultiWalletBalanceFetcher import com.tangem.domain.tokens.wallet.implementor.SingleWalletBalanceFetcher import com.tangem.domain.tokens.wallet.implementor.SingleWalletWithTokenBalanceFetcher -import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import io.mockk.* import kotlinx.coroutines.test.runTest @@ -37,6 +42,7 @@ internal class WalletBalanceFetcherTest { private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher = mockk() private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher = mockk() private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher = mockk() + private val stakingIdFactory: StakingIdFactory = mockk() private val fetcher = WalletBalanceFetcher( currenciesRepository = currenciesRepository, @@ -46,6 +52,7 @@ internal class WalletBalanceFetcherTest { multiNetworkStatusFetcher = multiNetworkStatusFetcher, multiQuoteStatusFetcher = multiQuoteStatusFetcher, multiYieldBalanceFetcher = multiYieldBalanceFetcher, + stakingIdFactory = stakingIdFactory, dispatchers = TestingCoroutineDispatcherProvider(), ) @@ -83,6 +90,7 @@ internal class WalletBalanceFetcherTest { singleWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any()) multiNetworkStatusFetcher(params = any()) multiQuoteStatusFetcher(params = any()) + stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any()) multiYieldBalanceFetcher(params = any()) } } @@ -113,6 +121,7 @@ internal class WalletBalanceFetcherTest { singleWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any()) multiNetworkStatusFetcher(params = any()) multiQuoteStatusFetcher(params = any()) + stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any()) multiYieldBalanceFetcher(params = any()) } } @@ -146,6 +155,7 @@ internal class WalletBalanceFetcherTest { singleWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any()) multiNetworkStatusFetcher(params = any()) multiQuoteStatusFetcher(params = any()) + stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any()) multiYieldBalanceFetcher(params = any()) } } @@ -177,6 +187,7 @@ internal class WalletBalanceFetcherTest { singleWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any()) multiNetworkStatusFetcher(params = any()) multiQuoteStatusFetcher(params = any()) + stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any()) multiYieldBalanceFetcher(params = any()) } } @@ -222,6 +233,7 @@ internal class WalletBalanceFetcherTest { singleWalletWithTokenBalanceFetcher.getCryptoCurrencies(userWalletId = any()) singleWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any()) multiQuoteStatusFetcher(params = any()) + stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any()) multiYieldBalanceFetcher(params = any()) } } @@ -267,6 +279,7 @@ internal class WalletBalanceFetcherTest { singleWalletWithTokenBalanceFetcher.getCryptoCurrencies(userWalletId = any()) singleWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any()) multiNetworkStatusFetcher(params = any()) + stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any()) multiYieldBalanceFetcher(params = any()) } } @@ -281,7 +294,7 @@ internal class WalletBalanceFetcherTest { val currencies = cryptoCurrencyFactory.ethereumAndStellar.toSet() val yieldBalanceFetcherParams = MultiYieldBalanceFetcher.Params( userWalletId = userWalletId, - currencyIdWithNetworkMap = currencies.associateTo(hashMapOf()) { it.id to it.network }, + stakingIds = setOf(ethereumStakingId, stellarStakingId), ) val exception = IllegalStateException("Error") @@ -289,6 +302,12 @@ internal class WalletBalanceFetcherTest { every { currenciesRepository.getCardTypesResolver(userWalletId = userWalletId) } returns cardTypesResolver coEvery { multiWalletBalanceFetcher.getCryptoCurrencies(userWalletId = userWalletId) } returns currencies every { multiWalletBalanceFetcher.fetchingSources } returns setOf(STAKING) + coEvery { + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.ethereum) + } returns Either.Right(ethereumStakingId) + coEvery { + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.stellar) + } returns Either.Right(stellarStakingId) coEvery { multiYieldBalanceFetcher(params = yieldBalanceFetcherParams) } returns exception.left() // Act @@ -305,6 +324,8 @@ internal class WalletBalanceFetcherTest { currenciesRepository.getCardTypesResolver(userWalletId = userWalletId) multiWalletBalanceFetcher.getCryptoCurrencies(userWalletId = userWalletId) multiWalletBalanceFetcher.fetchingSources + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.ethereum) + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.stellar) multiYieldBalanceFetcher(params = yieldBalanceFetcherParams) } @@ -316,6 +337,131 @@ internal class WalletBalanceFetcherTest { } } + @Test + fun `fetch failure if stakingIdFactory RETURNS UnsupportedCurrency for all currencies`() = runTest { + // Arrange + val cardTypesResolver = mockk { + every { isMultiwalletAllowed() } returns true + } + + val currencies = cryptoCurrencyFactory.ethereumAndStellar.toSet() + + every { currenciesRepository.getCardTypesResolver(userWalletId = userWalletId) } returns cardTypesResolver + coEvery { multiWalletBalanceFetcher.getCryptoCurrencies(userWalletId = userWalletId) } returns currencies + every { multiWalletBalanceFetcher.fetchingSources } returns setOf(STAKING) + coEvery { + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = any()) + } returns Either.Left(StakingIdFactory.Error.UnsupportedCurrency) + + // Act + val actual = fetcher(params = WalletBalanceFetcher.Params(userWalletId = userWalletId)) + + // Assert + assertEitherRight(actual) + + coVerifyOrder { + currenciesRepository.getCardTypesResolver(userWalletId = userWalletId) + multiWalletBalanceFetcher.getCryptoCurrencies(userWalletId = userWalletId) + multiWalletBalanceFetcher.fetchingSources + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.ethereum) + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.stellar) + } + + coVerify(inverse = true) { + singleWalletWithTokenBalanceFetcher.getCryptoCurrencies(userWalletId = any()) + singleWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any()) + multiNetworkStatusFetcher(params = any()) + multiQuoteStatusFetcher(params = any()) + multiYieldBalanceFetcher(params = any()) + } + } + + @Test + fun `fetch failure if stakingIdFactory RETURNS UnableToGetAddress for all currencies`() = runTest { + // Arrange + val cardTypesResolver = mockk { + every { isMultiwalletAllowed() } returns true + } + + val currencies = cryptoCurrencyFactory.ethereumAndStellar.toSet() + val stakingId = Either.Left( + StakingIdFactory.Error.UnableToGetAddress(integrationId = StakingIntegrationID.EthereumToken.Polygon), + ) + + every { currenciesRepository.getCardTypesResolver(userWalletId = userWalletId) } returns cardTypesResolver + coEvery { multiWalletBalanceFetcher.getCryptoCurrencies(userWalletId = userWalletId) } returns currencies + every { multiWalletBalanceFetcher.fetchingSources } returns setOf(STAKING) + coEvery { stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = any()) } returns stakingId + + // Act + val actual = fetcher(params = WalletBalanceFetcher.Params(userWalletId = userWalletId)) + + // Assert + assertEitherRight(actual) + + coVerifyOrder { + currenciesRepository.getCardTypesResolver(userWalletId = userWalletId) + multiWalletBalanceFetcher.getCryptoCurrencies(userWalletId = userWalletId) + multiWalletBalanceFetcher.fetchingSources + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.ethereum) + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.stellar) + } + + coVerify(inverse = true) { + singleWalletWithTokenBalanceFetcher.getCryptoCurrencies(userWalletId = any()) + singleWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any()) + multiNetworkStatusFetcher(params = any()) + multiQuoteStatusFetcher(params = any()) + multiYieldBalanceFetcher(params = any()) + } + } + + @Test + fun `fetch failure if stakingIdFactory RETURNS UnableToGetAddress and UnsupportedCurrency`() = runTest { + // Arrange + val cardTypesResolver = mockk { + every { isMultiwalletAllowed() } returns true + } + + val currencies = cryptoCurrencyFactory.ethereumAndStellar.toSet() + val ethereumStakingId = Either.Left( + StakingIdFactory.Error.UnableToGetAddress(integrationId = StakingIntegrationID.EthereumToken.Polygon), + ) + val stellarStakingId = Either.Left(StakingIdFactory.Error.UnsupportedCurrency) + + every { currenciesRepository.getCardTypesResolver(userWalletId = userWalletId) } returns cardTypesResolver + coEvery { multiWalletBalanceFetcher.getCryptoCurrencies(userWalletId = userWalletId) } returns currencies + every { multiWalletBalanceFetcher.fetchingSources } returns setOf(STAKING) + coEvery { + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.ethereum) + } returns ethereumStakingId + coEvery { + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.stellar) + } returns stellarStakingId + + // Act + val actual = fetcher(params = WalletBalanceFetcher.Params(userWalletId = userWalletId)) + + // Assert + assertEitherRight(actual) + + coVerifyOrder { + currenciesRepository.getCardTypesResolver(userWalletId = userWalletId) + multiWalletBalanceFetcher.getCryptoCurrencies(userWalletId = userWalletId) + multiWalletBalanceFetcher.fetchingSources + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.ethereum) + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.stellar) + } + + coVerify(inverse = true) { + singleWalletWithTokenBalanceFetcher.getCryptoCurrencies(userWalletId = any()) + singleWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any()) + multiNetworkStatusFetcher(params = any()) + multiQuoteStatusFetcher(params = any()) + multiYieldBalanceFetcher(params = any()) + } + } + @Test fun `fetch failure if all fetching sources RETURNS LEFT`() = runTest { // Arrange @@ -337,7 +483,7 @@ internal class WalletBalanceFetcherTest { val yieldBalanceFetcherParams = MultiYieldBalanceFetcher.Params( userWalletId = userWalletId, - currencyIdWithNetworkMap = currencies.associateTo(hashMapOf()) { it.id to it.network }, + stakingIds = setOf(ethereumStakingId, stellarStakingId), ) val exception = IllegalStateException("Error") @@ -347,6 +493,12 @@ internal class WalletBalanceFetcherTest { every { multiWalletBalanceFetcher.fetchingSources } returns setOf(NETWORK, QUOTE, STAKING) coEvery { multiNetworkStatusFetcher(params = networkStatusFetcherParams) } returns exception.left() coEvery { multiQuoteStatusFetcher(params = quoteStatusFetcherParams) } returns exception.left() + coEvery { + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.ethereum) + } returns Either.Right(ethereumStakingId) + coEvery { + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.stellar) + } returns Either.Right(stellarStakingId) coEvery { multiYieldBalanceFetcher(params = yieldBalanceFetcherParams) } returns exception.left() // Act @@ -367,6 +519,8 @@ internal class WalletBalanceFetcherTest { multiWalletBalanceFetcher.fetchingSources multiNetworkStatusFetcher(params = networkStatusFetcherParams) multiQuoteStatusFetcher(params = quoteStatusFetcherParams) + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.ethereum) + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.stellar) multiYieldBalanceFetcher(params = yieldBalanceFetcherParams) } @@ -397,7 +551,7 @@ internal class WalletBalanceFetcherTest { val yieldBalanceFetcherParams = MultiYieldBalanceFetcher.Params( userWalletId = userWalletId, - currencyIdWithNetworkMap = currencies.associateTo(hashMapOf()) { it.id to it.network }, + stakingIds = setOf(ethereumStakingId, stellarStakingId), ) every { currenciesRepository.getCardTypesResolver(userWalletId = userWalletId) } returns cardTypesResolver @@ -405,6 +559,12 @@ internal class WalletBalanceFetcherTest { every { multiWalletBalanceFetcher.fetchingSources } returns setOf(NETWORK, QUOTE, STAKING) coEvery { multiNetworkStatusFetcher(params = networkStatusFetcherParams) } returns Unit.right() coEvery { multiQuoteStatusFetcher(params = quoteStatusFetcherParams) } returns Unit.right() + coEvery { + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.ethereum) + } returns Either.Right(ethereumStakingId) + coEvery { + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.stellar) + } returns Either.Right(stellarStakingId) coEvery { multiYieldBalanceFetcher(params = yieldBalanceFetcherParams) } returns Unit.right() // Act @@ -420,6 +580,8 @@ internal class WalletBalanceFetcherTest { multiWalletBalanceFetcher.fetchingSources multiNetworkStatusFetcher(params = networkStatusFetcherParams) multiQuoteStatusFetcher(params = quoteStatusFetcherParams) + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.ethereum) + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyFactory.stellar) multiYieldBalanceFetcher(params = yieldBalanceFetcherParams) } @@ -475,6 +637,7 @@ internal class WalletBalanceFetcherTest { coVerify(inverse = true) { multiWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any()) singleWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any()) + stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any()) multiYieldBalanceFetcher(params = any()) } } @@ -524,6 +687,7 @@ internal class WalletBalanceFetcherTest { coVerify(inverse = true) { multiWalletBalanceFetcher.getCryptoCurrencies(userWalletId = any()) singleWalletWithTokenBalanceFetcher.getCryptoCurrencies(userWalletId = any()) + stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any()) multiYieldBalanceFetcher(params = any()) } } @@ -531,5 +695,7 @@ internal class WalletBalanceFetcherTest { private companion object { val userWalletId = UserWalletId("011") + val ethereumStakingId = StakingID(integrationId = "ethereum", address = "0x1") + val stellarStakingId = StakingID(integrationId = "stellar", address = "0x1") } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt index e88a4566e0..ab80da33c7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt @@ -15,19 +15,20 @@ import com.tangem.domain.feedback.GetCardInfoUseCase import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.promo.ShouldShowPromoWalletUseCase import com.tangem.domain.promo.models.PromoId import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher import com.tangem.domain.settings.NeverToSuggestRateAppUseCase import com.tangem.domain.settings.RemindToRateAppLaterUseCase +import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType import com.tangem.domain.wallets.models.UnlockWalletsError -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase import com.tangem.domain.wallets.usecase.UnlockWalletsUseCase @@ -113,6 +114,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher, private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher, private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher, + private val stakingIdFactory: StakingIdFactory, private val appRouter: AppRouter, ) : BaseWalletClickIntents(), WalletWarningsClickIntents { @@ -399,11 +401,12 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( .onLeft { Timber.e("Unable to fetch quotes: $it") } }, async { + val stakingIds = currencies.mapNotNullTo(hashSetOf()) { + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it).getOrNull() + } + multiYieldBalanceFetcher( - params = MultiYieldBalanceFetcher.Params( - userWalletId = userWalletId, - currencyIdWithNetworkMap = currencies.associate { it.id to it.network }, - ), + params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds), ) .onLeft { Timber.e("Unable to fetch yield balances: $it") } }, From 03f4f51fc7e9fc1439c704af0c94fc2cce3e4843 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 28 Jul 2025 09:54:30 +0400 Subject: [PATCH 07/53] Updated on 2026-08-14 --- .../tap/di/domain/TokensDomainModule.kt | 9 +- .../data/staking/DefaultStakingRepository.kt | 37 ---- .../data/staking/di/StakingDataModule.kt | 3 - .../data/staking/utils/StakingIdFactory.kt | 77 ------- .../staking/utils/StakingIdFactoryTest.kt | 188 ------------------ .../staking/repositories/StakingRepository.kt | 15 -- .../BaseCurrencyStatusOperations.kt | 61 +++--- .../CachedCurrenciesStatusesOperations.kt | 6 +- 8 files changed, 42 insertions(+), 354 deletions(-) delete mode 100644 data/staking/src/main/java/com/tangem/data/staking/utils/StakingIdFactory.kt delete mode 100644 data/staking/src/test/kotlin/com/tangem/data/staking/utils/StakingIdFactoryTest.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index 70d26f85a2..5ee93bbafa 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -13,6 +13,7 @@ import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher +import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier import com.tangem.domain.staking.repositories.StakingRepository import com.tangem.domain.staking.single.SingleYieldBalanceFetcher import com.tangem.domain.staking.single.SingleYieldBalanceSupplier @@ -404,7 +405,6 @@ internal object TokensDomainModule { tokensFeatureToggles: TokensFeatureToggles, currenciesRepository: CurrenciesRepository, quotesRepository: QuotesRepository, - stakingRepository: StakingRepository, singleNetworkStatusSupplier: SingleNetworkStatusSupplier, multiNetworkStatusSupplier: MultiNetworkStatusSupplier, multiNetworkStatusFetcher: MultiNetworkStatusFetcher, @@ -412,6 +412,7 @@ internal object TokensDomainModule { multiQuoteStatusFetcher: MultiQuoteStatusFetcher, singleQuoteStatusSupplier: SingleQuoteStatusSupplier, singleYieldBalanceSupplier: SingleYieldBalanceSupplier, + multiYieldBalanceSupplier: MultiYieldBalanceSupplier, multiYieldBalanceFetcher: MultiYieldBalanceFetcher, multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, stakingIdFactory: StakingIdFactory, @@ -419,7 +420,6 @@ internal object TokensDomainModule { return CachedCurrenciesStatusesOperations( currenciesRepository = currenciesRepository, quotesRepository = quotesRepository, - stakingRepository = stakingRepository, singleNetworkStatusSupplier = singleNetworkStatusSupplier, multiNetworkStatusSupplier = multiNetworkStatusSupplier, multiNetworkStatusFetcher = multiNetworkStatusFetcher, @@ -427,6 +427,7 @@ internal object TokensDomainModule { multiQuoteStatusFetcher = multiQuoteStatusFetcher, singleQuoteStatusSupplier = singleQuoteStatusSupplier, singleYieldBalanceSupplier = singleYieldBalanceSupplier, + multiYieldBalanceSupplier = multiYieldBalanceSupplier, multiYieldBalanceFetcher = multiYieldBalanceFetcher, tokensFeatureToggles = tokensFeatureToggles, multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, @@ -440,7 +441,6 @@ internal object TokensDomainModule { tokensFeatureToggles: TokensFeatureToggles, currenciesRepository: CurrenciesRepository, quotesRepository: QuotesRepository, - stakingRepository: StakingRepository, singleNetworkStatusSupplier: SingleNetworkStatusSupplier, multiNetworkStatusSupplier: MultiNetworkStatusSupplier, multiNetworkStatusFetcher: MultiNetworkStatusFetcher, @@ -448,6 +448,7 @@ internal object TokensDomainModule { multiQuoteStatusFetcher: MultiQuoteStatusFetcher, singleQuoteStatusSupplier: SingleQuoteStatusSupplier, singleYieldBalanceSupplier: SingleYieldBalanceSupplier, + multiYieldBalanceSupplier: MultiYieldBalanceSupplier, multiYieldBalanceFetcher: MultiYieldBalanceFetcher, multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, stakingIdFactory: StakingIdFactory, @@ -455,7 +456,6 @@ internal object TokensDomainModule { return CachedCurrenciesStatusesOperations( currenciesRepository = currenciesRepository, quotesRepository = quotesRepository, - stakingRepository = stakingRepository, singleNetworkStatusSupplier = singleNetworkStatusSupplier, multiNetworkStatusSupplier = multiNetworkStatusSupplier, multiNetworkStatusFetcher = multiNetworkStatusFetcher, @@ -463,6 +463,7 @@ internal object TokensDomainModule { multiQuoteStatusFetcher = multiQuoteStatusFetcher, singleQuoteStatusSupplier = singleQuoteStatusSupplier, singleYieldBalanceSupplier = singleYieldBalanceSupplier, + multiYieldBalanceSupplier = multiYieldBalanceSupplier, multiYieldBalanceFetcher = multiYieldBalanceFetcher, multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, tokensFeatureToggles = tokensFeatureToggles, diff --git a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt index 15787789bf..dfb797e129 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt @@ -20,7 +20,6 @@ import com.tangem.data.staking.converters.transaction.StakingTransactionConverte import com.tangem.data.staking.converters.transaction.StakingTransactionStatusConverter import com.tangem.data.staking.converters.transaction.StakingTransactionTypeConverter import com.tangem.data.staking.store.YieldsBalancesStore -import com.tangem.data.staking.utils.StakingIdFactory import com.tangem.datasource.api.common.response.ApiResponse import com.tangem.datasource.api.common.response.getOrThrow import com.tangem.datasource.api.stakekit.StakeKitApi @@ -60,7 +59,6 @@ import com.tangem.utils.extensions.orZero import kotlinx.coroutines.flow.* import kotlinx.coroutines.withContext import timber.log.Timber -import java.math.BigDecimal @Suppress("LargeClass", "LongParameterList", "TooManyFunctions") internal class DefaultStakingRepository( @@ -71,7 +69,6 @@ internal class DefaultStakingRepository( private val walletManagersFacade: WalletManagersFacade, private val getUserWalletUseCase: GetUserWalletUseCase, private val stakingFeatureToggles: StakingFeatureToggles, - private val stakingIdFactory: StakingIdFactory, moshi: Moshi, ) : StakingRepository { @@ -375,32 +372,6 @@ internal class DefaultStakingRepository( } } - override suspend fun getSingleYieldBalanceSync( - userWalletId: UserWalletId, - cryptoCurrency: CryptoCurrency, - ): YieldBalance { - val stakingId = stakingIdFactory.create( - userWalletId = userWalletId, - currencyId = cryptoCurrency.id, - network = cryptoCurrency.network, - ) ?: error("Could not create stakingId") - - return stakingBalanceStoreV2.getSyncOrNull(userWalletId = userWalletId, stakingId = stakingId) - ?: YieldBalance.Error(integrationId = stakingId.integrationId, address = stakingId.address) - } - - override suspend fun getMultiYieldBalanceSync( - userWalletId: UserWalletId, - cryptoCurrencies: List, - ): List? { - val stakingIds = cryptoCurrencies.mapNotNull { - stakingIdFactory.create(userWalletId = userWalletId, currencyId = it.id, network = it.network) - } - - return stakingBalanceStoreV2.getAllSyncOrNull(userWalletId) - ?.filter { it.getStakingId() in stakingIds } - } - override suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean { return withContext(dispatchers.default) { val balances = stakingBalanceStoreV2.getAllSyncOrNull(userWalletId) ?: return@withContext false @@ -415,14 +386,6 @@ internal class DefaultStakingRepository( } } - override fun getActionRequirementAmount(integrationId: String, stakingActionType: StakingActionType): BigDecimal? { - return when { - stakingIdFactory.isPolygonIntegrationId(integrationId) && - stakingActionType == StakingActionType.CLAIM_REWARDS -> BigDecimal.ONE - else -> null - } - } - private suspend fun createActionRequestBody( userWalletId: UserWalletId, network: Network, diff --git a/data/staking/src/main/java/com/tangem/data/staking/di/StakingDataModule.kt b/data/staking/src/main/java/com/tangem/data/staking/di/StakingDataModule.kt index 45d3cac792..a5c5a41e2b 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/di/StakingDataModule.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/di/StakingDataModule.kt @@ -10,7 +10,6 @@ import com.tangem.data.staking.DefaultStakingTransactionHashRepository import com.tangem.data.staking.converters.error.StakeKitErrorConverter import com.tangem.data.staking.store.YieldsBalancesStore import com.tangem.data.staking.toggles.DefaultStakingFeatureToggles -import com.tangem.data.staking.utils.StakingIdFactory import com.tangem.datasource.api.stakekit.StakeKitApi import com.tangem.datasource.api.stakekit.models.response.model.error.StakeKitErrorResponse import com.tangem.datasource.di.NetworkMoshi @@ -45,7 +44,6 @@ internal object StakingDataModule { walletManagersFacade: WalletManagersFacade, getUserWalletUseCase: GetUserWalletUseCase, stakingFeatureToggles: StakingFeatureToggles, - stakingIdFactory: StakingIdFactory, @NetworkMoshi moshi: Moshi, ): StakingRepository { return DefaultStakingRepository( @@ -57,7 +55,6 @@ internal object StakingDataModule { getUserWalletUseCase = getUserWalletUseCase, stakingFeatureToggles = stakingFeatureToggles, moshi = moshi, - stakingIdFactory = stakingIdFactory, ) } diff --git a/data/staking/src/main/java/com/tangem/data/staking/utils/StakingIdFactory.kt b/data/staking/src/main/java/com/tangem/data/staking/utils/StakingIdFactory.kt deleted file mode 100644 index 02c5f0b9a2..0000000000 --- a/data/staking/src/main/java/com/tangem/data/staking/utils/StakingIdFactory.kt +++ /dev/null @@ -1,77 +0,0 @@ -package com.tangem.data.staking.utils - -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchainsdk.utils.toCoinId -import com.tangem.blockchainsdk.utils.toMigratedCoinId -import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.models.network.Network -import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.staking.model.StakingID -import com.tangem.domain.walletmanager.WalletManagersFacade -import javax.inject.Inject - -/** - * Factory of [StakingID] - * - * @property walletManagersFacade wallet manager facade - * -[REDACTED_AUTHOR] - */ -internal class StakingIdFactory @Inject constructor( - private val walletManagersFacade: WalletManagersFacade, -) { - - suspend fun create(userWalletId: UserWalletId, currencyId: CryptoCurrency.ID, network: Network): StakingID? { - val address = walletManagersFacade.getDefaultAddress(userWalletId = userWalletId, network = network) - val integrationId = createIntegrationId(currencyId) - - if (address == null || integrationId == null) return null - - return StakingID(integrationId = integrationId, address = address) - } - - fun createIntegrationId(currencyId: CryptoCurrency.ID): String? { - val integrationKey = with(currencyId) { rawNetworkId.plus(rawCurrencyId) } - return integrationIdMap[integrationKey] - } - - fun isPolygonIntegrationId(integrationId: String): Boolean = integrationId == ETHEREUM_POLYGON_INTEGRATION_ID - - @Suppress("UnusedPrivateMember", "unused") - companion object { - - private const val TON_INTEGRATION_ID = "ton-ton-chorus-one-pools-staking" - private const val SOLANA_INTEGRATION_ID = "solana-sol-native-multivalidator-staking" - private const val COSMOS_INTEGRATION_ID = "cosmos-atom-native-staking" - private const val ETHEREUM_POLYGON_INTEGRATION_ID = "ethereum-matic-native-staking" - private const val BINANCE_INTEGRATION_ID = "bsc-bnb-native-staking" - private const val POLKADOT_INTEGRATION_ID = "polkadot-dot-validator-staking" - private const val AVALANCHE_INTEGRATION_ID = "avalanche-avax-native-staking" - private const val TRON_INTEGRATION_ID = "tron-trx-native-staking" - private const val CRONOS_INTEGRATION_ID = "cronos-cro-native-staking" - private const val KAVA_INTEGRATION_ID = "kava-kava-native-staking" - private const val NEAR_INTEGRATION_ID = "near-near-native-staking" - private const val TEZOS_INTEGRATION_ID = "tezos-xtz-native-staking" - private const val CARDANO_INTEGRATION_ID = "cardano-ada-native-staking" - - // uncomment items as implementation is ready - private val integrationIdMap = mapOf( - Blockchain.TON.toDefaultKey() to TON_INTEGRATION_ID, - Blockchain.Solana.toDefaultKey() to SOLANA_INTEGRATION_ID, - Blockchain.Cosmos.toDefaultKey() to COSMOS_INTEGRATION_ID, - Blockchain.Tron.toDefaultKey() to TRON_INTEGRATION_ID, - Blockchain.Ethereum.id + Blockchain.Polygon.toMigratedCoinId() to ETHEREUM_POLYGON_INTEGRATION_ID, - // Blockchain.Ethereum.id + Blockchain.Polygon.toCoinId() to ETHEREUM_POLYGON_INTEGRATION_ID, - Blockchain.BSC.toDefaultKey() to BINANCE_INTEGRATION_ID, - // Blockchain.Polkadot.toDefaultKey() to POLKADOT_INTEGRATION_ID, - // Blockchain.Avalanche.toDefaultKey() to AVALANCHE_INTEGRATION_ID, - // Blockchain.Cronos.toDefaultKey() to CRONOS_INTEGRATION_ID, - // Blockchain.Kava.toDefaultKey() to KAVA_INTEGRATION_ID, - // Blockchain.Near.toDefaultKey() to NEAR_INTEGRATION_ID, - // Blockchain.Tezos.toDefaultKey() to TEZOS_INTEGRATION_ID, - Blockchain.Cardano.toDefaultKey() to CARDANO_INTEGRATION_ID, - ) - - private fun Blockchain.toDefaultKey(): String = id + toCoinId() - } -} \ No newline at end of file diff --git a/data/staking/src/test/kotlin/com/tangem/data/staking/utils/StakingIdFactoryTest.kt b/data/staking/src/test/kotlin/com/tangem/data/staking/utils/StakingIdFactoryTest.kt deleted file mode 100644 index 9244ac2684..0000000000 --- a/data/staking/src/test/kotlin/com/tangem/data/staking/utils/StakingIdFactoryTest.kt +++ /dev/null @@ -1,188 +0,0 @@ -package com.tangem.data.staking.utils - -import com.google.common.truth.Truth -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchainsdk.utils.toCoinId -import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory -import com.tangem.common.test.utils.ProvideTestModels -import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.staking.model.StakingID -import com.tangem.domain.walletmanager.WalletManagersFacade -import io.mockk.clearMocks -import io.mockk.coEvery -import io.mockk.coVerify -import io.mockk.mockk -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestInstance -import org.junit.jupiter.params.ParameterizedTest - -/** -[REDACTED_AUTHOR] - */ -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -internal class StakingIdFactoryTest { - - private val walletManagersFacade: WalletManagersFacade = mockk() - private val factory = StakingIdFactory(walletManagersFacade = walletManagersFacade) - - @BeforeEach - fun resetMocks() { - clearMocks(walletManagersFacade) - } - - @Nested - @TestInstance(TestInstance.Lifecycle.PER_CLASS) - inner class CreateIntegrationId { - - @ParameterizedTest - @ProvideTestModels - fun createIntegrationId(model: CreateIntegrationIdModel) { - // Act - val actual = factory.createIntegrationId(currencyId = model.currencyId) - - // Assert - Truth.assertThat(actual).isEqualTo(model.expected) - } - - private fun provideTestModels() = listOf( - CreateIntegrationIdModel( - currencyId = createCurrencyId(blockchain = Blockchain.TON), - expected = "ton-ton-chorus-one-pools-staking", - ), - CreateIntegrationIdModel( - currencyId = createCurrencyId(blockchain = Blockchain.Solana), - expected = "solana-sol-native-multivalidator-staking", - ), - CreateIntegrationIdModel( - currencyId = createCurrencyId(blockchain = Blockchain.Cosmos), - expected = "cosmos-atom-native-staking", - ), - CreateIntegrationIdModel( - currencyId = createCurrencyId(blockchain = Blockchain.Tron), - expected = "tron-trx-native-staking", - ), - CreateIntegrationIdModel( - currencyId = CryptoCurrency.ID.fromValue(value = "coin⟨ETH⟩polygon-ecosystem-token⚓"), - expected = "ethereum-matic-native-staking", - ), - CreateIntegrationIdModel( - currencyId = createCurrencyId(blockchain = Blockchain.BSC), - expected = "bsc-bnb-native-staking", - ), - CreateIntegrationIdModel( - currencyId = createCurrencyId(blockchain = Blockchain.Cardano), - expected = "cardano-ada-native-staking", - ), - CreateIntegrationIdModel( - currencyId = createCurrencyId(blockchain = Blockchain.Bitcoin), - expected = null, - ), - ) - } - - data class CreateIntegrationIdModel(val currencyId: CryptoCurrency.ID, val expected: String?) - - @Nested - @TestInstance(TestInstance.Lifecycle.PER_CLASS) - inner class Create { - - private val defaultAddress = "address" - - @Test - fun `create returns null if address is null`() = runTest { - // Arrange - val userWalletId = UserWalletId(stringValue = "011") - val currency = MockCryptoCurrencyFactory().createCoin(Blockchain.TON) - - coEvery { - walletManagersFacade.getDefaultAddress(userWalletId = userWalletId, network = currency.network) - } returns null - - // Act - val actual = factory.create( - userWalletId = userWalletId, - currencyId = currency.id, - network = currency.network, - ) - - // Assert - val expected = null - Truth.assertThat(actual).isEqualTo(expected) - - coVerify(exactly = 1) { - walletManagersFacade.getDefaultAddress(userWalletId = userWalletId, network = currency.network) - } - } - - @ParameterizedTest - @ProvideTestModels - fun create(model: CreateModel) = runTest { - // Arrange - val userWalletId = UserWalletId(stringValue = "011") - val network = MockCryptoCurrencyFactory().createCoin(Blockchain.TON).network - - coEvery { - walletManagersFacade.getDefaultAddress(userWalletId = userWalletId, network = network) - } returns defaultAddress - - // Act - val actual = factory.create(userWalletId = userWalletId, currencyId = model.currencyId, network = network) - - // Assert - Truth.assertThat(actual).isEqualTo(model.expected) - - coVerify(exactly = 1) { - walletManagersFacade.getDefaultAddress(userWalletId = userWalletId, network = network) - } - } - - private fun provideTestModels() = listOf( - CreateModel( - currencyId = createCurrencyId(blockchain = Blockchain.TON), - expected = createStakingId(integrationId = "ton-ton-chorus-one-pools-staking"), - ), - CreateModel( - currencyId = createCurrencyId(blockchain = Blockchain.Solana), - expected = createStakingId(integrationId = "solana-sol-native-multivalidator-staking"), - ), - CreateModel( - currencyId = createCurrencyId(blockchain = Blockchain.Cosmos), - expected = createStakingId(integrationId = "cosmos-atom-native-staking"), - ), - CreateModel( - currencyId = createCurrencyId(blockchain = Blockchain.Tron), - expected = createStakingId(integrationId = "tron-trx-native-staking"), - ), - CreateModel( - currencyId = CryptoCurrency.ID.fromValue(value = "coin⟨ETH⟩polygon-ecosystem-token⚓"), - expected = createStakingId(integrationId = "ethereum-matic-native-staking"), - ), - CreateModel( - currencyId = createCurrencyId(blockchain = Blockchain.BSC), - expected = createStakingId(integrationId = "bsc-bnb-native-staking"), - ), - CreateModel( - currencyId = createCurrencyId(blockchain = Blockchain.Cardano), - expected = createStakingId(integrationId = "cardano-ada-native-staking"), - ), - CreateModel( - currencyId = createCurrencyId(blockchain = Blockchain.Bitcoin), - expected = null, - ), - ) - - private fun createStakingId(integrationId: String): StakingID { - return StakingID(integrationId = integrationId, address = defaultAddress) - } - } - - data class CreateModel(val currencyId: CryptoCurrency.ID, val expected: StakingID?) - - private fun createCurrencyId(blockchain: Blockchain): CryptoCurrency.ID { - return CryptoCurrency.ID.fromValue(value = "coin⟨${blockchain.id}⟩${blockchain.toCoinId()}⚓") - } -} \ No newline at end of file diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt index dcb09a9010..3be0818547 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt @@ -10,15 +10,12 @@ import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.staking.model.StakingEntryInfo import com.tangem.domain.staking.model.stakekit.NetworkType import com.tangem.domain.staking.model.stakekit.Yield -import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.staking.model.stakekit.action.StakingAction import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus -import com.tangem.domain.staking.model.stakekit.action.StakingActionType import com.tangem.domain.staking.model.stakekit.transaction.ActionParams import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction import kotlinx.coroutines.flow.Flow -import java.math.BigDecimal @Suppress("TooManyFunctions") interface StakingRepository { @@ -45,13 +42,6 @@ interface StakingRepository { stakingActionStatus: StakingActionStatus, ): List - suspend fun getSingleYieldBalanceSync(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): YieldBalance - - suspend fun getMultiYieldBalanceSync( - userWalletId: UserWalletId, - cryptoCurrencies: List, - ): List? - suspend fun createAction(userWalletId: UserWalletId, network: Network, params: ActionParams): StakingAction suspend fun estimateGas(userWalletId: UserWalletId, network: Network, params: ActionParams): StakingGasEstimate @@ -64,9 +54,4 @@ interface StakingRepository { ): Pair suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean - - /** - * Return action requirement amount - */ - fun getActionRequirementAmount(integrationId: String, stakingActionType: StakingActionType): BigDecimal? } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt index d3c6c507a6..9aaf902fa7 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt @@ -1,10 +1,7 @@ package com.tangem.domain.tokens.operations import arrow.core.* -import arrow.core.raise.Raise -import arrow.core.raise.catch -import arrow.core.raise.either -import arrow.core.raise.recover +import arrow.core.raise.* import com.tangem.blockchainsdk.utils.toBlockchain import com.tangem.domain.core.utils.EitherFlow import com.tangem.domain.models.currency.CryptoCurrency @@ -23,7 +20,8 @@ import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.model.StakingID import com.tangem.domain.staking.model.isStakingSupported import com.tangem.domain.staking.model.stakekit.YieldBalance -import com.tangem.domain.staking.repositories.StakingRepository +import com.tangem.domain.staking.multi.MultiYieldBalanceProducer +import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier import com.tangem.domain.staking.single.SingleYieldBalanceProducer import com.tangem.domain.staking.single.SingleYieldBalanceSupplier import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesProducer @@ -39,7 +37,6 @@ import kotlinx.coroutines.flow.* * Base operations for working with currency status * * @property currenciesRepository repository for currencies - * @property stakingRepository repository for staking * [REDACTED_AUTHOR] */ @@ -47,11 +44,11 @@ import kotlinx.coroutines.flow.* abstract class BaseCurrencyStatusOperations( private val currenciesRepository: CurrenciesRepository, private val quotesRepository: QuotesRepository, - private val stakingRepository: StakingRepository, private val multiNetworkStatusSupplier: MultiNetworkStatusSupplier, private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier, private val singleQuoteStatusSupplier: SingleQuoteStatusSupplier, private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier, + private val multiYieldBalanceSupplier: MultiYieldBalanceSupplier, private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, private val stakingIdFactory: StakingIdFactory, private val tokensFeatureToggles: TokensFeatureToggles, @@ -411,34 +408,44 @@ abstract class BaseCurrencyStatusOperations( private suspend fun getYieldBalancesSync( userWalletId: UserWalletId, cryptoCurrencies: List, - ): Either> { - return catch( - block = { - val balances = stakingRepository.getMultiYieldBalanceSync( - userWalletId = userWalletId, - cryptoCurrencies = cryptoCurrencies, - ) + ): Either> = either { + val stakingIds = cryptoCurrencies.mapNotNull { cryptoCurrency -> + stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = cryptoCurrency) + .getOrNull() + } - if (balances.isNullOrEmpty()) { - Error.EmptyYieldBalances.left() - } else { - balances.right() - } - }, - catch = { Error.EmptyYieldBalances.left() }, + ensure(stakingIds.isNotEmpty()) { Error.EmptyYieldBalances } + + val balances = multiYieldBalanceSupplier.getSyncOrNull( + params = MultiYieldBalanceProducer.Params(userWalletId = userWalletId), ) + .orEmpty() + .filter { it.getStakingId() in stakingIds } + + ensure(balances.isNotEmpty()) { Error.EmptyYieldBalances } + + balances } private suspend fun getYieldBalanceSync( userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, - ): Either { - return catch( - block = { stakingRepository.getSingleYieldBalanceSync(userWalletId, cryptoCurrency).right() }, - catch = { - Error.EmptyYieldBalances.left() - }, + ): Either = either { + val stakingId = stakingIdFactory.create(userWalletId, cryptoCurrency) + .mapLeft { + val exception = IllegalStateException("$it") + Error.DataError(exception) + } + .bind() + + val yieldBalance = singleYieldBalanceSupplier.getSyncOrNull( + params = SingleYieldBalanceProducer.Params( + userWalletId = userWalletId, + stakingId = stakingId, + ), ) + + ensureNotNull(yieldBalance) { Error.EmptyYieldBalances } } private suspend fun Raise.getPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt index 6302bef5ef..0948480baa 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt @@ -28,7 +28,7 @@ import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher -import com.tangem.domain.staking.repositories.StakingRepository +import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier import com.tangem.domain.staking.single.SingleYieldBalanceProducer import com.tangem.domain.staking.single.SingleYieldBalanceSupplier import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier @@ -47,7 +47,6 @@ import kotlinx.coroutines.flow.* class CachedCurrenciesStatusesOperations( private val currenciesRepository: CurrenciesRepository, quotesRepository: QuotesRepository, - stakingRepository: StakingRepository, private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier, multiNetworkStatusSupplier: MultiNetworkStatusSupplier, private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher, @@ -55,6 +54,7 @@ class CachedCurrenciesStatusesOperations( private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher, private val singleQuoteStatusSupplier: SingleQuoteStatusSupplier, private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier, + multiYieldBalanceSupplier: MultiYieldBalanceSupplier, private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher, multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, private val stakingIdFactory: StakingIdFactory, @@ -63,11 +63,11 @@ class CachedCurrenciesStatusesOperations( BaseCurrencyStatusOperations( currenciesRepository = currenciesRepository, quotesRepository = quotesRepository, - stakingRepository = stakingRepository, multiNetworkStatusSupplier = multiNetworkStatusSupplier, singleNetworkStatusSupplier = singleNetworkStatusSupplier, singleQuoteStatusSupplier = singleQuoteStatusSupplier, singleYieldBalanceSupplier = singleYieldBalanceSupplier, + multiYieldBalanceSupplier = multiYieldBalanceSupplier, multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, stakingIdFactory = stakingIdFactory, tokensFeatureToggles = tokensFeatureToggles, From 2f91afa9223571dcc836f2547b6684ea14dc8785 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 30 Jul 2025 10:57:51 +0300 Subject: [PATCH 08/53] Updated on 2026-08-14 --- .../com/tangem/tap/di/data/CardDataModule.kt | 34 ----- .../tangem/tap/di/domain/CardDomainModule.kt | 5 +- .../tap/di/domain/ManageTokensDomainModule.kt | 2 +- .../tap/di/domain/MarketsDomainModule.kt | 2 +- .../tap/di/domain/TransactionDomainModule.kt | 2 +- .../tangem/tap/di/hot/TangemHotSdkModule.kt | 6 - .../domain/hot/HotWalletPasswordRequester.kt | 9 -- .../tangem/tap/domain/hot/TangemHotSigner.kt | 78 ---------- .../sdk/impl/DefaultTangemSdkManager.kt | 2 +- .../tasks/product/CreateProductWalletTask.kt | 2 +- .../domain/tasks/product/DerivationsFinder.kt | 2 +- .../domain/tasks/product/ScanProductTask.kt | 4 +- .../visa/VisaCustomerWalletApproveTask.kt | 2 +- .../hot/DefaultHotWalletPasswordRequester.kt | 13 -- .../domain/token/MockCryptoCurrencyFactory.kt | 4 +- .../data/common/network/NetworkFactory.kt | 4 +- .../data/common/network/NetworkFactoryTest.kt | 4 +- data/manage-tokens/build.gradle.kts | 1 + .../DefaultCustomTokensRepository.kt | 43 ++++-- .../utils/ManagedCryptoCurrencyFactory.kt | 4 +- data/visa/build.gradle.kts | 1 + .../data/visa/utils/VisaCurrencyFactory.kt | 5 +- .../walletmanager/WalletManagerFactory.kt | 4 +- data/wallets/build.gradle.kts | 12 +- .../DefaultColdMapDerivationsRepository.kt | 130 +++++++--------- .../cold/UserWalletIdPreflightReadFilter.kt | 25 ++++ .../DefaultDerivationsRepository.kt | 95 ++++++++++++ .../derivations}/MissedDerivationsFinder.kt | 58 +++++--- .../data/wallets/di/WalletsDataModule.kt | 24 +++ .../hot/DefaultHotMapDerivationsRepository.kt | 139 ++++++++++++++++++ .../data/wallets}/hot/HotWalletAccessor.kt | 24 +-- .../wallets}/hot/TangemHotWalletSigner.kt | 2 +- .../DefaultDerivationsRepositoryTest.kt | 47 +++--- .../wallets/derivations}/DerivedKeysMocks.kt | 2 +- .../MissedDerivationsFinderTest.kt | 19 +-- .../domain/card/TangemCardTypesResolver.kt | 3 +- .../card/common/util/ScanResponseExt.kt | 15 -- domain/manage-tokens/build.gradle.kts | 1 + .../managetokens/SaveManagedTokensUseCase.kt | 2 +- .../domain/markets/SaveMarketTokensUseCase.kt | 2 +- .../wallets/builder/HotUserWalletBuilder.kt | 2 +- .../ColdMapDerivationsRepository.kt | 35 +++++ .../derivations}/DerivationStyleProvider.kt | 3 +- .../derivations/DerivationStyleProviderExt.kt | 17 +++ .../derivations}/DerivationsRepository.kt | 4 +- .../HotMapDerivationsRepository.kt | 35 +++++ .../hot}/HotWalletPasswordRequester.kt | 2 +- .../repository/HotDerivationsRepository.kt | 8 - .../usecase}/DerivePublicKeysUseCase.kt | 7 +- .../GetExtendedPublicKeyForCurrencyUseCase.kt | 4 +- .../usecase}/HasMissedDerivationsUseCase.kt | 12 +- features/hot-wallet/api/build.gradle.kts | 1 + .../HotAccessCodeRequestComponent.kt | 1 + .../DefaultHotAccessCodeRequestComponent.kt | 3 +- .../HotAccessCodeRequestModel.kt | 2 +- .../di/ComponentModuleBinds.kt | 2 +- .../proxy/HotWalletPasswordRequesterProxy.kt | 2 +- features/manage-tokens/impl/build.gradle.kts | 1 + .../model/CustomTokenFormModel.kt | 4 +- .../managetokens/model/ManageTokensModel.kt | 2 +- .../model/OnboardingManageTokensModel.kt | 2 +- .../impl/model/MarketsPortfolioModel.kt | 2 +- .../model/OnrampAddToPortfolioModel.kt | 2 +- .../referral/domain/ReferralInteractorImpl.kt | 2 +- .../domain/di/ReferralDomainModule.kt | 2 +- .../tokendetails/model/TokenDetailsModel.kt | 2 +- .../intents/WalletWarningsClickIntents.kt | 2 +- 67 files changed, 623 insertions(+), 371 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/di/data/CardDataModule.kt delete mode 100644 app/src/main/java/com/tangem/tap/domain/hot/HotWalletPasswordRequester.kt delete mode 100644 app/src/main/java/com/tangem/tap/domain/hot/TangemHotSigner.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/hot/DefaultHotWalletPasswordRequester.kt rename app/src/main/java/com/tangem/tap/domain/card/DefaultDerivationsRepository.kt => data/wallets/src/main/java/com/tangem/data/wallets/cold/DefaultColdMapDerivationsRepository.kt (56%) create mode 100644 data/wallets/src/main/java/com/tangem/data/wallets/cold/UserWalletIdPreflightReadFilter.kt create mode 100644 data/wallets/src/main/java/com/tangem/data/wallets/derivations/DefaultDerivationsRepository.kt rename {app/src/main/java/com/tangem/tap/domain/card => data/wallets/src/main/java/com/tangem/data/wallets/derivations}/MissedDerivationsFinder.kt (64%) create mode 100644 data/wallets/src/main/java/com/tangem/data/wallets/hot/DefaultHotMapDerivationsRepository.kt rename {app/src/main/java/com/tangem/tap/domain => data/wallets/src/main/java/com/tangem/data/wallets}/hot/HotWalletAccessor.kt (84%) rename {app/src/main/java/com/tangem/tap/domain => data/wallets/src/main/java/com/tangem/data/wallets}/hot/TangemHotWalletSigner.kt (99%) rename {app/src/test/kotlin/com/tangem/tap/domain/card => data/wallets/src/test/java/com/tangem/data/wallets/derivations}/DefaultDerivationsRepositoryTest.kt (80%) rename {app/src/test/kotlin/com/tangem/tap/domain/card => data/wallets/src/test/java/com/tangem/data/wallets/derivations}/DerivedKeysMocks.kt (95%) rename {app/src/test/kotlin/com/tangem/tap/domain/card => data/wallets/src/test/java/com/tangem/data/wallets/derivations}/MissedDerivationsFinderTest.kt (90%) create mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/ColdMapDerivationsRepository.kt rename domain/{card/src/main/kotlin/com/tangem/domain/card => wallets/src/main/java/com/tangem/domain/wallets/derivations}/DerivationStyleProvider.kt (92%) create mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/DerivationStyleProviderExt.kt rename domain/{card/src/main/kotlin/com/tangem/domain/card/repository => wallets/src/main/java/com/tangem/domain/wallets/derivations}/DerivationsRepository.kt (92%) create mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/HotMapDerivationsRepository.kt rename {features/hot-wallet/api/src/main/kotlin/com/tangem/features/hotwallet => domain/wallets/src/main/java/com/tangem/domain/wallets/hot}/HotWalletPasswordRequester.kt (91%) delete mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/repository/HotDerivationsRepository.kt rename domain/{card/src/main/kotlin/com/tangem/domain/card => wallets/src/main/java/com/tangem/domain/wallets/usecase}/DerivePublicKeysUseCase.kt (74%) rename domain/{card/src/main/kotlin/com/tangem/domain/card => wallets/src/main/java/com/tangem/domain/wallets/usecase}/GetExtendedPublicKeyForCurrencyUseCase.kt (98%) rename domain/{card/src/main/kotlin/com/tangem/domain/card => wallets/src/main/java/com/tangem/domain/wallets/usecase}/HasMissedDerivationsUseCase.kt (75%) diff --git a/app/src/main/java/com/tangem/tap/di/data/CardDataModule.kt b/app/src/main/java/com/tangem/tap/di/data/CardDataModule.kt deleted file mode 100644 index 788367340b..0000000000 --- a/app/src/main/java/com/tangem/tap/di/data/CardDataModule.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.tangem.tap.di.data - -import com.tangem.data.common.network.NetworkFactory -import com.tangem.datasource.local.userwallet.UserWalletsStore -import com.tangem.domain.card.repository.DerivationsRepository -import com.tangem.sdk.api.TangemSdkManager -import com.tangem.tap.domain.card.DefaultDerivationsRepository -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -internal object CardDataModule { - - @Singleton - @Provides - fun providesDerivationsRepository( - tangemSdkManager: TangemSdkManager, - userWalletsStore: UserWalletsStore, - networkFactory: NetworkFactory, - dispatchers: CoroutineDispatcherProvider, - ): DerivationsRepository { - return DefaultDerivationsRepository( - tangemSdkManager = tangemSdkManager, - userWalletsStore = userWalletsStore, - networkFactory = networkFactory, - dispatchers = dispatchers, - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt index fc0942279a..0aa400353e 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt @@ -2,11 +2,14 @@ package com.tangem.tap.di.domain import com.tangem.domain.card.* import com.tangem.domain.card.repository.CardRepository -import com.tangem.domain.card.repository.DerivationsRepository +import com.tangem.domain.wallets.derivations.DerivationsRepository import com.tangem.domain.demo.models.DemoConfig import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.legacy.UserWalletsListManager +import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase +import com.tangem.domain.wallets.usecase.GetExtendedPublicKeyForCurrencyUseCase +import com.tangem.domain.wallets.usecase.HasMissedDerivationsUseCase import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase import com.tangem.sdk.api.TangemSdkManager diff --git a/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt index 0db474821f..89bd3b4ca9 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt @@ -1,6 +1,6 @@ package com.tangem.tap.di.domain -import com.tangem.domain.card.repository.DerivationsRepository +import com.tangem.domain.wallets.derivations.DerivationsRepository import com.tangem.domain.managetokens.* import com.tangem.domain.managetokens.repository.CustomTokensRepository import com.tangem.domain.managetokens.repository.ManageTokensRepository diff --git a/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt index 71f6985831..1bb8a1ec1c 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt @@ -1,7 +1,7 @@ package com.tangem.tap.di.domain import com.tangem.blockchainsdk.utils.ExcludedBlockchains -import com.tangem.domain.card.repository.DerivationsRepository +import com.tangem.domain.wallets.derivations.DerivationsRepository import com.tangem.domain.markets.* import com.tangem.domain.markets.repositories.MarketsTokenRepository import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher diff --git a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt index eb56a8d16c..3538d33584 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt @@ -1,5 +1,6 @@ package com.tangem.tap.di.domain +import com.tangem.data.wallets.hot.TangemHotWalletSigner import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.demo.models.DemoConfig import com.tangem.domain.networks.single.SingleNetworkStatusFetcher @@ -11,7 +12,6 @@ import com.tangem.domain.transaction.FeeRepository import com.tangem.domain.transaction.TransactionRepository import com.tangem.domain.transaction.usecase.* import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.tap.domain.hot.TangemHotWalletSigner import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/app/src/main/java/com/tangem/tap/di/hot/TangemHotSdkModule.kt b/app/src/main/java/com/tangem/tap/di/hot/TangemHotSdkModule.kt index f68e642f94..c21b55928b 100644 --- a/app/src/main/java/com/tangem/tap/di/hot/TangemHotSdkModule.kt +++ b/app/src/main/java/com/tangem/tap/di/hot/TangemHotSdkModule.kt @@ -1,8 +1,6 @@ package com.tangem.tap.di.hot import com.tangem.hot.sdk.TangemHotSdk -import com.tangem.tap.domain.hot.HotWalletPasswordRequester -import com.tangem.tap.features.hot.DefaultHotWalletPasswordRequester import com.tangem.tap.features.hot.TangemHotSDKProxy import dagger.Binds import dagger.Module @@ -17,8 +15,4 @@ internal interface TangemHotSdkModule { @Binds @Singleton fun bindTangemHotSdk(proxy: TangemHotSDKProxy): TangemHotSdk - - @Binds - @Singleton - fun bindHotWalletPasswordRequester(impl: DefaultHotWalletPasswordRequester): HotWalletPasswordRequester } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/hot/HotWalletPasswordRequester.kt b/app/src/main/java/com/tangem/tap/domain/hot/HotWalletPasswordRequester.kt deleted file mode 100644 index 1f80fd785f..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/hot/HotWalletPasswordRequester.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.tangem.tap.domain.hot - -import com.tangem.hot.sdk.model.HotAuth -import com.tangem.hot.sdk.model.HotWalletId - -interface HotWalletPasswordRequester { - - suspend fun requestPassword(hotWalletId: HotWalletId): HotAuth.Password -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/hot/TangemHotSigner.kt b/app/src/main/java/com/tangem/tap/domain/hot/TangemHotSigner.kt deleted file mode 100644 index 0b71ca974c..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/hot/TangemHotSigner.kt +++ /dev/null @@ -1,78 +0,0 @@ -package com.tangem.tap.domain.hot - -import com.tangem.blockchain.common.TransactionSigner -import com.tangem.blockchain.common.Wallet -import com.tangem.common.CompletionResult -import com.tangem.common.core.TangemSdkError -import com.tangem.common.map -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.hot.sdk.model.DataToSign -import com.tangem.operations.sign.SignData -import dagger.assisted.Assisted -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject - -class TangemHotSigner @AssistedInject constructor( - @Assisted private val userWallet: UserWallet.Hot, - private val hotWalletAccessor: HotWalletAccessor, -) : TransactionSigner { - - override suspend fun sign(hash: ByteArray, publicKey: Wallet.PublicKey): CompletionResult { - return sign(listOf(hash), publicKey).map { it.first() } - } - - override suspend fun sign( - hashes: List, - publicKey: Wallet.PublicKey, - ): CompletionResult> { - val wallet = userWallet.wallets.orEmpty().firstOrNull { it.publicKey == publicKey.seedKey } - ?: return CompletionResult.Failure( - TangemSdkError.ExceptionError(IllegalStateException("wallet is locked")), - ) - - val result = hotWalletAccessor.signHashes( - hotWalletId = userWallet.hotWalletId, - dataToSign = listOf( - DataToSign( - curve = wallet.curve, - hashes = hashes, - derivationPath = publicKey.derivationPath, - ), - ), - ) - - return CompletionResult.Success(result.map { it.signatures }.flatten()) - } - - override suspend fun multiSign( - dataToSign: List, - publicKey: Wallet.PublicKey, - ): CompletionResult> { - val result = hotWalletAccessor.signHashes( - hotWalletId = userWallet.hotWalletId, - dataToSign = dataToSign.map { signData -> - val wallet = userWallet.wallets.orEmpty().firstOrNull { it.publicKey == signData.publicKey } - ?: return CompletionResult.Failure( - TangemSdkError.ExceptionError(IllegalStateException("wallet is locked")), - ) - - DataToSign( - curve = wallet.curve, - hashes = listOf(signData.hash), - derivationPath = signData.derivationPath, - ) - }, - ) - - return CompletionResult.Success( - result.mapIndexed { index, data -> - dataToSign[index].publicKey to data.signatures.first() - }.toMap(), - ) - } - - @AssistedFactory - interface Factory { - fun create(@Assisted userWallet: UserWallet.Hot): TangemHotSigner - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt index 83c7a9c1fb..aaf4f87888 100644 --- a/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt @@ -20,7 +20,7 @@ import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.card.common.util.cardTypesResolver -import com.tangem.domain.card.common.util.derivationStyleProvider +import com.tangem.domain.wallets.derivations.derivationStyleProvider import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.wallet.UserWalletId diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt index acd5a2e14a..fba95394c0 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt @@ -14,7 +14,7 @@ import com.tangem.common.map import com.tangem.crypto.bip39.Mnemonic import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.card.CardTypesResolver -import com.tangem.domain.card.DerivationStyleProvider +import com.tangem.domain.wallets.derivations.DerivationStyleProvider import com.tangem.domain.card.common.TapWorkarounds.isTestCard import com.tangem.domain.card.configs.CardConfig import com.tangem.domain.models.scan.CardDTO diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/DerivationsFinder.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/DerivationsFinder.kt index 6e6a912b0d..fa255fdacb 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/DerivationsFinder.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/DerivationsFinder.kt @@ -6,7 +6,7 @@ import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.datasource.local.token.UserTokensResponseStore -import com.tangem.domain.card.DerivationStyleProvider +import com.tangem.domain.wallets.derivations.DerivationStyleProvider import com.tangem.domain.card.common.TapWorkarounds.useOldStyleDerivation import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.wallet.UserWalletId diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt index 90275890f2..df05ce2f7e 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt @@ -14,14 +14,14 @@ import com.tangem.common.tlv.Tlv import com.tangem.common.tlv.TlvDecoder import com.tangem.crypto.CryptoUtils import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.domain.card.DerivationStyleProvider +import com.tangem.domain.wallets.derivations.DerivationStyleProvider import com.tangem.domain.card.common.TapWorkarounds.isExcluded import com.tangem.domain.card.common.TapWorkarounds.isNotSupportedInThatRelease import com.tangem.domain.card.common.TapWorkarounds.isStart2Coin import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins import com.tangem.domain.card.common.TapWorkarounds.isVisa import com.tangem.domain.common.TwinsHelper -import com.tangem.domain.card.common.util.derivationStyleProvider +import com.tangem.domain.wallets.derivations.derivationStyleProvider import com.tangem.domain.card.common.visa.VisaUtilities import com.tangem.domain.card.configs.CardConfig import com.tangem.domain.models.scan.CardDTO diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCustomerWalletApproveTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCustomerWalletApproveTask.kt index 3ef65f5c16..a44a04e051 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCustomerWalletApproveTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCustomerWalletApproveTask.kt @@ -16,7 +16,7 @@ import com.tangem.common.extensions.toHexString import com.tangem.core.error.ext.tangemError import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey -import com.tangem.domain.card.common.util.derivationStyleProvider +import com.tangem.domain.wallets.derivations.derivationStyleProvider import com.tangem.domain.card.common.visa.VisaUtilities import com.tangem.domain.card.common.visa.VisaWalletPublicKeyUtility import com.tangem.domain.card.common.visa.VisaWalletPublicKeyUtility.findKeyWithoutDerivation diff --git a/app/src/main/java/com/tangem/tap/features/hot/DefaultHotWalletPasswordRequester.kt b/app/src/main/java/com/tangem/tap/features/hot/DefaultHotWalletPasswordRequester.kt deleted file mode 100644 index b9d1c6ae6c..0000000000 --- a/app/src/main/java/com/tangem/tap/features/hot/DefaultHotWalletPasswordRequester.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.tangem.tap.features.hot - -import com.tangem.hot.sdk.model.HotAuth -import com.tangem.hot.sdk.model.HotWalletId -import com.tangem.tap.domain.hot.HotWalletPasswordRequester -import javax.inject.Inject - -class DefaultHotWalletPasswordRequester @Inject constructor() : HotWalletPasswordRequester { - - override suspend fun requestPassword(hotWalletId: HotWalletId): HotAuth.Password { - return HotAuth.Password("TODO [REDACTED_TASK_KEY]".toCharArray()) // TODO [REDACTED_TASK_KEY] - } -} \ No newline at end of file diff --git a/common/test/src/main/java/com/tangem/common/test/domain/token/MockCryptoCurrencyFactory.kt b/common/test/src/main/java/com/tangem/common/test/domain/token/MockCryptoCurrencyFactory.kt index efba92dc00..ee2579d3ca 100644 --- a/common/test/src/main/java/com/tangem/common/test/domain/token/MockCryptoCurrencyFactory.kt +++ b/common/test/src/main/java/com/tangem/common/test/domain/token/MockCryptoCurrencyFactory.kt @@ -8,8 +8,8 @@ import com.tangem.blockchainsdk.utils.toNetworkId import com.tangem.common.test.domain.card.MockScanResponseFactory import com.tangem.common.test.domain.wallet.MockUserWalletFactory import com.tangem.data.common.currency.CryptoCurrencyFactory -import com.tangem.domain.card.DerivationStyleProvider -import com.tangem.domain.card.common.util.derivationStyleProvider +import com.tangem.domain.wallets.derivations.DerivationStyleProvider +import com.tangem.domain.wallets.derivations.derivationStyleProvider import com.tangem.domain.card.configs.GenericCardConfig import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network diff --git a/data/common/src/main/kotlin/com/tangem/data/common/network/NetworkFactory.kt b/data/common/src/main/kotlin/com/tangem/data/common/network/NetworkFactory.kt index bd48e9cea2..e518072440 100644 --- a/data/common/src/main/kotlin/com/tangem/data/common/network/NetworkFactory.kt +++ b/data/common/src/main/kotlin/com/tangem/data/common/network/NetworkFactory.kt @@ -6,11 +6,11 @@ import com.tangem.blockchain.common.FeePaidCurrency import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.toBlockchain import com.tangem.blockchainsdk.utils.toNetworkId -import com.tangem.domain.card.DerivationStyleProvider import com.tangem.domain.card.common.extensions.canHandleToken -import com.tangem.domain.card.common.util.derivationStyleProvider +import com.tangem.domain.wallets.derivations.derivationStyleProvider import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.wallets.derivations.DerivationStyleProvider import timber.log.Timber import javax.inject.Inject diff --git a/data/common/src/test/kotlin/com/tangem/data/common/network/NetworkFactoryTest.kt b/data/common/src/test/kotlin/com/tangem/data/common/network/NetworkFactoryTest.kt index 766643ffe0..4cbcb9a0a5 100644 --- a/data/common/src/test/kotlin/com/tangem/data/common/network/NetworkFactoryTest.kt +++ b/data/common/src/test/kotlin/com/tangem/data/common/network/NetworkFactoryTest.kt @@ -7,10 +7,10 @@ import com.tangem.common.test.domain.card.MockScanResponseFactory import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory import com.tangem.common.test.domain.wallet.MockUserWalletFactory import com.tangem.common.test.utils.ProvideTestModels -import com.tangem.domain.card.DerivationStyleProvider +import com.tangem.domain.wallets.derivations.DerivationStyleProvider import com.tangem.domain.card.configs.GenericCardConfig import com.tangem.domain.card.configs.MultiWalletCardConfig -import com.tangem.domain.card.common.util.derivationStyleProvider +import com.tangem.domain.wallets.derivations.derivationStyleProvider import com.tangem.domain.models.network.Network import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.wallet.UserWallet diff --git a/data/manage-tokens/build.gradle.kts b/data/manage-tokens/build.gradle.kts index c0da8f1be9..9095c4f1ab 100644 --- a/data/manage-tokens/build.gradle.kts +++ b/data/manage-tokens/build.gradle.kts @@ -19,6 +19,7 @@ dependencies { implementation(projects.domain.models) implementation(projects.domain.manageTokens) implementation(projects.domain.card) + implementation(projects.domain.wallets) implementation(projects.domain.tokens.models) implementation(projects.domain.wallets.models) implementation(projects.domain.legacy) diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt index d311b44167..34beb0198c 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt @@ -22,8 +22,8 @@ import com.tangem.domain.managetokens.model.ManagedCryptoCurrency import com.tangem.domain.managetokens.repository.CustomTokensRepository import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.withContext @@ -247,26 +247,43 @@ internal class DefaultCustomTokensRepository( val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { "User wallet [$userWalletId] not found while getting supported networks" } - val scanResponse = userWallet.requireColdWallet().scanResponse // TODO [REDACTED_TASK_KEY] - Blockchain.entries - .mapNotNull { blockchain -> - val canHandleBlockchain = scanResponse.card.canHandleBlockchain( - blockchain, - scanResponse.cardTypesResolver, - excludedBlockchains, - ) + when (userWallet) { + is UserWallet.Hot -> { + Blockchain.entries.mapNotNull { + // TODO: refactor [REDACTED_JIRA]\ + if (it.isTestnet() || it in excludedBlockchains) return@mapNotNull null - if (canHandleBlockchain) { networkFactory.create( - blockchain = blockchain, + blockchain = it, extraDerivationPath = null, userWallet = userWallet, ) - } else { - null } } + is UserWallet.Cold -> { + val scanResponse = userWallet.scanResponse + + Blockchain.entries + .mapNotNull { blockchain -> + val canHandleBlockchain = scanResponse.card.canHandleBlockchain( + blockchain, + scanResponse.cardTypesResolver, + excludedBlockchains, + ) + + if (canHandleBlockchain) { + networkFactory.create( + blockchain = blockchain, + extraDerivationPath = null, + userWallet = userWallet, + ) + } else { + null + } + } + } + } } override fun createDerivationPath(rawPath: String): Network.DerivationPath { diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt index 444a4b5bb6..d785c4e2b6 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt @@ -13,14 +13,14 @@ import com.tangem.data.common.network.NetworkFactory import com.tangem.datasource.api.tangemTech.models.CoinsResponse import com.tangem.datasource.api.tangemTech.models.UserTokensResponse import com.tangem.datasource.local.config.testnet.models.TestnetTokensConfig -import com.tangem.domain.card.DerivationStyleProvider import com.tangem.domain.card.common.extensions.canHandleToken -import com.tangem.domain.card.common.util.derivationStyleProvider import com.tangem.domain.managetokens.model.ManagedCryptoCurrency import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.wallets.derivations.DerivationStyleProvider +import com.tangem.domain.wallets.derivations.derivationStyleProvider import timber.log.Timber internal class ManagedCryptoCurrencyFactory( diff --git a/data/visa/build.gradle.kts b/data/visa/build.gradle.kts index 05af092bd5..ff1d5fb9ef 100644 --- a/data/visa/build.gradle.kts +++ b/data/visa/build.gradle.kts @@ -22,6 +22,7 @@ dependencies { /** Project - Domain */ implementation(projects.domain.visa) implementation(projects.domain.card) + implementation(projects.domain.wallets) implementation(projects.domain.models) implementation(projects.domain.wallets.models) implementation(projects.domain.appCurrency.models) diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaCurrencyFactory.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaCurrencyFactory.kt index f4a46dab61..829d185c39 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaCurrencyFactory.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaCurrencyFactory.kt @@ -3,12 +3,11 @@ package com.tangem.data.visa.utils import com.tangem.blockchain.common.Blockchain import com.tangem.data.common.currency.CryptoCurrencyFactory import com.tangem.data.common.network.NetworkFactory -import com.tangem.domain.card.common.util.derivationStyleProvider import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.NetworkAddress import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.visa.model.VisaCurrency +import com.tangem.domain.wallets.derivations.derivationStyleProvider import com.tangem.lib.visa.model.VisaContractInfo import org.joda.time.DateTime import org.joda.time.DateTimeZone @@ -34,7 +33,7 @@ internal class VisaCurrencyFactory @Inject constructor( val currencyNetwork = networkFactory.create( blockchain = Blockchain.Polygon, extraDerivationPath = null, - derivationStyleProvider = userWallet.requireColdWallet().scanResponse.derivationStyleProvider, + derivationStyleProvider = userWallet.derivationStyleProvider, canHandleTokens = true, ) ?: error("Unable to create network for Visa currency") diff --git a/data/wallet-manager/src/main/java/com/tangem/data/walletmanager/WalletManagerFactory.kt b/data/wallet-manager/src/main/java/com/tangem/data/walletmanager/WalletManagerFactory.kt index 01e9213ddc..eff1e685f6 100644 --- a/data/wallet-manager/src/main/java/com/tangem/data/walletmanager/WalletManagerFactory.kt +++ b/data/wallet-manager/src/main/java/com/tangem/data/walletmanager/WalletManagerFactory.kt @@ -7,10 +7,10 @@ import com.tangem.blockchainsdk.BlockchainSDKFactory import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.data.walletmanager.extensions.makePublicKey import com.tangem.data.walletmanager.extensions.makeWalletManagerForApp -import com.tangem.domain.card.DerivationStyleProvider -import com.tangem.domain.card.common.util.derivationStyleProvider +import com.tangem.domain.wallets.derivations.DerivationStyleProvider import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.wallets.derivations.derivationStyleProvider import timber.log.Timber internal class WalletManagerFactory( diff --git a/data/wallets/build.gradle.kts b/data/wallets/build.gradle.kts index fc8569a999..f7867f6b5d 100644 --- a/data/wallets/build.gradle.kts +++ b/data/wallets/build.gradle.kts @@ -11,9 +11,14 @@ android { } dependencies { + implementation(projects.data.common) /** Tangem libraries */ - implementation(tangemDeps.blockchain) // android-library + implementation(tangemDeps.blockchain) + implementation(tangemDeps.card.core) + implementation(tangemDeps.hot.core) + implementation(projects.libs.tangemSdkApi) + implementation(projects.libs.blockchainSdk) /** Core */ implementation(projects.core.datasource) @@ -21,6 +26,7 @@ dependencies { /** Domain */ implementation(projects.domain.wallets) + implementation(projects.domain.card) api(projects.domain.models) /** Domain models */ @@ -29,15 +35,17 @@ dependencies { /** DI */ implementation(deps.hilt.android) - implementation(project(":domain:legacy")) kapt(deps.hilt.kapt) /** Other deps */ implementation(deps.androidx.datastore) implementation(deps.arrow.core) + implementation(deps.kotlin.coroutines) + implementation(deps.timber) /** tests */ testImplementation(projects.domain.models) + testImplementation(projects.common.test) testImplementation(deps.test.junit) testImplementation(deps.test.coroutine) testImplementation(deps.test.truth) diff --git a/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivationsRepository.kt b/data/wallets/src/main/java/com/tangem/data/wallets/cold/DefaultColdMapDerivationsRepository.kt similarity index 56% rename from app/src/main/java/com/tangem/tap/domain/card/DefaultDerivationsRepository.kt rename to data/wallets/src/main/java/com/tangem/data/wallets/cold/DefaultColdMapDerivationsRepository.kt index b09a27d4cd..e00bde14e5 100644 --- a/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivationsRepository.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/cold/DefaultColdMapDerivationsRepository.kt @@ -1,51 +1,50 @@ -package com.tangem.tap.domain.card +package com.tangem.data.wallets.cold import com.tangem.blockchain.common.Blockchain import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.common.CompletionResult import com.tangem.common.card.EllipticCurve import com.tangem.common.core.TangemSdkError -import com.tangem.common.doOnFailure -import com.tangem.common.doOnSuccess import com.tangem.common.extensions.ByteArrayKey import com.tangem.common.extensions.toMapKey import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.data.common.network.NetworkFactory -import com.tangem.datasource.local.userwallet.UserWalletsStore -import com.tangem.domain.card.BackendId -import com.tangem.domain.card.repository.DerivationsRepository +import com.tangem.data.wallets.derivations.Derivations +import com.tangem.data.wallets.derivations.MissedDerivationsFinder import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.models.wallet.requireColdWallet +import com.tangem.domain.wallets.derivations.ColdMapDerivationsRepository +import com.tangem.domain.wallets.usecase.BackendId import com.tangem.operations.derivation.ExtendedPublicKeysMap import com.tangem.sdk.api.TangemSdkManager -import com.tangem.tap.domain.tasks.UserWalletIdPreflightReadFilter import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.withContext import timber.log.Timber +import javax.inject.Inject -internal typealias Derivations = Map> private typealias DerivedKeys = Map -internal class DefaultDerivationsRepository( +internal class DefaultColdMapDerivationsRepository @Inject constructor( private val tangemSdkManager: TangemSdkManager, - private val userWalletsStore: UserWalletsStore, private val networkFactory: NetworkFactory, private val dispatchers: CoroutineDispatcherProvider, -) : DerivationsRepository { +) : ColdMapDerivationsRepository { - override suspend fun derivePublicKeys(userWalletId: UserWalletId, currencies: List) { - derivePublicKeysByNetworks(userWalletId = userWalletId, networks = currencies.map(CryptoCurrency::network)) + override suspend fun derivePublicKeys( + userWallet: UserWallet.Cold, + currencies: List, + ): UserWallet.Cold = withContext(dispatchers.io) { + derivePublicKeysByNetworks(userWallet = userWallet, networks = currencies.map(CryptoCurrency::network)) } - override suspend fun derivePublicKeysByNetworkIds(userWalletId: UserWalletId, networkIds: List) { - val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found") - + override suspend fun derivePublicKeysByNetworkIds( + userWallet: UserWallet.Cold, + networkIds: List, + ): UserWallet.Cold = withContext(dispatchers.io) { derivePublicKeysByNetworks( - userWalletId = userWalletId, + userWallet = userWallet, networks = networkIds.mapNotNull { networkFactory.create( blockchain = Blockchain.fromNetworkId(it.value) ?: return@mapNotNull null, @@ -56,44 +55,55 @@ internal class DefaultDerivationsRepository( ) } - override suspend fun derivePublicKeysByNetworks(userWalletId: UserWalletId, networks: List) { - val userWallet = withContext(dispatchers.io) { - userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found") - } - - if (userWallet is UserWallet.Hot) { - return - } - - userWallet.requireColdWallet() - + override suspend fun derivePublicKeysByNetworks( + userWallet: UserWallet.Cold, + networks: List, + ): UserWallet.Cold = withContext(dispatchers.io) { if (!userWallet.scanResponse.card.settings.isHDWalletAllowed) { Timber.d("Nothing to derive") - return + return@withContext userWallet } - val derivations = MissedDerivationsFinder(scanResponse = userWallet.scanResponse) + val derivations = MissedDerivationsFinder(userWallet) .findByNetworks(networks) .ifEmpty { Timber.d("Nothing to derive") - return + return@withContext userWallet } - derivePublicKeys(userWalletId = userWalletId, derivations = derivations) + return@withContext derivePublicKeys(userWallet = userWallet, derivations = derivations).first + } + + override suspend fun derivePublicKeys( + userWallet: UserWallet.Cold, + derivations: Map>, + ): Pair> = withContext(dispatchers.io) { + // todo replace it in task [REDACTED_JIRA] + val preflightReadFilter = UserWalletIdPreflightReadFilter(userWallet.walletId) + val result = tangemSdkManager.derivePublicKeys( + cardId = null, + derivations = derivations, + preflightReadFilter = preflightReadFilter, + ) + + when (result) { + is CompletionResult.Success -> { + userWallet.updateDerivedKeys(result.data.entries).also { + validateDerivations(scanResponse = it.scanResponse, derivations = derivations) + } to result.data.entries + } + is CompletionResult.Failure -> { + throw result.error + } + } } override suspend fun hasMissedDerivations( - userWalletId: UserWalletId, + userWallet: UserWallet.Cold, networksWithDerivationPath: Map, - ): Boolean { - val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found") - - if (userWallet is UserWallet.Hot) { - return false - } - + ): Boolean = withContext(dispatchers.io) { val derivations = - MissedDerivationsFinder(scanResponse = userWallet.requireColdWallet().scanResponse) + MissedDerivationsFinder(userWallet) .findByNetworks( networksWithDerivationPath.mapNotNull { (backendId, extraDerivationPath) -> networkFactory.create( @@ -104,28 +114,7 @@ internal class DefaultDerivationsRepository( }, ) - return derivations.isNotEmpty() - } - - override suspend fun derivePublicKeys(userWalletId: UserWalletId, derivations: Derivations): DerivedKeys { - // todo replace it in task [REDACTED_JIRA] - val preflightReadFilter = UserWalletIdPreflightReadFilter(userWalletId) - tangemSdkManager.derivePublicKeys( - cardId = null, - derivations = derivations, - preflightReadFilter = preflightReadFilter, - ).doOnSuccess { response -> - updatePublicKeys(userWalletId = userWalletId, keys = response.entries) - .doOnSuccess { - // TODO [REDACTED_TASK_KEY] - validateDerivations(scanResponse = it.requireColdWallet().scanResponse, derivations = derivations) - return response.entries - } - .doOnFailure { throw it } - } - .doOnFailure { throw it } - - error("This code should never be reached") + derivations.isNotEmpty() } /** @@ -144,16 +133,7 @@ internal class DefaultDerivationsRepository( } } - private suspend fun updatePublicKeys(userWalletId: UserWalletId, keys: DerivedKeys): CompletionResult { - return withContext(dispatchers.io) { - userWalletsStore.update( - userWalletId = userWalletId, - update = { userWallet -> userWallet.requireColdWallet().updateDerivedKeys(keys) }, // TODO [REDACTED_TASK_KEY] - ) - } - } - - private fun UserWallet.Cold.updateDerivedKeys(keys: DerivedKeys): UserWallet { + private fun UserWallet.Cold.updateDerivedKeys(keys: DerivedKeys): UserWallet.Cold { return copy( scanResponse = scanResponse.copy( derivedKeys = getUpdatedDerivedKeys(oldKeys = scanResponse.derivedKeys, newKeys = keys), diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/cold/UserWalletIdPreflightReadFilter.kt b/data/wallets/src/main/java/com/tangem/data/wallets/cold/UserWalletIdPreflightReadFilter.kt new file mode 100644 index 0000000000..dcb1dc574b --- /dev/null +++ b/data/wallets/src/main/java/com/tangem/data/wallets/cold/UserWalletIdPreflightReadFilter.kt @@ -0,0 +1,25 @@ +package com.tangem.data.wallets.cold + +import com.tangem.common.card.Card +import com.tangem.common.core.SessionEnvironment +import com.tangem.common.core.TangemSdkError +import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.wallets.builder.UserWalletIdBuilder +import com.tangem.operations.preflightread.PreflightReadFilter + +/** + * [PreflightReadFilter] for checking if card has expected user wallet id + * +[REDACTED_AUTHOR] + */ +class UserWalletIdPreflightReadFilter(private val expectedUserWalletId: UserWalletId) : PreflightReadFilter { + + override fun onCardRead(card: Card, environment: SessionEnvironment) = Unit + + override fun onFullCardRead(card: Card, environment: SessionEnvironment) { + val actualUserWalletId = UserWalletIdBuilder.card(card = CardDTO(card)).build() ?: return + + if (expectedUserWalletId != actualUserWalletId) throw TangemSdkError.WalletNotFound() + } +} \ No newline at end of file diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/derivations/DefaultDerivationsRepository.kt b/data/wallets/src/main/java/com/tangem/data/wallets/derivations/DefaultDerivationsRepository.kt new file mode 100644 index 0000000000..862b90b035 --- /dev/null +++ b/data/wallets/src/main/java/com/tangem/data/wallets/derivations/DefaultDerivationsRepository.kt @@ -0,0 +1,95 @@ +package com.tangem.data.wallets.derivations + +import com.tangem.common.CompletionResult +import com.tangem.common.extensions.ByteArrayKey +import com.tangem.common.map +import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.datasource.local.userwallet.UserWalletsStore +import com.tangem.domain.wallets.derivations.ColdMapDerivationsRepository +import com.tangem.domain.wallets.derivations.DerivationsRepository +import com.tangem.domain.wallets.derivations.HotMapDerivationsRepository +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.network.Network +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.wallets.usecase.BackendId +import com.tangem.operations.derivation.ExtendedPublicKeysMap +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.withContext +import javax.inject.Inject + +internal class DefaultDerivationsRepository @Inject constructor( + private val userWalletsStore: UserWalletsStore, + private val hotDerivationsRepository: HotMapDerivationsRepository, + private val coldDerivationsRepository: ColdMapDerivationsRepository, + private val dispatchers: CoroutineDispatcherProvider, +) : DerivationsRepository { + + override suspend fun derivePublicKeys(userWalletId: UserWalletId, currencies: List) { + derivePublicKeysByNetworks(userWalletId = userWalletId, networks = currencies.map(CryptoCurrency::network)) + } + + override suspend fun derivePublicKeysByNetworkIds(userWalletId: UserWalletId, networkIds: List) { + val userWallet = userWalletsStore.getSyncStrict(userWalletId) + when (userWallet) { + is UserWallet.Cold -> coldDerivationsRepository.derivePublicKeysByNetworkIds(userWallet, networkIds) + is UserWallet.Hot -> hotDerivationsRepository.derivePublicKeysByNetworkIds(userWallet, networkIds) + }.also { + userWallet.update(it) + } + } + + override suspend fun derivePublicKeysByNetworks(userWalletId: UserWalletId, networks: List) { + val userWallet = userWalletsStore.getSyncStrict(userWalletId) + when (userWallet) { + is UserWallet.Cold -> coldDerivationsRepository.derivePublicKeysByNetworks(userWallet, networks) + is UserWallet.Hot -> hotDerivationsRepository.derivePublicKeysByNetworks(userWallet, networks) + }.also { + userWallet.update(it) + } + } + + override suspend fun derivePublicKeys( + userWalletId: UserWalletId, + derivations: Map>, + ): Map { + val userWallet = userWalletsStore.getSyncStrict(userWalletId) + return when (userWallet) { + is UserWallet.Cold -> coldDerivationsRepository.derivePublicKeys(userWallet, derivations) + is UserWallet.Hot -> hotDerivationsRepository.derivePublicKeys(userWallet, derivations) + }.let { + userWallet.update(it.first) + it.second + } + } + + override suspend fun hasMissedDerivations( + userWalletId: UserWalletId, + networksWithDerivationPath: Map, + ): Boolean { + return when (val userWallet = userWalletsStore.getSyncStrict(userWalletId)) { + is UserWallet.Cold -> coldDerivationsRepository.hasMissedDerivations(userWallet, networksWithDerivationPath) + is UserWallet.Hot -> hotDerivationsRepository.hasMissedDerivations(userWallet, networksWithDerivationPath) + } + } + + private suspend fun UserWallet.update(newUserWallet: UserWallet) = withContext(dispatchers.io) { + check(this@update.walletId == newUserWallet.walletId) { + "Cannot update UserWallet with different walletId: ${newUserWallet.walletId}" + } + + if (this@update == newUserWallet) { + return@withContext // No update needed + } + + val updateResult = userWalletsStore.update( + userWalletId = newUserWallet.walletId, + update = { userWalletToUpdate -> newUserWallet }, + ) + + when (updateResult) { + is CompletionResult.Failure -> throw updateResult.error + is CompletionResult.Success -> updateResult.data + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/card/MissedDerivationsFinder.kt b/data/wallets/src/main/java/com/tangem/data/wallets/derivations/MissedDerivationsFinder.kt similarity index 64% rename from app/src/main/java/com/tangem/tap/domain/card/MissedDerivationsFinder.kt rename to data/wallets/src/main/java/com/tangem/data/wallets/derivations/MissedDerivationsFinder.kt index 7e740159e5..5ea8f38be1 100644 --- a/app/src/main/java/com/tangem/tap/domain/card/MissedDerivationsFinder.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/derivations/MissedDerivationsFinder.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.domain.card +package com.tangem.data.wallets.derivations import com.tangem.blockchain.blockchains.cardano.CardanoUtils import com.tangem.blockchain.common.Blockchain @@ -8,23 +8,26 @@ import com.tangem.common.extensions.ByteArrayKey import com.tangem.common.extensions.toMapKey import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.card.configs.CardConfig -import com.tangem.domain.card.common.util.derivationStyleProvider +import com.tangem.domain.card.configs.Wallet2CardConfig import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network import com.tangem.domain.models.scan.KeyWalletPublicKey -import com.tangem.domain.models.scan.ScanResponse +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.wallets.derivations.derivationStyleProvider import com.tangem.operations.derivation.ExtendedPublicKeysMap +import kotlin.collections.forEach private typealias DerivationData = Pair> +internal typealias Derivations = Map> /** * Finder of missed derivations * - * @property scanResponse scanning response + * @property userWallet User wallet to find derivations for * [REDACTED_AUTHOR] */ -internal class MissedDerivationsFinder(private val scanResponse: ScanResponse) { +internal class MissedDerivationsFinder(private val userWallet: UserWallet) { /** Find missed derivations for given currencies [currencies] */ fun find(currencies: List): Derivations { @@ -48,30 +51,39 @@ internal class MissedDerivationsFinder(private val scanResponse: ScanResponse) { } private fun List.mapToNewDerivations(): List { - val config = CardConfig.createConfig(scanResponse.card) + val config = when (userWallet) { + is UserWallet.Cold -> CardConfig.createConfig(userWallet.scanResponse.card) + is UserWallet.Hot -> Wallet2CardConfig // TODO create config [REDACTED_TASK_KEY] + } return mapNotNull { network -> val blockchain = network.toBlockchain() val curve = config.primaryCurve(blockchain) ?: return@mapNotNull null - findNewDerivations(curve = curve, scanResponse = scanResponse, network = network) + val walletPublicKey = when (userWallet) { + is UserWallet.Cold -> { + val wallet = userWallet.scanResponse.card.wallets.firstOrNull { it.curve == curve } + wallet?.publicKey + } + is UserWallet.Hot -> { + val wallet = userWallet.wallets?.firstOrNull { it.curve == curve } + wallet?.publicKey + } + } + + walletPublicKey?.let { + findNewDerivations(curve = curve, publicKey = it, network = network) + } } } - private fun findNewDerivations( - curve: EllipticCurve, - scanResponse: ScanResponse, - network: Network, - ): DerivationData? { - val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null - val publicKey = wallet.publicKey.toMapKey() - + private fun findNewDerivations(curve: EllipticCurve, publicKey: ByteArray, network: Network): DerivationData? { val derivationCandidates = network .getDerivationCandidates(curve) .ifEmpty { return null } - .filterAlreadyDerivedKeys(publicKey) + .filterAlreadyDerivedKeys(publicKey.toMapKey()) .ifEmpty { return null } - return publicKey to derivationCandidates + return publicKey.toMapKey() to derivationCandidates } private fun Network.getDerivationCandidates(curve: EllipticCurve): List { @@ -88,7 +100,7 @@ internal class MissedDerivationsFinder(private val scanResponse: ScanResponse) { private fun Blockchain.getDerivationPath(curve: EllipticCurve): DerivationPath? { return if (getSupportedCurves().contains(curve)) { - derivationPath(style = scanResponse.derivationStyleProvider.getDerivationStyle()) + derivationPath(style = userWallet.derivationStyleProvider.getDerivationStyle()) } else { null } @@ -118,7 +130,15 @@ internal class MissedDerivationsFinder(private val scanResponse: ScanResponse) { } private fun getAlreadyDerivedKeys(publicKey: KeyWalletPublicKey): List { - val extendedPublicKeysMap = scanResponse.derivedKeys[publicKey] ?: ExtendedPublicKeysMap(emptyMap()) + val extendedPublicKeysMap = when (userWallet) { + is UserWallet.Cold -> userWallet.scanResponse.derivedKeys[publicKey] ?: ExtendedPublicKeysMap(emptyMap()) + is UserWallet.Hot -> { + val wallets = userWallet.wallets ?: return emptyList() + wallets.firstOrNull { it.publicKey.contentEquals(publicKey.bytes) }?.derivedKeys + ?: ExtendedPublicKeysMap(emptyMap()) + } + } + return extendedPublicKeysMap.keys.toList() } } \ No newline at end of file diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt b/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt index a154dc4f99..5564622241 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt @@ -2,14 +2,21 @@ package com.tangem.data.wallets.di import com.tangem.data.wallets.DefaultWalletNamesMigrationRepository import com.tangem.data.wallets.DefaultWalletsRepository +import com.tangem.data.wallets.cold.DefaultColdMapDerivationsRepository +import com.tangem.data.wallets.derivations.DefaultDerivationsRepository +import com.tangem.data.wallets.hot.DefaultHotMapDerivationsRepository import com.tangem.datasource.api.common.AuthProvider import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.local.datastore.RuntimeStateStore import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.userwallet.UserWalletsStore +import com.tangem.domain.wallets.derivations.ColdMapDerivationsRepository +import com.tangem.domain.wallets.derivations.DerivationsRepository +import com.tangem.domain.wallets.derivations.HotMapDerivationsRepository import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import dagger.Binds import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -44,4 +51,21 @@ internal object WalletsDataModule { fun provideMigrateNamesRepository(appPreferencesStore: AppPreferencesStore): WalletNamesMigrationRepository { return DefaultWalletNamesMigrationRepository(appPreferencesStore) } +} + +@Module +@InstallIn(SingletonComponent::class) +internal interface WalletsDataBindsModule { + + @Binds + @Singleton + fun bindDerivationsRepository(impl: DefaultDerivationsRepository): DerivationsRepository + + @Binds + @Singleton + fun bindHotMapDerivationsRepository(impl: DefaultHotMapDerivationsRepository): HotMapDerivationsRepository + + @Binds + @Singleton + fun bindColdMapDerivationsRepository(impl: DefaultColdMapDerivationsRepository): ColdMapDerivationsRepository } \ No newline at end of file diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/hot/DefaultHotMapDerivationsRepository.kt b/data/wallets/src/main/java/com/tangem/data/wallets/hot/DefaultHotMapDerivationsRepository.kt new file mode 100644 index 0000000000..3eb9431fc3 --- /dev/null +++ b/data/wallets/src/main/java/com/tangem/data/wallets/hot/DefaultHotMapDerivationsRepository.kt @@ -0,0 +1,139 @@ +package com.tangem.data.wallets.hot + +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchainsdk.utils.fromNetworkId +import com.tangem.common.extensions.ByteArrayKey +import com.tangem.common.extensions.toMapKey +import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.data.common.network.NetworkFactory +import com.tangem.data.wallets.derivations.MissedDerivationsFinder +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.network.Network +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.wallets.derivations.HotMapDerivationsRepository +import com.tangem.domain.wallets.usecase.BackendId +import com.tangem.hot.sdk.model.DeriveWalletRequest +import com.tangem.operations.derivation.ExtendedPublicKeysMap +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.withContext +import timber.log.Timber +import javax.inject.Inject + +internal class DefaultHotMapDerivationsRepository @Inject constructor( + private val networkFactory: NetworkFactory, + private val hotWalletAccessor: HotWalletAccessor, + private val dispatchers: CoroutineDispatcherProvider, +) : HotMapDerivationsRepository { + + override suspend fun derivePublicKeys( + userWallet: UserWallet.Hot, + currencies: List, + ): UserWallet.Hot { + return derivePublicKeysByNetworks(userWallet = userWallet, networks = currencies.map(CryptoCurrency::network)) + } + + override suspend fun derivePublicKeysByNetworkIds( + userWallet: UserWallet.Hot, + networkIds: List, + ): UserWallet.Hot { + return derivePublicKeysByNetworks( + userWallet = userWallet, + networks = networkIds.mapNotNull { + networkFactory.create( + blockchain = Blockchain.fromNetworkId(it.value) ?: return@mapNotNull null, + extraDerivationPath = null, + userWallet = userWallet, + ) + }, + ) + } + + override suspend fun derivePublicKeysByNetworks( + userWallet: UserWallet.Hot, + networks: List, + ): UserWallet.Hot = withContext(dispatchers.default) { + val derivations = MissedDerivationsFinder(userWallet) + .findByNetworks(networks) + .ifEmpty { + Timber.d("Nothing to derive") + return@withContext userWallet + } + + derivePublicKeys(userWallet, derivations).first + } + + override suspend fun derivePublicKeys( + userWallet: UserWallet.Hot, + derivations: Map>, + ): Pair> { + val wallets = userWallet.wallets ?: return userWallet to emptyMap() + + val request = DeriveWalletRequest( + derivations.map { entry -> + val wallet = wallets.first { it.publicKey.contentEquals(entry.key.bytes) } + DeriveWalletRequest.Request( + curve = wallet.curve, + paths = entry.value, + ) + }, + ) + val result = hotWalletAccessor.derivePublicKeys( + hotWalletId = userWallet.hotWalletId, + request = request, + ) + val newKeys = + result.responses.associate { ByteArrayKey(it.seedKey.publicKey) to ExtendedPublicKeysMap(it.publicKeys) } + + return userWallet.updateWithNewKeys(newKeys) to newKeys + } + + override suspend fun hasMissedDerivations( + userWallet: UserWallet.Hot, + networksWithDerivationPath: Map, + ): Boolean = withContext(dispatchers.default) { + val derivations = MissedDerivationsFinder(userWallet) + .findByNetworks( + networksWithDerivationPath.mapNotNull { (backendId, extraDerivationPath) -> + networkFactory.create( + blockchain = Blockchain.fromNetworkId(backendId) ?: return@mapNotNull null, + extraDerivationPath = extraDerivationPath, + userWallet = userWallet, + ) + }, + ) + + derivations.isNotEmpty() + } + + private fun UserWallet.Hot.updateWithNewKeys(newKeys: Map): UserWallet.Hot { + val wallets = this.wallets ?: return this + val derivedKeys = wallets.associate { + it.publicKey.toMapKey() to ExtendedPublicKeysMap(it.derivedKeys) + } + val updatedKeys = getUpdatedDerivedKeys( + oldKeys = derivedKeys, + newKeys = newKeys, + ) + + return copy( + wallets = wallets.map { wallet -> + wallet.copy( + derivedKeys = updatedKeys[wallet.publicKey.toMapKey()] ?: ExtendedPublicKeysMap(emptyMap()), + ) + }, + ) + } + + private fun getUpdatedDerivedKeys( + oldKeys: Map, + newKeys: Map, + ): Map { + return (oldKeys.keys + newKeys.keys).toSet() + .associateWith { walletKey -> + val oldDerivations = ExtendedPublicKeysMap(oldKeys[walletKey] ?: emptyMap()) + val newDerivations = newKeys[walletKey] ?: ExtendedPublicKeysMap(emptyMap()) + + ExtendedPublicKeysMap(oldDerivations + newDerivations) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/hot/HotWalletAccessor.kt b/data/wallets/src/main/java/com/tangem/data/wallets/hot/HotWalletAccessor.kt similarity index 84% rename from app/src/main/java/com/tangem/tap/domain/hot/HotWalletAccessor.kt rename to data/wallets/src/main/java/com/tangem/data/wallets/hot/HotWalletAccessor.kt index a65a2ecd53..9ae11b61c8 100644 --- a/app/src/main/java/com/tangem/tap/domain/hot/HotWalletAccessor.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/hot/HotWalletAccessor.kt @@ -1,7 +1,7 @@ -package com.tangem.tap.domain.hot +package com.tangem.data.wallets.hot import com.tangem.common.core.TangemSdkError -import com.tangem.features.hotwallet.HotWalletPasswordRequester +import com.tangem.domain.wallets.hot.HotWalletPasswordRequester import com.tangem.hot.sdk.TangemHotSdk import com.tangem.hot.sdk.exception.WrongPasswordException import com.tangem.hot.sdk.model.* @@ -12,7 +12,17 @@ class HotWalletAccessor @Inject constructor( private val hotWalletPasswordRequester: HotWalletPasswordRequester, ) { - suspend fun signHashes(hotWalletId: HotWalletId, dataToSign: List): List { + suspend fun signHashes(hotWalletId: HotWalletId, dataToSign: List): List = + hotSdkRequest(hotWalletId) { unlock -> + tangemHotSdk.signHashes(unlockHotWallet = unlock, dataToSign = dataToSign) + } + + suspend fun derivePublicKeys(hotWalletId: HotWalletId, request: DeriveWalletRequest): DerivedPublicKeyResponse = + hotSdkRequest(hotWalletId) { unlock -> + tangemHotSdk.derivePublicKey(unlockHotWallet = unlock, request = request) + } + + private suspend fun hotSdkRequest(hotWalletId: HotWalletId, block: suspend (unlock: UnlockHotWallet) -> T): T { val auth = when (hotWalletId.authType) { HotWalletId.AuthType.NoPassword -> HotAuth.NoAuth HotWalletId.AuthType.Password -> requestPassword(false) @@ -20,13 +30,7 @@ class HotWalletAccessor @Inject constructor( } return runCatchingSdkErrors(hotWalletId, auth) { - tangemHotSdk.signHashes( - unlockHotWallet = UnlockHotWallet( - walletId = hotWalletId, - auth = it, - ), - dataToSign = dataToSign, - ).also { + block(UnlockHotWallet(hotWalletId, it)).also { hotWalletPasswordRequester.dismiss() } } diff --git a/app/src/main/java/com/tangem/tap/domain/hot/TangemHotWalletSigner.kt b/data/wallets/src/main/java/com/tangem/data/wallets/hot/TangemHotWalletSigner.kt similarity index 99% rename from app/src/main/java/com/tangem/tap/domain/hot/TangemHotWalletSigner.kt rename to data/wallets/src/main/java/com/tangem/data/wallets/hot/TangemHotWalletSigner.kt index ba64c2fda2..b5bfc9da12 100644 --- a/app/src/main/java/com/tangem/tap/domain/hot/TangemHotWalletSigner.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/hot/TangemHotWalletSigner.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.domain.hot +package com.tangem.data.wallets.hot import com.tangem.blockchain.common.TransactionSigner import com.tangem.blockchain.common.Wallet diff --git a/app/src/test/kotlin/com/tangem/tap/domain/card/DefaultDerivationsRepositoryTest.kt b/data/wallets/src/test/java/com/tangem/data/wallets/derivations/DefaultDerivationsRepositoryTest.kt similarity index 80% rename from app/src/test/kotlin/com/tangem/tap/domain/card/DefaultDerivationsRepositoryTest.kt rename to data/wallets/src/test/java/com/tangem/data/wallets/derivations/DefaultDerivationsRepositoryTest.kt index f5484b4b50..eb812166f7 100644 --- a/app/src/test/kotlin/com/tangem/tap/domain/card/DefaultDerivationsRepositoryTest.kt +++ b/data/wallets/src/test/java/com/tangem/data/wallets/derivations/DefaultDerivationsRepositoryTest.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.domain.card +package com.tangem.data.wallets.derivations import android.annotation.SuppressLint import com.google.common.truth.Truth @@ -7,6 +7,7 @@ import com.tangem.common.CompletionResult import com.tangem.common.test.domain.card.MockScanResponseFactory import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory import com.tangem.data.common.network.NetworkFactory +import com.tangem.data.wallets.cold.DefaultColdMapDerivationsRepository import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.card.ScanCardException import com.tangem.domain.card.configs.GenericCardConfig @@ -14,7 +15,7 @@ import com.tangem.domain.card.configs.MultiWalletCardConfig import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.operations.derivation.DerivationTaskResponse -import com.tangem.tap.domain.sdk.impl.DefaultTangemSdkManager +import com.tangem.sdk.api.TangemSdkManager import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import io.mockk.coEvery import io.mockk.coVerify @@ -27,13 +28,17 @@ import org.junit.Test */ internal class DefaultDerivationsRepositoryTest { - private val tangemSdkManager = mockk() + private val tangemSdkManager = mockk() private val userWalletsStore = mockk() private val repository = DefaultDerivationsRepository( - tangemSdkManager = tangemSdkManager, userWalletsStore = userWalletsStore, dispatchers = TestingCoroutineDispatcherProvider(), - networkFactory = NetworkFactory(excludedBlockchains = ExcludedBlockchains()), + hotDerivationsRepository = mockk(), + coldDerivationsRepository = DefaultColdMapDerivationsRepository( + tangemSdkManager = tangemSdkManager, + networkFactory = NetworkFactory(excludedBlockchains = ExcludedBlockchains()), + dispatchers = TestingCoroutineDispatcherProvider(), + ), ) private val defaultUserWalletId = UserWalletId("011") @@ -48,7 +53,7 @@ internal class DefaultDerivationsRepositoryTest { @Test fun `error if userWalletId not found`() = runTest { - coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns null + coEvery { userWalletsStore.getSyncStrict(defaultUserWalletId) } throws IllegalStateException() runCatching { repository.derivePublicKeys(userWalletId = defaultUserWalletId, currencies = emptyList()) @@ -56,7 +61,7 @@ internal class DefaultDerivationsRepositoryTest { .onSuccess { error("Should throws exception") } .onFailure { Truth.assertThat(it).isInstanceOf(IllegalStateException::class.java) } - coVerify(exactly = 1) { userWalletsStore.getSyncOrNull(defaultUserWalletId) } + coVerify(exactly = 1) { userWalletsStore.getSyncStrict(defaultUserWalletId) } coVerify(inverse = true) { tangemSdkManager.derivePublicKeys(null, any(), any()) } coVerify(inverse = true) { userWalletsStore.update(defaultUserWalletId, any()) } } @@ -64,13 +69,17 @@ internal class DefaultDerivationsRepositoryTest { @SuppressLint("CheckResult") @Test fun `success if card is not supported derivations`() = runTest { - coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns defaultUserWallet + coEvery { userWalletsStore.getSyncStrict(defaultUserWalletId) } returns defaultUserWallet - runCatching { repository.derivePublicKeys(userWalletId = defaultUserWalletId, currencies = emptyList()) } + repository.derivePublicKeys(userWalletId = defaultUserWalletId, currencies = emptyList()) + + runCatching { } .onSuccess { Truth.assertThat(it) } - .onFailure { error("Should returns success") } + .onFailure { + error("Should returns success") + } - coVerify(exactly = 1) { userWalletsStore.getSyncOrNull(defaultUserWalletId) } + coVerify(exactly = 1) { userWalletsStore.getSyncStrict(defaultUserWalletId) } coVerify(inverse = true) { tangemSdkManager.derivePublicKeys(null, any(), any()) } coVerify(inverse = true) { userWalletsStore.update(defaultUserWalletId, any()) } } @@ -81,13 +90,13 @@ internal class DefaultDerivationsRepositoryTest { val userWallet = defaultUserWallet.copy( scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()), ) - coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet + coEvery { userWalletsStore.getSyncStrict(defaultUserWalletId) } returns userWallet runCatching { repository.derivePublicKeys(userWalletId = defaultUserWalletId, currencies = emptyList()) } .onSuccess { Truth.assertThat(it) } .onFailure { error("Should returns success") } - coVerify(exactly = 1) { userWalletsStore.getSyncOrNull(defaultUserWalletId) } + coVerify(exactly = 1) { userWalletsStore.getSyncStrict(defaultUserWalletId) } coVerify(inverse = true) { tangemSdkManager.derivePublicKeys(null, any(), any()) } coVerify(inverse = true) { userWalletsStore.update(defaultUserWalletId, any()) } } @@ -102,7 +111,7 @@ internal class DefaultDerivationsRepositoryTest { ), ) - coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet + coEvery { userWalletsStore.getSyncStrict(defaultUserWalletId) } returns userWallet runCatching { repository.derivePublicKeys( @@ -113,7 +122,7 @@ internal class DefaultDerivationsRepositoryTest { .onSuccess { Truth.assertThat(it) } .onFailure { error("Should returns success") } - coVerify(exactly = 1) { userWalletsStore.getSyncOrNull(defaultUserWalletId) } + coVerify(exactly = 1) { userWalletsStore.getSyncStrict(defaultUserWalletId) } coVerify(inverse = true) { tangemSdkManager.derivePublicKeys(null, any(), any()) } coVerify(inverse = true) { userWalletsStore.update(defaultUserWalletId, any()) } } @@ -123,7 +132,7 @@ internal class DefaultDerivationsRepositoryTest { val userWallet = defaultUserWallet.copy( scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()), ) - coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet + coEvery { userWalletsStore.getSyncStrict(defaultUserWalletId) } returns userWallet coEvery { tangemSdkManager.derivePublicKeys(null, any(), any()) } throws ScanCardException.UserCancelled runCatching { @@ -135,7 +144,7 @@ internal class DefaultDerivationsRepositoryTest { .onSuccess { error("Should throws exception") } .onFailure { Truth.assertThat(it).isInstanceOf(ScanCardException.UserCancelled::class.java) } - coVerify(exactly = 1) { userWalletsStore.getSyncOrNull(defaultUserWalletId) } + coVerify(exactly = 1) { userWalletsStore.getSyncStrict(defaultUserWalletId) } coVerify(exactly = 1) { tangemSdkManager.derivePublicKeys(null, any(), any()) } coVerify(inverse = true) { userWalletsStore.update(defaultUserWalletId, any()) } } @@ -146,7 +155,7 @@ internal class DefaultDerivationsRepositoryTest { val userWallet = defaultUserWallet.copy( scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()), ) - coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet + coEvery { userWalletsStore.getSyncStrict(defaultUserWalletId) } returns userWallet coEvery { tangemSdkManager.derivePublicKeys(null, any(), any()) } returns CompletionResult.Success( DerivationTaskResponse(DerivedKeysMocks.ethereumDerivedKeys), ) @@ -161,7 +170,7 @@ internal class DefaultDerivationsRepositoryTest { .onSuccess { Truth.assertThat(it) } .onFailure { error("Should returns success but $it") } - coVerify(exactly = 1) { userWalletsStore.getSyncOrNull(defaultUserWalletId) } + coVerify(exactly = 1) { userWalletsStore.getSyncStrict(defaultUserWalletId) } coVerify(exactly = 1) { tangemSdkManager.derivePublicKeys(null, any(), any()) } coVerify(exactly = 1) { userWalletsStore.update(defaultUserWalletId, any()) } } diff --git a/app/src/test/kotlin/com/tangem/tap/domain/card/DerivedKeysMocks.kt b/data/wallets/src/test/java/com/tangem/data/wallets/derivations/DerivedKeysMocks.kt similarity index 95% rename from app/src/test/kotlin/com/tangem/tap/domain/card/DerivedKeysMocks.kt rename to data/wallets/src/test/java/com/tangem/data/wallets/derivations/DerivedKeysMocks.kt index a8398c930c..ae0e28b70b 100644 --- a/app/src/test/kotlin/com/tangem/tap/domain/card/DerivedKeysMocks.kt +++ b/data/wallets/src/test/java/com/tangem/data/wallets/derivations/DerivedKeysMocks.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.domain.card +package com.tangem.data.wallets.derivations import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.derivation.DerivationConfigV2 diff --git a/app/src/test/kotlin/com/tangem/tap/domain/card/MissedDerivationsFinderTest.kt b/data/wallets/src/test/java/com/tangem/data/wallets/derivations/MissedDerivationsFinderTest.kt similarity index 90% rename from app/src/test/kotlin/com/tangem/tap/domain/card/MissedDerivationsFinderTest.kt rename to data/wallets/src/test/java/com/tangem/data/wallets/derivations/MissedDerivationsFinderTest.kt index f623b13c56..426a1bc144 100644 --- a/app/src/test/kotlin/com/tangem/tap/domain/card/MissedDerivationsFinderTest.kt +++ b/data/wallets/src/test/java/com/tangem/data/wallets/derivations/MissedDerivationsFinderTest.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.domain.card +package com.tangem.data.wallets.derivations import com.google.common.truth.Truth import com.tangem.blockchain.blockchains.cardano.CardanoUtils @@ -13,7 +13,7 @@ import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.card.configs.GenericCardConfig import com.tangem.domain.card.configs.MultiWalletCardConfig import com.tangem.domain.card.configs.Wallet2CardConfig -import com.tangem.domain.card.common.util.derivationStyleProvider +import com.tangem.domain.wallets.derivations.derivationStyleProvider import org.junit.Test /** @@ -24,7 +24,8 @@ internal class MissedDerivationsFinderTest { @Test fun `empty derivations for empty currencies`() { val scanResponse = MockScanResponseFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()) - val finder = MissedDerivationsFinder(scanResponse) + val userWallet = MockUserWalletFactory.create(scanResponse) + val finder = MissedDerivationsFinder(userWallet) val actual = finder.find(emptyList()) @@ -36,7 +37,7 @@ internal class MissedDerivationsFinderTest { // Bls is not supported val scanResponse = MockScanResponseFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()) val userWallet = MockUserWalletFactory.create(scanResponse) - val finder = MissedDerivationsFinder(scanResponse) + val finder = MissedDerivationsFinder(userWallet) val currencies = MockCryptoCurrencyFactory(userWallet).chia.let(::listOf) val actual = finder.find(currencies) @@ -58,7 +59,7 @@ internal class MissedDerivationsFinderTest { ) } val userWallet = MockUserWalletFactory.create(scanResponse) - val finder = MissedDerivationsFinder(scanResponse) + val finder = MissedDerivationsFinder(userWallet) val currencies = MockCryptoCurrencyFactory(userWallet).chiaAndEthereum val actual = finder.find(currencies) @@ -73,7 +74,7 @@ internal class MissedDerivationsFinderTest { fun `derivations for custom token`() { val scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()) val userWallet = MockUserWalletFactory.create(scanResponse) - val finder = MissedDerivationsFinder(scanResponse) + val finder = MissedDerivationsFinder(userWallet) val currencies = MockCryptoCurrencyFactory(userWallet).ethereumTokenWithBinanceDerivation val actual = finder.find(currencies) @@ -91,7 +92,7 @@ internal class MissedDerivationsFinderTest { fun `derivations for cardano`() { val scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()) val userWallet = MockUserWalletFactory.create(scanResponse) - val finder = MissedDerivationsFinder(scanResponse) + val finder = MissedDerivationsFinder(userWallet) val currencies = MockCryptoCurrencyFactory(userWallet).cardano.let(::listOf) val actual = finder.find(currencies) @@ -117,7 +118,7 @@ internal class MissedDerivationsFinderTest { derivedKeys = DerivedKeysMocks.ethereumDerivedKeys, ) val userWallet = MockUserWalletFactory.create(scanResponse) - val finder = MissedDerivationsFinder(scanResponse) + val finder = MissedDerivationsFinder(userWallet) val currencies = MockCryptoCurrencyFactory(userWallet).ethereum.let(::listOf) val actual = finder.find(currencies) @@ -132,7 +133,7 @@ internal class MissedDerivationsFinderTest { derivedKeys = DerivedKeysMocks.ethereumDerivedKeys, ) val userWallet = MockUserWalletFactory.create(scanResponse) - val finder = MissedDerivationsFinder(scanResponse) + val finder = MissedDerivationsFinder(userWallet) val currencies = MockCryptoCurrencyFactory(userWallet).ethereumAndStellar val actual = finder.find(currencies) diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/TangemCardTypesResolver.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/TangemCardTypesResolver.kt index f98a8ed3a7..e8badaf526 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/TangemCardTypesResolver.kt +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/TangemCardTypesResolver.kt @@ -1,6 +1,7 @@ package com.tangem.domain.card import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.Blockchain.Companion.fromId import com.tangem.blockchain.common.Token import com.tangem.common.card.EllipticCurve import com.tangem.common.card.FirmwareVersion @@ -76,7 +77,7 @@ internal class TangemCardTypesResolver( } else { return Blockchain.Unknown } - Blockchain.Companion.fromBlockchainName(blockchainName) + Blockchain.fromBlockchainName(blockchainName) } } } diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/common/util/ScanResponseExt.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/common/util/ScanResponseExt.kt index 6fb8c34eb4..0a6e22e8e5 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/common/util/ScanResponseExt.kt +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/common/util/ScanResponseExt.kt @@ -6,10 +6,7 @@ import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.toMapKey import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.card.CardTypesResolver -import com.tangem.domain.card.DerivationStyleProvider import com.tangem.domain.card.TangemCardTypesResolver -import com.tangem.domain.card.TangemDerivationStyleProvider -import com.tangem.domain.card.TangemHotDerivationStyleProvider import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins import com.tangem.domain.card.common.TapWorkarounds.isTestCard import com.tangem.domain.card.configs.CardConfig @@ -25,18 +22,6 @@ val ScanResponse.cardTypesResolver: CardTypesResolver walletData = walletData, ) -val UserWallet.derivationStyleProvider: DerivationStyleProvider - get() = when (this) { - is UserWallet.Cold -> this.scanResponse.derivationStyleProvider - is UserWallet.Hot -> TangemHotDerivationStyleProvider() - } - -val ScanResponse.derivationStyleProvider: DerivationStyleProvider - get() = card.derivationStyleProvider - -val CardDTO.derivationStyleProvider: DerivationStyleProvider - get() = TangemDerivationStyleProvider(this) - val UserWallet.Cold.cardTypesResolver: CardTypesResolver get() = scanResponse.cardTypesResolver diff --git a/domain/manage-tokens/build.gradle.kts b/domain/manage-tokens/build.gradle.kts index c60557ebe2..e5fffdace4 100644 --- a/domain/manage-tokens/build.gradle.kts +++ b/domain/manage-tokens/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(projects.domain.staking) implementation(projects.domain.tokens) implementation(projects.domain.card) + implementation(projects.domain.wallets) implementation(projects.domain.legacy) /* Core */ diff --git a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt index 065f27f412..6c1b21b89b 100644 --- a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt +++ b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt @@ -2,7 +2,6 @@ package com.tangem.domain.managetokens import arrow.core.Either import arrow.core.flatten -import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.managetokens.model.ManagedCryptoCurrency import com.tangem.domain.managetokens.repository.CustomTokensRepository import com.tangem.domain.models.currency.CryptoCurrency @@ -14,6 +13,7 @@ import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.domain.wallets.derivations.DerivationsRepository @Suppress("LongParameterList") class SaveManagedTokensUseCase( diff --git a/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt b/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt index 47e769d11f..3417a876fd 100644 --- a/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt +++ b/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt @@ -1,7 +1,7 @@ package com.tangem.domain.markets import arrow.core.Either -import com.tangem.domain.card.repository.DerivationsRepository +import com.tangem.domain.wallets.derivations.DerivationsRepository import com.tangem.domain.markets.repositories.MarketsTokenRepository import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/builder/HotUserWalletBuilder.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/builder/HotUserWalletBuilder.kt index ba55e47d78..8b8bda5d78 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/builder/HotUserWalletBuilder.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/builder/HotUserWalletBuilder.kt @@ -25,7 +25,7 @@ class HotUserWalletBuilder @AssistedInject constructor( ) { suspend fun build(): UserWallet.Hot = withContext(dispatcherProvider.default) { - val allNetworks = Blockchain.entries + val allNetworks = Blockchain.entries // TODO [REDACTED_TASK_KEY] add derivation config val curves = allNetworks.map { it.getSupportedCurves() }.flatten().toSet() val requests = curves.sortedBy { it.ordinal }.map { curve -> val derivationPaths = allNetworks.filter { curve in it.getSupportedCurves() } diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/ColdMapDerivationsRepository.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/ColdMapDerivationsRepository.kt new file mode 100644 index 0000000000..86da09c677 --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/ColdMapDerivationsRepository.kt @@ -0,0 +1,35 @@ +package com.tangem.domain.wallets.derivations + +import com.tangem.common.extensions.ByteArrayKey +import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.network.Network +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.wallets.usecase.BackendId +import com.tangem.operations.derivation.ExtendedPublicKeysMap + +interface ColdMapDerivationsRepository { + + @Throws + suspend fun derivePublicKeys(userWallet: UserWallet.Cold, currencies: List): UserWallet.Cold + + suspend fun derivePublicKeysByNetworkIds( + userWallet: UserWallet.Cold, + networkIds: List, + ): UserWallet.Cold + + @Throws + suspend fun derivePublicKeysByNetworks(userWallet: UserWallet.Cold, networks: List): UserWallet.Cold + + @Throws + suspend fun derivePublicKeys( + userWallet: UserWallet.Cold, + derivations: Map>, + ): Pair> + + /** Check if user [userWallet] has missed derivations using map of [Network.ID] with extraDerivationPath */ + suspend fun hasMissedDerivations( + userWallet: UserWallet.Cold, + networksWithDerivationPath: Map, + ): Boolean +} \ No newline at end of file diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/DerivationStyleProvider.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/DerivationStyleProvider.kt similarity index 92% rename from domain/card/src/main/kotlin/com/tangem/domain/card/DerivationStyleProvider.kt rename to domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/DerivationStyleProvider.kt index 36267e8d35..51e7ee6763 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/DerivationStyleProvider.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/DerivationStyleProvider.kt @@ -1,4 +1,4 @@ -package com.tangem.domain.card +package com.tangem.domain.wallets.derivations import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.domain.card.common.TapWorkarounds.isWallet2 @@ -25,7 +25,6 @@ internal class TangemDerivationStyleProvider( } } -// TODO remove this class [REDACTED_TASK_KEY] internal class TangemHotDerivationStyleProvider : DerivationStyleProvider { override fun getDerivationStyle(): DerivationStyle? = DerivationStyle.V3 } \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/DerivationStyleProviderExt.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/DerivationStyleProviderExt.kt new file mode 100644 index 0000000000..6525c2d528 --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/DerivationStyleProviderExt.kt @@ -0,0 +1,17 @@ +package com.tangem.domain.wallets.derivations + +import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.models.scan.ScanResponse +import com.tangem.domain.models.wallet.UserWallet + +val UserWallet.derivationStyleProvider: DerivationStyleProvider + get() = when (this) { + is UserWallet.Cold -> scanResponse.derivationStyleProvider + is UserWallet.Hot -> TangemHotDerivationStyleProvider() + } + +val ScanResponse.derivationStyleProvider: DerivationStyleProvider + get() = card.derivationStyleProvider + +val CardDTO.derivationStyleProvider: DerivationStyleProvider + get() = TangemDerivationStyleProvider(this) \ No newline at end of file diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/repository/DerivationsRepository.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/DerivationsRepository.kt similarity index 92% rename from domain/card/src/main/kotlin/com/tangem/domain/card/repository/DerivationsRepository.kt rename to domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/DerivationsRepository.kt index 0f655df789..43b5190947 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/repository/DerivationsRepository.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/DerivationsRepository.kt @@ -1,11 +1,11 @@ -package com.tangem.domain.card.repository +package com.tangem.domain.wallets.derivations import com.tangem.common.extensions.ByteArrayKey import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.domain.card.BackendId import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.wallets.usecase.BackendId import com.tangem.operations.derivation.ExtendedPublicKeysMap interface DerivationsRepository { diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/HotMapDerivationsRepository.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/HotMapDerivationsRepository.kt new file mode 100644 index 0000000000..65364e9a80 --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/derivations/HotMapDerivationsRepository.kt @@ -0,0 +1,35 @@ +package com.tangem.domain.wallets.derivations + +import com.tangem.common.extensions.ByteArrayKey +import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.network.Network +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.wallets.usecase.BackendId +import com.tangem.operations.derivation.ExtendedPublicKeysMap + +interface HotMapDerivationsRepository { + + @Throws + suspend fun derivePublicKeys(userWallet: UserWallet.Hot, currencies: List): UserWallet.Hot + + suspend fun derivePublicKeysByNetworkIds( + userWallet: UserWallet.Hot, + networkIds: List, + ): UserWallet.Hot + + @Throws + suspend fun derivePublicKeysByNetworks(userWallet: UserWallet.Hot, networks: List): UserWallet.Hot + + @Throws + suspend fun derivePublicKeys( + userWallet: UserWallet.Hot, + derivations: Map>, + ): Pair> + + /** Check if user [userWallet] has missed derivations using map of [Network.ID] with extraDerivationPath */ + suspend fun hasMissedDerivations( + userWallet: UserWallet.Hot, + networksWithDerivationPath: Map, + ): Boolean +} \ No newline at end of file diff --git a/features/hot-wallet/api/src/main/kotlin/com/tangem/features/hotwallet/HotWalletPasswordRequester.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/hot/HotWalletPasswordRequester.kt similarity index 91% rename from features/hot-wallet/api/src/main/kotlin/com/tangem/features/hotwallet/HotWalletPasswordRequester.kt rename to domain/wallets/src/main/java/com/tangem/domain/wallets/hot/HotWalletPasswordRequester.kt index eac354ece4..6f8391e887 100644 --- a/features/hot-wallet/api/src/main/kotlin/com/tangem/features/hotwallet/HotWalletPasswordRequester.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/hot/HotWalletPasswordRequester.kt @@ -1,4 +1,4 @@ -package com.tangem.features.hotwallet +package com.tangem.domain.wallets.hot import com.tangem.hot.sdk.model.HotAuth diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/HotDerivationsRepository.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/HotDerivationsRepository.kt deleted file mode 100644 index 5616695e03..0000000000 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/HotDerivationsRepository.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tangem.domain.wallets.repository - -import com.tangem.domain.models.network.Network - -interface HotDerivationsRepository { - - fun getAllSupportedNetworks(): Set -} \ No newline at end of file diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/DerivePublicKeysUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/DerivePublicKeysUseCase.kt similarity index 74% rename from domain/card/src/main/kotlin/com/tangem/domain/card/DerivePublicKeysUseCase.kt rename to domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/DerivePublicKeysUseCase.kt index 86ed75fe6d..164d43a5cf 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/DerivePublicKeysUseCase.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/DerivePublicKeysUseCase.kt @@ -1,16 +1,17 @@ -package com.tangem.domain.card + +package com.tangem.domain.wallets.usecase import arrow.core.Either -import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.wallets.derivations.DerivationsRepository class DerivePublicKeysUseCase( private val derivationsRepository: DerivationsRepository, ) { suspend operator fun invoke(userWalletId: UserWalletId, currencies: List): Either { - return Either.catch { + return Either.Companion.catch { derivationsRepository.derivePublicKeys(userWalletId, currencies) } } diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/GetExtendedPublicKeyForCurrencyUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetExtendedPublicKeyForCurrencyUseCase.kt similarity index 98% rename from domain/card/src/main/kotlin/com/tangem/domain/card/GetExtendedPublicKeyForCurrencyUseCase.kt rename to domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetExtendedPublicKeyForCurrencyUseCase.kt index ebbd38f355..e548eef8b7 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/GetExtendedPublicKeyForCurrencyUseCase.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetExtendedPublicKeyForCurrencyUseCase.kt @@ -1,4 +1,4 @@ -package com.tangem.domain.card +package com.tangem.domain.wallets.usecase import arrow.core.Either import arrow.core.right @@ -10,10 +10,10 @@ import com.tangem.common.extensions.calculateSha256 import com.tangem.crypto.NetworkType import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey -import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.domain.wallets.derivations.DerivationsRepository import com.tangem.operations.derivation.ExtendedPublicKeysMap /** diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/HasMissedDerivationsUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/HasMissedDerivationsUseCase.kt similarity index 75% rename from domain/card/src/main/kotlin/com/tangem/domain/card/HasMissedDerivationsUseCase.kt rename to domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/HasMissedDerivationsUseCase.kt index 69aa1442c2..c9b008133b 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/HasMissedDerivationsUseCase.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/HasMissedDerivationsUseCase.kt @@ -1,22 +1,20 @@ -package com.tangem.domain.card +package com.tangem.domain.wallets.usecase -import com.tangem.domain.card.repository.DerivationsRepository -import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.wallets.derivations.DerivationsRepository + +typealias BackendId = String /** * Use case to check if user has missed derivations * [REDACTED_AUTHOR] */ - -typealias BackendId = String - class HasMissedDerivationsUseCase( private val derivationsRepository: DerivationsRepository, ) { - /** Check if user [userWalletId] has missed derivations using map of [Network.ID] with extraDerivationPath */ + /** Check if user [userWalletId] has missed derivations using map of [com.tangem.domain.models.network.Network.ID] with extraDerivationPath */ suspend operator fun invoke( userWalletId: UserWalletId, networksWithDerivationPath: Map, diff --git a/features/hot-wallet/api/build.gradle.kts b/features/hot-wallet/api/build.gradle.kts index 310329cc67..a51cd40254 100644 --- a/features/hot-wallet/api/build.gradle.kts +++ b/features/hot-wallet/api/build.gradle.kts @@ -12,6 +12,7 @@ dependencies { /* Project - Domain */ implementation(projects.domain.models) + implementation(projects.domain.wallets) implementation(projects.domain.wallets.models) /* Project - Core */ diff --git a/features/hot-wallet/api/src/main/kotlin/com/tangem/features/hotwallet/HotAccessCodeRequestComponent.kt b/features/hot-wallet/api/src/main/kotlin/com/tangem/features/hotwallet/HotAccessCodeRequestComponent.kt index 6e6c9c7c8b..49b893e3a6 100644 --- a/features/hot-wallet/api/src/main/kotlin/com/tangem/features/hotwallet/HotAccessCodeRequestComponent.kt +++ b/features/hot-wallet/api/src/main/kotlin/com/tangem/features/hotwallet/HotAccessCodeRequestComponent.kt @@ -2,6 +2,7 @@ package com.tangem.features.hotwallet import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.wallets.hot.HotWalletPasswordRequester interface HotAccessCodeRequestComponent : ComposableContentComponent, HotWalletPasswordRequester { diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/DefaultHotAccessCodeRequestComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/DefaultHotAccessCodeRequestComponent.kt index beaba4f29a..f23af23895 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/DefaultHotAccessCodeRequestComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/DefaultHotAccessCodeRequestComponent.kt @@ -1,14 +1,13 @@ package com.tangem.features.hotwallet.accesscoderequest -import androidx.compose.foundation.focusable import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.components.FullScreen +import com.tangem.domain.wallets.hot.HotWalletPasswordRequester import com.tangem.features.hotwallet.HotAccessCodeRequestComponent -import com.tangem.features.hotwallet.HotWalletPasswordRequester import com.tangem.features.hotwallet.accesscoderequest.ui.HotAccessCodeRequestFullScreenContent import dagger.assisted.Assisted import dagger.assisted.AssistedFactory diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/HotAccessCodeRequestModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/HotAccessCodeRequestModel.kt index 28fb18f009..c34ec66391 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/HotAccessCodeRequestModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/HotAccessCodeRequestModel.kt @@ -2,7 +2,7 @@ package com.tangem.features.hotwallet.accesscoderequest import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model -import com.tangem.features.hotwallet.HotWalletPasswordRequester +import com.tangem.domain.wallets.hot.HotWalletPasswordRequester import com.tangem.features.hotwallet.accesscoderequest.entity.HotAccessCodeRequestUM import com.tangem.hot.sdk.model.HotAuth import com.tangem.utils.coroutines.CoroutineDispatcherProvider diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/di/ComponentModuleBinds.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/di/ComponentModuleBinds.kt index b1fb298d59..70bb695f83 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/di/ComponentModuleBinds.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/di/ComponentModuleBinds.kt @@ -1,8 +1,8 @@ package com.tangem.features.hotwallet.accesscoderequest.di import com.tangem.core.decompose.model.Model +import com.tangem.domain.wallets.hot.HotWalletPasswordRequester import com.tangem.features.hotwallet.HotAccessCodeRequestComponent -import com.tangem.features.hotwallet.HotWalletPasswordRequester import com.tangem.features.hotwallet.accesscoderequest.DefaultHotAccessCodeRequestComponent import com.tangem.features.hotwallet.accesscoderequest.HotAccessCodeRequestModel import com.tangem.features.hotwallet.accesscoderequest.proxy.HotWalletPasswordRequesterProxy diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/proxy/HotWalletPasswordRequesterProxy.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/proxy/HotWalletPasswordRequesterProxy.kt index 29a16c9c2f..5208d91418 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/proxy/HotWalletPasswordRequesterProxy.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/proxy/HotWalletPasswordRequesterProxy.kt @@ -1,6 +1,6 @@ package com.tangem.features.hotwallet.accesscoderequest.proxy -import com.tangem.features.hotwallet.HotWalletPasswordRequester +import com.tangem.domain.wallets.hot.HotWalletPasswordRequester import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first diff --git a/features/manage-tokens/impl/build.gradle.kts b/features/manage-tokens/impl/build.gradle.kts index 3fcac8e481..e28cec1763 100644 --- a/features/manage-tokens/impl/build.gradle.kts +++ b/features/manage-tokens/impl/build.gradle.kts @@ -30,6 +30,7 @@ dependencies { implementation(projects.domain.manageTokens) implementation(projects.domain.tokens) implementation(projects.domain.tokens.models) + implementation(projects.domain.wallets) implementation(projects.domain.wallets.models) implementation(projects.domain.swap.models) diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/CustomTokenFormModel.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/CustomTokenFormModel.kt index fdcf5a9d1d..08d5f86574 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/CustomTokenFormModel.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/CustomTokenFormModel.kt @@ -9,12 +9,12 @@ import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.message.DialogMessage -import com.tangem.domain.card.DerivePublicKeysUseCase -import com.tangem.domain.card.HasMissedDerivationsUseCase import com.tangem.domain.managetokens.model.exceptoin.CustomTokenFormValidationException import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase +import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase +import com.tangem.domain.wallets.usecase.HasMissedDerivationsUseCase import com.tangem.features.managetokens.analytics.CustomTokenAnalyticsEvent import com.tangem.features.managetokens.component.CustomTokenFormComponent import com.tangem.features.managetokens.entity.customtoken.ClickableFieldUM diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt index ff768a5e76..489033da8d 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt @@ -17,9 +17,9 @@ import com.tangem.core.ui.event.triggeredEvent import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.message.SnackbarMessage -import com.tangem.domain.card.HasMissedDerivationsUseCase import com.tangem.domain.managetokens.SaveManagedTokensUseCase import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.wallets.usecase.HasMissedDerivationsUseCase import com.tangem.features.managetokens.analytics.CustomTokenAnalyticsEvent import com.tangem.features.managetokens.analytics.ManageTokensAnalyticEvent import com.tangem.features.managetokens.component.ManageTokensComponent diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/OnboardingManageTokensModel.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/OnboardingManageTokensModel.kt index d6b6ca0899..30f64dad46 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/OnboardingManageTokensModel.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/OnboardingManageTokensModel.kt @@ -13,10 +13,10 @@ import com.tangem.core.ui.event.triggeredEvent import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.message.SnackbarMessage -import com.tangem.domain.card.HasMissedDerivationsUseCase import com.tangem.domain.managetokens.SaveManagedTokensUseCase import com.tangem.domain.redux.OnboardingManageTokensAction import com.tangem.domain.redux.ReduxStateHolder +import com.tangem.domain.wallets.usecase.HasMissedDerivationsUseCase import com.tangem.features.managetokens.analytics.ManageTokensAnalyticEvent import com.tangem.features.managetokens.component.ManageTokensSource import com.tangem.features.managetokens.component.OnboardingManageTokensComponent diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt index deb96eacdb..e17c925595 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt @@ -15,7 +15,7 @@ import com.tangem.core.ui.message.DialogMessage import com.tangem.core.ui.message.SnackbarMessage import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.card.HasMissedDerivationsUseCase +import com.tangem.domain.wallets.usecase.HasMissedDerivationsUseCase import com.tangem.domain.managetokens.CheckCurrencyUnsupportedUseCase import com.tangem.domain.managetokens.model.CurrencyUnsupportedState import com.tangem.domain.markets.SaveMarketTokensUseCase diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/model/OnrampAddToPortfolioModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/model/OnrampAddToPortfolioModel.kt index 3f47acea4b..63b2225169 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/model/OnrampAddToPortfolioModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/model/OnrampAddToPortfolioModel.kt @@ -4,7 +4,7 @@ import arrow.core.getOrElse import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer -import com.tangem.domain.card.DerivePublicKeysUseCase +import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.wallets.usecase.GetUserWalletUseCase diff --git a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralInteractorImpl.kt b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralInteractorImpl.kt index 083ea5d82b..9520d1847e 100644 --- a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralInteractorImpl.kt +++ b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralInteractorImpl.kt @@ -2,7 +2,7 @@ package com.tangem.feature.referral.domain import arrow.core.getOrElse import com.tangem.common.core.TangemSdkError -import com.tangem.domain.card.DerivePublicKeysUseCase +import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.wallets.usecase.GetUserWalletUseCase diff --git a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/di/ReferralDomainModule.kt b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/di/ReferralDomainModule.kt index 4e5166e070..bb4729aac8 100644 --- a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/di/ReferralDomainModule.kt +++ b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/di/ReferralDomainModule.kt @@ -2,7 +2,7 @@ package com.tangem.feature.referral.domain.di import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelComponent -import com.tangem.domain.card.DerivePublicKeysUseCase +import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.feature.referral.domain.ReferralInteractor diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt index cb0d326470..33f4a326c7 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt @@ -33,7 +33,6 @@ import com.tangem.core.ui.message.SnackbarMessage import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase -import com.tangem.domain.card.GetExtendedPublicKeyForCurrencyUseCase import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.models.currency.CryptoCurrency @@ -69,6 +68,7 @@ import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase +import com.tangem.domain.wallets.usecase.GetExtendedPublicKeyForCurrencyUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase import com.tangem.feature.tokendetails.deeplink.TokenDetailsDeepLinkActionListener diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt index ab80da33c7..b6c6a7f60b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt @@ -9,7 +9,7 @@ import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.ui.extensions.resourceReference -import com.tangem.domain.card.DerivePublicKeysUseCase +import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase import com.tangem.domain.card.SetCardWasScannedUseCase import com.tangem.domain.feedback.GetCardInfoUseCase import com.tangem.domain.feedback.SendFeedbackEmailUseCase From 154308b973efe71720f467888ca88252c6035449 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 30 Jul 2025 12:42:28 +0300 Subject: [PATCH 09/53] Updated on 2026-08-14 --- .../androidTest/kotlin/com/tangem/tests/DetailsTest.kt | 2 +- .../kotlin/com/tangem/tests/OrganizeTokensTest.kt | 8 ++++---- .../androidTest/kotlin/com/tangem/tests/StoriesTest.kt | 3 +-- app/src/main/assets/tangem-app-config | 2 +- tangem-android-tools | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/src/androidTest/kotlin/com/tangem/tests/DetailsTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/DetailsTest.kt index 1242a1285e..805c4bfe72 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/DetailsTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/DetailsTest.kt @@ -61,7 +61,7 @@ class DetailsTest : BaseTestCase() { } } - @Test + // @Test fun wallet2DetailsTest() = setupHooks().run { scenario(OpenMainScreenScenario(composeTestRule, ProductType.Wallet2)) diff --git a/app/src/androidTest/kotlin/com/tangem/tests/OrganizeTokensTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/OrganizeTokensTest.kt index 3856e30249..e64113e05e 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/OrganizeTokensTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/OrganizeTokensTest.kt @@ -184,8 +184,8 @@ class OrganizeTokensTest : BaseTestCase() { } step("Check positions of tokens by balance on 'Organize tokens' screen") { onOrganizeTokensScreen { - tokenWithTitleAndPosition(ethereumTitle, 1).assertIsDisplayed() - tokenWithTitleAndPosition(polygonTitle, 2).assertIsDisplayed() + tokenWithTitleAndPosition(polygonTitle, 1).assertIsDisplayed() + tokenWithTitleAndPosition(ethereumTitle, 2).assertIsDisplayed() tokenWithTitleAndPosition(bitcoinTitle, 3).assertIsDisplayed() } } @@ -194,8 +194,8 @@ class OrganizeTokensTest : BaseTestCase() { } step("Check positions of tokens by balance on 'Organize tokens' screen") { onMainScreen { - tokenWithTitleAndPosition(ethereumTitle, 0).assertIsDisplayed() - tokenWithTitleAndPosition(polygonTitle, 1).assertIsDisplayed() + tokenWithTitleAndPosition(polygonTitle, 0).assertIsDisplayed() + tokenWithTitleAndPosition(ethereumTitle, 1).assertIsDisplayed() tokenWithTitleAndPosition(bitcoinTitle, 2).assertIsDisplayed() } } diff --git a/app/src/androidTest/kotlin/com/tangem/tests/StoriesTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/StoriesTest.kt index c17f7fc08d..303ce3f6a0 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/StoriesTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/StoriesTest.kt @@ -8,12 +8,11 @@ import com.tangem.screens.onStoriesScreen import com.tangem.tap.features.home.redux.HomeMiddleware.NEW_BUY_WALLET_URL import dagger.hilt.android.testing.HiltAndroidTest import io.github.kakaocup.kakao.intent.KIntent -import org.junit.Test @HiltAndroidTest class StoriesTest : BaseTestCase() { - @Test + // @Test fun clickOnOrderButtonTest() = setupHooks().run { onDisclaimerScreen { diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index a4cff08e57..3ac868e93f 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit a4cff08e572fdd4e51d75a5e1a3deef14cfb1f0b +Subproject commit 3ac868e93f88498258867d457f8b8c4577b40f98 diff --git a/tangem-android-tools b/tangem-android-tools index 35539d4e63..936cd7232c 160000 --- a/tangem-android-tools +++ b/tangem-android-tools @@ -1 +1 @@ -Subproject commit 35539d4e63d1f3ec4ec4f625f7875684ed5033c4 +Subproject commit 936cd7232c2d316c91e2901034417b0b38a63dd1 From e51453c99b1105bbdc39a2d7e2f472407f1c2ae6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 30 Jul 2025 12:50:32 +0300 Subject: [PATCH 10/53] Updated on 2026-08-14 --- .../domain/token/MockCryptoCurrencyFactory.kt | 5 + core/res/src/main/res/values/strings.xml | 9 ++ .../components/inputrow/InputRowRecipient.kt | 51 ++++++- .../data/common/network/NetworkFactory.kt | 8 ++ .../multi/DefaultMultiYieldBalanceFetcher.kt | 4 +- .../tangem/domain/models/network/Network.kt | 14 ++ .../tangem/domain/tokens/mock/MockNetworks.kt | 3 + .../PreviewCustomTokenSelectorComponent.kt | 1 + .../preview/PreviewManageTokensComponent.kt | 1 + .../PreviewOnboardingManageTokensComponent.kt | 1 + .../SendDestinationInitialStateTransformer.kt | 6 +- .../destination/ui/DestinationBlock.kt | 135 +++++++++--------- .../destination/ui/SendDestinationContent.kt | 1 + .../ui/preview/SwapAmountContentPreview.kt | 1 + .../ExpressStatusBottomSheetStateProvider.kt | 1 + 15 files changed, 173 insertions(+), 68 deletions(-) diff --git a/common/test/src/main/java/com/tangem/common/test/domain/token/MockCryptoCurrencyFactory.kt b/common/test/src/main/java/com/tangem/common/test/domain/token/MockCryptoCurrencyFactory.kt index ee2579d3ca..c56e3718ef 100644 --- a/common/test/src/main/java/com/tangem/common/test/domain/token/MockCryptoCurrencyFactory.kt +++ b/common/test/src/main/java/com/tangem/common/test/domain/token/MockCryptoCurrencyFactory.kt @@ -65,6 +65,10 @@ class MockCryptoCurrencyFactory(private val userWallet: UserWallet.Cold = defaul hasFiatFeeRate = blockchain.feePaidCurrency() !is FeePaidCurrency.FeeResource, canHandleTokens = false, transactionExtrasType = Network.TransactionExtrasType.NONE, + nameResolvingType = when (blockchain) { + Blockchain.Ethereum, Blockchain.EthereumTestnet -> Network.NameResolvingType.ENS + else -> Network.NameResolvingType.NONE + }, ) return factory.createCoin(network = network) @@ -95,6 +99,7 @@ class MockCryptoCurrencyFactory(private val userWallet: UserWallet.Cold = defaul hasFiatFeeRate = true, canHandleTokens = true, transactionExtrasType = Network.TransactionExtrasType.NONE, + nameResolvingType = Network.NameResolvingType.NONE, ), name = "NEVER-MIND", symbol = "NEVER-MIND", diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 2ee799d6f3..8623c22b55 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -64,6 +64,10 @@ Add Wallet Select a wallet to log in Welcome back! + + %d card + %d cards + Phone Wallet You successfully backed up your wallet. These words can’t be recovered if lost. Make sure to keep it somewhere secure. @@ -866,6 +870,7 @@ %1$s, %2$s Destination Tag Enter address + Enter ENS name or address Address is the same as wallet address Minimum amount is %s Minimum change is %s @@ -947,11 +952,15 @@ Invalid amount Fee exceeds balance Total amount exceeds balance + Are you sure you want to change the token? After changing, previous data will be reset. + Changing token Swap and send Send any token, and we’ll convert it on the way. Your recipient gets exactly what they need—seamlessly. Recipient will receive Will be sent a recipient Amount to receive + Are you sure you want to cancel the conversion? After changing, previous data will be reset. + Confirm cancelation Send with swap Transaction sent Prepare to scan card or ring you want to set up. diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowRecipient.kt b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowRecipient.kt index b48f8fd849..418341d870 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowRecipient.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowRecipient.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment.Companion.CenterEnd @@ -19,6 +20,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.dp import com.tangem.core.ui.R import com.tangem.core.ui.components.buttons.small.TangemIconButton import com.tangem.core.ui.components.fields.SimpleTextField @@ -69,6 +71,7 @@ fun InputRowRecipient( showDivider: Boolean = false, isLoading: Boolean = false, isValuePasted: Boolean = false, + resolvedAddress: String? = null, ) { val (titleText, color) = if (isError && error != null) { error to TangemTheme.colors.text.warning @@ -144,11 +147,15 @@ fun InputRowRecipient( } else { TangemTheme.colors.text.primary2 }, - modifier = Modifier - .padding(start = TangemTheme.dimens.spacing8), + modifier = Modifier.padding(start = TangemTheme.dimens.spacing8), ) } } + + ResolvedAddressRow( + isLoading = isLoading, + resolvedAddress = resolvedAddress, + ) } } } @@ -203,6 +210,38 @@ private fun RowScope.InputIcon(isLoading: Boolean, value: String) { } } +@Composable +private fun ResolvedAddressRow(isLoading: Boolean, resolvedAddress: String?) { + AnimatedContent( + targetState = if (resolvedAddress.isNullOrBlank() || isLoading) { + ResolvedState.Hide + } else { + ResolvedState.Show(resolvedAddress) + }, + label = "Resolved Address", + ) { state -> + if (state is ResolvedState.Show) { + Column { + HorizontalDivider( + thickness = 0.5.dp, + modifier = Modifier.padding(top = 12.dp, bottom = 12.dp), + color = TangemTheme.colors.stroke.primary, + ) + Text( + text = state.address, + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) + } + } + } +} + +private sealed interface ResolvedState { + data object Hide : ResolvedState + data class Show(val address: String) : ResolvedState +} + //region preview @Preview @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @@ -224,6 +263,7 @@ private fun InputRowRecipientPreview( onQrCodeClick = {}, modifier = Modifier.background(TangemTheme.colors.background.primary), isRedesignEnabled = false, + resolvedAddress = value.resolvedAddress, ) } } @@ -232,6 +272,7 @@ private data class InputRowRecipientPreviewData( val value: String, val isError: Boolean, val isLoading: Boolean = false, + val resolvedAddress: String? = null, ) private class InputRowRecipientPreviewDataProvider : PreviewParameterProvider { @@ -250,6 +291,12 @@ private class InputRowRecipientPreviewDataProvider : PreviewParameterProvider Network.NameResolvingType.ENS + else -> Network.NameResolvingType.NONE + } + } + @VisibleForTesting(otherwise = VisibleForTesting.NONE) fun createNetworkStandardType(blockchain: Blockchain) = getNetworkStandardType(blockchain) diff --git a/data/staking/src/main/java/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcher.kt b/data/staking/src/main/java/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcher.kt index 2e21a957da..28f27f5139 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcher.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcher.kt @@ -149,7 +149,9 @@ internal class DefaultMultiYieldBalanceFetcher @Inject constructor( // TODO: in the future, consider optimizing this part .chunked(size = 15) // StakeKitApi limitation: no more than 15 requests at the same time .map { - async(dispatchers.io) { stakeKitApi.getMultipleYieldBalances(it).bind() } + async(dispatchers.io) { + stakeKitApi.getMultipleYieldBalances(it).bind() + } } .awaitAll() .flatten() diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/network/Network.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/network/Network.kt index 95d2ea6ad0..a955b53e54 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/network/Network.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/network/Network.kt @@ -20,6 +20,7 @@ import kotlinx.serialization.Serializable * currency (for those blockchains that have FeeResource instead of a standard type of fee) * @property canHandleTokens indicates whether the network can handle tokens * @property transactionExtrasType the type of extras supported for sending a transaction + * @property nameResolvingType the type of on-chain name resolution supported by the network (e.g., ENS, SNS etc) */ @Serializable data class Network( @@ -33,6 +34,7 @@ data class Network( val hasFiatFeeRate: Boolean, val canHandleTokens: Boolean, val transactionExtrasType: TransactionExtrasType, + val nameResolvingType: NameResolvingType, ) { /** Raw ID */ @@ -161,4 +163,16 @@ data class Network( -> true } } + + /** + * Represents the type of on-chain name resolution supported by the network. + */ + enum class NameResolvingType { + + /** No name resolution supported */ + NONE, + + /** Ethereum Name Service (ENS) */ + ENS, + } } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworks.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworks.kt index a717323abe..8f3529b210 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworks.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworks.kt @@ -24,6 +24,7 @@ internal object MockNetworks { hasFiatFeeRate = true, canHandleTokens = true, transactionExtrasType = Network.TransactionExtrasType.NONE, + nameResolvingType = Network.NameResolvingType.NONE, ) val network2 = Network( @@ -37,6 +38,7 @@ internal object MockNetworks { hasFiatFeeRate = true, canHandleTokens = true, transactionExtrasType = Network.TransactionExtrasType.NONE, + nameResolvingType = Network.NameResolvingType.NONE, ) val network3 = Network( @@ -50,6 +52,7 @@ internal object MockNetworks { hasFiatFeeRate = true, canHandleTokens = true, transactionExtrasType = Network.TransactionExtrasType.NONE, + nameResolvingType = Network.NameResolvingType.NONE, ) val verifiedNetworksStatuses: NonEmptySet diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenSelectorComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenSelectorComponent.kt index 80da5da487..eca3ff5cc5 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenSelectorComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewCustomTokenSelectorComponent.kt @@ -65,6 +65,7 @@ internal class PreviewCustomTokenSelectorComponent( hasFiatFeeRate = false, canHandleTokens = false, transactionExtrasType = Network.TransactionExtrasType.NONE, + nameResolvingType = Network.NameResolvingType.NONE, ), name = "Network $index", type = "N$index", diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewManageTokensComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewManageTokensComponent.kt index 55ac0bba8f..33f07fbef6 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewManageTokensComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewManageTokensComponent.kt @@ -163,6 +163,7 @@ internal class PreviewManageTokensComponent( hasFiatFeeRate = false, canHandleTokens = false, transactionExtrasType = Network.TransactionExtrasType.NONE, + nameResolvingType = Network.NameResolvingType.NONE, ), name = "NETWORK$networkIndex", type = "N$networkIndex", diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewOnboardingManageTokensComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewOnboardingManageTokensComponent.kt index bc1276188d..28da1876e7 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewOnboardingManageTokensComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewOnboardingManageTokensComponent.kt @@ -104,6 +104,7 @@ internal class PreviewOnboardingManageTokensComponent( hasFiatFeeRate = false, canHandleTokens = false, transactionExtrasType = Network.TransactionExtrasType.NONE, + nameResolvingType = Network.NameResolvingType.NONE, ), name = "NETWORK$networkIndex", type = "N$networkIndex", diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/transformers/SendDestinationInitialStateTransformer.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/transformers/SendDestinationInitialStateTransformer.kt index e2cbe95c33..39cb1fd77b 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/transformers/SendDestinationInitialStateTransformer.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/transformers/SendDestinationInitialStateTransformer.kt @@ -22,6 +22,10 @@ internal class SendDestinationInitialStateTransformer( Network.TransactionExtrasType.MEMO -> R.string.send_extras_hint_memo Network.TransactionExtrasType.DESTINATION_TAG -> R.string.send_destination_tag_field } + val placeholder = when (cryptoCurrency.network.nameResolvingType) { + Network.NameResolvingType.NONE -> resourceReference(R.string.send_enter_address_field) + Network.NameResolvingType.ENS -> resourceReference(R.string.send_enter_address_field_ens) + } return DestinationUM.Content( isPrimaryButtonEnabled = false, isInitialized = isInitialized, @@ -32,7 +36,7 @@ internal class SendDestinationInitialStateTransformer( keyboardType = KeyboardType.Text, ), error = null, - placeholder = resourceReference(R.string.send_enter_address_field), + placeholder = placeholder, label = resourceReference(R.string.send_recipient), isValuePasted = false, ), diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/ui/DestinationBlock.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/ui/DestinationBlock.kt index bb84ed1e1e..e1c1b5da9f 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/ui/DestinationBlock.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/ui/DestinationBlock.kt @@ -76,11 +76,21 @@ private fun AddressBlock(address: DestinationTextFieldUM.RecipientAddress) { .clip(RoundedCornerShape(TangemTheme.dimens.radius18)) .background(TangemTheme.colors.background.tertiary), ) - Text( - text = address.value, - style = TangemTheme.typography.body2, - color = TangemTheme.colors.text.primary1, - ) + Column(modifier = Modifier.weight(1f)) { + Text( + text = address.value, + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.primary1, + ) + val blockchainAddress = address.blockchainAddress + if (!blockchainAddress.isNullOrBlank()) { + Text( + text = blockchainAddress, + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) + } + } } } @@ -120,12 +130,21 @@ private fun AddressWithMemoBlock( horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing24), modifier = Modifier.padding(top = TangemTheme.dimens.spacing8), ) { - Text( - modifier = Modifier.weight(1f), - text = address.value, - style = TangemTheme.typography.body2, - color = TangemTheme.colors.text.primary1, - ) + Column(modifier = Modifier.weight(1f)) { + Text( + text = address.value, + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.primary1, + ) + val blockchainAddress = address.blockchainAddress + if (!blockchainAddress.isNullOrBlank()) { + Text( + text = blockchainAddress, + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) + } + } IdentIcon( address = address.value, modifier = Modifier @@ -134,6 +153,7 @@ private fun AddressWithMemoBlock( .background(TangemTheme.colors.background.tertiary), ) } + if (memo != null && memo.value.isNotBlank()) { Text( text = stringResourceSafe(R.string.send_memo, memo.value), @@ -162,64 +182,51 @@ private fun DestinationBlockPreview( } private class DestinationBlockPreviewProvider : PreviewParameterProvider { + val previewItem = DestinationUM.Content( + isPrimaryButtonEnabled = true, + addressTextField = DestinationTextFieldUM.RecipientAddress( + value = "0x34B4492A412D84A6E606288f3Bd714b89135D4dE", + keyboardOptions = KeyboardOptions.Default, + placeholder = TextReference.Str("Enter address"), + label = TextReference.Str("Recipient Address"), + isError = false, + error = null, + isValuePasted = false, + ), + memoTextField = DestinationTextFieldUM.RecipientMemo( + value = "Test memo for transaction", + keyboardOptions = KeyboardOptions.Default, + placeholder = TextReference.Str("Enter memo (optional)"), + label = TextReference.Str("Memo"), + isError = false, + error = null, + disabledText = TextReference.Str("Memo disabled"), + isEnabled = true, + isValuePasted = false, + ), + recent = emptyList().toImmutableList(), + wallets = emptyList().toImmutableList(), + networkName = "Ethereum", + isValidating = false, + isInitialized = true, + isRedesignEnabled = false, + ) + override val values: Sequence get() = sequenceOf( - DestinationUM.Content( - isPrimaryButtonEnabled = true, - addressTextField = DestinationTextFieldUM.RecipientAddress( - value = "0x34B4492A412D84A6E606288f3Bd714b89135D4dE", - keyboardOptions = KeyboardOptions.Default, - placeholder = TextReference.Str("Enter address"), - label = TextReference.Str("Recipient Address"), - isError = false, - error = null, - isValuePasted = false, + previewItem, + previewItem.copy( + addressTextField = previewItem.addressTextField.copy( + value = "vitalik.eth", + blockchainAddress = "0x34B4492A412D84A6E606288f3Bd714b89135D4dE", ), - memoTextField = DestinationTextFieldUM.RecipientMemo( - value = "Test memo for transaction", - keyboardOptions = KeyboardOptions.Default, - placeholder = TextReference.Str("Enter memo (optional)"), - label = TextReference.Str("Memo"), - isError = false, - error = null, - disabledText = TextReference.Str("Memo disabled"), - isEnabled = true, - isValuePasted = false, - ), - recent = emptyList().toImmutableList(), - wallets = emptyList().toImmutableList(), - networkName = "Ethereum", - isValidating = false, - isInitialized = true, - isRedesignEnabled = false, ), - DestinationUM.Content( - isPrimaryButtonEnabled = true, - addressTextField = DestinationTextFieldUM.RecipientAddress( - value = "0x34B4492A412D84A6E606288f3Bd714b89135D4dE", - keyboardOptions = KeyboardOptions.Default, - placeholder = TextReference.Str("Enter address"), - label = TextReference.Str("Recipient Address"), - isError = false, - error = null, - isValuePasted = false, + previewItem.copy(isRedesignEnabled = true), + previewItem.copy( + addressTextField = previewItem.addressTextField.copy( + value = "vitalik.eth", + blockchainAddress = "0x34B4492A412D84A6E606288f3Bd714b89135D4dE", ), - memoTextField = DestinationTextFieldUM.RecipientMemo( - value = "Test memo for transaction", - keyboardOptions = KeyboardOptions.Default, - placeholder = TextReference.Str("Enter memo (optional)"), - label = TextReference.Str("Memo"), - isError = false, - error = null, - disabledText = TextReference.Str("Memo disabled"), - isEnabled = true, - isValuePasted = false, - ), - recent = emptyList().toImmutableList(), - wallets = emptyList().toImmutableList(), - networkName = "Ethereum", - isValidating = false, - isInitialized = true, isRedesignEnabled = true, ), ) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/ui/SendDestinationContent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/ui/SendDestinationContent.kt index 31966f7310..da247fa248 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/ui/SendDestinationContent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/ui/SendDestinationContent.kt @@ -142,6 +142,7 @@ private fun LazyListScope.addressItem( color = TangemTheme.colors.background.action, shape = TangemTheme.shapes.roundedCornersXMedium, ), + resolvedAddress = address.blockchainAddress, ) } } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt index b40ecc6c80..d7fd561378 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt @@ -39,6 +39,7 @@ internal data object SwapAmountContentPreview { hasFiatFeeRate = false, canHandleTokens = false, transactionExtrasType = Network.TransactionExtrasType.NONE, + nameResolvingType = Network.NameResolvingType.NONE, ), name = "Bitcoin", diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusBottomSheetStateProvider.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusBottomSheetStateProvider.kt index 8e123b32df..fc4a214ee0 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusBottomSheetStateProvider.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/express/ExpressStatusBottomSheetStateProvider.kt @@ -107,5 +107,6 @@ class ExpressStatusBottomSheetStateProvider : PreviewParameterProvider Date: Sun, 27 Jul 2025 20:47:11 +0500 Subject: [PATCH 11/53] Updated on 2026-08-14 --- .../hotwallet/accesscode/Constants.kt | 3 + .../confirm/ConfirmAccessCodeComponent.kt | 52 ++++++++++++ .../confirm/ConfirmAccessCodeModel.kt | 45 ++++++++++ .../confirm/entity/ConfirmAccessCodeUM.kt | 12 +++ .../confirm/ui/ConfirmAccessCodeContent.kt | 46 ++++++++++ .../di/AccessCodeModule.kt} | 12 ++- .../set}/SetAccessCodeComponent.kt | 24 ++++-- .../accesscode/set/SetAccessCodeModel.kt | 45 ++++++++++ .../accesscode/set/entity/SetAccessCodeUM.kt | 12 +++ .../accesscode/set/ui/SetAccessCodeContent.kt | 46 ++++++++++ .../ui/AccessCodeEnter.kt} | 55 ++++-------- .../{root => entry}/AddExistingWalletModel.kt | 36 ++++---- .../DefaultAddExistingWalletComponent.kt | 0 .../di/AddExistingWalletModule.kt | 0 .../entity/AddExistingWalletUM.kt | 0 .../routing/AddExistingWalletChildFactory.kt | 18 +++- .../routing/AddExistingWalletRoute.kt | 5 +- .../ui/AddExistingWalletContent.kt | 0 .../setaccesscode/SetAccessCodeModel.kt | 84 ------------------- .../setaccesscode/entity/SetAccessCodeUM.kt | 22 ----- .../setaccesscode/ui/SetAccessCodeContent.kt | 59 ------------- 21 files changed, 343 insertions(+), 233 deletions(-) create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/Constants.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ConfirmAccessCodeComponent.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ConfirmAccessCodeModel.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/entity/ConfirmAccessCodeUM.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ui/ConfirmAccessCodeContent.kt rename features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/{setaccesscode/di/SetAccessCodeModule.kt => accesscode/di/AccessCodeModule.kt} (52%) rename features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/{setaccesscode => accesscode/set}/SetAccessCodeComponent.kt (65%) create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/SetAccessCodeModel.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/entity/SetAccessCodeUM.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/ui/SetAccessCodeContent.kt rename features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/{setaccesscode/ui/SetAccessCodeEnter.kt => accesscode/ui/AccessCodeEnter.kt} (66%) rename features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/{root => entry}/AddExistingWalletModel.kt (75%) rename features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/{root => entry}/DefaultAddExistingWalletComponent.kt (100%) rename features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/{root => entry}/di/AddExistingWalletModule.kt (100%) rename features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/{root => entry}/entity/AddExistingWalletUM.kt (100%) rename features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/{root => entry}/routing/AddExistingWalletChildFactory.kt (74%) rename features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/{root => entry}/routing/AddExistingWalletRoute.kt (79%) rename features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/{root => entry}/ui/AddExistingWalletContent.kt (100%) delete mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/SetAccessCodeModel.kt delete mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/entity/SetAccessCodeUM.kt delete mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/SetAccessCodeContent.kt diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/Constants.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/Constants.kt new file mode 100644 index 0000000000..ca5ecaa367 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/Constants.kt @@ -0,0 +1,3 @@ +package com.tangem.features.hotwallet.accesscode + +const val ACCESS_CODE_LENGTH = 6 \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ConfirmAccessCodeComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ConfirmAccessCodeComponent.kt new file mode 100644 index 0000000000..849eb2cb57 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ConfirmAccessCodeComponent.kt @@ -0,0 +1,52 @@ +package com.tangem.features.hotwallet.accesscode.confirm + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect +import com.tangem.features.hotwallet.accesscode.confirm.ui.SetAccessCodeConfirmContent +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +internal class ConfirmAccessCodeComponent @AssistedInject constructor( + @Assisted private val context: AppComponentContext, + @Assisted private val params: Params, +) : ComposableContentComponent, AppComponentContext by context { + + private val model: ConfirmAccessCodeModel = getOrCreateModel(params) + + @Composable + override fun Content(modifier: Modifier) { + val state by model.uiState.collectAsStateWithLifecycle() + + DisableScreenshotsDisposableEffect() + + SetAccessCodeConfirmContent( + accessCode = state.accessCode, + onAccessCodeChange = state.onAccessCodeChange, + accessCodeLength = state.accessCodeLength, + onConfirm = state.onConfirm, + buttonEnabled = state.buttonEnabled, + modifier = modifier, + ) + } + + interface ModelCallbacks { + fun onAccessCodeConfirmed() + } + + data class Params( + val accessCodeToConfirm: String, + val callbacks: ModelCallbacks, + ) + + @AssistedFactory + interface Factory { + fun create(context: AppComponentContext, params: Params): ConfirmAccessCodeComponent + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ConfirmAccessCodeModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ConfirmAccessCodeModel.kt new file mode 100644 index 0000000000..25a77bdbdc --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ConfirmAccessCodeModel.kt @@ -0,0 +1,45 @@ +package com.tangem.features.hotwallet.accesscode.confirm + +import androidx.compose.runtime.Stable +import com.tangem.core.decompose.di.ModelScoped +import com.tangem.core.decompose.model.Model +import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.features.hotwallet.accesscode.confirm.entity.ConfirmAccessCodeUM +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update +import javax.inject.Inject + +@Stable +@ModelScoped +internal class ConfirmAccessCodeModel @Inject constructor( + paramsContainer: ParamsContainer, + override val dispatchers: CoroutineDispatcherProvider, +) : Model() { + + private val params = paramsContainer.require() + + internal val uiState: StateFlow + field = MutableStateFlow(getInitialState()) + + private fun getInitialState() = ConfirmAccessCodeUM( + accessCode = "", + onAccessCodeChange = ::onAccessCodeChange, + buttonEnabled = false, + onConfirm = ::onConfirm, + ) + + private fun onAccessCodeChange(value: String) { + uiState.update { + it.copy( + accessCode = value, + buttonEnabled = value == params.accessCodeToConfirm, + ) + } + } + + private fun onConfirm() { + params.callbacks.onAccessCodeConfirmed() + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/entity/ConfirmAccessCodeUM.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/entity/ConfirmAccessCodeUM.kt new file mode 100644 index 0000000000..f2df925578 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/entity/ConfirmAccessCodeUM.kt @@ -0,0 +1,12 @@ +package com.tangem.features.hotwallet.accesscode.confirm.entity + +import com.tangem.features.hotwallet.accesscode.ACCESS_CODE_LENGTH + +internal data class ConfirmAccessCodeUM( + val accessCode: String, + val onAccessCodeChange: (String) -> Unit, + val buttonEnabled: Boolean, + val onConfirm: () -> Unit, +) { + val accessCodeLength: Int = ACCESS_CODE_LENGTH +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ui/ConfirmAccessCodeContent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ui/ConfirmAccessCodeContent.kt new file mode 100644 index 0000000000..d85e2ea0d1 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ui/ConfirmAccessCodeContent.kt @@ -0,0 +1,46 @@ +package com.tangem.features.hotwallet.accesscode.confirm.ui + +import androidx.compose.foundation.layout.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.components.PrimaryButton +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.features.hotwallet.accesscode.ui.AccessCodeEnter +import com.tangem.core.res.R + +@Composable +internal fun SetAccessCodeConfirmContent( + accessCode: String, + onAccessCodeChange: (String) -> Unit, + accessCodeLength: Int, + onConfirm: () -> Unit, + buttonEnabled: Boolean, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxSize() + .navigationBarsPadding(), + ) { + AccessCodeEnter( + modifier = Modifier + .padding(top = 16.dp) + .weight(1f), + accessCode = accessCode, + onAccessCodeChange = onAccessCodeChange, + accessCodeLength = accessCodeLength, + reEnterAccessCodeState = true, + ) + + PrimaryButton( + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 16.dp, bottom = 16.dp) + .imePadding(), + text = stringResourceSafe(R.string.common_confirm), + onClick = onConfirm, + enabled = buttonEnabled, + ) + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/di/SetAccessCodeModule.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/di/AccessCodeModule.kt similarity index 52% rename from features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/di/SetAccessCodeModule.kt rename to features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/di/AccessCodeModule.kt index fccaaac528..8c79d221da 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/di/SetAccessCodeModule.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/di/AccessCodeModule.kt @@ -1,7 +1,8 @@ -package com.tangem.features.hotwallet.setaccesscode.di +package com.tangem.features.hotwallet.accesscode.di import com.tangem.core.decompose.model.Model -import com.tangem.features.hotwallet.setaccesscode.SetAccessCodeModel +import com.tangem.features.hotwallet.accesscode.confirm.ConfirmAccessCodeModel +import com.tangem.features.hotwallet.accesscode.set.SetAccessCodeModel import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -11,10 +12,15 @@ import dagger.multibindings.IntoMap @Module @InstallIn(SingletonComponent::class) -internal interface SetAccessCodeModule { +internal interface AccessCodeModule { @Binds @IntoMap @ClassKey(SetAccessCodeModel::class) fun bindSetAccessCodeModel(model: SetAccessCodeModel): Model + + @Binds + @IntoMap + @ClassKey(ConfirmAccessCodeModel::class) + fun bindConfirmAccessCodeModel(model: ConfirmAccessCodeModel): Model } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/SetAccessCodeComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/SetAccessCodeComponent.kt similarity index 65% rename from features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/SetAccessCodeComponent.kt rename to features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/SetAccessCodeComponent.kt index b1f774aa93..c8a03346c0 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/SetAccessCodeComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/SetAccessCodeComponent.kt @@ -1,4 +1,4 @@ -package com.tangem.features.hotwallet.setaccesscode +package com.tangem.features.hotwallet.accesscode.set import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -8,8 +8,9 @@ import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect -import com.tangem.features.hotwallet.setaccesscode.ui.SetAccessCodeContent +import com.tangem.features.hotwallet.accesscode.set.ui.SetAccessCodeContent import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject internal class SetAccessCodeComponent @AssistedInject constructor( @@ -23,21 +24,28 @@ internal class SetAccessCodeComponent @AssistedInject constructor( override fun Content(modifier: Modifier) { val state by model.uiState.collectAsStateWithLifecycle() + DisableScreenshotsDisposableEffect() + SetAccessCodeContent( - state = state, - onBack = { model.onBack() }, + accessCode = state.accessCode, + onAccessCodeChange = state.onAccessCodeChange, + accessCodeLength = state.accessCodeLength, + onContinue = state.onContinue, + buttonEnabled = state.buttonEnabled, modifier = modifier, ) - - DisableScreenshotsDisposableEffect() } interface ModelCallbacks { - fun onBackClick() - fun onAccessCodeSet() + fun onAccessCodeSet(accessCode: String) } data class Params( val callbacks: ModelCallbacks, ) + + @AssistedFactory + interface Factory { + fun create(context: AppComponentContext, params: Params): SetAccessCodeComponent + } } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/SetAccessCodeModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/SetAccessCodeModel.kt new file mode 100644 index 0000000000..4f57d50886 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/SetAccessCodeModel.kt @@ -0,0 +1,45 @@ +package com.tangem.features.hotwallet.accesscode.set + +import androidx.compose.runtime.Stable +import com.tangem.core.decompose.di.ModelScoped +import com.tangem.core.decompose.model.Model +import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.features.hotwallet.accesscode.set.entity.SetAccessCodeUM +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update +import javax.inject.Inject + +@Stable +@ModelScoped +internal class SetAccessCodeModel @Inject constructor( + paramsContainer: ParamsContainer, + override val dispatchers: CoroutineDispatcherProvider, +) : Model() { + + private val params = paramsContainer.require() + + internal val uiState: StateFlow + field = MutableStateFlow(getInitialState()) + + private fun getInitialState() = SetAccessCodeUM( + accessCode = "", + onAccessCodeChange = ::onAccessCodeChange, + buttonEnabled = false, + onContinue = ::onContinue, + ) + + private fun onAccessCodeChange(value: String) { + uiState.update { + it.copy( + accessCode = value, + buttonEnabled = value.length == uiState.value.accessCodeLength, + ) + } + } + + private fun onContinue() { + params.callbacks.onAccessCodeSet(uiState.value.accessCode) + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/entity/SetAccessCodeUM.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/entity/SetAccessCodeUM.kt new file mode 100644 index 0000000000..68fa89c2fe --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/entity/SetAccessCodeUM.kt @@ -0,0 +1,12 @@ +package com.tangem.features.hotwallet.accesscode.set.entity + +import com.tangem.features.hotwallet.accesscode.ACCESS_CODE_LENGTH + +internal data class SetAccessCodeUM( + val accessCode: String, + val onAccessCodeChange: (String) -> Unit, + val buttonEnabled: Boolean, + val onContinue: () -> Unit, +) { + val accessCodeLength: Int = ACCESS_CODE_LENGTH +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/ui/SetAccessCodeContent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/ui/SetAccessCodeContent.kt new file mode 100644 index 0000000000..0fcf53c2df --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/ui/SetAccessCodeContent.kt @@ -0,0 +1,46 @@ +package com.tangem.features.hotwallet.accesscode.set.ui + +import androidx.compose.foundation.layout.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.components.PrimaryButton +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.features.hotwallet.accesscode.ui.AccessCodeEnter +import com.tangem.core.res.R + +@Composable +internal fun SetAccessCodeContent( + accessCode: String, + onAccessCodeChange: (String) -> Unit, + accessCodeLength: Int, + onContinue: () -> Unit, + buttonEnabled: Boolean, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxSize() + .navigationBarsPadding(), + ) { + AccessCodeEnter( + modifier = Modifier + .padding(top = 16.dp) + .weight(1f), + accessCode = accessCode, + onAccessCodeChange = onAccessCodeChange, + accessCodeLength = accessCodeLength, + reEnterAccessCodeState = false, + ) + + PrimaryButton( + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 16.dp, bottom = 16.dp) + .imePadding(), + text = stringResourceSafe(R.string.common_continue), + onClick = onContinue, + enabled = buttonEnabled, + ) + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/SetAccessCodeEnter.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/ui/AccessCodeEnter.kt similarity index 66% rename from features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/SetAccessCodeEnter.kt rename to features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/ui/AccessCodeEnter.kt index bd787c1822..ba80fd0bb6 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/SetAccessCodeEnter.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/ui/AccessCodeEnter.kt @@ -1,4 +1,4 @@ -package com.tangem.features.hotwallet.setaccesscode.ui +package com.tangem.features.hotwallet.accesscode.ui import android.content.res.Configuration import androidx.compose.foundation.background @@ -15,11 +15,12 @@ import com.tangem.core.ui.components.fields.PinTextField import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview -import com.tangem.features.hotwallet.setaccesscode.entity.SetAccessCodeUM @Composable -internal fun SetAccessCodeEnter( - state: SetAccessCodeUM, +internal fun AccessCodeEnter( + accessCode: String, + onAccessCodeChange: (String) -> Unit, + accessCodeLength: Int, reEnterAccessCodeState: Boolean, modifier: Modifier = Modifier, ) { @@ -52,7 +53,7 @@ internal fun SetAccessCodeEnter( } else { stringResourceSafe( R.string.access_code_create_description, - state.accessCodeLength, + accessCodeLength, ) }, style = TangemTheme.typography.body1, @@ -67,18 +68,10 @@ internal fun SetAccessCodeEnter( contentAlignment = Alignment.Center, ) { PinTextField( - length = state.accessCodeLength, + length = accessCodeLength, isPasswordVisual = true, - value = if (reEnterAccessCodeState) { - state.accessCodeSecond - } else { - state.accessCodeFirst - }, - onValueChange = if (reEnterAccessCodeState) { - state.onAccessCodeSecondChange - } else { - state.onAccessCodeFirstChange - }, + value = accessCode, + onValueChange = onAccessCodeChange, ) } } @@ -89,37 +82,25 @@ internal fun SetAccessCodeEnter( @Composable private fun Preview() { TangemThemePreview { - SetAccessCodeEnter( + AccessCodeEnter( + accessCode = "123456", + onAccessCodeChange = {}, + accessCodeLength = 6, reEnterAccessCodeState = false, - state = SetAccessCodeUM( - step = SetAccessCodeUM.Step.AccessCode, - accessCodeFirst = "", - accessCodeSecond = "", - onAccessCodeFirstChange = {}, - onAccessCodeSecondChange = {}, - buttonEnabled = false, - onContinue = {}, - ), ) } } -@Composable @Preview(showBackground = true, widthDp = 360) @Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable private fun Preview2() { TangemThemePreview { - SetAccessCodeEnter( + AccessCodeEnter( + accessCode = "123456", + onAccessCodeChange = {}, + accessCodeLength = 6, reEnterAccessCodeState = true, - state = SetAccessCodeUM( - step = SetAccessCodeUM.Step.ConfirmAccessCode, - accessCodeFirst = "", - accessCodeSecond = "", - onAccessCodeFirstChange = {}, - onAccessCodeSecondChange = {}, - buttonEnabled = false, - onContinue = {}, - ), ) } } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/AddExistingWalletModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt similarity index 75% rename from features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/AddExistingWalletModel.kt rename to features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt index 92b04ffe28..c021c4b032 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/AddExistingWalletModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt @@ -9,11 +9,12 @@ import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.navigation.Router import com.tangem.domain.settings.ShouldAskPermissionUseCase -import com.tangem.features.hotwallet.setaccesscode.SetAccessCodeComponent -import com.tangem.features.hotwallet.addexistingwallet.start.AddExistingWalletStartComponent import com.tangem.features.hotwallet.addexistingwallet.im.port.AddExistingWalletImportComponent import com.tangem.features.hotwallet.addexistingwallet.root.routing.AddExistingWalletRoute +import com.tangem.features.hotwallet.addexistingwallet.start.AddExistingWalletStartComponent import com.tangem.features.hotwallet.manualbackup.completed.ManualBackupCompletedComponent +import com.tangem.features.hotwallet.accesscode.confirm.ConfirmAccessCodeComponent +import com.tangem.features.hotwallet.accesscode.set.SetAccessCodeComponent import com.tangem.features.hotwallet.setupfinished.MobileWalletSetupFinishedComponent import com.tangem.features.pushnotifications.api.PushNotificationsComponent import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION @@ -31,20 +32,22 @@ internal class AddExistingWalletModel @Inject constructor( val addExistingWalletStartModelCallbacks = AddExistingWalletStartModelCallbacks() val addExistingWalletImportModelCallbacks = AddExistingWalletImportModelCallbacks() val manualBackupCompletedComponentModelCallbacks = ManualBackupCompletedComponentModelCallbacks() - val accessCodeModelCallbacks = AccessCodeModelCallbacks() val pushNotificationsComponentModelCallbacks = PushNotificationsComponentModelCallbacks() + val setAccessCodeModelCallbacks = SetAccessCodeModelCallbacks() + val confirmAccessCodeModelCallbacks = ConfirmAccessCodeModelCallbacks() val mobileWalletSetupFinishedComponentModelCallbacks = MobileWalletSetupFinishedComponentModelCallbacks() val stackNavigation = StackNavigation() fun onChildBack(currentRoute: AddExistingWalletRoute) { when (currentRoute) { - AddExistingWalletRoute.Import -> stackNavigation.pop() - AddExistingWalletRoute.BackupCompleted -> Unit - AddExistingWalletRoute.AccessCode -> stackNavigation.pop() - AddExistingWalletRoute.PushNotifications -> Unit - AddExistingWalletRoute.SetupFinished -> Unit - AddExistingWalletRoute.Start -> Unit + is AddExistingWalletRoute.Import -> stackNavigation.pop() + is AddExistingWalletRoute.BackupCompleted -> Unit + is AddExistingWalletRoute.ConfirmAccessCode -> stackNavigation.pop() + is AddExistingWalletRoute.SetAccessCode -> stackNavigation.pop() + is AddExistingWalletRoute.PushNotifications -> Unit + is AddExistingWalletRoute.SetupFinished -> Unit + is AddExistingWalletRoute.Start -> Unit } } @@ -66,16 +69,18 @@ internal class AddExistingWalletModel @Inject constructor( inner class ManualBackupCompletedComponentModelCallbacks : ManualBackupCompletedComponent.ModelCallbacks { override fun onContinueClick() { - stackNavigation.push(AddExistingWalletRoute.AccessCode) + stackNavigation.push(AddExistingWalletRoute.SetAccessCode) } } - inner class AccessCodeModelCallbacks : SetAccessCodeComponent.ModelCallbacks { - override fun onBackClick() { - stackNavigation.pop() + inner class SetAccessCodeModelCallbacks : SetAccessCodeComponent.ModelCallbacks { + override fun onAccessCodeSet(accessCode: String) { + stackNavigation.push(AddExistingWalletRoute.ConfirmAccessCode(accessCode)) } + } - override fun onAccessCodeSet() { + inner class ConfirmAccessCodeModelCallbacks : ConfirmAccessCodeComponent.ModelCallbacks { + override fun onAccessCodeConfirmed() { modelScope.launch { val shouldRequestPush = shouldAskPermissionUseCase(PUSH_PERMISSION) if (shouldRequestPush) { @@ -95,7 +100,8 @@ internal class AddExistingWalletModel @Inject constructor( } } - inner class MobileWalletSetupFinishedComponentModelCallbacks : MobileWalletSetupFinishedComponent.ModelCallbacks { + inner class MobileWalletSetupFinishedComponentModelCallbacks : + MobileWalletSetupFinishedComponent.ModelCallbacks { override fun onContinueClick() { router.replaceAll(AppRoute.Wallet) } diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/DefaultAddExistingWalletComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/DefaultAddExistingWalletComponent.kt similarity index 100% rename from features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/DefaultAddExistingWalletComponent.kt rename to features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/DefaultAddExistingWalletComponent.kt diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/di/AddExistingWalletModule.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/di/AddExistingWalletModule.kt similarity index 100% rename from features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/di/AddExistingWalletModule.kt rename to features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/di/AddExistingWalletModule.kt diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/entity/AddExistingWalletUM.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/entity/AddExistingWalletUM.kt similarity index 100% rename from features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/entity/AddExistingWalletUM.kt rename to features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/entity/AddExistingWalletUM.kt diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/routing/AddExistingWalletChildFactory.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt similarity index 74% rename from features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/routing/AddExistingWalletChildFactory.kt rename to features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt index e4926281e5..38642f07bf 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/routing/AddExistingWalletChildFactory.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt @@ -2,7 +2,8 @@ package com.tangem.features.hotwallet.addexistingwallet.root.routing import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.ui.decompose.ComposableContentComponent -import com.tangem.features.hotwallet.setaccesscode.SetAccessCodeComponent +import com.tangem.features.hotwallet.accesscode.set.SetAccessCodeComponent +import com.tangem.features.hotwallet.accesscode.confirm.ConfirmAccessCodeComponent import com.tangem.features.hotwallet.addexistingwallet.root.AddExistingWalletModel import com.tangem.features.hotwallet.addexistingwallet.start.AddExistingWalletStartComponent import com.tangem.features.hotwallet.addexistingwallet.im.port.AddExistingWalletImportComponent @@ -13,6 +14,8 @@ import javax.inject.Inject internal class AddExistingWalletChildFactory @Inject constructor( private val pushNotificationsComponent: PushNotificationsComponent.Factory, + private val setAccessCodeSetComponentFactory: SetAccessCodeComponent.Factory, + private val confirmAccessCodeComponentFactory: ConfirmAccessCodeComponent.Factory, ) { fun createChild( @@ -39,10 +42,17 @@ internal class AddExistingWalletChildFactory @Inject constructor( callbacks = model.manualBackupCompletedComponentModelCallbacks, ), ) - is AddExistingWalletRoute.AccessCode -> SetAccessCodeComponent( + is AddExistingWalletRoute.SetAccessCode -> setAccessCodeSetComponentFactory.create( context = childContext, params = SetAccessCodeComponent.Params( - callbacks = model.accessCodeModelCallbacks, + callbacks = model.setAccessCodeModelCallbacks, + ), + ) + is AddExistingWalletRoute.ConfirmAccessCode -> confirmAccessCodeComponentFactory.create( + context = childContext, + params = ConfirmAccessCodeComponent.Params( + accessCodeToConfirm = route.accessCode, + callbacks = model.confirmAccessCodeModelCallbacks, ), ) is AddExistingWalletRoute.PushNotifications -> pushNotificationsComponent.create( @@ -51,7 +61,7 @@ internal class AddExistingWalletChildFactory @Inject constructor( callbacks = model.pushNotificationsComponentModelCallbacks, ), ) - AddExistingWalletRoute.SetupFinished -> MobileWalletSetupFinishedComponent( + is AddExistingWalletRoute.SetupFinished -> MobileWalletSetupFinishedComponent( context = childContext, params = MobileWalletSetupFinishedComponent.Params( callbacks = model.mobileWalletSetupFinishedComponentModelCallbacks, diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/routing/AddExistingWalletRoute.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletRoute.kt similarity index 79% rename from features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/routing/AddExistingWalletRoute.kt rename to features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletRoute.kt index c08f900605..f4f83e4193 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/routing/AddExistingWalletRoute.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletRoute.kt @@ -15,7 +15,10 @@ internal sealed class AddExistingWalletRoute : Route { object BackupCompleted : AddExistingWalletRoute() @Serializable - object AccessCode : AddExistingWalletRoute() + object SetAccessCode : AddExistingWalletRoute() + + @Serializable + data class ConfirmAccessCode(val accessCode: String) : AddExistingWalletRoute() @Serializable object PushNotifications : AddExistingWalletRoute() diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/ui/AddExistingWalletContent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/ui/AddExistingWalletContent.kt similarity index 100% rename from features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/ui/AddExistingWalletContent.kt rename to features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/ui/AddExistingWalletContent.kt diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/SetAccessCodeModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/SetAccessCodeModel.kt deleted file mode 100644 index 945ff9ad20..0000000000 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/SetAccessCodeModel.kt +++ /dev/null @@ -1,84 +0,0 @@ -package com.tangem.features.hotwallet.setaccesscode - -import androidx.compose.runtime.Stable -import com.tangem.core.decompose.di.ModelScoped -import com.tangem.core.decompose.model.Model -import com.tangem.core.decompose.model.ParamsContainer -import com.tangem.features.hotwallet.setaccesscode.entity.SetAccessCodeUM -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.update -import javax.inject.Inject - -@Stable -@ModelScoped -internal class SetAccessCodeModel @Inject constructor( - paramsContainer: ParamsContainer, - override val dispatchers: CoroutineDispatcherProvider, -) : Model() { - - private val params = paramsContainer.require() - - internal val uiState: StateFlow - field = MutableStateFlow(getInitialState()) - - fun onBack() { - when (uiState.value.step) { - SetAccessCodeUM.Step.AccessCode -> { - params.callbacks.onBackClick() - } - SetAccessCodeUM.Step.ConfirmAccessCode -> { - uiState.update { - it.copy( - step = SetAccessCodeUM.Step.AccessCode, - accessCodeSecond = "", - ) - } - } - } - } - - private fun getInitialState() = SetAccessCodeUM( - step = SetAccessCodeUM.Step.AccessCode, - accessCodeFirst = "", - accessCodeSecond = "", - onAccessCodeFirstChange = ::onAccessCodeFirstChange, - onAccessCodeSecondChange = ::onAccessCodeSecondChange, - buttonEnabled = false, - onContinue = ::onContinue, - ) - - private fun onAccessCodeFirstChange(value: String) { - uiState.update { - it.copy( - accessCodeFirst = value, - buttonEnabled = value.length == uiState.value.accessCodeLength, - ) - } - } - - private fun onAccessCodeSecondChange(value: String) { - uiState.update { - it.copy( - accessCodeSecond = value, - buttonEnabled = uiState.value.accessCodeFirst == uiState.value.accessCodeSecond, - ) - } - } - - private fun onContinue() { - when (uiState.value.step) { - SetAccessCodeUM.Step.AccessCode -> { - uiState.update { - it.copy( - step = SetAccessCodeUM.Step.ConfirmAccessCode, - ) - } - } - SetAccessCodeUM.Step.ConfirmAccessCode -> { - params.callbacks.onAccessCodeSet() - } - } - } -} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/entity/SetAccessCodeUM.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/entity/SetAccessCodeUM.kt deleted file mode 100644 index a5e98ca830..0000000000 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/entity/SetAccessCodeUM.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.tangem.features.hotwallet.setaccesscode.entity - -internal data class SetAccessCodeUM( - val step: Step, - val accessCodeFirst: String, - val accessCodeSecond: String, - val onAccessCodeFirstChange: (String) -> Unit, - val onAccessCodeSecondChange: (String) -> Unit, - val buttonEnabled: Boolean, - val onContinue: () -> Unit, -) { - val accessCodeLength: Int = ACCESS_CODE_LENGTH - - enum class Step { - AccessCode, - ConfirmAccessCode, - } - - companion object { - private const val ACCESS_CODE_LENGTH = 6 - } -} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/SetAccessCodeContent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/SetAccessCodeContent.kt deleted file mode 100644 index 64449365ae..0000000000 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/SetAccessCodeContent.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.tangem.features.hotwallet.setaccesscode.ui - -import androidx.activity.compose.BackHandler -import androidx.compose.animation.AnimatedContent -import androidx.compose.foundation.layout.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.tangem.core.ui.components.PrimaryButton -import com.tangem.core.ui.extensions.stringResourceSafe -import com.tangem.core.ui.res.TangemAnimations -import com.tangem.features.hotwallet.setaccesscode.entity.SetAccessCodeUM -import com.tangem.features.hotwallet.setaccesscode.entity.SetAccessCodeUM.Step.* -import com.tangem.core.res.R - -@Composable -internal fun SetAccessCodeContent(state: SetAccessCodeUM, onBack: () -> Unit, modifier: Modifier = Modifier) { - BackHandler(onBack = onBack) - - Column( - modifier = modifier - .fillMaxSize() - .navigationBarsPadding(), - ) { - AnimatedContent( - modifier = Modifier.weight(1f), - targetState = state.step, - transitionSpec = TangemAnimations.AnimatedContent - .slide { initial, target -> target.ordinal > initial.ordinal }, - label = "AnimatedContent", - ) { step -> - when (step) { - AccessCode -> SetAccessCodeEnter( - modifier = Modifier.padding(top = 16.dp), - state = state, - reEnterAccessCodeState = false, - ) - ConfirmAccessCode -> SetAccessCodeEnter( - modifier = Modifier.padding(top = 16.dp), - state = state, - reEnterAccessCodeState = true, - ) - } - } - - PrimaryButton( - modifier = Modifier - .fillMaxWidth() - .padding(start = 16.dp, end = 16.dp, bottom = 16.dp) - .imePadding(), - text = if (state.step == ConfirmAccessCode) { - stringResourceSafe(R.string.common_confirm) - } else { - stringResourceSafe(R.string.common_continue) - }, - onClick = state.onContinue, - ) - } -} \ No newline at end of file From c89a95265d36b41c3299f19477bbec71e4da08d7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 27 Jul 2025 22:16:37 +0500 Subject: [PATCH 12/53] Updated on 2026-08-14 --- .../GiveTxPermisssionBottomSheet.kt | 4 +- .../components/appbar/AppBarWithBackButton.kt | 4 +- .../appbar/AppBarWithBackButtonAndIcon.kt | 8 ++-- .../ui/components/appbar/TangemTopAppBar.kt | 24 +++++----- .../ui/components/appbar/TopAppBarButton.kt | 44 ++++++++++++++----- .../appbar/models/TopAppBarButtonUM.kt | 30 ++++++++++--- .../sheet/TangemBottomSheetTitle.kt | 4 +- .../disclaimer/impl/ui/DisclaimerScreen.kt | 5 +-- .../preview/PreviewManageTokensComponent.kt | 4 +- .../managetokens/model/ManageTokensModel.kt | 4 +- .../features/nft/details/ui/NFTDetails.kt | 5 +-- .../features/nft/receive/ui/NFTReceive.kt | 5 +-- .../features/nft/traits/ui/NFTAssetTraits.kt | 5 +-- .../v2/stepper/impl/ui/OnboardingStepper.kt | 2 +- .../main/entity/OnrampMainComponentUM.kt | 9 ++-- .../main/entity/factory/OnrampStateFactory.kt | 11 ++++- .../redirect/model/OnrampRedirectModel.kt | 5 +-- .../ui/OnrampSuccessComponentContent.kt | 5 +-- .../presentation/QrScanningContent.kt | 12 ++--- .../ExcludedBlockchainsScreen.kt | 4 +- .../connections/model/WcConnectionsModel.kt | 5 +-- .../connections/ui/WcConnectionsContent.kt | 12 +++-- .../ui/preview/WcConnectionsPreviewData.kt | 5 +-- 23 files changed, 126 insertions(+), 90 deletions(-) diff --git a/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/permission/GiveTxPermisssionBottomSheet.kt b/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/permission/GiveTxPermisssionBottomSheet.kt index 9476ea0cad..4815b47788 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/permission/GiveTxPermisssionBottomSheet.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/permission/GiveTxPermisssionBottomSheet.kt @@ -43,9 +43,9 @@ fun GiveTxPermissionBottomSheet(config: TangemBottomSheetConfig) { config = config, containerColor = TangemTheme.colors.background.secondary, titleText = resourceReference(R.string.give_permission_title), - titleAction = TopAppBarButtonUM( + titleAction = TopAppBarButtonUM.Icon( iconRes = R.drawable.ic_information_24, - onIconClicked = { isPermissionAlertShow = true }, + onClicked = { isPermissionAlertShow = true }, ), content = { content: GiveTxPermissionBottomSheetConfig -> GiveTxPermissionBottomSheetContent(content = content) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButton.kt b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButton.kt index bd95803a6a..4c1b0f0e4f 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButton.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButton.kt @@ -32,9 +32,9 @@ fun AppBarWithBackButton( TangemTopAppBar( modifier = modifier, title = text, - startButton = TopAppBarButtonUM( + startButton = TopAppBarButtonUM.Icon( iconRes = iconRes ?: R.drawable.ic_back_24, - onIconClicked = onBackClick, + onClicked = onBackClick, ), containerColor = containerColor, ) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButtonAndIcon.kt b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButtonAndIcon.kt index aaa6fcf804..47544cffc6 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButtonAndIcon.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButtonAndIcon.kt @@ -27,14 +27,14 @@ fun AppBarWithBackButtonAndIcon( title = text, subtitle = subtitle, containerColor = backgroundColor, - startButton = TopAppBarButtonUM( + startButton = TopAppBarButtonUM.Icon( iconRes = backIconRes ?: R.drawable.ic_back_24, - onIconClicked = onBackClick, + onClicked = onBackClick, ), endButton = if (iconRes != null && onIconClick != null) { - TopAppBarButtonUM( + TopAppBarButtonUM.Icon( iconRes = iconRes, - onIconClicked = onIconClick, + onClicked = onIconClick, ) } else { null diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/TangemTopAppBar.kt b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/TangemTopAppBar.kt index 319beeb81f..c963651693 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/TangemTopAppBar.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/TangemTopAppBar.kt @@ -301,25 +301,25 @@ private class BasicTopAppBarPMPreviewProvider : PreviewParameterProvider { + IconButton( + enabled = button.enabled, + modifier = modifier.size(TangemTheme.dimens.size32), + onClick = button.onClicked, + ) { + Icon( + modifier = Modifier.size(TangemTheme.dimens.size24), + painter = painterResource(id = button.iconRes), + tint = tint, + contentDescription = null, + ) + } + } + is TopAppBarButtonUM.Text -> { + Text( + modifier = modifier + .conditional(button.enabled) { + clickable { button.onClicked() } + } + .padding(4.dp), + text = button.text.resolveReference(), + color = tint, + style = TangemTheme.typography.body1, + ) + } } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/models/TopAppBarButtonUM.kt b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/models/TopAppBarButtonUM.kt index 7104e25cf9..aa06b614a0 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/models/TopAppBarButtonUM.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/models/TopAppBarButtonUM.kt @@ -2,21 +2,39 @@ package com.tangem.core.ui.components.appbar.models import androidx.annotation.DrawableRes import com.tangem.core.ui.R +import com.tangem.core.ui.extensions.TextReference -data class TopAppBarButtonUM( - @DrawableRes val iconRes: Int, - val onIconClicked: () -> Unit, - val enabled: Boolean = true, +sealed class TopAppBarButtonUM( + open val onClicked: () -> Unit, + open val enabled: Boolean = true, ) { + data class Icon( + @DrawableRes val iconRes: Int, + override val onClicked: () -> Unit, + override val enabled: Boolean = true, + ) : TopAppBarButtonUM(onClicked, enabled) + + data class Text( + val text: TextReference, + override val onClicked: () -> Unit, + override val enabled: Boolean = true, + ) : TopAppBarButtonUM(onClicked, enabled) + @Suppress("FunctionName") companion object { fun Back(onBackClicked: () -> Unit) = Back(true, onBackClicked) - fun Back(enabled: Boolean = true, onBackClicked: () -> Unit) = TopAppBarButtonUM( + fun Back(enabled: Boolean = true, onBackClicked: () -> Unit) = Icon( iconRes = R.drawable.ic_back_24, - onIconClicked = onBackClicked, + onClicked = onBackClicked, + enabled = enabled, + ) + + fun Text(text: TextReference, onTextClicked: () -> Unit, enabled: Boolean = true) = Text( + text = text, + onClicked = onTextClicked, enabled = enabled, ) } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/sheet/TangemBottomSheetTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/sheet/TangemBottomSheetTitle.kt index 8ca60f6218..9003e830c0 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/sheet/TangemBottomSheetTitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/sheet/TangemBottomSheetTitle.kt @@ -57,9 +57,9 @@ private fun Preview_TangemBottomSheetTitle() { TangemThemePreview { TangemBottomSheetTitle( title = "Title", - endButton = TopAppBarButtonUM( + endButton = TopAppBarButtonUM.Icon( iconRes = R.drawable.ic_information_24, - onIconClicked = {}, + onClicked = {}, ), containerColor = TangemTheme.colors.background.secondary, ) diff --git a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/ui/DisclaimerScreen.kt b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/ui/DisclaimerScreen.kt index 72c38a4347..4a68dba57e 100644 --- a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/ui/DisclaimerScreen.kt +++ b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/ui/DisclaimerScreen.kt @@ -66,9 +66,8 @@ internal fun DisclaimerScreen(state: DisclaimerUM) { ) { TangemTopAppBar( title = resourceReference(R.string.disclaimer_title), - startButton = TopAppBarButtonUM( - iconRes = R.drawable.ic_back_24, - onIconClicked = state.popBack, + startButton = TopAppBarButtonUM.Back( + onBackClicked = state.popBack, ).takeIf { state.isTosAccepted }, titleAlignment = Alignment.CenterHorizontally, textColor = textColor, diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewManageTokensComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewManageTokensComponent.kt index 33f07fbef6..d7caaf672c 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewManageTokensComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewManageTokensComponent.kt @@ -42,9 +42,9 @@ internal class PreviewManageTokensComponent( ManageTokensTopBarUM.ManageContent( title = resourceReference(id = R.string.main_manage_tokens), onBackButtonClick = {}, - endButton = TopAppBarButtonUM( + endButton = TopAppBarButtonUM.Icon( iconRes = R.drawable.ic_plus_24, - onIconClicked = {}, + onClicked = {}, ), ) } else { diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt index 489033da8d..91e1814599 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt @@ -129,9 +129,9 @@ internal class ManageTokensModel @Inject constructor( topBar = ManageTokensTopBarUM.ManageContent( title = resourceReference(id = R.string.main_manage_tokens), onBackButtonClick = router::pop, - endButton = TopAppBarButtonUM( + endButton = TopAppBarButtonUM.Icon( iconRes = R.drawable.ic_plus_24, - onIconClicked = ::navigateToAddCustomToken, + onClicked = ::navigateToAddCustomToken, ), ), search = SearchBarUM( diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/ui/NFTDetails.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/ui/NFTDetails.kt index 2c3ad2ede5..45319ee1ba 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/ui/NFTDetails.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/ui/NFTDetails.kt @@ -28,9 +28,8 @@ internal fun NFTDetails(state: NFTDetailsUM, modifier: Modifier = Modifier) { ) { TangemTopAppBar( modifier = Modifier, - startButton = TopAppBarButtonUM( - iconRes = R.drawable.ic_back_24, - onIconClicked = state.onBackClick, + startButton = TopAppBarButtonUM.Back( + onBackClicked = state.onBackClick, ), title = state.nftAsset.name, ) diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceive.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceive.kt index 21c147c3a9..dd9cfc1aa7 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceive.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceive.kt @@ -28,9 +28,8 @@ internal fun NFTReceive(state: NFTReceiveUM, modifier: Modifier = Modifier) { ) { TangemTopAppBar( modifier = Modifier, - startButton = TopAppBarButtonUM( - iconRes = R.drawable.ic_close_24, - onIconClicked = state.onBackClick, + startButton = TopAppBarButtonUM.Back( + onBackClicked = state.onBackClick, ), title = stringResourceSafe(id = R.string.nft_receive_title), subtitle = state.appBarSubtitle.resolveReference(), diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/traits/ui/NFTAssetTraits.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/traits/ui/NFTAssetTraits.kt index 9f18df108f..f1ebd94f83 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/traits/ui/NFTAssetTraits.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/traits/ui/NFTAssetTraits.kt @@ -22,9 +22,8 @@ internal fun NFTAssetTraits(state: NFTAssetTraitsUM, modifier: Modifier = Modifi ) { TangemTopAppBar( modifier = Modifier, - startButton = TopAppBarButtonUM( - iconRes = R.drawable.ic_back_24, - onIconClicked = state.onBackClick, + startButton = TopAppBarButtonUM.Back( + onBackClicked = state.onBackClick, ), title = stringResourceSafe(R.string.nft_traits_title), ) diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/ui/OnboardingStepper.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/ui/OnboardingStepper.kt index 50136e9d10..e3e01fe44d 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/ui/OnboardingStepper.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/ui/OnboardingStepper.kt @@ -49,7 +49,7 @@ internal fun OnboardingStepper( ) { TangemTopAppBar( startButton = TopAppBarButtonUM.Back(onBackClick), - endButton = TopAppBarButtonUM(iconRes = R.drawable.ic_chat_24, onIconClicked = onSupportButtonClick) + endButton = TopAppBarButtonUM.Icon(iconRes = R.drawable.ic_chat_24, onClicked = onSupportButtonClick) .takeIf { state.steps != state.currentStep }, title = if (state.steps == state.currentStep) { resourceReference(R.string.common_done) diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/OnrampMainComponentUM.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/OnrampMainComponentUM.kt index 4015d007a8..ae9041847c 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/OnrampMainComponentUM.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/OnrampMainComponentUM.kt @@ -24,14 +24,13 @@ internal sealed interface OnrampMainComponentUM { ) : OnrampMainComponentUM { override val topBarConfig: OnrampMainTopBarUM = OnrampMainTopBarUM( title = combinedReference(resourceReference(R.string.common_buy), stringReference(" $currency")), - startButtonUM = TopAppBarButtonUM( - iconRes = R.drawable.ic_close_24, - onIconClicked = onClose, + startButtonUM = TopAppBarButtonUM.Back( + onBackClicked = onClose, enabled = true, ), - endButtonUM = TopAppBarButtonUM( + endButtonUM = TopAppBarButtonUM.Icon( iconRes = R.drawable.ic_more_vertical_24, - onIconClicked = openSettings, + onClicked = openSettings, enabled = false, ), ) diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/OnrampStateFactory.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/OnrampStateFactory.kt index 0fbbcbe0ca..8eb611e8fe 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/OnrampStateFactory.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/OnrampStateFactory.kt @@ -6,6 +6,7 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import com.tangem.common.ui.amountScreen.models.AmountFieldModel import com.tangem.common.ui.notifications.NotificationUM +import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.models.currency.CryptoCurrency @@ -37,7 +38,10 @@ internal class OnrampStateFactory( fun getReadyState(currency: OnrampCurrency): OnrampMainComponentUM.Content { val state = currentStateProvider() - val endButton = state.topBarConfig.endButtonUM.copy(enabled = true) + val endButton = when (val button = state.topBarConfig.endButtonUM) { + is TopAppBarButtonUM.Icon -> button.copy(enabled = true) + is TopAppBarButtonUM.Text -> button.copy(enabled = true) + } return OnrampMainComponentUM.Content( topBarConfig = state.topBarConfig.copy(endButtonUM = endButton), buyButtonConfig = state.buyButtonConfig, @@ -80,7 +84,10 @@ internal class OnrampStateFactory( fun getErrorState(errorCode: String? = null, onRefresh: () -> Unit): OnrampMainComponentUM { val state = currentStateProvider() - val endButton = state.topBarConfig.endButtonUM.copy(enabled = true) + val endButton = when (val button = state.topBarConfig.endButtonUM) { + is TopAppBarButtonUM.Icon -> button.copy(enabled = true) + is TopAppBarButtonUM.Text -> button.copy(enabled = true) + } return when (state) { is OnrampMainComponentUM.Content -> state.copy( diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/redirect/model/OnrampRedirectModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/redirect/model/OnrampRedirectModel.kt index ce08252b7b..d19b0fc95b 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/redirect/model/OnrampRedirectModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/redirect/model/OnrampRedirectModel.kt @@ -54,9 +54,8 @@ internal class OnrampRedirectModel @Inject constructor( resourceReference(R.string.common_buy), stringReference(" ${params.cryptoCurrency.name}"), ), - startButtonUM = TopAppBarButtonUM( - iconRes = R.drawable.ic_close_24, - onIconClicked = appRouter::pop, + startButtonUM = TopAppBarButtonUM.Back( + onBackClicked = appRouter::pop, enabled = true, ), ), diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/ui/OnrampSuccessComponentContent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/ui/OnrampSuccessComponentContent.kt index 39ea071d73..36a1e410e8 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/ui/OnrampSuccessComponentContent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/ui/OnrampSuccessComponentContent.kt @@ -61,9 +61,8 @@ private fun Content(state: OnrampSuccessComponentUM.Content, onBackClick: () -> .systemBarsPadding(), topBar = { TangemTopAppBar( - startButton = TopAppBarButtonUM( - iconRes = R.drawable.ic_close_24, - onIconClicked = onBackClick, + startButton = TopAppBarButtonUM.Back( + onBackClicked = onBackClick, ), ) }, diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningContent.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningContent.kt index b26bb96aa2..7e11df9de8 100644 --- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningContent.kt +++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningContent.kt @@ -66,9 +66,9 @@ internal fun QrScanningContent( TangemTopAppBar( modifier = Modifier.statusBarsPadding(), title = uiState.topBarConfig.title?.resolveReference(), - startButton = TopAppBarButtonUM( + startButton = TopAppBarButtonUM.Icon( iconRes = uiState.topBarConfig.startIcon, - onIconClicked = uiState.onBackClick, + onClicked = uiState.onBackClick, ), textColor = TangemTheme.colors.text.constantWhite, iconTint = TangemColorPalette.White, @@ -80,9 +80,9 @@ internal fun QrScanningContent( label = "Flash Change", ) { TopAppBarButton( - button = TopAppBarButtonUM( + button = TopAppBarButtonUM.Icon( iconRes = if (it) R.drawable.ic_flash_on_24 else R.drawable.ic_flash_off_24, - onIconClicked = { + onClicked = { isFlash = !isFlash }, ), @@ -91,9 +91,9 @@ internal fun QrScanningContent( } TopAppBarButton( - button = TopAppBarButtonUM( + button = TopAppBarButtonUM.Icon( iconRes = R.drawable.ic_gallery_24, - onIconClicked = uiState.onGalleryClick, + onClicked = uiState.onGalleryClick, ), tint = TangemColorPalette.White, ) diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/ExcludedBlockchainsScreen.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/ExcludedBlockchainsScreen.kt index 772c22afab..277ab7fb86 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/ExcludedBlockchainsScreen.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/excludedblockchains/ExcludedBlockchainsScreen.kt @@ -46,9 +46,9 @@ internal fun ExcludedBlockchainsScreen(state: ExcludedBlockchainsScreenUM, modif TangemTopAppBar( title = resourceReference(R.string.excluded_blockchains), startButton = TopAppBarButtonUM.Back(onBackClicked = state.popBack), - endButton = TopAppBarButtonUM( + endButton = TopAppBarButtonUM.Icon( iconRes = R.drawable.ic_refresh_24, - onIconClicked = state.onRecoverClick, + onClicked = state.onRecoverClick, ), ) }, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt index f39cba49ce..9efea80a5b 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt @@ -134,9 +134,8 @@ internal class WcConnectionsModel @Inject constructor( private fun getInitialState(): WcConnectionsState { return WcConnectionsState( topAppBarConfig = WcConnectionsTopAppBarConfig( - startButtonUM = TopAppBarButtonUM( - iconRes = R.drawable.ic_back_24, - onIconClicked = router::pop, + startButtonUM = TopAppBarButtonUM.Back( + onBackClicked = router::pop, enabled = true, ), disconnectAllItem = TangemDropdownMenuItem( diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcConnectionsContent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcConnectionsContent.kt index 0e9068267d..b0a695365f 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcConnectionsContent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcConnectionsContent.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.util.fastForEach import coil.compose.AsyncImage import com.tangem.core.ui.components.BottomFade import com.tangem.core.ui.components.PrimaryButton +import com.tangem.core.ui.components.appbar.TopAppBarButton import com.tangem.core.ui.components.dropdownmenu.TangemDropdownItem import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenu import com.tangem.core.ui.components.snackbar.TangemSnackbarHost @@ -251,13 +252,10 @@ private fun ConnectionsTopBar( actionIconContentColor = TangemTheme.colors.icon.primary1, ), navigationIcon = { - IconButton(onClick = config.startButtonUM.onIconClicked) { - Icon( - painter = painterResource(id = config.startButtonUM.iconRes), - tint = TangemTheme.colors.icon.primary1, - contentDescription = "Back", - ) - } + TopAppBarButton( + button = config.startButtonUM, + tint = TangemTheme.colors.icon.primary1, + ) }, title = { Text( diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/preview/WcConnectionsPreviewData.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/preview/WcConnectionsPreviewData.kt index 62530a542a..d2cbeb2a8c 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/preview/WcConnectionsPreviewData.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/preview/WcConnectionsPreviewData.kt @@ -118,9 +118,8 @@ internal object WcConnectionsPreviewData { ) val stateWithEmptyConnections = WcConnectionsState( topAppBarConfig = WcConnectionsTopAppBarConfig( - startButtonUM = TopAppBarButtonUM( - iconRes = R.drawable.ic_back_24, - onIconClicked = {}, + startButtonUM = TopAppBarButtonUM.Back( + onBackClicked = {}, enabled = true, ), disconnectAllItem = TangemDropdownMenuItem( From c761f40891d238608da19ca9e69e4a94c286326e Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 28 Jul 2025 13:57:47 +0500 Subject: [PATCH 13/53] Updated on 2026-08-14 --- .../entry/AddExistingWalletModel.kt | 37 +++--- .../AddExistingWalletStepperStateManager.kt | 79 +++++++++++++ .../DefaultAddExistingWalletComponent.kt | 43 ++++++- .../entry/di/AddExistingWalletModule.kt | 32 +++++- .../entry/entity/AddExistingWalletUM.kt | 2 +- .../routing/AddExistingWalletChildFactory.kt | 4 +- .../entry/routing/AddExistingWalletRoute.kt | 2 +- .../entry/ui/AddExistingWalletContent.kt | 25 +++-- .../stepper/api/HotWalletStepperComponent.kt | 33 ++++++ .../stepper/di/HotWalletStepperModule.kt | 18 +++ .../impl/DefaultHotWalletStepperComponent.kt | 48 ++++++++ .../stepper/impl/HotWalletStepperModel.kt | 42 +++++++ .../stepper/impl/ui/HotWalletStepper.kt | 106 ++++++++++++++++++ 13 files changed, 437 insertions(+), 34 deletions(-) create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletStepperStateManager.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/api/HotWalletStepperComponent.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/di/HotWalletStepperModule.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/DefaultHotWalletStepperComponent.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/HotWalletStepperModel.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/ui/HotWalletStepper.kt diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt index c021c4b032..ad499a7aa4 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt @@ -1,8 +1,9 @@ -package com.tangem.features.hotwallet.addexistingwallet.root +package com.tangem.features.hotwallet.addexistingwallet.entry import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.pop import com.arkivanov.decompose.router.stack.push +import com.arkivanov.decompose.router.stack.replaceAll import com.arkivanov.decompose.router.stack.replaceCurrent import com.tangem.common.routing.AppRoute import com.tangem.core.decompose.di.ModelScoped @@ -10,7 +11,7 @@ import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.navigation.Router import com.tangem.domain.settings.ShouldAskPermissionUseCase import com.tangem.features.hotwallet.addexistingwallet.im.port.AddExistingWalletImportComponent -import com.tangem.features.hotwallet.addexistingwallet.root.routing.AddExistingWalletRoute +import com.tangem.features.hotwallet.addexistingwallet.entry.routing.AddExistingWalletRoute import com.tangem.features.hotwallet.addexistingwallet.start.AddExistingWalletStartComponent import com.tangem.features.hotwallet.manualbackup.completed.ManualBackupCompletedComponent import com.tangem.features.hotwallet.accesscode.confirm.ConfirmAccessCodeComponent @@ -43,14 +44,31 @@ internal class AddExistingWalletModel @Inject constructor( when (currentRoute) { is AddExistingWalletRoute.Import -> stackNavigation.pop() is AddExistingWalletRoute.BackupCompleted -> Unit + is AddExistingWalletRoute.SetAccessCode -> Unit is AddExistingWalletRoute.ConfirmAccessCode -> stackNavigation.pop() - is AddExistingWalletRoute.SetAccessCode -> stackNavigation.pop() is AddExistingWalletRoute.PushNotifications -> Unit is AddExistingWalletRoute.SetupFinished -> Unit is AddExistingWalletRoute.Start -> Unit } } + fun onSkipAccessCode() { + navigateToPushNotificationsOrNext() + } + + private fun navigateToPushNotificationsOrNext() { + modelScope.launch { + val shouldRequestPush = shouldAskPermissionUseCase(PUSH_PERMISSION) + if (shouldRequestPush) { + // is yet blocked by [REDACTED_TASK_KEY] + // stackNavigation.replaceAll(AddExistingWalletRoute.PushNotifications) + stackNavigation.replaceAll(AddExistingWalletRoute.SetupFinished) + } else { + stackNavigation.replaceAll(AddExistingWalletRoute.SetupFinished) + } + } + } + inner class AddExistingWalletStartModelCallbacks : AddExistingWalletStartComponent.ModelCallbacks { override fun onBackClick() { router.pop() @@ -69,7 +87,7 @@ internal class AddExistingWalletModel @Inject constructor( inner class ManualBackupCompletedComponentModelCallbacks : ManualBackupCompletedComponent.ModelCallbacks { override fun onContinueClick() { - stackNavigation.push(AddExistingWalletRoute.SetAccessCode) + stackNavigation.replaceCurrent(AddExistingWalletRoute.SetAccessCode) } } @@ -81,16 +99,7 @@ internal class AddExistingWalletModel @Inject constructor( inner class ConfirmAccessCodeModelCallbacks : ConfirmAccessCodeComponent.ModelCallbacks { override fun onAccessCodeConfirmed() { - modelScope.launch { - val shouldRequestPush = shouldAskPermissionUseCase(PUSH_PERMISSION) - if (shouldRequestPush) { - // is yet blocked by [REDACTED_TASK_KEY] - // stackNavigation.replaceCurrent(AddExistingWalletRoute.PushNotifications) - stackNavigation.replaceCurrent(AddExistingWalletRoute.SetupFinished) - } else { - stackNavigation.replaceCurrent(AddExistingWalletRoute.SetupFinished) - } - } + navigateToPushNotificationsOrNext() } } diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletStepperStateManager.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletStepperStateManager.kt new file mode 100644 index 0000000000..14eabf9ebc --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletStepperStateManager.kt @@ -0,0 +1,79 @@ +package com.tangem.features.hotwallet.addexistingwallet.entry + +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.features.hotwallet.addexistingwallet.entry.routing.AddExistingWalletRoute +import com.tangem.features.hotwallet.impl.R +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent + +internal class AddExistingWalletStepperStateManager { + + fun getStepperState(route: AddExistingWalletRoute): HotWalletStepperComponent.StepperUM? { + return when (route) { + is AddExistingWalletRoute.Start -> null + + is AddExistingWalletRoute.Import -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_IMPORT, + steps = STEPS_COUNT, + title = resourceReference(R.string.wallet_import_seed_navtitle), + showBackButton = true, + showSkipButton = false, + showFeedbackButton = true, + ) + + is AddExistingWalletRoute.BackupCompleted -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_BACKUP, + steps = STEPS_COUNT, + title = resourceReference(R.string.common_backup), + showBackButton = false, + showSkipButton = false, + showFeedbackButton = false, + ) + + is AddExistingWalletRoute.SetAccessCode -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_ACCESS_CODE, + steps = STEPS_COUNT, + title = resourceReference(R.string.access_code_navtitle), + showBackButton = false, + showSkipButton = true, + showFeedbackButton = false, + ) + + is AddExistingWalletRoute.ConfirmAccessCode -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_ACCESS_CODE, + steps = STEPS_COUNT, + title = resourceReference(R.string.access_code_navtitle), + showBackButton = true, + showSkipButton = true, + showFeedbackButton = false, + ) + + is AddExistingWalletRoute.PushNotifications -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_NOTIFICATIONS, + steps = STEPS_COUNT, + title = resourceReference(R.string.onboarding_title_notifications), + showBackButton = false, + showSkipButton = false, + showFeedbackButton = false, + ) + + is AddExistingWalletRoute.SetupFinished -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_DONE, + steps = STEPS_COUNT, + title = resourceReference(R.string.common_done), + showBackButton = false, + showSkipButton = false, + showFeedbackButton = false, + ) + } + } + + companion object { + private const val STEPS_COUNT = 5 + + private const val STEP_IMPORT = 1 + private const val STEP_BACKUP = 2 + private const val STEP_ACCESS_CODE = 3 + private const val STEP_NOTIFICATIONS = 4 + private const val STEP_DONE = 5 + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/DefaultAddExistingWalletComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/DefaultAddExistingWalletComponent.kt index 1792942faf..53e611282b 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/DefaultAddExistingWalletComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/DefaultAddExistingWalletComponent.kt @@ -1,4 +1,4 @@ -package com.tangem.features.hotwallet.addexistingwallet.root +package com.tangem.features.hotwallet.addexistingwallet.entry import androidx.activity.compose.BackHandler import androidx.compose.runtime.Composable @@ -6,14 +6,16 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.decompose.router.stack.childStack -import com.arkivanov.decompose.router.stack.pop import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.ui.extensions.TextReference import com.tangem.features.hotwallet.AddExistingWalletComponent -import com.tangem.features.hotwallet.addexistingwallet.root.routing.AddExistingWalletChildFactory -import com.tangem.features.hotwallet.addexistingwallet.root.routing.AddExistingWalletRoute -import com.tangem.features.hotwallet.addexistingwallet.root.ui.AddExistingWalletContent +import com.tangem.features.hotwallet.addexistingwallet.entry.routing.AddExistingWalletChildFactory +import com.tangem.features.hotwallet.addexistingwallet.entry.routing.AddExistingWalletRoute +import com.tangem.features.hotwallet.addexistingwallet.entry.ui.AddExistingWalletContent +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent +import com.tangem.features.hotwallet.stepper.impl.DefaultHotWalletStepperComponent import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -21,7 +23,9 @@ import dagger.assisted.AssistedInject internal class DefaultAddExistingWalletComponent @AssistedInject constructor( @Assisted appComponentContext: AppComponentContext, @Assisted private val params: Unit, + private val stepperStateManager: AddExistingWalletStepperStateManager, addExistingWalletChildFactory: AddExistingWalletChildFactory, + stepperComponentFactory: DefaultHotWalletStepperComponent.Factory, ) : AddExistingWalletComponent, AppComponentContext by appComponentContext { private val model: AddExistingWalletModel = getOrCreateModel(params) @@ -43,13 +47,42 @@ internal class DefaultAddExistingWalletComponent @AssistedInject constructor( }, ) + private val stepperComponent = stepperComponentFactory.create( + context = this, + params = HotWalletStepperComponent.Params( + initState = HotWalletStepperComponent.StepperUM( + currentStep = 0, + steps = 0, + title = TextReference.EMPTY, + showBackButton = false, + showSkipButton = false, + showFeedbackButton = false, + ), + callback = object : HotWalletStepperComponent.ModelCallback { + override fun onBackClick() { + onChildBack() + } + + override fun onSkipClick() { + model.onSkipAccessCode() + } + }, + ), + ) + @Composable override fun Content(modifier: Modifier) { val stackState by innerStack.subscribeAsState() + val currentRoute = stackState.active.configuration BackHandler(onBack = ::onChildBack) + + val stepperState = stepperStateManager.getStepperState(currentRoute) + stepperState?.let { stepperComponent.updateState(it) } + AddExistingWalletContent( stackState = stackState, + stepperComponent = stepperComponent.takeIf { stepperState != null }, ) } diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/di/AddExistingWalletModule.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/di/AddExistingWalletModule.kt index 0c6a950236..a8af84bae5 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/di/AddExistingWalletModule.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/di/AddExistingWalletModule.kt @@ -1,11 +1,16 @@ -package com.tangem.features.hotwallet.addexistingwallet.root.di +package com.tangem.features.hotwallet.addexistingwallet.entry.di import com.tangem.core.decompose.model.Model import com.tangem.features.hotwallet.AddExistingWalletComponent -import com.tangem.features.hotwallet.addexistingwallet.root.AddExistingWalletModel -import com.tangem.features.hotwallet.addexistingwallet.root.DefaultAddExistingWalletComponent +import com.tangem.features.hotwallet.addexistingwallet.entry.AddExistingWalletModel +import com.tangem.features.hotwallet.addexistingwallet.entry.AddExistingWalletStepperStateManager +import com.tangem.features.hotwallet.addexistingwallet.entry.DefaultAddExistingWalletComponent +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent +import com.tangem.features.hotwallet.stepper.impl.DefaultHotWalletStepperComponent +import com.tangem.features.hotwallet.stepper.impl.HotWalletStepperModel import dagger.Binds import dagger.Module +import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import dagger.multibindings.ClassKey @@ -14,7 +19,7 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -internal interface AddExistingWalletModule { +internal interface AddExistingWalletModuleBinds { @Binds @Singleton @@ -26,4 +31,23 @@ internal interface AddExistingWalletModule { @IntoMap @ClassKey(AddExistingWalletModel::class) fun bindAddExistingWalletModel(model: AddExistingWalletModel): Model + + @Binds + fun bindFactory(impl: DefaultHotWalletStepperComponent.Factory): HotWalletStepperComponent.Factory + + @Binds + @IntoMap + @ClassKey(HotWalletStepperModel::class) + fun bindHotWalletStepperModel(model: HotWalletStepperModel): Model +} + +@Module +@InstallIn(SingletonComponent::class) +internal object AddExistingWalletModule { + + @Provides + @Singleton + fun provideAddExistingWalletStepperStateManager(): AddExistingWalletStepperStateManager { + return AddExistingWalletStepperStateManager() + } } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/entity/AddExistingWalletUM.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/entity/AddExistingWalletUM.kt index 8030308588..fb538425f2 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/entity/AddExistingWalletUM.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/entity/AddExistingWalletUM.kt @@ -1,4 +1,4 @@ -package com.tangem.features.hotwallet.addexistingwallet.root.entity +package com.tangem.features.hotwallet.addexistingwallet.entry.entity internal data class AddExistingWalletUM( val onBackClick: () -> Unit, diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt index 38642f07bf..10bdd316a8 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt @@ -1,10 +1,10 @@ -package com.tangem.features.hotwallet.addexistingwallet.root.routing +package com.tangem.features.hotwallet.addexistingwallet.entry.routing import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.features.hotwallet.accesscode.set.SetAccessCodeComponent import com.tangem.features.hotwallet.accesscode.confirm.ConfirmAccessCodeComponent -import com.tangem.features.hotwallet.addexistingwallet.root.AddExistingWalletModel +import com.tangem.features.hotwallet.addexistingwallet.entry.AddExistingWalletModel import com.tangem.features.hotwallet.addexistingwallet.start.AddExistingWalletStartComponent import com.tangem.features.hotwallet.addexistingwallet.im.port.AddExistingWalletImportComponent import com.tangem.features.hotwallet.manualbackup.completed.ManualBackupCompletedComponent diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletRoute.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletRoute.kt index f4f83e4193..f1102b80eb 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletRoute.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletRoute.kt @@ -1,4 +1,4 @@ -package com.tangem.features.hotwallet.addexistingwallet.root.routing +package com.tangem.features.hotwallet.addexistingwallet.entry.routing import com.tangem.core.decompose.navigation.Route import kotlinx.serialization.Serializable diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/ui/AddExistingWalletContent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/ui/AddExistingWalletContent.kt index bc43867900..f0fd3f4adf 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/ui/AddExistingWalletContent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/ui/AddExistingWalletContent.kt @@ -1,6 +1,7 @@ -package com.tangem.features.hotwallet.addexistingwallet.root.ui +package com.tangem.features.hotwallet.addexistingwallet.entry.ui import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.systemBarsPadding @@ -12,19 +13,29 @@ import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation import com.arkivanov.decompose.router.stack.ChildStack import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.res.TangemTheme -import com.tangem.features.hotwallet.addexistingwallet.root.routing.AddExistingWalletRoute +import com.tangem.features.hotwallet.addexistingwallet.entry.routing.AddExistingWalletRoute +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent @Composable -internal fun AddExistingWalletContent(stackState: ChildStack) { - Children( - stack = stackState, - animation = stackAnimation(slide()), +internal fun AddExistingWalletContent( + stackState: ChildStack, + stepperComponent: HotWalletStepperComponent?, +) { + Column( modifier = Modifier .background(color = TangemTheme.colors.background.primary) .fillMaxSize() .imePadding() .systemBarsPadding(), ) { - it.instance.Content(Modifier.fillMaxSize()) + stepperComponent?.Content(Modifier) + + Children( + stack = stackState, + animation = stackAnimation(slide()), + modifier = Modifier.fillMaxSize(), + ) { + it.instance.Content(Modifier.fillMaxSize()) + } } } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/api/HotWalletStepperComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/api/HotWalletStepperComponent.kt new file mode 100644 index 0000000000..c14de91c72 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/api/HotWalletStepperComponent.kt @@ -0,0 +1,33 @@ +package com.tangem.features.hotwallet.stepper.api + +import androidx.annotation.IntRange +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.core.ui.extensions.TextReference +import kotlinx.coroutines.flow.StateFlow + +interface HotWalletStepperComponent : ComposableContentComponent { + + data class StepperUM( + @IntRange(from = 0) val currentStep: Int, + @IntRange(from = 0) val steps: Int, + val title: TextReference, + val showBackButton: Boolean, + val showSkipButton: Boolean, + val showFeedbackButton: Boolean, + ) + + interface ModelCallback { + fun onBackClick() + fun onSkipClick() + } + + class Params( + val initState: StepperUM, + val callback: ModelCallback, + ) + + val state: StateFlow + + interface Factory : ComponentFactory +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/di/HotWalletStepperModule.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/di/HotWalletStepperModule.kt new file mode 100644 index 0000000000..211c9265f3 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/di/HotWalletStepperModule.kt @@ -0,0 +1,18 @@ +package com.tangem.features.hotwallet.stepper.di + +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent +import com.tangem.features.hotwallet.stepper.impl.DefaultHotWalletStepperComponent +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +internal interface HotWalletStepperModule { + + @Binds + fun bindHotWalletStepperComponentFactory( + impl: DefaultHotWalletStepperComponent.Factory, + ): HotWalletStepperComponent.Factory +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/DefaultHotWalletStepperComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/DefaultHotWalletStepperComponent.kt new file mode 100644 index 0000000000..320920105b --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/DefaultHotWalletStepperComponent.kt @@ -0,0 +1,48 @@ +package com.tangem.features.hotwallet.stepper.impl + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent +import com.tangem.features.hotwallet.stepper.impl.ui.HotWalletStepper +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +internal class DefaultHotWalletStepperComponent @AssistedInject constructor( + @Assisted val context: AppComponentContext, + @Assisted val params: HotWalletStepperComponent.Params, +) : HotWalletStepperComponent, AppComponentContext by context { + + private val model: HotWalletStepperModel = getOrCreateModel(params) + + override val state = model.uiState + + fun updateState(newState: HotWalletStepperComponent.StepperUM) { + model.updateState(newState) + } + + @Composable + override fun Content(modifier: Modifier) { + val uiState by state.collectAsStateWithLifecycle() + + HotWalletStepper( + state = uiState, + modifier = modifier, + onBackClick = model::onBackClick, + onSkipClick = model::onSkipClick, + onFeedbackClick = model::onFeedbackClick, + ) + } + + @AssistedFactory + interface Factory : HotWalletStepperComponent.Factory { + override fun create( + context: AppComponentContext, + params: HotWalletStepperComponent.Params, + ): DefaultHotWalletStepperComponent + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/HotWalletStepperModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/HotWalletStepperModel.kt new file mode 100644 index 0000000000..ed64c2fb93 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/HotWalletStepperModel.kt @@ -0,0 +1,42 @@ +package com.tangem.features.hotwallet.stepper.impl + +import androidx.compose.runtime.Stable +import com.tangem.core.decompose.di.ModelScoped +import com.tangem.core.decompose.model.Model +import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import javax.inject.Inject + +@Stable +@ModelScoped +internal class HotWalletStepperModel @Inject constructor( + paramsContainer: ParamsContainer, + override val dispatchers: CoroutineDispatcherProvider, +) : Model() { + + val params = paramsContainer.require() + + val uiState: StateFlow + field = MutableStateFlow(params.initState) + + fun updateState(newState: HotWalletStepperComponent.StepperUM) { + uiState.value = newState + } + + fun onBackClick() { + params.callback.onBackClick() + } + + fun onSkipClick() { + // TODO send analytics + params.callback.onSkipClick() + } + + fun onFeedbackClick() { + // TODO send analytics + // openFeedback() + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/ui/HotWalletStepper.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/ui/HotWalletStepper.kt new file mode 100644 index 0000000000..f9af5a29b8 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/ui/HotWalletStepper.kt @@ -0,0 +1,106 @@ +package com.tangem.features.hotwallet.stepper.impl.ui + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.components.appbar.TangemTopAppBar +import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM +import com.tangem.core.ui.components.progressbar.TangemLinearProgressIndicator +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.res.TangemAnimations +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.features.hotwallet.impl.R +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent + +@Composable +internal fun HotWalletStepper( + state: HotWalletStepperComponent.StepperUM, + onBackClick: () -> Unit, + onSkipClick: () -> Unit, + onFeedbackClick: () -> Unit, + modifier: Modifier = Modifier, +) { + val fraction = state.currentStep.toFloat() / state.steps.coerceAtLeast(1) + val animatedIndicatorFraction by TangemAnimations.horizontalIndicatorAsState(targetFraction = fraction) + + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + TangemTopAppBar( + startButton = if (state.showBackButton) { + TopAppBarButtonUM.Back { onBackClick() } + } else { + null + }, + endButton = when { + state.showSkipButton -> TopAppBarButtonUM.Text( + text = resourceReference(R.string.common_skip), + onClicked = { onSkipClick() }, + ) + state.showFeedbackButton -> TopAppBarButtonUM.Icon( + iconRes = R.drawable.ic_chat_24, + onClicked = { onFeedbackClick() }, + ) + else -> null + }, + title = state.title, + containerColor = TangemTheme.colors.background.primary, + modifier = modifier, + titleAlignment = Alignment.CenterHorizontally, + ) + + TangemLinearProgressIndicator( + modifier = Modifier + .padding(horizontal = 16.dp) + .height(4.dp) + .fillMaxWidth(), + progress = { animatedIndicatorFraction }, + color = TangemTheme.colors.icon.primary1, + backgroundColor = TangemTheme.colors.icon.primary1.copy(alpha = 0.4f), + strokeCap = StrokeCap.Round, + ) + } +} + +@Preview +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun HotWalletStepper_Preview() { + TangemThemePreview { + Box( + Modifier + .fillMaxSize() + .background(TangemTheme.colors.background.primary), + ) { + HotWalletStepper( + modifier = Modifier.align(Alignment.TopCenter), + state = HotWalletStepperComponent.StepperUM( + currentStep = 2, + steps = 3, + title = resourceReference(R.string.common_done), + showBackButton = true, + showSkipButton = false, + showFeedbackButton = true, + ), + onBackClick = {}, + onSkipClick = {}, + onFeedbackClick = {}, + ) + } + } +} \ No newline at end of file From 95c984d9e3edbf629c2f255f4725754ed6534d6a Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 29 Jul 2025 13:59:01 +0500 Subject: [PATCH 14/53] Updated on 2026-08-14 --- .../hotwallet/accesscode/Constants.kt | 3 -- .../confirm/ConfirmAccessCodeComponent.kt | 52 ------------------- .../confirm/entity/ConfirmAccessCodeUM.kt | 12 ----- .../accesscode/di/AccessCodeModule.kt | 26 ---------- .../accesscode/set/SetAccessCodeModel.kt | 45 ---------------- .../accesscode/set/entity/SetAccessCodeUM.kt | 12 ----- .../accesscode/set/ui/SetAccessCodeContent.kt | 46 ---------------- .../HotAccessCodeRequestModel.kt | 5 +- .../entry/AddExistingWalletModel.kt | 10 ++-- .../routing/AddExistingWalletChildFactory.kt | 20 +++---- .../AccessCodeComponent.kt} | 29 ++++++++--- .../AccessCodeModel.kt} | 28 ++++++---- .../hotwallet/setaccesscode/Constants.kt | 3 ++ .../setaccesscode/di/AccessCodeModule.kt | 20 +++++++ .../setaccesscode/entity/AccessCodeUM.kt | 12 +++++ .../ui/AccessCodeEnter.kt | 2 +- .../ui/AccessCodeLayout.kt} | 25 +++++---- .../stepper/impl/ui/HotWalletStepper.kt | 6 +-- 18 files changed, 107 insertions(+), 249 deletions(-) delete mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/Constants.kt delete mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ConfirmAccessCodeComponent.kt delete mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/entity/ConfirmAccessCodeUM.kt delete mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/di/AccessCodeModule.kt delete mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/SetAccessCodeModel.kt delete mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/entity/SetAccessCodeUM.kt delete mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/ui/SetAccessCodeContent.kt rename features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/{accesscode/set/SetAccessCodeComponent.kt => setaccesscode/AccessCodeComponent.kt} (62%) rename features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/{accesscode/confirm/ConfirmAccessCodeModel.kt => setaccesscode/AccessCodeModel.kt} (53%) create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/Constants.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/di/AccessCodeModule.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/entity/AccessCodeUM.kt rename features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/{accesscode => setaccesscode}/ui/AccessCodeEnter.kt (98%) rename features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/{accesscode/confirm/ui/ConfirmAccessCodeContent.kt => setaccesscode/ui/AccessCodeLayout.kt} (59%) diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/Constants.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/Constants.kt deleted file mode 100644 index ca5ecaa367..0000000000 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/Constants.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.tangem.features.hotwallet.accesscode - -const val ACCESS_CODE_LENGTH = 6 \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ConfirmAccessCodeComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ConfirmAccessCodeComponent.kt deleted file mode 100644 index 849eb2cb57..0000000000 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ConfirmAccessCodeComponent.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.tangem.features.hotwallet.accesscode.confirm - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.ui.Modifier -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.tangem.core.decompose.context.AppComponentContext -import com.tangem.core.decompose.model.getOrCreateModel -import com.tangem.core.ui.decompose.ComposableContentComponent -import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect -import com.tangem.features.hotwallet.accesscode.confirm.ui.SetAccessCodeConfirmContent -import dagger.assisted.Assisted -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject - -internal class ConfirmAccessCodeComponent @AssistedInject constructor( - @Assisted private val context: AppComponentContext, - @Assisted private val params: Params, -) : ComposableContentComponent, AppComponentContext by context { - - private val model: ConfirmAccessCodeModel = getOrCreateModel(params) - - @Composable - override fun Content(modifier: Modifier) { - val state by model.uiState.collectAsStateWithLifecycle() - - DisableScreenshotsDisposableEffect() - - SetAccessCodeConfirmContent( - accessCode = state.accessCode, - onAccessCodeChange = state.onAccessCodeChange, - accessCodeLength = state.accessCodeLength, - onConfirm = state.onConfirm, - buttonEnabled = state.buttonEnabled, - modifier = modifier, - ) - } - - interface ModelCallbacks { - fun onAccessCodeConfirmed() - } - - data class Params( - val accessCodeToConfirm: String, - val callbacks: ModelCallbacks, - ) - - @AssistedFactory - interface Factory { - fun create(context: AppComponentContext, params: Params): ConfirmAccessCodeComponent - } -} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/entity/ConfirmAccessCodeUM.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/entity/ConfirmAccessCodeUM.kt deleted file mode 100644 index f2df925578..0000000000 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/entity/ConfirmAccessCodeUM.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.tangem.features.hotwallet.accesscode.confirm.entity - -import com.tangem.features.hotwallet.accesscode.ACCESS_CODE_LENGTH - -internal data class ConfirmAccessCodeUM( - val accessCode: String, - val onAccessCodeChange: (String) -> Unit, - val buttonEnabled: Boolean, - val onConfirm: () -> Unit, -) { - val accessCodeLength: Int = ACCESS_CODE_LENGTH -} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/di/AccessCodeModule.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/di/AccessCodeModule.kt deleted file mode 100644 index 8c79d221da..0000000000 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/di/AccessCodeModule.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.tangem.features.hotwallet.accesscode.di - -import com.tangem.core.decompose.model.Model -import com.tangem.features.hotwallet.accesscode.confirm.ConfirmAccessCodeModel -import com.tangem.features.hotwallet.accesscode.set.SetAccessCodeModel -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import dagger.multibindings.ClassKey -import dagger.multibindings.IntoMap - -@Module -@InstallIn(SingletonComponent::class) -internal interface AccessCodeModule { - - @Binds - @IntoMap - @ClassKey(SetAccessCodeModel::class) - fun bindSetAccessCodeModel(model: SetAccessCodeModel): Model - - @Binds - @IntoMap - @ClassKey(ConfirmAccessCodeModel::class) - fun bindConfirmAccessCodeModel(model: ConfirmAccessCodeModel): Model -} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/SetAccessCodeModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/SetAccessCodeModel.kt deleted file mode 100644 index 4f57d50886..0000000000 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/SetAccessCodeModel.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.tangem.features.hotwallet.accesscode.set - -import androidx.compose.runtime.Stable -import com.tangem.core.decompose.di.ModelScoped -import com.tangem.core.decompose.model.Model -import com.tangem.core.decompose.model.ParamsContainer -import com.tangem.features.hotwallet.accesscode.set.entity.SetAccessCodeUM -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.update -import javax.inject.Inject - -@Stable -@ModelScoped -internal class SetAccessCodeModel @Inject constructor( - paramsContainer: ParamsContainer, - override val dispatchers: CoroutineDispatcherProvider, -) : Model() { - - private val params = paramsContainer.require() - - internal val uiState: StateFlow - field = MutableStateFlow(getInitialState()) - - private fun getInitialState() = SetAccessCodeUM( - accessCode = "", - onAccessCodeChange = ::onAccessCodeChange, - buttonEnabled = false, - onContinue = ::onContinue, - ) - - private fun onAccessCodeChange(value: String) { - uiState.update { - it.copy( - accessCode = value, - buttonEnabled = value.length == uiState.value.accessCodeLength, - ) - } - } - - private fun onContinue() { - params.callbacks.onAccessCodeSet(uiState.value.accessCode) - } -} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/entity/SetAccessCodeUM.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/entity/SetAccessCodeUM.kt deleted file mode 100644 index 68fa89c2fe..0000000000 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/entity/SetAccessCodeUM.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.tangem.features.hotwallet.accesscode.set.entity - -import com.tangem.features.hotwallet.accesscode.ACCESS_CODE_LENGTH - -internal data class SetAccessCodeUM( - val accessCode: String, - val onAccessCodeChange: (String) -> Unit, - val buttonEnabled: Boolean, - val onContinue: () -> Unit, -) { - val accessCodeLength: Int = ACCESS_CODE_LENGTH -} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/ui/SetAccessCodeContent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/ui/SetAccessCodeContent.kt deleted file mode 100644 index 0fcf53c2df..0000000000 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/ui/SetAccessCodeContent.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.tangem.features.hotwallet.accesscode.set.ui - -import androidx.compose.foundation.layout.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.tangem.core.ui.components.PrimaryButton -import com.tangem.core.ui.extensions.stringResourceSafe -import com.tangem.features.hotwallet.accesscode.ui.AccessCodeEnter -import com.tangem.core.res.R - -@Composable -internal fun SetAccessCodeContent( - accessCode: String, - onAccessCodeChange: (String) -> Unit, - accessCodeLength: Int, - onContinue: () -> Unit, - buttonEnabled: Boolean, - modifier: Modifier = Modifier, -) { - Column( - modifier = modifier - .fillMaxSize() - .navigationBarsPadding(), - ) { - AccessCodeEnter( - modifier = Modifier - .padding(top = 16.dp) - .weight(1f), - accessCode = accessCode, - onAccessCodeChange = onAccessCodeChange, - accessCodeLength = accessCodeLength, - reEnterAccessCodeState = false, - ) - - PrimaryButton( - modifier = Modifier - .fillMaxWidth() - .padding(start = 16.dp, end = 16.dp, bottom = 16.dp) - .imePadding(), - text = stringResourceSafe(R.string.common_continue), - onClick = onContinue, - enabled = buttonEnabled, - ) - } -} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/HotAccessCodeRequestModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/HotAccessCodeRequestModel.kt index c34ec66391..05e2ef611b 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/HotAccessCodeRequestModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/HotAccessCodeRequestModel.kt @@ -3,6 +3,7 @@ package com.tangem.features.hotwallet.accesscoderequest import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.domain.wallets.hot.HotWalletPasswordRequester +import com.tangem.features.hotwallet.setaccesscode.ACCESS_CODE_LENGTH import com.tangem.features.hotwallet.accesscoderequest.entity.HotAccessCodeRequestUM import com.tangem.hot.sdk.model.HotAuth import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -82,8 +83,4 @@ internal class HotAccessCodeRequestModel @Inject constructor( it.copy(isShown = false) } } - - private companion object { - const val ACCESS_CODE_LENGTH = 6 - } } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt index ad499a7aa4..1d192ffae5 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt @@ -14,8 +14,7 @@ import com.tangem.features.hotwallet.addexistingwallet.im.port.AddExistingWallet import com.tangem.features.hotwallet.addexistingwallet.entry.routing.AddExistingWalletRoute import com.tangem.features.hotwallet.addexistingwallet.start.AddExistingWalletStartComponent import com.tangem.features.hotwallet.manualbackup.completed.ManualBackupCompletedComponent -import com.tangem.features.hotwallet.accesscode.confirm.ConfirmAccessCodeComponent -import com.tangem.features.hotwallet.accesscode.set.SetAccessCodeComponent +import com.tangem.features.hotwallet.setaccesscode.AccessCodeComponent import com.tangem.features.hotwallet.setupfinished.MobileWalletSetupFinishedComponent import com.tangem.features.pushnotifications.api.PushNotificationsComponent import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION @@ -34,8 +33,7 @@ internal class AddExistingWalletModel @Inject constructor( val addExistingWalletImportModelCallbacks = AddExistingWalletImportModelCallbacks() val manualBackupCompletedComponentModelCallbacks = ManualBackupCompletedComponentModelCallbacks() val pushNotificationsComponentModelCallbacks = PushNotificationsComponentModelCallbacks() - val setAccessCodeModelCallbacks = SetAccessCodeModelCallbacks() - val confirmAccessCodeModelCallbacks = ConfirmAccessCodeModelCallbacks() + val accessCodeModelCallbacks = AccessCodeModelCallbacks() val mobileWalletSetupFinishedComponentModelCallbacks = MobileWalletSetupFinishedComponentModelCallbacks() val stackNavigation = StackNavigation() @@ -91,13 +89,11 @@ internal class AddExistingWalletModel @Inject constructor( } } - inner class SetAccessCodeModelCallbacks : SetAccessCodeComponent.ModelCallbacks { + inner class AccessCodeModelCallbacks : AccessCodeComponent.ModelCallbacks { override fun onAccessCodeSet(accessCode: String) { stackNavigation.push(AddExistingWalletRoute.ConfirmAccessCode(accessCode)) } - } - inner class ConfirmAccessCodeModelCallbacks : ConfirmAccessCodeComponent.ModelCallbacks { override fun onAccessCodeConfirmed() { navigateToPushNotificationsOrNext() } diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt index 10bdd316a8..90ffa797d0 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt @@ -2,8 +2,7 @@ package com.tangem.features.hotwallet.addexistingwallet.entry.routing import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.ui.decompose.ComposableContentComponent -import com.tangem.features.hotwallet.accesscode.set.SetAccessCodeComponent -import com.tangem.features.hotwallet.accesscode.confirm.ConfirmAccessCodeComponent +import com.tangem.features.hotwallet.setaccesscode.AccessCodeComponent import com.tangem.features.hotwallet.addexistingwallet.entry.AddExistingWalletModel import com.tangem.features.hotwallet.addexistingwallet.start.AddExistingWalletStartComponent import com.tangem.features.hotwallet.addexistingwallet.im.port.AddExistingWalletImportComponent @@ -14,8 +13,7 @@ import javax.inject.Inject internal class AddExistingWalletChildFactory @Inject constructor( private val pushNotificationsComponent: PushNotificationsComponent.Factory, - private val setAccessCodeSetComponentFactory: SetAccessCodeComponent.Factory, - private val confirmAccessCodeComponentFactory: ConfirmAccessCodeComponent.Factory, + private val accessCodeComponentFactory: AccessCodeComponent.Factory, ) { fun createChild( @@ -42,17 +40,19 @@ internal class AddExistingWalletChildFactory @Inject constructor( callbacks = model.manualBackupCompletedComponentModelCallbacks, ), ) - is AddExistingWalletRoute.SetAccessCode -> setAccessCodeSetComponentFactory.create( + is AddExistingWalletRoute.SetAccessCode -> accessCodeComponentFactory.create( context = childContext, - params = SetAccessCodeComponent.Params( - callbacks = model.setAccessCodeModelCallbacks, + params = AccessCodeComponent.Params( + isConfirmMode = false, + callbacks = model.accessCodeModelCallbacks, ), ) - is AddExistingWalletRoute.ConfirmAccessCode -> confirmAccessCodeComponentFactory.create( + is AddExistingWalletRoute.ConfirmAccessCode -> accessCodeComponentFactory.create( context = childContext, - params = ConfirmAccessCodeComponent.Params( + params = AccessCodeComponent.Params( + isConfirmMode = true, accessCodeToConfirm = route.accessCode, - callbacks = model.confirmAccessCodeModelCallbacks, + callbacks = model.accessCodeModelCallbacks, ), ) is AddExistingWalletRoute.PushNotifications -> pushNotificationsComponent.create( diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/SetAccessCodeComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/AccessCodeComponent.kt similarity index 62% rename from features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/SetAccessCodeComponent.kt rename to features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/AccessCodeComponent.kt index c8a03346c0..4200b68d42 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/set/SetAccessCodeComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/AccessCodeComponent.kt @@ -1,4 +1,4 @@ -package com.tangem.features.hotwallet.accesscode.set +package com.tangem.features.hotwallet.setaccesscode import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -8,17 +8,19 @@ import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect -import com.tangem.features.hotwallet.accesscode.set.ui.SetAccessCodeContent +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.features.hotwallet.setaccesscode.ui.AccessCodeLayout +import com.tangem.core.res.R import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -internal class SetAccessCodeComponent @AssistedInject constructor( +internal class AccessCodeComponent @AssistedInject constructor( @Assisted private val context: AppComponentContext, @Assisted private val params: Params, ) : ComposableContentComponent, AppComponentContext by context { - private val model: SetAccessCodeModel = getOrCreateModel(params) + private val model: AccessCodeModel = getOrCreateModel(params) @Composable override fun Content(modifier: Modifier) { @@ -26,26 +28,37 @@ internal class SetAccessCodeComponent @AssistedInject constructor( DisableScreenshotsDisposableEffect() - SetAccessCodeContent( + AccessCodeLayout( + modifier = modifier, accessCode = state.accessCode, onAccessCodeChange = state.onAccessCodeChange, accessCodeLength = state.accessCodeLength, - onContinue = state.onContinue, + reEnterAccessCodeState = params.isConfirmMode, + buttonText = stringResourceSafe( + if (params.isConfirmMode) { + R.string.common_confirm + } else { + R.string.common_continue + }, + ), + onButtonClick = state.onButtonClick, buttonEnabled = state.buttonEnabled, - modifier = modifier, ) } interface ModelCallbacks { fun onAccessCodeSet(accessCode: String) + fun onAccessCodeConfirmed() } data class Params( + val isConfirmMode: Boolean, + val accessCodeToConfirm: String? = null, val callbacks: ModelCallbacks, ) @AssistedFactory interface Factory { - fun create(context: AppComponentContext, params: Params): SetAccessCodeComponent + fun create(context: AppComponentContext, params: Params): AccessCodeComponent } } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ConfirmAccessCodeModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/AccessCodeModel.kt similarity index 53% rename from features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ConfirmAccessCodeModel.kt rename to features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/AccessCodeModel.kt index 25a77bdbdc..d978ba32f6 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ConfirmAccessCodeModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/AccessCodeModel.kt @@ -1,10 +1,10 @@ -package com.tangem.features.hotwallet.accesscode.confirm +package com.tangem.features.hotwallet.setaccesscode import androidx.compose.runtime.Stable import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer -import com.tangem.features.hotwallet.accesscode.confirm.entity.ConfirmAccessCodeUM +import com.tangem.features.hotwallet.setaccesscode.entity.AccessCodeUM import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -13,33 +13,41 @@ import javax.inject.Inject @Stable @ModelScoped -internal class ConfirmAccessCodeModel @Inject constructor( +internal class AccessCodeModel @Inject constructor( paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, ) : Model() { - private val params = paramsContainer.require() + private val params = paramsContainer.require() - internal val uiState: StateFlow + internal val uiState: StateFlow field = MutableStateFlow(getInitialState()) - private fun getInitialState() = ConfirmAccessCodeUM( + private fun getInitialState() = AccessCodeUM( accessCode = "", onAccessCodeChange = ::onAccessCodeChange, buttonEnabled = false, - onConfirm = ::onConfirm, + onButtonClick = ::onButtonClick, ) private fun onAccessCodeChange(value: String) { uiState.update { it.copy( accessCode = value, - buttonEnabled = value == params.accessCodeToConfirm, + buttonEnabled = if (params.isConfirmMode) { + value == params.accessCodeToConfirm + } else { + value.length == uiState.value.accessCodeLength + }, ) } } - private fun onConfirm() { - params.callbacks.onAccessCodeConfirmed() + private fun onButtonClick() { + if (params.isConfirmMode) { + params.callbacks.onAccessCodeConfirmed() + } else { + params.callbacks.onAccessCodeSet(uiState.value.accessCode) + } } } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/Constants.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/Constants.kt new file mode 100644 index 0000000000..8494ba6e8b --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/Constants.kt @@ -0,0 +1,3 @@ +package com.tangem.features.hotwallet.setaccesscode + +const val ACCESS_CODE_LENGTH = 6 \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/di/AccessCodeModule.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/di/AccessCodeModule.kt new file mode 100644 index 0000000000..c6fb7f93cc --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/di/AccessCodeModule.kt @@ -0,0 +1,20 @@ +package com.tangem.features.hotwallet.setaccesscode.di + +import com.tangem.core.decompose.model.Model +import com.tangem.features.hotwallet.setaccesscode.AccessCodeModel +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +@InstallIn(SingletonComponent::class) +internal interface AccessCodeModule { + + @Binds + @IntoMap + @ClassKey(AccessCodeModel::class) + fun bindAccessCodeModel(model: AccessCodeModel): Model +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/entity/AccessCodeUM.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/entity/AccessCodeUM.kt new file mode 100644 index 0000000000..836a816175 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/entity/AccessCodeUM.kt @@ -0,0 +1,12 @@ +package com.tangem.features.hotwallet.setaccesscode.entity + +import com.tangem.features.hotwallet.setaccesscode.ACCESS_CODE_LENGTH + +internal data class AccessCodeUM( + val accessCode: String, + val onAccessCodeChange: (String) -> Unit, + val buttonEnabled: Boolean, + val onButtonClick: () -> Unit, +) { + val accessCodeLength: Int = ACCESS_CODE_LENGTH +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/ui/AccessCodeEnter.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCodeEnter.kt similarity index 98% rename from features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/ui/AccessCodeEnter.kt rename to features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCodeEnter.kt index ba80fd0bb6..62e4388d9a 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/ui/AccessCodeEnter.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCodeEnter.kt @@ -1,4 +1,4 @@ -package com.tangem.features.hotwallet.accesscode.ui +package com.tangem.features.hotwallet.setaccesscode.ui import android.content.res.Configuration import androidx.compose.foundation.background diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ui/ConfirmAccessCodeContent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCodeLayout.kt similarity index 59% rename from features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ui/ConfirmAccessCodeContent.kt rename to features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCodeLayout.kt index d85e2ea0d1..aee581c976 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/confirm/ui/ConfirmAccessCodeContent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCodeLayout.kt @@ -1,20 +1,25 @@ -package com.tangem.features.hotwallet.accesscode.confirm.ui +package com.tangem.features.hotwallet.setaccesscode.ui -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.tangem.core.ui.components.PrimaryButton -import com.tangem.core.ui.extensions.stringResourceSafe -import com.tangem.features.hotwallet.accesscode.ui.AccessCodeEnter -import com.tangem.core.res.R +@Suppress("LongParameterList") @Composable -internal fun SetAccessCodeConfirmContent( +internal fun AccessCodeLayout( accessCode: String, onAccessCodeChange: (String) -> Unit, accessCodeLength: Int, - onConfirm: () -> Unit, + reEnterAccessCodeState: Boolean, + buttonText: String, + onButtonClick: () -> Unit, buttonEnabled: Boolean, modifier: Modifier = Modifier, ) { @@ -30,7 +35,7 @@ internal fun SetAccessCodeConfirmContent( accessCode = accessCode, onAccessCodeChange = onAccessCodeChange, accessCodeLength = accessCodeLength, - reEnterAccessCodeState = true, + reEnterAccessCodeState = reEnterAccessCodeState, ) PrimaryButton( @@ -38,8 +43,8 @@ internal fun SetAccessCodeConfirmContent( .fillMaxWidth() .padding(start = 16.dp, end = 16.dp, bottom = 16.dp) .imePadding(), - text = stringResourceSafe(R.string.common_confirm), - onClick = onConfirm, + text = buttonText, + onClick = onButtonClick, enabled = buttonEnabled, ) } diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/ui/HotWalletStepper.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/ui/HotWalletStepper.kt index f9af5a29b8..ea029d37be 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/ui/HotWalletStepper.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/ui/HotWalletStepper.kt @@ -43,18 +43,18 @@ internal fun HotWalletStepper( ) { TangemTopAppBar( startButton = if (state.showBackButton) { - TopAppBarButtonUM.Back { onBackClick() } + TopAppBarButtonUM.Back(onBackClick) } else { null }, endButton = when { state.showSkipButton -> TopAppBarButtonUM.Text( text = resourceReference(R.string.common_skip), - onClicked = { onSkipClick() }, + onClicked = onSkipClick, ) state.showFeedbackButton -> TopAppBarButtonUM.Icon( iconRes = R.drawable.ic_chat_24, - onClicked = { onFeedbackClick() }, + onClicked = onFeedbackClick, ) else -> null }, From 79eab436df5c220b1ce32a6f354b7e1cda9ff742 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 30 Jul 2025 15:11:40 +0300 Subject: [PATCH 15/53] Updated on 2026-08-14 --- .../kotlin/com/tangem/tests/BuyTokenTest.kt | 14 +++++++++++--- .../kotlin/com/tangem/tests/OrganizeTokensTest.kt | 8 ++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/app/src/androidTest/kotlin/com/tangem/tests/BuyTokenTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/BuyTokenTest.kt index d65c64ed2b..1c05fa58dc 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/BuyTokenTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/BuyTokenTest.kt @@ -348,10 +348,18 @@ class BuyTokenTest : BaseTestCase() { onBuyTokenDetailsScreen { providerTitle.performClick() } } step("Assert available provider name is displayed") { - onSelectProviderBottomSheet { availableProviderItem.assertIsDisplayed() } + onSelectProviderBottomSheet { + flakySafely(timeoutMs = 20_000) { + availableProviderItem.assertIsDisplayed() + } + } } step("Assert unavailable provider name is displayed") { - onSelectProviderBottomSheet { unavailableProviderItem.assertIsDisplayed() } + onSelectProviderBottomSheet { + flakySafely(timeoutMs = 20_000) { + unavailableProviderItem.assertIsDisplayed() + } + } } step("Click on 'Expand payment methods' button") { onSelectProviderBottomSheet { paymentMethodExpandButton.clickWithAssertion() } @@ -393,7 +401,7 @@ class BuyTokenTest : BaseTestCase() { } } - @AllureId("2570") + @AllureId("3479") @DisplayName("Onramp: validate 'Select payment method' bottom sheet") @Test fun validatePaymentMethodScreenTest() { diff --git a/app/src/androidTest/kotlin/com/tangem/tests/OrganizeTokensTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/OrganizeTokensTest.kt index e64113e05e..6d89ee640d 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/OrganizeTokensTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/OrganizeTokensTest.kt @@ -79,12 +79,16 @@ class OrganizeTokensTest : BaseTestCase() { setupHooks().run { val ethereumTitle = "Ethereum" val bitcoinTitle = "Bitcoin" + val balance = "$184.85" step("Open 'Main Screen'") { scenario(OpenMainScreenScenario(composeTestRule)) } step("Click on 'Synchronize addresses' button" ) { onMainScreen { synchronizeAddressesButton.clickWithAssertion() } } + step("Assert wallet balance = '$balance'") { + onMainScreen { walletBalance().assertTextContains(balance) } + } step("Check positions of tokens on 'Main Screen'") { onMainScreen { tokenWithTitleAndPosition(bitcoinTitle, 0).assertIsDisplayed() @@ -154,12 +158,16 @@ class OrganizeTokensTest : BaseTestCase() { val ethereumTitle = "Ethereum" val bitcoinTitle = "Bitcoin" val polygonTitle = "Polygon" + val balance = "$184.85" step("Open 'Main Screen'") { scenario(OpenMainScreenScenario(composeTestRule)) } step("Click on 'Synchronize addresses' button" ) { onMainScreen { synchronizeAddressesButton.clickWithAssertion() } } + step("Assert wallet balance = '$balance'") { + onMainScreen { walletBalance().assertTextContains(balance) } + } step("Check positions of tokens on 'Main Screen'") { onMainScreen { tokenWithTitleAndPosition(bitcoinTitle, 0).assertIsDisplayed() From 78720c5cab895049e3c7e46caaab1d93b2f47e31 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 30 Jul 2025 11:18:48 +0400 Subject: [PATCH 16/53] Updated on 2026-08-14 --- core/datasource/build.gradle.kts | 1 + .../token/converter/YieldBalanceConverter.kt | 64 +++--- .../DefaultSingleYieldBalanceProducer.kt | 7 +- .../store/DefaultYieldsBalancesStore.kt | 16 +- .../tangem/data/staking/YieldBalanceExt.kt | 2 +- .../DefaultSingleYieldBalanceProducerTest.kt | 4 +- .../YieldsBalancesStoreUpdateMethodsTest.kt | 7 +- domain/staking/build.gradle.kts | 1 + domain/staking/models/build.gradle.kts | 1 + .../tangem/domain/staking/model/StakingID.kt | 4 +- .../staking/model/stakekit/YieldBalance.kt | 203 +++++------------- .../model/stakekit/YieldBalanceItem.kt | 130 +++++++++++ .../BaseCurrencyStatusOperations.kt | 2 +- .../CachedCurrenciesStatusesOperations.kt | 16 +- .../operations/CurrencyStatusOperations.kt | 3 +- .../utils/CurrencyStatusProxyCreator.kt | 10 +- features/staking/impl/build.gradle.kts | 1 + .../state/converters/BalanceItemConverter.kt | 6 +- .../converters/YieldBalancesConverter.kt | 2 +- gradle/dependencies.toml | 2 + 20 files changed, 267 insertions(+), 215 deletions(-) create mode 100644 domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalanceItem.kt diff --git a/core/datasource/build.gradle.kts b/core/datasource/build.gradle.kts index 7c44fe9963..0bd12e57fd 100644 --- a/core/datasource/build.gradle.kts +++ b/core/datasource/build.gradle.kts @@ -52,6 +52,7 @@ dependencies { /** Coroutines */ implementation(deps.kotlin.coroutines) implementation(deps.kotlin.coroutines.rx2) + implementation(deps.kotlin.datetime) /** Logging */ implementation(deps.timber) diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/YieldBalanceConverter.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/YieldBalanceConverter.kt index de55373f39..5d86e9a0be 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/YieldBalanceConverter.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/YieldBalanceConverter.kt @@ -1,53 +1,61 @@ package com.tangem.datasource.local.token.converter +import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO import com.tangem.domain.models.StatusSource +import com.tangem.domain.staking.model.StakingID import com.tangem.domain.staking.model.stakekit.BalanceItem import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.staking.model.stakekit.YieldBalanceItem import com.tangem.utils.converter.Converter +import kotlinx.datetime.Instant class YieldBalanceConverter( private val source: StatusSource, -) : Converter { +) : Converter { constructor(isCached: Boolean) : this(source = if (isCached) StatusSource.CACHE else StatusSource.ACTUAL) - override fun convert(value: YieldBalanceWrapperDTO): YieldBalance { + override fun convert(value: YieldBalanceWrapperDTO): YieldBalance? { + val stakingId = StakingID( + integrationId = value.integrationId ?: return null, + address = value.addresses.address, + ) + return if (value.balances.isEmpty()) { - YieldBalance.Empty( - integrationId = value.integrationId, - address = value.addresses.address, - source = source, - ) + YieldBalance.Empty(stakingId = stakingId, source = source) } else { YieldBalance.Data( - integrationId = value.integrationId, - address = value.addresses.address, + stakingId = stakingId, balance = YieldBalanceItem( - items = value.balances.map { item -> - BalanceItem( - groupId = item.groupId, - token = TokenConverter.convert(item.tokenDTO), - type = BalanceTypeConverter.convert(item.type), - amount = item.amount, - rawCurrencyId = item.tokenDTO.coinGeckoId, - // tron-specific. operates validatorAddresses instead of validatorAddress - validatorAddress = item.validatorAddress ?: item.validatorAddresses?.get(0), - date = item.date?.toDateTime(), - pendingActions = PendingActionConverter - .convertList(item.pendingActions) - .sortedBy { it.passthrough }, - pendingActionsConstraints = PendingActionConstraintsConverter - .convertList(item.pendingActionConstraints.orEmpty()), - isPending = false, - ) - } - .sortedWith(compareBy({ it.type }, { it.amount })), + items = value.balances + .map { item -> item.toBalanceItem() } + .sortedWith(comparator = compareBy({ it.type }, { it.amount })), integrationId = value.integrationId, ), source = source, ) } } + + private fun BalanceDTO.toBalanceItem(): BalanceItem { + val item = this + + return BalanceItem( + groupId = item.groupId, + token = TokenConverter.convert(item.tokenDTO), + type = BalanceTypeConverter.convert(item.type), + amount = item.amount, + rawCurrencyId = item.tokenDTO.coinGeckoId, + // tron-specific. operates validatorAddresses instead of validatorAddress + validatorAddress = item.validatorAddress ?: item.validatorAddresses?.get(0), + date = item.date?.toString()?.let { Instant.parse(it) }, + pendingActions = PendingActionConverter + .convertList(item.pendingActions) + .sortedBy { it.passthrough }, + pendingActionsConstraints = PendingActionConstraintsConverter + .convertList(item.pendingActionConstraints.orEmpty()), + isPending = false, + ) + } } \ No newline at end of file diff --git a/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducer.kt b/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducer.kt index cac9b3c1a3..b84ffe3439 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducer.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducer.kt @@ -35,10 +35,7 @@ internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor( ) : SingleYieldBalanceProducer { override val fallback: YieldBalance by lazy { - YieldBalance.Error( - integrationId = params.stakingId.integrationId, - address = params.stakingId.address, - ) + YieldBalance.Error(stakingId = params.stakingId) } override fun produce(): Flow { @@ -50,7 +47,7 @@ internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor( .mapNotNull { balances -> val currentStakingId = params.stakingId - val currentBalances = balances.filter { it.getStakingId() == currentStakingId } + val currentBalances = balances.filter { it.stakingId == currentStakingId } if (currentBalances.size > 1) { analyticsExceptionHandler.sendException( diff --git a/data/staking/src/main/java/com/tangem/data/staking/store/DefaultYieldsBalancesStore.kt b/data/staking/src/main/java/com/tangem/data/staking/store/DefaultYieldsBalancesStore.kt index 10186038e7..797c65a1cc 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/store/DefaultYieldsBalancesStore.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/store/DefaultYieldsBalancesStore.kt @@ -46,6 +46,8 @@ internal class DefaultYieldsBalancesStore( value = cachedStatuses.map { (stringWalletId, wrappers) -> val key = UserWalletId(stringWalletId) val value = YieldBalanceConverter(isCached = true).convertSet(input = wrappers) + .filterNotNull() + .toSet() key to value } @@ -61,7 +63,7 @@ internal class DefaultYieldsBalancesStore( override suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingId: StakingID): YieldBalance? { return runtimeStore.getSyncOrNull() ?.get(userWalletId) - ?.firstOrNull { stakingId == it.getStakingId() } + ?.firstOrNull { it.stakingId == stakingId } } override suspend fun getAllSyncOrNull(userWalletId: UserWalletId): Set? { @@ -96,11 +98,13 @@ internal class DefaultYieldsBalancesStore( private suspend fun storeInRuntime(userWalletId: UserWalletId, values: Set) { val newBalances = YieldBalanceConverter(isCached = false).convertSet(input = values) + .filterNotNull() + .toSet() runtimeStore.update(default = emptyMap()) { saved -> saved.toMutableMap().apply { this[userWalletId] = saved[userWalletId] - ?.addOrReplace(newBalances) { old, new -> old.getStakingId() == new.getStakingId() } + ?.addOrReplace(newBalances) { old, new -> old.stakingId == new.stakingId } ?: newBalances } } @@ -135,7 +139,7 @@ internal class DefaultYieldsBalancesStore( val balances = stakingIds.mapNotNullTo(hashSetOf()) { stakingId -> val balance = portfolioBalances - .firstOrNull { stakingId == it.getStakingId() } + .firstOrNull { it.stakingId == stakingId } ?: ifNotFound(stakingId) ?: return@mapNotNullTo null @@ -143,7 +147,7 @@ internal class DefaultYieldsBalancesStore( } val updatedBalances = portfolioBalances.addOrReplace(items = balances) { old, new -> - old.getStakingId() == new.getStakingId() + old.stakingId == new.stakingId } put(key = userWalletId, value = updatedBalances) @@ -151,9 +155,7 @@ internal class DefaultYieldsBalancesStore( } } - private fun createErrorYieldBalance(id: StakingID): YieldBalance { - return YieldBalance.Error(integrationId = id.integrationId, address = id.address) - } + private fun createErrorYieldBalance(id: StakingID): YieldBalance = YieldBalance.Error(stakingId = id) private fun YieldBalanceWrapperDTO.getStakingId(): StakingID? { val integrationId = integrationId diff --git a/data/staking/src/test/kotlin/com/tangem/data/staking/YieldBalanceExt.kt b/data/staking/src/test/kotlin/com/tangem/data/staking/YieldBalanceExt.kt index c04560fbeb..6481744478 100644 --- a/data/staking/src/test/kotlin/com/tangem/data/staking/YieldBalanceExt.kt +++ b/data/staking/src/test/kotlin/com/tangem/data/staking/YieldBalanceExt.kt @@ -6,5 +6,5 @@ import com.tangem.domain.models.StatusSource import com.tangem.domain.staking.model.stakekit.YieldBalance internal fun YieldBalanceWrapperDTO.toDomain(source: StatusSource = StatusSource.CACHE): YieldBalance { - return YieldBalanceConverter(source = source).convert(this) + return YieldBalanceConverter(source = source).convert(this)!! } \ No newline at end of file diff --git a/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducerTest.kt b/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducerTest.kt index b6f14b7e6b..022f10c3fc 100644 --- a/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducerTest.kt +++ b/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducerTest.kt @@ -84,7 +84,7 @@ internal class DefaultSingleYieldBalanceProducerTest { val producerFlow = producer.produceWithFallback() val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId).toDomain() - val updatedBalance = YieldBalance.Error(integrationId = tonId.integrationId, address = tonId.address) + val updatedBalance = YieldBalance.Error(stakingId = tonId) // Act (first emit) multiFlow.emit(value = setOf(balance)) @@ -162,7 +162,7 @@ internal class DefaultSingleYieldBalanceProducerTest { val actual1 = getEmittedValues(flow = producerFlow) // Assert (first emit) - val fallbackStatus = YieldBalance.Error(integrationId = tonId.integrationId, address = "0x1") + val fallbackStatus = YieldBalance.Error(stakingId = tonId.copy(address = "0x1")) Truth.assertThat(actual1).hasSize(1) Truth.assertThat(actual1).containsExactly(fallbackStatus) diff --git a/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreUpdateMethodsTest.kt b/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreUpdateMethodsTest.kt index 9d80251ee9..1bece04b93 100644 --- a/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreUpdateMethodsTest.kt +++ b/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreUpdateMethodsTest.kt @@ -129,12 +129,7 @@ internal class YieldsBalancesStoreUpdateMethodsTest { store.storeError(userWalletId = userWalletId, stakingIds = setOf(stakingId)) val runtimeExpected = mapOf( - userWalletId to setOf( - YieldBalance.Error( - integrationId = stakingId.integrationId, - address = stakingId.address, - ), - ), + userWalletId to setOf(YieldBalance.Error(stakingId)), ) Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) diff --git a/domain/staking/build.gradle.kts b/domain/staking/build.gradle.kts index 770bfe4838..7b65991ac1 100644 --- a/domain/staking/build.gradle.kts +++ b/domain/staking/build.gradle.kts @@ -19,6 +19,7 @@ dependencies { api(projects.core.analytics) api(projects.core.utils) + implementation(deps.kotlin.datetime) implementation(deps.kotlin.serialization) implementation(deps.jodatime) diff --git a/domain/staking/models/build.gradle.kts b/domain/staking/models/build.gradle.kts index 10abc551b9..5d2fa9274e 100644 --- a/domain/staking/models/build.gradle.kts +++ b/domain/staking/models/build.gradle.kts @@ -11,6 +11,7 @@ dependencies { implementation(projects.domain.core) implementation(projects.domain.models) + implementation(deps.kotlin.datetime) implementation(deps.kotlin.serialization) implementation(deps.jodatime) diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/StakingID.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/StakingID.kt index 6313b9277c..59618e15b0 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/StakingID.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/StakingID.kt @@ -1,4 +1,6 @@ package com.tangem.domain.staking.model -// TODO: make part of YieldBalance in the future +import kotlinx.serialization.Serializable + +@Serializable data class StakingID(val integrationId: String, val address: String) \ No newline at end of file diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt index 44e0681f52..1dd03af2b9 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt @@ -2,166 +2,67 @@ package com.tangem.domain.staking.model.stakekit import com.tangem.domain.models.StatusSource import com.tangem.domain.staking.model.StakingID -import com.tangem.domain.staking.model.stakekit.action.StakingActionType -import org.joda.time.DateTime -import java.math.BigDecimal +import kotlinx.serialization.Serializable -sealed class YieldBalance { +/** + * Represents a yield balance in the staking system + */ +@Serializable +sealed interface YieldBalance { - abstract val integrationId: String? - abstract val address: String? - abstract val source: StatusSource + /** The unique identifier of the staking operation */ + val stakingId: StakingID + /** The source of the status information */ + val source: StatusSource + + /** + * Represents a yield balance with actual data + * + * @property stakingId the unique identifier of the staking operation + * @property source the source of the status information + * @property balance the balance details of the yield + */ + @Serializable + data class Data( + override val stakingId: StakingID, + override val source: StatusSource, + val balance: YieldBalanceItem, + ) : YieldBalance + + /** + * Represents an empty yield balance + * + * @property stakingId the unique identifier of the staking operation + * @property source the source of the status information + */ + @Serializable + data class Empty( + override val stakingId: StakingID, + override val source: StatusSource, + ) : YieldBalance + + /** + * Represents an error state for the yield balance + * + * @property stakingId the unique identifier of the staking operation + */ + @Serializable + data class Error(override val stakingId: StakingID) : YieldBalance { + override val source: StatusSource = StatusSource.ACTUAL + } + + /** + * Creates a copy of the current yield balance with a new status source + * + * @param source the new source of the status information + */ fun copySealed(source: StatusSource): YieldBalance { return when (this) { is Data -> copy(source = source) is Empty -> copy(source = source) is Error, - is Unsupported, -> this } } - - fun getStakingId(): StakingID? { - val integrationId = integrationId - val address = address - - if (integrationId == null || address == null) return null - - return StakingID(integrationId = integrationId, address = address) - } - - data class Data( - override val integrationId: String?, - override val address: String, - override val source: StatusSource, - val balance: YieldBalanceItem, - ) : YieldBalance() - - data class Empty( - override val integrationId: String?, - override val address: String, - override val source: StatusSource, - ) : YieldBalance() - - data object Unsupported : YieldBalance() { - override val integrationId: String? = null - override val address: String? = null - override val source: StatusSource = StatusSource.ACTUAL - } - - data class Error(override val integrationId: String?, override val address: String?) : YieldBalance() { - override val source: StatusSource = StatusSource.ACTUAL - } -} - -data class YieldBalanceItem( - val items: List, - val integrationId: String?, -) - -data class BalanceItem( - val groupId: String, - val token: Token, - val type: BalanceType, - val amount: BigDecimal, - val rawCurrencyId: String?, - val validatorAddress: String?, - val date: DateTime?, - val pendingActions: List, - val pendingActionsConstraints: List, - val isPending: Boolean, -) - -data class PendingActionConstraints( - val type: StakingActionType, - val amountArg: PendingAction.PendingActionArgs.Amount?, -) - -data class PendingAction( - val type: StakingActionType, - val passthrough: String, - val args: PendingActionArgs?, -) { - data class PendingActionArgs( - val amount: Amount?, - val duration: Duration?, - val validatorAddress: Boolean?, - val validatorAddresses: Boolean?, - val tronResource: TronResource?, - val signatureVerification: Boolean?, - ) { - data class Amount( - val required: Boolean, - val minimum: BigDecimal?, - val maximum: BigDecimal?, - ) - - data class Duration( - val required: Boolean, - val minimum: Int?, - val maximum: Int?, - ) - - data class TronResource( - val required: Boolean, - val options: List, - ) - } -} - -/** - * IMPORTANT!!! - * Order is used to sort balances. - */ -@Suppress("MagicNumber") -enum class BalanceType(val order: Int) { - AVAILABLE(1), - STAKED(2), - PREPARING(3), - LOCKED(4), - UNSTAKING(5), - UNLOCKING(6), - UNSTAKED(7), - REWARDS(8), - UNKNOWN(9), - ; - - companion object { - fun BalanceType.isClickable() = when (this) { - STAKED, - UNSTAKED, - LOCKED, - -> true - AVAILABLE, - UNSTAKING, - PREPARING, - REWARDS, - UNLOCKING, - UNKNOWN, - -> false - } - } -} - -enum class RewardBlockType { - NoRewards, - Rewards, - RewardsRequirementsError, - RewardUnavailable, - ; - - /** - * Indicated whether action can be performed on available reward - * For example, pending action CLAIM_REWARDS could be called - */ - val isActionable: Boolean - get() = when (this) { - NoRewards, - RewardUnavailable, - -> false - RewardsRequirementsError, - Rewards, - -> true - } } \ No newline at end of file diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalanceItem.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalanceItem.kt new file mode 100644 index 0000000000..b5bbd086b5 --- /dev/null +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalanceItem.kt @@ -0,0 +1,130 @@ +package com.tangem.domain.staking.model.stakekit + +import com.tangem.domain.core.serialization.SerializedBigDecimal +import com.tangem.domain.staking.model.stakekit.action.StakingActionType +import kotlinx.datetime.Instant +import kotlinx.serialization.Serializable + +@Serializable +data class YieldBalanceItem( + val items: List, + val integrationId: String, +) + +@Serializable +data class BalanceItem( + val groupId: String, + val token: Token, + val type: BalanceType, + val amount: SerializedBigDecimal, + val rawCurrencyId: String?, + val validatorAddress: String?, + val date: Instant?, + val pendingActions: List, + val pendingActionsConstraints: List, + val isPending: Boolean, +) + +@Serializable +data class PendingActionConstraints( + val type: StakingActionType, + val amountArg: PendingAction.PendingActionArgs.Amount?, +) + +@Serializable +data class PendingAction( + val type: StakingActionType, + val passthrough: String, + val args: PendingActionArgs?, +) { + + @Serializable + data class PendingActionArgs( + val amount: Amount?, + val duration: Duration?, + val validatorAddress: Boolean?, + val validatorAddresses: Boolean?, + val tronResource: TronResource?, + val signatureVerification: Boolean?, + ) { + + @Serializable + data class Amount( + val required: Boolean, + val minimum: SerializedBigDecimal?, + val maximum: SerializedBigDecimal?, + ) + + @Serializable + data class Duration( + val required: Boolean, + val minimum: Int?, + val maximum: Int?, + ) + + @Serializable + data class TronResource( + val required: Boolean, + val options: List, + ) + } +} + +/** + * IMPORTANT!!! + * Order is used to sort balances. + */ +@Serializable +@Suppress("MagicNumber") +enum class BalanceType(val order: Int) { + AVAILABLE(1), + STAKED(2), + PREPARING(3), + LOCKED(4), + UNSTAKING(5), + UNLOCKING(6), + UNSTAKED(7), + REWARDS(8), + UNKNOWN(9), + ; + + companion object { + + fun BalanceType.isClickable() = when (this) { + STAKED, + UNSTAKED, + LOCKED, + -> true + AVAILABLE, + UNSTAKING, + PREPARING, + REWARDS, + UNLOCKING, + UNKNOWN, + -> false + } + } +} + +@Serializable +enum class RewardBlockType { + NoRewards, + Rewards, + RewardsRequirementsError, + RewardUnavailable, + ; + + /** + * Indicated whether action can be performed on available reward + * For example, pending action CLAIM_REWARDS could be called + */ + val isActionable: Boolean + get() = when (this) { + NoRewards, + RewardUnavailable, + -> false + RewardsRequirementsError, + Rewards, + -> true + } +} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt index 9aaf902fa7..70f695f4ef 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt @@ -420,7 +420,7 @@ abstract class BaseCurrencyStatusOperations( params = MultiYieldBalanceProducer.Params(userWalletId = userWalletId), ) .orEmpty() - .filter { it.getStakingId() in stakingIds } + .filter { it.stakingId in stakingIds } ensure(balances.isNotEmpty()) { Error.EmptyYieldBalances } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt index 0948480baa..797a76ca5f 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt @@ -25,6 +25,7 @@ import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher import com.tangem.domain.quotes.single.SingleQuoteStatusProducer import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier import com.tangem.domain.staking.StakingIdFactory +import com.tangem.domain.staking.model.StakingID import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher @@ -277,12 +278,17 @@ class CachedCurrenciesStatusesOperations( ): YieldBalance? { if (yieldBalances.isNullOrEmpty()) return null - val supportedIntegration = StakingIntegrationID.create(currencyId = currency.id)?.value ?: return null - + val supportedIntegration = StakingIntegrationID.create(currencyId = currency.id)?.value val address = extractAddress(networkStatus) - return yieldBalances.firstOrNull { it.integrationId == supportedIntegration && it.address == address } - ?: YieldBalance.Error(integrationId = supportedIntegration, address = address) + return if (supportedIntegration != null && address != null) { + val stakingId = StakingID(integrationId = supportedIntegration, address = address) + + yieldBalances.firstOrNull { it.stakingId == stakingId } + ?: YieldBalance.Error(stakingId = stakingId) + } else { + null + } } private fun getCurrencies(userWalletId: UserWalletId): EitherFlow> { @@ -390,7 +396,7 @@ class CachedCurrenciesStatusesOperations( ) .onEach { balance -> state.update { loadedBalances -> - loadedBalances.addOrReplace(balance) { balance.getStakingId() == it } + loadedBalances.addOrReplace(balance) { balance.stakingId == it } } } .launchIn(scope = this) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt index 82c396b43b..ca1b7b0fcb 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt @@ -80,7 +80,8 @@ internal class CurrencyStatusOperations( val hasCurrentNetworkTransactions = networkStatusValue.pendingTransactions.isNotEmpty() val currentTransactions = networkStatusValue.pendingTransactions.getOrElse(currency.id, ::emptySet) val yieldBalanceData = yieldBalance as? YieldBalance.Data - val isCurrentAddressStaking = yieldBalanceData?.address == networkStatusValue.address.defaultAddress.value + val isCurrentAddressStaking = + yieldBalanceData?.stakingId?.address == networkStatusValue.address.defaultAddress.value val filteredTokenBalances = yieldBalanceData?.balance?.items?.filter { it.token.coinGeckoId == currency.id.rawCurrencyId?.value } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt index c1778e6af1..53b7db96ff 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt @@ -7,6 +7,7 @@ import arrow.core.toNonEmptySetOrNull import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.quote.QuoteStatus +import com.tangem.domain.staking.model.StakingID import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.tokens.model.CryptoCurrencyStatus @@ -74,9 +75,12 @@ class CurrencyStatusProxyCreator { val address = extractAddress(networkStatus) val supportedIntegration = StakingIntegrationID.create(currencyId = currency.id)?.value - val yieldBalance = if (supportedIntegration != null) { - yieldBalances?.firstOrNull { it.integrationId == supportedIntegration && it.address == address } - ?: YieldBalance.Error(integrationId = supportedIntegration, address = address) + + val yieldBalance = if (supportedIntegration != null && address != null) { + val stakingId = StakingID(integrationId = supportedIntegration, address = address) + + yieldBalances?.firstOrNull { it.stakingId == stakingId } + ?: YieldBalance.Error(stakingId = stakingId) } else { null } diff --git a/features/staking/impl/build.gradle.kts b/features/staking/impl/build.gradle.kts index 762201aacb..67246e25f1 100644 --- a/features/staking/impl/build.gradle.kts +++ b/features/staking/impl/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { implementation(deps.androidx.paging.runtime) /** Other dependencies */ + implementation(deps.kotlin.datetime) implementation(deps.kotlin.immutable.collections) implementation(deps.material) implementation(deps.arrow.core) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt index 6b747f4dbd..bdc0f43c77 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt @@ -22,7 +22,7 @@ import com.tangem.utils.Provider import com.tangem.utils.converter.Converter import com.tangem.utils.extensions.orZero import kotlinx.collections.immutable.toPersistentList -import org.joda.time.DateTime +import kotlinx.datetime.Instant import java.math.BigDecimal import java.util.Calendar @@ -122,7 +122,7 @@ internal class BalanceItemConverter( -> null } - private fun getUnbondingDate(date: DateTime?): TextReference? { + private fun getUnbondingDate(date: Instant?): TextReference? { val unbondingPeriod = yield.metadata.cooldownPeriod?.days ?: return null if (date == null) { return combinedReference( @@ -136,7 +136,7 @@ internal class BalanceItemConverter( nowCalendar.resetHours() val endDate = Calendar.getInstance() - endDate.timeInMillis = date.millis + endDate.timeInMillis = date.toEpochMilliseconds() endDate.resetHours() val days = ((endDate.timeInMillis - nowCalendar.timeInMillis) / DAY_IN_MILLIS).toInt() diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt index ee906adb33..f82c1ece1f 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt @@ -50,7 +50,7 @@ internal class YieldBalancesConverter( ?.firstOrNull { it.type == StakingActionType.CLAIM_REWARDS } InnerYieldBalanceState.Data( - integrationId = yieldBalance?.integrationId, + integrationId = yieldBalance?.stakingId?.integrationId, reward = YieldReward( rewardsCrypto = cryptoRewardsValue.format { crypto(cryptoCurrency) }, rewardsFiat = fiatRewardsValue.format { diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 03d9b1f740..1f665af06c 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -73,6 +73,7 @@ viewBindingDelegate = "1.5.9" xmlShimmer = "1.1.3" zxingQrCode = "3.5.1" kotlinSerialization = "1.8.0" +kotlinDatetime = "0.6.2" arrow = "1.2.4" # 2.0.1 breaks the build reownCore = "1.1.2" reownWeb3 = "1.1.2" @@ -258,6 +259,7 @@ viewBindingDelegate = { module = "com.github.kirich1409:viewbindingpropertydeleg xmlShimmer = { module = "com.github.skydoves:androidveil", version.ref = "xmlShimmer" } zxing-qrCore = { module = "com.google.zxing:core", version.ref = "zxingQrCode" } kotlin-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinSerialization" } +kotlin-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinDatetime" } arrow-core = { module = "io.arrow-kt:arrow-core", version.ref = "arrow" } arrow-fx = { module = "io.arrow-kt:arrow-fx-coroutines", version.ref = "arrow" } reownCore = { module = "com.reown:android-core", version.ref = "reownCore" } From fb54de419b0410683984152f36cb8386e10c07e6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 30 Jul 2025 17:56:53 +0500 Subject: [PATCH 17/53] Updated on 2026-08-14 --- app/build.gradle.kts | 2 + .../kotlin/com/tangem/tests/StoriesTest.kt | 4 +- .../main/java/com/tangem/tap/MainActivity.kt | 31 +- .../analytics/events/IntroductionProcess.kt | 18 -- .../tap/common/analytics/events/Shop.kt | 30 -- .../CardContextInterceptor.kt | 2 +- .../tap/common/compose/extensions/Dp.kt | 17 -- .../tap/common/compose/extensions/Painter.kt | 20 -- .../com/tangem/tap/common/extensions/Int.kt | 3 - .../com/tangem/tap/common/redux/AppReducer.kt | 2 - .../com/tangem/tap/common/redux/AppState.kt | 4 - .../tangem/tap/common/ui/ScanFailsDialog.kt | 8 +- .../com/tangem/tap/di/IntentHandlingModule.kt | 34 +++ .../details/redux/DetailsMiddleware.kt | 2 +- .../ui/resetcard/model/ResetCardModel.kt | 2 +- .../tap/features/home/DefaultHomeComponent.kt | 90 ------ .../com/tangem/tap/features/home/HomeModel.kt | 166 ----------- .../tap/features/home/RegionProvider.kt | 16 -- .../home/TangemTangemErrorsHandler.kt | 44 --- .../tap/features/home/api/HomeComponent.kt | 9 - .../tap/features/home/di/HomeFeatureModule.kt | 25 -- .../home/errors/TangemSdkErrorHandler.kt | 8 - .../tap/features/home/redux/HomeAction.kt | 21 -- .../tap/features/home/redux/HomeMiddleware.kt | 147 ---------- .../tap/features/home/redux/HomeReducer.kt | 31 -- .../handlers/BackgroundScanIntentHandler.kt | 32 +-- .../WalletConnectLinkIntentHandler.kt | 2 +- .../welcome/component/WelcomeComponent.kt | 2 + .../features/welcome/model/WelcomeModel.kt | 8 +- .../welcome/redux/WelcomeMiddleware.kt | 15 +- .../tangem/tap/routing/utils/ChildFactory.kt | 7 +- .../com/tangem/common/routing/AppRoute.kt | 7 +- .../routing/entity/InitScreenLaunchMode.kt | 13 + .../routing/entity/SerializableIntent.kt | 1 + .../tangem/core/ui/utils}/AnimatedValue.kt | 2 +- .../com/tangem/core/ui/utils/DensityUtils.kt | 20 +- .../com/tangem/core/ui/utils}/ImageBitmap.kt | 2 +- .../res/drawable-hdpi/img_meet_tangem.webp | Bin .../img_revolutionary_wallet.webp | Bin .../img_tangem_for_everyone.webp | Bin .../res/drawable-mdpi/img_meet_tangem.webp | Bin .../img_revolutionary_wallet.webp | Bin .../img_tangem_for_everyone.webp | Bin .../res/drawable-xhdpi/img_meet_tangem.webp | Bin .../img_revolutionary_wallet.webp | Bin .../img_tangem_for_everyone.webp | Bin .../res/drawable-xxhdpi/img_meet_tangem.webp | Bin .../img_revolutionary_wallet.webp | Bin .../img_tangem_for_everyone.webp | Bin .../res/drawable-xxxhdpi/img_meet_tangem.webp | Bin .../img_revolutionary_wallet.webp | Bin .../img_tangem_for_everyone.webp | Bin .../ui}/src/main/res/drawable/currency0.webp | Bin .../ui}/src/main/res/drawable/currency1.webp | Bin .../ui}/src/main/res/drawable/currency2.webp | Bin .../ui}/src/main/res/drawable/currency3.webp | Bin .../ui}/src/main/res/drawable/currency4.webp | Bin .../ui}/src/main/res/drawable/dapps1.webp | Bin .../ui}/src/main/res/drawable/dapps2.webp | Bin .../ui}/src/main/res/drawable/dapps3.webp | Bin .../ui}/src/main/res/drawable/dapps4.webp | Bin .../ui}/src/main/res/drawable/dapps5.webp | Bin .../src/main/res/drawable/ic_tangem_logo.xml | 0 .../img_card_placeholder_wallet_2.webp | Bin .../java/com/tangem/utils/extensions/Int.kt | 6 - .../disclaimer/impl/model/DisclaimerModel.kt | 2 +- features/home/api/.gitignore | 1 + features/home/api/build.gradle.kts | 17 ++ .../tangem/features/home/api/HomeComponent.kt | 17 ++ features/home/impl/.gitignore | 1 + features/home/impl/build.gradle.kts | 69 +++++ .../home/impl/DefaultHomeComponent.kt | 40 +++ .../home/impl/analytics/AnalyticsParam.kt | 9 + .../impl/analytics/IntroductionProcess.kt | 14 + .../analytics/ParamCardCurrencyConverter.kt | 23 ++ .../features/home/impl/analytics/Shop.kt | 11 + .../home/impl/di/HomeFeatureModule.kt | 33 +++ .../features/home/impl/model/HomeModel.kt | 268 ++++++++++++++++++ .../com/tangem/features/home/impl/ui/Home.kt | 35 +++ .../home/impl/ui}/compose/StoriesAnimation.kt | 6 +- .../home/impl/ui}/compose/StoriesScreen.kt | 25 +- .../home/impl/ui}/compose/StoriesScreenV2.kt | 23 +- .../home/impl/ui}/compose/content/Content.kt | 8 +- .../compose/content/CurrenciesWeb3Content.kt | 18 +- .../compose/content/FirstStoriesContent.kt | 6 +- .../compose/content/FloatingCardsContent.kt | 10 +- .../impl/ui}/compose/views/HomeButtons.kt | 4 +- .../impl/ui}/compose/views/HomeButtonsV2.kt | 4 +- .../compose/views/SearchCurrenciesButton.kt | 4 +- .../impl/ui}/compose/views/StoriesButton.kt | 2 +- .../ui}/compose/views/StoriesProgressBar.kt | 2 +- .../features/home/impl/ui/state/HomeUM.kt | 19 +- .../entry/impl/model/OnboardingEntryModel.kt | 2 +- .../model/WalletSettingsModel.kt | 2 +- .../model/intents/WalletCardClickIntents.kt | 2 +- .../router/DefaultWalletRouter.kt | 2 +- .../features/welcome/WelcomeComponent.kt | 2 + settings.gradle.kts | 3 + 98 files changed, 750 insertions(+), 787 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/common/analytics/events/IntroductionProcess.kt delete mode 100644 app/src/main/java/com/tangem/tap/common/analytics/events/Shop.kt delete mode 100644 app/src/main/java/com/tangem/tap/common/compose/extensions/Dp.kt delete mode 100644 app/src/main/java/com/tangem/tap/common/compose/extensions/Painter.kt delete mode 100644 app/src/main/java/com/tangem/tap/common/extensions/Int.kt create mode 100644 app/src/main/java/com/tangem/tap/di/IntentHandlingModule.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/home/DefaultHomeComponent.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/home/HomeModel.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/home/RegionProvider.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/home/TangemTangemErrorsHandler.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/home/api/HomeComponent.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/home/di/HomeFeatureModule.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/home/errors/TangemSdkErrorHandler.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt create mode 100644 common/routing/src/main/kotlin/com/tangem/common/routing/entity/InitScreenLaunchMode.kt rename {app/src/main/java/com/tangem/tap/common/compose/extensions => core/ui/src/main/java/com/tangem/core/ui/utils}/AnimatedValue.kt (96%) rename {app/src/main/java/com/tangem/tap/common/compose/extensions => core/ui/src/main/java/com/tangem/core/ui/utils}/ImageBitmap.kt (92%) rename {app => core/ui}/src/main/res/drawable-hdpi/img_meet_tangem.webp (100%) rename {app => core/ui}/src/main/res/drawable-hdpi/img_revolutionary_wallet.webp (100%) rename {app => core/ui}/src/main/res/drawable-hdpi/img_tangem_for_everyone.webp (100%) rename {app => core/ui}/src/main/res/drawable-mdpi/img_meet_tangem.webp (100%) rename {app => core/ui}/src/main/res/drawable-mdpi/img_revolutionary_wallet.webp (100%) rename {app => core/ui}/src/main/res/drawable-mdpi/img_tangem_for_everyone.webp (100%) rename {app => core/ui}/src/main/res/drawable-xhdpi/img_meet_tangem.webp (100%) rename {app => core/ui}/src/main/res/drawable-xhdpi/img_revolutionary_wallet.webp (100%) rename {app => core/ui}/src/main/res/drawable-xhdpi/img_tangem_for_everyone.webp (100%) rename {app => core/ui}/src/main/res/drawable-xxhdpi/img_meet_tangem.webp (100%) rename {app => core/ui}/src/main/res/drawable-xxhdpi/img_revolutionary_wallet.webp (100%) rename {app => core/ui}/src/main/res/drawable-xxhdpi/img_tangem_for_everyone.webp (100%) rename {app => core/ui}/src/main/res/drawable-xxxhdpi/img_meet_tangem.webp (100%) rename {app => core/ui}/src/main/res/drawable-xxxhdpi/img_revolutionary_wallet.webp (100%) rename {app => core/ui}/src/main/res/drawable-xxxhdpi/img_tangem_for_everyone.webp (100%) rename {app => core/ui}/src/main/res/drawable/currency0.webp (100%) rename {app => core/ui}/src/main/res/drawable/currency1.webp (100%) rename {app => core/ui}/src/main/res/drawable/currency2.webp (100%) rename {app => core/ui}/src/main/res/drawable/currency3.webp (100%) rename {app => core/ui}/src/main/res/drawable/currency4.webp (100%) rename {app => core/ui}/src/main/res/drawable/dapps1.webp (100%) rename {app => core/ui}/src/main/res/drawable/dapps2.webp (100%) rename {app => core/ui}/src/main/res/drawable/dapps3.webp (100%) rename {app => core/ui}/src/main/res/drawable/dapps4.webp (100%) rename {app => core/ui}/src/main/res/drawable/dapps5.webp (100%) rename {app => core/ui}/src/main/res/drawable/ic_tangem_logo.xml (100%) rename {app => core/ui}/src/main/res/drawable/img_card_placeholder_wallet_2.webp (100%) delete mode 100644 core/utils/src/main/java/com/tangem/utils/extensions/Int.kt create mode 100644 features/home/api/.gitignore create mode 100644 features/home/api/build.gradle.kts create mode 100644 features/home/api/src/main/kotlin/com/tangem/features/home/api/HomeComponent.kt create mode 100644 features/home/impl/.gitignore create mode 100644 features/home/impl/build.gradle.kts create mode 100644 features/home/impl/src/main/kotlin/com/tangem/features/home/impl/DefaultHomeComponent.kt create mode 100644 features/home/impl/src/main/kotlin/com/tangem/features/home/impl/analytics/AnalyticsParam.kt create mode 100644 features/home/impl/src/main/kotlin/com/tangem/features/home/impl/analytics/IntroductionProcess.kt create mode 100644 features/home/impl/src/main/kotlin/com/tangem/features/home/impl/analytics/ParamCardCurrencyConverter.kt create mode 100644 features/home/impl/src/main/kotlin/com/tangem/features/home/impl/analytics/Shop.kt create mode 100644 features/home/impl/src/main/kotlin/com/tangem/features/home/impl/di/HomeFeatureModule.kt create mode 100644 features/home/impl/src/main/kotlin/com/tangem/features/home/impl/model/HomeModel.kt create mode 100644 features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/Home.kt rename {app/src/main/java/com/tangem/tap/features/home => features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui}/compose/StoriesAnimation.kt (96%) rename {app/src/main/java/com/tangem/tap/features/home => features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui}/compose/StoriesScreen.kt (94%) rename {app/src/main/java/com/tangem/tap/features/home => features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui}/compose/StoriesScreenV2.kt (94%) rename {app/src/main/java/com/tangem/tap/features/home => features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui}/compose/content/Content.kt (96%) rename {app/src/main/java/com/tangem/tap/features/home => features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui}/compose/content/CurrenciesWeb3Content.kt (93%) rename {app/src/main/java/com/tangem/tap/features/home => features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui}/compose/content/FirstStoriesContent.kt (94%) rename {app/src/main/java/com/tangem/tap/features/home => features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui}/compose/content/FloatingCardsContent.kt (92%) rename {app/src/main/java/com/tangem/tap/features/home => features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui}/compose/views/HomeButtons.kt (97%) rename {app/src/main/java/com/tangem/tap/features/home => features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui}/compose/views/HomeButtonsV2.kt (98%) rename {app/src/main/java/com/tangem/tap/features/home => features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui}/compose/views/SearchCurrenciesButton.kt (94%) rename {app/src/main/java/com/tangem/tap/features/home => features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui}/compose/views/StoriesButton.kt (97%) rename {app/src/main/java/com/tangem/tap/features/home => features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui}/compose/views/StoriesProgressBar.kt (98%) rename app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt => features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/state/HomeUM.kt (58%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 040b46b637..19dd55b0e1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -232,6 +232,8 @@ dependencies { implementation(projects.features.welcome.impl) implementation(projects.features.createWalletSelection.api) implementation(projects.features.createWalletSelection.impl) + implementation(projects.features.home.api) + implementation(projects.features.home.impl) /** AndroidX libraries */ implementation(deps.androidx.core.ktx) diff --git a/app/src/androidTest/kotlin/com/tangem/tests/StoriesTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/StoriesTest.kt index 303ce3f6a0..8fe8ee6203 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/StoriesTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/StoriesTest.kt @@ -3,8 +3,8 @@ package com.tangem.tests import android.content.Intent.ACTION_VIEW import com.tangem.common.BaseTestCase import com.tangem.common.extensions.clickWithAssertion -import com.tangem.screens.onDisclaimerScreen -import com.tangem.screens.onStoriesScreen +import com.tangem.screens.DisclaimerTestScreen +import com.tangem.screens.StoriesTestScreen import com.tangem.tap.features.home.redux.HomeMiddleware.NEW_BUY_WALLET_URL import dagger.hilt.android.testing.HiltAndroidTest import io.github.kakaocup.kakao.intent.KIntent diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 776dbb7b61..7be16eab0a 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -176,13 +176,22 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { @Inject internal lateinit var testerMenuLauncher: TesterMenuLauncher + @Inject + internal lateinit var intentProcessor: IntentProcessor + + @Inject + internal lateinit var walletConnectLinkIntentHandler: WalletConnectLinkIntentHandler + + @Inject + internal lateinit var onPushClickedIntentHandler: OnPushClickedIntentHandler + + @Inject + internal lateinit var backgroundScanIntentHandler: BackgroundScanIntentHandler + internal val viewModel: MainViewModel by viewModels() private lateinit var appThemeModeFlow: SharedFlow - // TODO: fixme: inject through DI - private val intentProcessor: IntentProcessor = IntentProcessor() - private val dialogManager = DialogManager() private val onActivityResultCallbacks = mutableListOf() @@ -344,12 +353,10 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { } private fun initIntentHandlers() { - val hasSavedWalletsProvider = { userWalletsListManager.hasUserWallets } - intentProcessor.addHandler(OnPushClickedIntentHandler(analyticsEventsHandler)) - intentProcessor.addHandler(BackgroundScanIntentHandler(hasSavedWalletsProvider, lifecycleScope)) + intentProcessor.addHandler(onPushClickedIntentHandler) if (!walletConnectFeatureToggles.isRedesignedWalletConnectEnabled) { - intentProcessor.addHandler(WalletConnectLinkIntentHandler()) + intentProcessor.addHandler(walletConnectLinkIntentHandler) } } @@ -435,9 +442,15 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { } private fun navigateToInitialScreen(intentWhichStartedActivity: Intent?) { + val launchMode = backgroundScanIntentHandler.getInitScreenLaunchMode(intentWhichStartedActivity) if (userWalletsListManager.isLockable && userWalletsListManager.hasUserWallets) { store.dispatchNavigationAction { - replaceAll(AppRoute.Welcome(intentWhichStartedActivity?.let(::SerializableIntent))) + replaceAll( + AppRoute.Welcome( + launchMode = launchMode, + intent = intentWhichStartedActivity?.let(::SerializableIntent), + ), + ) } intentProcessor.handleIntent( intent = intentWhichStartedActivity, @@ -452,7 +465,7 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { val route = when { shouldShowTos -> AppRoute.Disclaimer(isTosAccepted = false) shouldShowInitialPush -> AppRoute.PushNotification - else -> AppRoute.Home + else -> AppRoute.Home(launchMode = launchMode) } store.dispatchNavigationAction { replaceAll(route) } diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/IntroductionProcess.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/IntroductionProcess.kt deleted file mode 100644 index b5ce0042ae..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/IntroductionProcess.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.tangem.tap.common.analytics.events - -import com.tangem.core.analytics.models.AnalyticsEvent - -/** -[REDACTED_AUTHOR] - */ -sealed class IntroductionProcess( - event: String, - params: Map = mapOf(), -) : AnalyticsEvent("Introduction Process", event, params) { - - class ScreenOpened : IntroductionProcess("Introduction Process Screen Opened") - class ButtonTokensList : IntroductionProcess("Button - Tokens List") - class ButtonBuyCards : IntroductionProcess("Button - Buy Cards") - class ButtonScanCard : IntroductionProcess("Button - Scan Card") - class ButtonRequestSupport : IntroductionProcess("Button - Request Support") -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Shop.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Shop.kt deleted file mode 100644 index 4076de8685..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Shop.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.tangem.tap.common.analytics.events - -import com.tangem.core.analytics.models.AnalyticsEvent -import com.tangem.tap.common.extensions.filterNotNull - -/** -[REDACTED_AUTHOR] - */ -sealed class Shop( - event: String, - params: Map = mapOf(), -) : AnalyticsEvent("Shop", event, params) { - - class ScreenOpened : Shop("Shop Screen Opened") - - class Purchased(sku: String, count: String, amount: String, couponCode: String?) : Shop( - event = "Purchased", - params = mapOf( - "SKU" to sku, - "Count" to count, - "Amount" to amount, - "Coupon Code" to couponCode, - ).filterNotNull(), - ) - - class Redirected(partnerName: String?) : Shop( - event = "Redirected", - params = partnerName?.let { mapOf("Partner" to partnerName) } ?: mapOf(), - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt b/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt index 654dbe07ca..8f14501047 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt @@ -6,9 +6,9 @@ import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.models.scan.ProductType import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.wallets.builder.UserWalletIdBuilder +import com.tangem.features.home.impl.analytics.IntroductionProcess import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.IntroductionProcess import com.tangem.tap.common.extensions.inject import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.proxy.redux.DaggerGraphState diff --git a/app/src/main/java/com/tangem/tap/common/compose/extensions/Dp.kt b/app/src/main/java/com/tangem/tap/common/compose/extensions/Dp.kt deleted file mode 100644 index 04240d7b55..0000000000 --- a/app/src/main/java/com/tangem/tap/common/compose/extensions/Dp.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.tangem.tap.common.compose.extensions - -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.DpSize - -/** -[REDACTED_AUTHOR] - */ -@Composable -fun Dp.toPx(): Float { - val currentDp = this - return with(LocalDensity.current) { currentDp.toPx() } -} - -fun DpSize.halfHeight(): Dp = this.height / 2 \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/extensions/Painter.kt b/app/src/main/java/com/tangem/tap/common/compose/extensions/Painter.kt deleted file mode 100644 index dfabb2cddf..0000000000 --- a/app/src/main/java/com/tangem/tap/common/compose/extensions/Painter.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.tangem.tap.common.compose.extensions - -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.unit.DpSize -import androidx.compose.ui.unit.dp -import com.tangem.sdk.extensions.pxToDp - -/** -[REDACTED_AUTHOR] - */ -@Composable -fun Painter.dpSize(): DpSize = DpSize( - intrinsicSize.width.pxToDp().dp, - intrinsicSize.height.pxToDp().dp, -) - -@Composable -private fun Float.pxToDp(): Float = LocalContext.current.pxToDp(this) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Int.kt b/app/src/main/java/com/tangem/tap/common/extensions/Int.kt deleted file mode 100644 index 555dc252bd..0000000000 --- a/app/src/main/java/com/tangem/tap/common/extensions/Int.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.tangem.tap.common.extensions - -fun Int.isEven() = this and 1 == 0 \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index 111bad630c..8bd03da756 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -3,7 +3,6 @@ package com.tangem.tap.common.redux import com.tangem.tap.common.redux.global.globalReducer import com.tangem.tap.features.details.redux.DetailsReducer import com.tangem.tap.features.details.redux.walletconnect.WalletConnectReducer -import com.tangem.tap.features.home.redux.HomeReducer import com.tangem.tap.features.welcome.redux.WelcomeReducer import com.tangem.tap.proxy.redux.DaggerGraphReducer import org.rekotlin.Action @@ -14,7 +13,6 @@ fun appReducer(action: Action, state: AppState?): AppState { return AppState( globalState = globalReducer(action, state), - homeState = HomeReducer.reduce(action, state), detailsState = DetailsReducer.reduce(action, state), walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState), welcomeState = WelcomeReducer.reduce(action, state), diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index 3df1fae79b..427a103a98 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -7,8 +7,6 @@ import com.tangem.tap.features.details.redux.DetailsMiddleware import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.features.details.redux.walletconnect.WalletConnectMiddleware import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState -import com.tangem.tap.features.home.redux.HomeMiddleware -import com.tangem.tap.features.home.redux.HomeState import com.tangem.tap.features.onboarding.products.wallet.redux.BackupMiddleware import com.tangem.tap.features.wallet.redux.middlewares.TradeCryptoMiddleware import com.tangem.tap.features.welcome.redux.WelcomeMiddleware @@ -20,7 +18,6 @@ import org.rekotlin.StateType data class AppState( val globalState: GlobalState = GlobalState(), - val homeState: HomeState = HomeState(), val detailsState: DetailsState = DetailsState(), val walletConnectState: WalletConnectState = WalletConnectState(), val welcomeState: WelcomeState = WelcomeState(), @@ -32,7 +29,6 @@ data class AppState( return listOf( logMiddleware, GlobalMiddleware.handler, - HomeMiddleware.handler, DetailsMiddleware().detailsMiddleware, WalletConnectMiddleware().walletConnectMiddleware, BackupMiddleware().backupMiddleware, diff --git a/app/src/main/java/com/tangem/tap/common/ui/ScanFailsDialog.kt b/app/src/main/java/com/tangem/tap/common/ui/ScanFailsDialog.kt index 6096730a8e..61b24a7d63 100644 --- a/app/src/main/java/com/tangem/tap/common/ui/ScanFailsDialog.kt +++ b/app/src/main/java/com/tangem/tap/common/ui/ScanFailsDialog.kt @@ -4,6 +4,7 @@ import android.content.Context import android.view.View import android.widget.TextView import androidx.appcompat.app.AlertDialog +import androidx.compose.ui.text.intl.Locale import androidx.core.view.isVisible import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.models.AnalyticsParam @@ -14,8 +15,6 @@ import com.tangem.tap.common.analytics.events.ScanFailsDialogAnalytics import com.tangem.tap.common.extensions.dispatchDialogHide import com.tangem.tap.common.extensions.dispatchOpenUrl import com.tangem.tap.common.extensions.inject -import com.tangem.tap.features.home.LocaleRegionProvider -import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.scope import com.tangem.tap.store @@ -29,6 +28,7 @@ internal object ScanFailsDialog { private const val HOW_TO_SCAN_RU_LINK = "https://tangem.com/ru/blog/post/scan-tangem-card/" private const val HOW_TO_SCAN_LINK = "https://tangem.com/en/blog/post/scan-tangem-card/" + private const val RUSSIA_LOCALE = "ru" fun create(context: Context, source: StateDialog.ScanFailsSource, onTryAgain: (() -> Unit)? = null): AlertDialog { return AlertDialog.Builder(context, R.style.CustomMaterialDialog).apply { @@ -62,8 +62,8 @@ internal object ScanFailsDialog { source = sourceAnalytics, ), ) - val locale = LocaleRegionProvider().getRegion() - val link = if (locale.lowercase() == RUSSIA_COUNTRY_CODE) HOW_TO_SCAN_RU_LINK else HOW_TO_SCAN_LINK + val locale = Locale.current.region + val link = if (locale.lowercase() == RUSSIA_LOCALE) HOW_TO_SCAN_RU_LINK else HOW_TO_SCAN_LINK store.dispatchOpenUrl(link) } customView.findViewById(R.id.request_support_button)?.setOnClickListener { diff --git a/app/src/main/java/com/tangem/tap/di/IntentHandlingModule.kt b/app/src/main/java/com/tangem/tap/di/IntentHandlingModule.kt new file mode 100644 index 0000000000..cd7becb2e0 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/di/IntentHandlingModule.kt @@ -0,0 +1,34 @@ +package com.tangem.tap.di + +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.tap.features.intentHandler.IntentProcessor +import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler +import com.tangem.tap.features.intentHandler.handlers.OnPushClickedIntentHandler +import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object IntentHandlingModule { + + @Provides + @Singleton + fun provideBackgroundScanIntentHandler(): BackgroundScanIntentHandler = BackgroundScanIntentHandler() + + @Provides + @Singleton + fun provideWalletConnectLinkIntentHandler(): WalletConnectLinkIntentHandler = WalletConnectLinkIntentHandler() + + @Provides + @Singleton + fun provideOnPushClickedIntentHandler(analyticsEventHandler: AnalyticsEventHandler): OnPushClickedIntentHandler = + OnPushClickedIntentHandler(analyticsEventHandler) + + @Provides + @Singleton + fun provideIntentProcessor(): IntentProcessor = IntentProcessor() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index af4c05b261..3aece96fa9 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -233,7 +233,7 @@ class DetailsMiddleware { deleteSavedAccessCodes() store.inject(DaggerGraphState::walletsRepository).saveShouldSaveUserWallets(item = false) - store.dispatchNavigationAction { replaceAll(AppRoute.Home) } + store.dispatchNavigationAction { replaceAll(AppRoute.Home()) } return CompletionResult.Success(Unit) } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/model/ResetCardModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/model/ResetCardModel.kt index 34e073bf69..2d0af3ca0b 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/model/ResetCardModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/model/ResetCardModel.kt @@ -268,7 +268,7 @@ internal class ResetCardModel @Inject constructor( if (isLocked && userWalletsListManager.hasUserWallets) { store.dispatchNavigationAction { popTo() } } else { - store.dispatchNavigationAction { replaceAll(AppRoute.Home) } + store.dispatchNavigationAction { replaceAll(AppRoute.Home()) } } } } diff --git a/app/src/main/java/com/tangem/tap/features/home/DefaultHomeComponent.kt b/app/src/main/java/com/tangem/tap/features/home/DefaultHomeComponent.kt deleted file mode 100644 index b89b6f5255..0000000000 --- a/app/src/main/java/com/tangem/tap/features/home/DefaultHomeComponent.kt +++ /dev/null @@ -1,90 +0,0 @@ -package com.tangem.tap.features.home - -import androidx.activity.compose.BackHandler -import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext -import com.arkivanov.essenty.lifecycle.subscribe -import com.tangem.core.decompose.context.AppComponentContext -import com.tangem.core.decompose.model.getOrCreateModel -import com.tangem.core.ui.components.SystemBarsIconsDisposable -import com.tangem.core.ui.utils.ChangeRootBackgroundColorEffect -import com.tangem.core.ui.utils.findActivity -import com.tangem.features.hotwallet.HotWalletFeatureToggles -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.features.home.api.HomeComponent -import com.tangem.tap.features.home.compose.StoriesScreen -import com.tangem.tap.features.home.compose.StoriesScreenV2 -import com.tangem.tap.features.home.redux.HomeAction -import com.tangem.tap.features.home.redux.HomeState -import com.tangem.tap.store -import dagger.assisted.Assisted -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject -import org.rekotlin.StoreSubscriber - -@Suppress("UnusedPrivateMember") -internal class DefaultHomeComponent @AssistedInject constructor( - @Assisted appComponentContext: AppComponentContext, - @Assisted params: Unit, - private val hotWalletFeatureToggles: HotWalletFeatureToggles, -) : HomeComponent, AppComponentContext by appComponentContext, StoreSubscriber { - - private val model: HomeModel = getOrCreateModel() - - private var homeState: MutableState = mutableStateOf(store.state.homeState) - - init { - lifecycle.subscribe( - onCreate = { - store.dispatch(HomeAction.OnCreate) - }, - onStart = { - store.subscribe(subscriber = this) { state -> - state - .skipRepeats { oldState, newState -> oldState.homeState == newState.homeState } - .select(AppState::homeState) - } - }, - onStop = { - store.unsubscribe(this) - }, - ) - } - - @Composable - override fun Content(modifier: Modifier) { - val activity = LocalContext.current.findActivity() - BackHandler(onBack = activity::finish) - SystemBarsIconsDisposable(darkIcons = false) - if (hotWalletFeatureToggles.isHotWalletEnabled) { - StoriesScreenV2( - homeState = homeState, - onCreateNewWalletButtonClick = model::onCreateNewWalletScreen, - onAddExistingWalletButtonClick = model::onAddExistingWalletScreen, - onScanButtonClick = model::onScanClick, - ) - } else { - StoriesScreen( - homeState = homeState, - onScanButtonClick = model::onScanClick, - onShopButtonClick = model::onShopClick, - onSearchTokensClick = model::onSearchClick, - ) - } - - ChangeRootBackgroundColorEffect(Color(color = 0xFF010101)) - } - - override fun newState(state: HomeState) { - homeState.value = state - } - - @AssistedFactory - interface Factory : HomeComponent.Factory { - override fun create(context: AppComponentContext, params: Unit): DefaultHomeComponent - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeModel.kt b/app/src/main/java/com/tangem/tap/features/home/HomeModel.kt deleted file mode 100644 index 6110c7ff0c..0000000000 --- a/app/src/main/java/com/tangem/tap/features/home/HomeModel.kt +++ /dev/null @@ -1,166 +0,0 @@ -package com.tangem.tap.features.home - -import androidx.compose.runtime.Stable -import com.google.firebase.analytics.ktx.analytics -import com.google.firebase.ktx.Firebase -import com.tangem.common.routing.AppRoute -import com.tangem.common.routing.AppRoute.ManageTokens.Source -import com.tangem.core.analytics.Analytics -import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.core.analytics.models.AnalyticsParam -import com.tangem.core.analytics.models.Basic -import com.tangem.core.decompose.di.ModelScoped -import com.tangem.core.decompose.model.Model -import com.tangem.core.decompose.navigation.Router -import com.tangem.core.navigation.url.UrlOpener -import com.tangem.domain.card.ScanCardProcessor -import com.tangem.domain.card.repository.CardSdkConfigRepository -import com.tangem.domain.card.common.util.cardTypesResolver -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.settings.repositories.SettingsRepository -import com.tangem.domain.settings.usercountry.GetUserCountryUseCase -import com.tangem.domain.settings.usercountry.models.UserCountry -import com.tangem.domain.tokens.TokensAction -import com.tangem.domain.wallets.builder.ColdUserWalletBuilder -import com.tangem.domain.wallets.usecase.SaveWalletUseCase -import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter -import com.tangem.tap.common.analytics.events.IntroductionProcess -import com.tangem.tap.common.analytics.events.Shop -import com.tangem.tap.common.extensions.dispatchNavigationAction -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.dispatchWithMain -import com.tangem.tap.common.extensions.onUserWalletSelected -import com.tangem.tap.features.home.redux.HIDE_PROGRESS_DELAY -import com.tangem.tap.features.home.redux.HomeAction -import com.tangem.tap.features.home.redux.HomeMiddleware.NEW_BUY_WALLET_URL -import com.tangem.tap.store -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.launch -import timber.log.Timber -import java.util.Locale -import javax.inject.Inject - -@Suppress("LongParameterList") -@Stable -@ModelScoped -internal class HomeModel @Inject constructor( - override val dispatchers: CoroutineDispatcherProvider, - private val scanCardProcessor: ScanCardProcessor, - private val saveWalletUseCase: SaveWalletUseCase, - private val cardSdkConfigRepository: CardSdkConfigRepository, - private val settingsRepository: SettingsRepository, - private val urlOpener: UrlOpener, - private val analyticsEventHandler: AnalyticsEventHandler, - private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory, - private val router: Router, - getUserCountryUseCase: GetUserCountryUseCase, -) : Model() { - - private val tangemErrorHandler = TangemTangemErrorsHandler(store) - - init { - getUserCountryUseCase.invoke() - .distinctUntilChanged() - .filterNotNull() - .onEach { - val userCountry = it.getOrNull() ?: UserCountry.Other(Locale.getDefault().country) - store.dispatchOnMain(HomeAction.UserCountryLoaded(userCountry)) - } - .flowOn(dispatchers.io) - .launchIn(modelScope) - } - - fun onCreateNewWalletScreen() { - router.push(AppRoute.CreateWalletSelection) - } - - fun onAddExistingWalletScreen() { - router.push(AppRoute.AddExistingWallet) - } - - fun onScanClick() { - analyticsEventHandler.send(IntroductionProcess.ButtonScanCard()) - scanCard() - } - - fun onShopClick() { - analyticsEventHandler.send(IntroductionProcess.ButtonBuyCards()) - analyticsEventHandler.send(Shop.ScreenOpened()) - - Firebase.analytics.appInstanceId - .addOnSuccessListener { urlOpener.openUrl(url = "$NEW_BUY_WALLET_URL&app_instance_id=$it") } - .addOnFailureListener { urlOpener.openUrl(url = NEW_BUY_WALLET_URL) } - } - - fun onSearchClick() { - analyticsEventHandler.send(IntroductionProcess.ButtonTokensList()) - - store.dispatch(TokensAction.SetArgs.ReadAccess) - store.dispatchNavigationAction { push(AppRoute.ManageTokens(Source.STORIES)) } - } - - private fun scanCard() { - modelScope.launch { - cardSdkConfigRepository.isBiometricsRequestPolicy = settingsRepository.shouldSaveAccessCodes() - - scanCardProcessor.scan( - analyticsSource = AnalyticsParam.ScreensSources.Intro, - onProgressStateChange = { showProgress -> - if (showProgress) { - store.dispatch(HomeAction.ScanInProgress(scanInProgress = true)) - } else { - delay(HIDE_PROGRESS_DELAY) - store.dispatch(HomeAction.ScanInProgress(scanInProgress = false)) - } - }, - onFailure = { - tangemErrorHandler.onErrorReceived(error = it) - delay(HIDE_PROGRESS_DELAY) - store.dispatch(HomeAction.ScanInProgress(scanInProgress = false)) - }, - onSuccess = ::proceedWithScanResponse, - ) - } - } - - private suspend fun proceedWithScanResponse(scanResponse: ScanResponse) { - val userWallet = coldUserWalletBuilderFactory.create(scanResponse = scanResponse).build() - - if (userWallet == null) { - Timber.e("User wallet not created") - return - } - - saveWalletUseCase(userWallet).fold( - ifLeft = { Timber.e(it.toString(), "Unable to save user wallet") }, - ifRight = { - sendSignedInCardAnalyticsEvent(scanResponse) - coroutineScope { store.onUserWalletSelected(userWallet = userWallet) } - }, - ) - - store.dispatchWithMain(HomeAction.ScanInProgress(scanInProgress = false)) - delay(HIDE_PROGRESS_DELAY) - - store.dispatchNavigationAction { replaceAll(AppRoute.Wallet) } - } - - private fun sendSignedInCardAnalyticsEvent(scanResponse: ScanResponse) { - val currency = ParamCardCurrencyConverter().convert(value = scanResponse.cardTypesResolver) - - if (currency != null) { - Analytics.send( - event = Basic.SignedIn( - currency = currency, - batch = scanResponse.card.batchId, - signInType = Basic.SignedIn.SignInType.Card, - walletsCount = "1", - hasBackup = scanResponse.card.backupStatus?.isActive, - ), - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/RegionProvider.kt b/app/src/main/java/com/tangem/tap/features/home/RegionProvider.kt deleted file mode 100644 index fb4ae25b2b..0000000000 --- a/app/src/main/java/com/tangem/tap/features/home/RegionProvider.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.tangem.tap.features.home - -import androidx.compose.ui.text.intl.Locale - -/** -[REDACTED_AUTHOR] - */ -interface RegionProvider { - fun getRegion(): String? -} - -class LocaleRegionProvider : RegionProvider { - override fun getRegion(): String = Locale.current.region -} - -const val RUSSIA_COUNTRY_CODE = "ru" \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/TangemTangemErrorsHandler.kt b/app/src/main/java/com/tangem/tap/features/home/TangemTangemErrorsHandler.kt deleted file mode 100644 index 40e51a8de8..0000000000 --- a/app/src/main/java/com/tangem/tap/features/home/TangemTangemErrorsHandler.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.tangem.tap.features.home - -import com.tangem.blockchain.common.BlockchainError -import com.tangem.common.core.TangemError -import com.tangem.common.core.TangemSdkError -import com.tangem.domain.redux.StateDialog -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.features.home.errors.TangemSdkErrorHandler -import org.rekotlin.Store -import timber.log.Timber - -class TangemTangemErrorsHandler(val store: Store) : TangemSdkErrorHandler { - - override fun onErrorReceived(error: TangemError) { - when (error) { - is TangemSdkError -> { - handleCardSdkError(error) - } - is BlockchainError -> { - handleBlockchainSdkError(error) - } - else -> { - Timber.e("Error happened", error) - } - } - } - - private fun handleCardSdkError(error: TangemSdkError) { - when (error) { - is TangemSdkError.NfcFeatureIsUnavailable -> { - store.dispatchOnMain(GlobalAction.ShowDialog(StateDialog.NfcFeatureIsUnavailable)) - } - else -> { - Timber.e(error, "Unable to scan card") - } - } - } - - private fun handleBlockchainSdkError(error: TangemError) { - Timber.e("Sdk error happened", error) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/api/HomeComponent.kt b/app/src/main/java/com/tangem/tap/features/home/api/HomeComponent.kt deleted file mode 100644 index 0c63e10a1c..0000000000 --- a/app/src/main/java/com/tangem/tap/features/home/api/HomeComponent.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.tangem.tap.features.home.api - -import com.tangem.core.decompose.factory.ComponentFactory -import com.tangem.core.ui.decompose.ComposableContentComponent - -interface HomeComponent : ComposableContentComponent { - - interface Factory : ComponentFactory -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/di/HomeFeatureModule.kt b/app/src/main/java/com/tangem/tap/features/home/di/HomeFeatureModule.kt deleted file mode 100644 index 9f92cc5583..0000000000 --- a/app/src/main/java/com/tangem/tap/features/home/di/HomeFeatureModule.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.tangem.tap.features.home.di - -import com.tangem.core.decompose.model.Model -import com.tangem.tap.features.home.DefaultHomeComponent -import com.tangem.tap.features.home.HomeModel -import com.tangem.tap.features.home.api.HomeComponent -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import dagger.multibindings.ClassKey -import dagger.multibindings.IntoMap - -@Module -@InstallIn(SingletonComponent::class) -internal interface HomeFeatureModule { - - @Binds - fun bindFactory(impl: DefaultHomeComponent.Factory): HomeComponent.Factory - - @Binds - @IntoMap - @ClassKey(HomeModel::class) - fun bindModel(model: HomeModel): Model -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/errors/TangemSdkErrorHandler.kt b/app/src/main/java/com/tangem/tap/features/home/errors/TangemSdkErrorHandler.kt deleted file mode 100644 index 7946b7ec5e..0000000000 --- a/app/src/main/java/com/tangem/tap/features/home/errors/TangemSdkErrorHandler.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tangem.tap.features.home.errors - -import com.tangem.common.core.TangemError - -interface TangemSdkErrorHandler { - - fun onErrorReceived(error: TangemError) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt deleted file mode 100644 index 9a449e938e..0000000000 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.tap.features.home.redux - -import com.tangem.domain.settings.usercountry.models.UserCountry -import kotlinx.coroutines.CoroutineScope -import org.rekotlin.Action - -sealed class HomeAction : Action { - - data object OnCreate : HomeAction() - - /** - * Action for scanning card - * - * @property scope lifecycle scope. It will be canceled when lifecycle-aware component is destroyed - */ - data class ReadCard(val scope: CoroutineScope) : HomeAction() - - data class ScanInProgress(val scanInProgress: Boolean) : HomeAction() - - data class UserCountryLoaded(val userCountry: UserCountry) : HomeAction() -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt deleted file mode 100644 index e66b7f8c84..0000000000 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ /dev/null @@ -1,147 +0,0 @@ -package com.tangem.tap.features.home.redux - -import android.content.res.Resources -import com.tangem.common.doOnFailure -import com.tangem.common.doOnResult -import com.tangem.common.doOnSuccess -import com.tangem.common.extensions.guard -import com.tangem.common.routing.AppRoute -import com.tangem.core.analytics.Analytics -import com.tangem.core.analytics.models.AnalyticsParam -import com.tangem.core.analytics.models.Basic -import com.tangem.domain.card.common.util.cardTypesResolver -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter -import com.tangem.tap.common.analytics.events.IntroductionProcess -import com.tangem.tap.common.extensions.dispatchNavigationAction -import com.tangem.tap.common.extensions.eraseContext -import com.tangem.tap.common.extensions.inject -import com.tangem.tap.common.extensions.onUserWalletSelected -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.proxy.redux.DaggerGraphState -import com.tangem.tap.scope -import com.tangem.tap.store -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import org.rekotlin.Action -import org.rekotlin.Middleware -import timber.log.Timber -import java.util.Locale - -internal const val HIDE_PROGRESS_DELAY = 400L - -object HomeMiddleware { - val handler = homeMiddleware - - private val SYSTEM_LANGUAGE = - runCatching { Resources.getSystem().configuration.locales[0].language }.getOrElse { "" } - private val APP_LANGUAGE = Locale.getDefault().language - private val UTM_MARKS = "utm_source=tangem-app" + - "&utm_medium=app" + - "&utm_campaign=prospect-$SYSTEM_LANGUAGE" + - "&utm_content=devicelang-$APP_LANGUAGE" - - val NEW_BUY_WALLET_URL = "https://buy.tangem.com/?$UTM_MARKS" -} - -private val homeMiddleware: Middleware = { _, _ -> - { next -> - { action -> - handleHomeAction(action) - next(action) - } - } -} - -private fun handleHomeAction(action: Action) { - when (action) { - is HomeAction.OnCreate -> { - Analytics.eraseContext() - Analytics.send(IntroductionProcess.ScreenOpened()) - - store.dispatch(GlobalAction.RestoreAppCurrency) - } - is HomeAction.ReadCard -> { - action.scope.launch { - readCard() - } - } - } -} - -private suspend fun readCard() { - val shouldSaveAccessCodes = store.inject(DaggerGraphState::settingsRepository).shouldSaveAccessCodes() - - store.inject(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy( - isBiometricsRequestPolicy = shouldSaveAccessCodes, - ) - - store.inject(DaggerGraphState::scanCardProcessor).scan( - analyticsSource = AnalyticsParam.ScreensSources.Intro, - onProgressStateChange = { showProgress -> - if (showProgress) { - store.dispatch(HomeAction.ScanInProgress(scanInProgress = true)) - } else { - delay(HIDE_PROGRESS_DELAY) - store.dispatch(HomeAction.ScanInProgress(scanInProgress = false)) - } - }, - onFailure = { - Timber.e(it, "Unable to scan card") - delay(HIDE_PROGRESS_DELAY) - store.dispatch(HomeAction.ScanInProgress(scanInProgress = false)) - }, - onSuccess = { scanResponse -> - proceedWithScanResponse(scanResponse) - }, - ) -} - -private fun proceedWithScanResponse(scanResponse: ScanResponse) = scope.launch { - val userWalletBuilder = store.inject(DaggerGraphState::coldUserWalletBuilderFactory).create(scanResponse) - - val userWallet = userWalletBuilder.build().guard { - Timber.e("User wallet not created") - return@launch - } - - val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager) - userWalletsListManager.save(userWallet) - .doOnFailure { error -> - Timber.e(error, "Unable to save user wallet") - } - .doOnSuccess { - sendSignedInCardAnalyticsEvent(scanResponse) - store.onUserWalletSelected(userWallet = userWallet) - } - .doOnResult { - navigateTo(AppRoute.Wallet) - } -} - -private fun sendSignedInCardAnalyticsEvent(scanResponse: ScanResponse) { - val currency = ParamCardCurrencyConverter().convert( - value = scanResponse.cardTypesResolver, - ) - - if (currency != null) { - val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager) - - Analytics.send( - event = Basic.SignedIn( - currency = currency, - batch = scanResponse.card.batchId, - signInType = Basic.SignedIn.SignInType.Card, - walletsCount = userWalletsListManager.walletsCount.toString(), - hasBackup = scanResponse.card.backupStatus?.isActive, - ), - ) - } -} - -private suspend fun navigateTo(route: AppRoute) { - store.dispatchNavigationAction { push(route) } - delay(HIDE_PROGRESS_DELAY) - store.dispatch(HomeAction.ScanInProgress(scanInProgress = false)) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt deleted file mode 100644 index 678de45d76..0000000000 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.tangem.tap.features.home.redux - -import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions -import com.tangem.tap.common.redux.AppState -import kotlinx.collections.immutable.toImmutableList -import org.rekotlin.Action - -object HomeReducer { - fun reduce(action: Action, state: AppState): HomeState = internalReduce(action, state) -} - -private fun internalReduce(action: Action, appState: AppState): HomeState { - if (action !is HomeAction) return appState.homeState - - return when (action) { - is HomeAction.ScanInProgress -> { - appState.homeState.copy(scanInProgress = action.scanInProgress) - } - is HomeAction.UserCountryLoaded -> { - val stories = if (action.userCountry.needApplyFCARestrictions()) { - getRestrictedStories() - } else { - Stories.entries - } - appState.homeState.copy( - stories = stories.toImmutableList(), - ) - } - else -> appState.homeState - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/BackgroundScanIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/BackgroundScanIntentHandler.kt index 90ef042a6b..766392b9dd 100644 --- a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/BackgroundScanIntentHandler.kt +++ b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/BackgroundScanIntentHandler.kt @@ -4,21 +4,12 @@ import android.content.Intent import android.nfc.NfcAdapter import android.nfc.Tag import android.os.Build -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.features.home.redux.HomeAction -import com.tangem.tap.features.intentHandler.IntentHandler -import com.tangem.tap.features.intentHandler.AffectsNavigation -import com.tangem.tap.features.welcome.redux.WelcomeAction -import com.tangem.tap.store -import kotlinx.coroutines.CoroutineScope +import com.tangem.common.routing.entity.InitScreenLaunchMode /** [REDACTED_AUTHOR] */ -class BackgroundScanIntentHandler( - private val hasSavedUserWalletsProvider: () -> Boolean, - private val scope: CoroutineScope, -) : IntentHandler, AffectsNavigation { +class BackgroundScanIntentHandler { private val nfcActions = arrayOf( NfcAdapter.ACTION_NDEF_DISCOVERED, @@ -26,8 +17,15 @@ class BackgroundScanIntentHandler( NfcAdapter.ACTION_TAG_DISCOVERED, ) - override fun handleIntent(intent: Intent?, isFromForeground: Boolean): Boolean { - if (isFromForeground) return true + fun getInitScreenLaunchMode(intent: Intent?): InitScreenLaunchMode { + return if (shouldOpenScanCard(intent)) { + InitScreenLaunchMode.WithCardScan + } else { + InitScreenLaunchMode.Standard + } + } + + private fun shouldOpenScanCard(intent: Intent?): Boolean { if (intent == null || intent.action !in nfcActions) return false val tag: Tag? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { @@ -36,15 +34,9 @@ class BackgroundScanIntentHandler( @Suppress("DEPRECATION") intent.getParcelableExtra(NfcAdapter.EXTRA_TAG) } - if (tag == null) return false intent.action = null - if (hasSavedUserWalletsProvider.invoke()) { - store.dispatchOnMain(WelcomeAction.ProceedWithCard) - } else { - store.dispatchOnMain(HomeAction.ReadCard(scope = scope)) - } - return true + return tag != null } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/WalletConnectLinkIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/WalletConnectLinkIntentHandler.kt index 6f138c18e5..e715290a1e 100644 --- a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/WalletConnectLinkIntentHandler.kt +++ b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/WalletConnectLinkIntentHandler.kt @@ -4,8 +4,8 @@ import android.content.Intent import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.removePrefixOrNull import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction -import com.tangem.tap.features.intentHandler.IntentHandler import com.tangem.tap.features.intentHandler.AffectsNavigation +import com.tangem.tap.features.intentHandler.IntentHandler import com.tangem.tap.store import timber.log.Timber import java.net.URLDecoder diff --git a/app/src/main/java/com/tangem/tap/features/welcome/component/WelcomeComponent.kt b/app/src/main/java/com/tangem/tap/features/welcome/component/WelcomeComponent.kt index 07a92373e1..215560a1ad 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/component/WelcomeComponent.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/component/WelcomeComponent.kt @@ -1,5 +1,6 @@ package com.tangem.tap.features.welcome.component +import com.tangem.common.routing.entity.InitScreenLaunchMode import com.tangem.common.routing.entity.SerializableIntent import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.decompose.ComposableContentComponent @@ -7,6 +8,7 @@ import com.tangem.core.ui.decompose.ComposableContentComponent interface WelcomeComponent : ComposableContentComponent { data class Params( + val launchMode: InitScreenLaunchMode, val intent: SerializableIntent?, ) diff --git a/app/src/main/java/com/tangem/tap/features/welcome/model/WelcomeModel.kt b/app/src/main/java/com/tangem/tap/features/welcome/model/WelcomeModel.kt index 8b1dc10be1..5965f011f3 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/model/WelcomeModel.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/model/WelcomeModel.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.welcome.model import com.tangem.common.core.TangemError +import com.tangem.common.routing.entity.InitScreenLaunchMode import com.tangem.core.analytics.Analytics import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model @@ -44,10 +45,9 @@ internal class WelcomeModel @Inject constructor( subscribeToStoreChanges() initGlobalState() - val welcomeAction = if (params.intent != null) { - WelcomeAction.ProceedWithIntent(params.intent.toIntent()) - } else { - WelcomeAction.ProceedWithBiometrics() + val welcomeAction = when (params.launchMode) { + is InitScreenLaunchMode.WithCardScan -> WelcomeAction.ProceedWithCard + is InitScreenLaunchMode.Standard -> WelcomeAction.ProceedWithBiometrics(params.intent?.toIntent()) } store.dispatch(welcomeAction) diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index ebbc8b2fc6..15c1ace4f6 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -20,10 +20,8 @@ import com.tangem.tap.* import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppState -import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler import com.tangem.tap.proxy.redux.DaggerGraphState -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import org.rekotlin.Middleware import timber.log.Timber @@ -44,7 +42,7 @@ internal class WelcomeMiddleware { private fun handleAction(action: WelcomeAction, state: WelcomeState) { mainScope.launch { when (action) { - is WelcomeAction.ProceedWithIntent -> proceedWithIntent(action.intent, scope = this) + is WelcomeAction.ProceedWithIntent -> proceedWithIntent(action.intent) is WelcomeAction.ProceedWithBiometrics -> proceedWithBiometrics( afterUnlockIntent = action.afterUnlockIntent ?: state.intent, ) @@ -55,7 +53,7 @@ internal class WelcomeMiddleware { } } - private suspend fun proceedWithIntent(initialIntent: Intent, scope: CoroutineScope) { + private suspend fun proceedWithIntent(initialIntent: Intent) { Timber.d( """ Proceeding with intent @@ -63,15 +61,12 @@ internal class WelcomeMiddleware { """.trimIndent(), ) - val handler = BackgroundScanIntentHandler( - scope = scope, - hasSavedUserWalletsProvider = { true }, - ) - val isBackgroundScanHandled = handler.handleIntent(initialIntent, isFromForeground = false) val hasUncompletedBackup = backupService.hasIncompletedBackup - if (!isBackgroundScanHandled && !hasUncompletedBackup) { + if (!hasUncompletedBackup) { store.dispatchWithMain(WelcomeAction.ProceedWithBiometrics(initialIntent)) + } else { + store.dispatchWithMain(WelcomeAction.ProceedWithCard) } } diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index 894658edf5..9ecfe8e5c6 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -40,7 +40,7 @@ import com.tangem.tap.features.details.ui.cardsettings.coderecovery.api.AccessCo import com.tangem.tap.features.details.ui.resetcard.api.ResetCardComponent import com.tangem.tap.features.details.ui.securitymode.api.SecurityModeComponent import com.tangem.tap.features.details.ui.walletconnect.api.WalletConnectComponent -import com.tangem.tap.features.home.api.HomeComponent +import com.tangem.features.home.api.HomeComponent import com.tangem.tap.features.welcome.component.WelcomeComponent import com.tangem.tap.routing.component.RoutingComponent.Child import dagger.hilt.android.scopes.ActivityScoped @@ -131,6 +131,7 @@ internal class ChildFactory @Inject constructor( createComponentChild( context = context, params = WelcomeComponent.Params( + launchMode = route.launchMode, intent = route.intent, ), componentFactory = welcomeComponentFactory, @@ -288,7 +289,7 @@ internal class ChildFactory @Inject constructor( is AppRoute.Home -> { createComponentChild( context = context, - params = Unit, + params = HomeComponent.Params(route.launchMode), componentFactory = homeComponentFactory, ) } @@ -380,7 +381,7 @@ internal class ChildFactory @Inject constructor( is AppRoute.PushNotification -> { createComponentChild( context = context, - params = PushNotificationsComponent.Params.Route(AppRoute.Home), + params = PushNotificationsComponent.Params.Route(AppRoute.Home()), componentFactory = pushNotificationsComponentFactory, ) } diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index 77a7b1f113..d1afb593f0 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -3,6 +3,7 @@ package com.tangem.common.routing import android.os.Bundle import com.tangem.common.routing.bundle.RouteBundleParams import com.tangem.common.routing.bundle.bundle +import com.tangem.common.routing.entity.InitScreenLaunchMode import com.tangem.common.routing.entity.SerializableIntent import com.tangem.core.decompose.navigation.Route import com.tangem.domain.appcurrency.model.AppCurrency @@ -22,10 +23,14 @@ sealed class AppRoute(val path: String) : Route { data object Initial : AppRoute(path = "/initial") @Serializable - data object Home : AppRoute(path = "/home") + data class Home( + val launchMode: InitScreenLaunchMode = InitScreenLaunchMode.Standard, + ) : AppRoute(path = "/home") @Serializable data class Welcome( + val launchMode: InitScreenLaunchMode = InitScreenLaunchMode.Standard, + // we still have this param to be handled by WalletConnectLinkIntentHandler in WelcomeMiddleware val intent: SerializableIntent? = null, ) : AppRoute(path = "/welcome"), RouteBundleParams { diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/entity/InitScreenLaunchMode.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/entity/InitScreenLaunchMode.kt new file mode 100644 index 0000000000..3e1e008994 --- /dev/null +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/entity/InitScreenLaunchMode.kt @@ -0,0 +1,13 @@ +package com.tangem.common.routing.entity + +import kotlinx.serialization.Serializable + +@Serializable +sealed class InitScreenLaunchMode { + + @Serializable + data object Standard : InitScreenLaunchMode() + + @Serializable + data object WithCardScan : InitScreenLaunchMode() +} \ No newline at end of file diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableIntent.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableIntent.kt index 38ef6f2096..cb4f2b871a 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableIntent.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableIntent.kt @@ -14,6 +14,7 @@ data class SerializableIntent( val packageValue: String?, val component: String?, val flags: Int, + // CAUTION: works wrong with SerializableBundle constructor(bundle: Bundle), need to be removed val extras: SerializableBundle?, ) { diff --git a/app/src/main/java/com/tangem/tap/common/compose/extensions/AnimatedValue.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/AnimatedValue.kt similarity index 96% rename from app/src/main/java/com/tangem/tap/common/compose/extensions/AnimatedValue.kt rename to core/ui/src/main/java/com/tangem/core/ui/utils/AnimatedValue.kt index b21726c8e7..20ba1ea49a 100644 --- a/app/src/main/java/com/tangem/tap/common/compose/extensions/AnimatedValue.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/AnimatedValue.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.common.compose.extensions +package com.tangem.core.ui.utils import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.AnimationVector1D diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/DensityUtils.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/DensityUtils.kt index 105d2416d3..0a3bb10329 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/utils/DensityUtils.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/DensityUtils.kt @@ -1,9 +1,15 @@ package com.tangem.core.ui.utils +import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import kotlin.math.roundToInt @Stable @Composable @@ -15,4 +21,16 @@ fun convertPxToDp(px: Float): Dp = convertPxToDp(px, density = LocalDensity.curr fun Dp.toPx(density: Float): Float = this.value * density -fun convertPxToDp(px: Float, density: Float): Dp = Dp(value = px / density) \ No newline at end of file +fun convertPxToDp(px: Float, density: Float): Dp = Dp(value = px / density) + +fun Context.dpToPx(dp: Float): Float = dp * resources.displayMetrics.density +fun Context.pxToDp(px: Float): Float = (px / resources.displayMetrics.density).roundToInt().toFloat() + +@Composable +fun Painter.dpSize(): DpSize = DpSize( + intrinsicSize.width.pxToDp().dp, + intrinsicSize.height.pxToDp().dp, +) + +@Composable +private fun Float.pxToDp(): Float = LocalContext.current.pxToDp(this) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/extensions/ImageBitmap.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/ImageBitmap.kt similarity index 92% rename from app/src/main/java/com/tangem/tap/common/compose/extensions/ImageBitmap.kt rename to core/ui/src/main/java/com/tangem/core/ui/utils/ImageBitmap.kt index 99911d9e6f..4f4cff9559 100644 --- a/app/src/main/java/com/tangem/tap/common/compose/extensions/ImageBitmap.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/ImageBitmap.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.common.compose.extensions +package com.tangem.core.ui.utils import androidx.annotation.DrawableRes import androidx.appcompat.content.res.AppCompatResources diff --git a/app/src/main/res/drawable-hdpi/img_meet_tangem.webp b/core/ui/src/main/res/drawable-hdpi/img_meet_tangem.webp similarity index 100% rename from app/src/main/res/drawable-hdpi/img_meet_tangem.webp rename to core/ui/src/main/res/drawable-hdpi/img_meet_tangem.webp diff --git a/app/src/main/res/drawable-hdpi/img_revolutionary_wallet.webp b/core/ui/src/main/res/drawable-hdpi/img_revolutionary_wallet.webp similarity index 100% rename from app/src/main/res/drawable-hdpi/img_revolutionary_wallet.webp rename to core/ui/src/main/res/drawable-hdpi/img_revolutionary_wallet.webp diff --git a/app/src/main/res/drawable-hdpi/img_tangem_for_everyone.webp b/core/ui/src/main/res/drawable-hdpi/img_tangem_for_everyone.webp similarity index 100% rename from app/src/main/res/drawable-hdpi/img_tangem_for_everyone.webp rename to core/ui/src/main/res/drawable-hdpi/img_tangem_for_everyone.webp diff --git a/app/src/main/res/drawable-mdpi/img_meet_tangem.webp b/core/ui/src/main/res/drawable-mdpi/img_meet_tangem.webp similarity index 100% rename from app/src/main/res/drawable-mdpi/img_meet_tangem.webp rename to core/ui/src/main/res/drawable-mdpi/img_meet_tangem.webp diff --git a/app/src/main/res/drawable-mdpi/img_revolutionary_wallet.webp b/core/ui/src/main/res/drawable-mdpi/img_revolutionary_wallet.webp similarity index 100% rename from app/src/main/res/drawable-mdpi/img_revolutionary_wallet.webp rename to core/ui/src/main/res/drawable-mdpi/img_revolutionary_wallet.webp diff --git a/app/src/main/res/drawable-mdpi/img_tangem_for_everyone.webp b/core/ui/src/main/res/drawable-mdpi/img_tangem_for_everyone.webp similarity index 100% rename from app/src/main/res/drawable-mdpi/img_tangem_for_everyone.webp rename to core/ui/src/main/res/drawable-mdpi/img_tangem_for_everyone.webp diff --git a/app/src/main/res/drawable-xhdpi/img_meet_tangem.webp b/core/ui/src/main/res/drawable-xhdpi/img_meet_tangem.webp similarity index 100% rename from app/src/main/res/drawable-xhdpi/img_meet_tangem.webp rename to core/ui/src/main/res/drawable-xhdpi/img_meet_tangem.webp diff --git a/app/src/main/res/drawable-xhdpi/img_revolutionary_wallet.webp b/core/ui/src/main/res/drawable-xhdpi/img_revolutionary_wallet.webp similarity index 100% rename from app/src/main/res/drawable-xhdpi/img_revolutionary_wallet.webp rename to core/ui/src/main/res/drawable-xhdpi/img_revolutionary_wallet.webp diff --git a/app/src/main/res/drawable-xhdpi/img_tangem_for_everyone.webp b/core/ui/src/main/res/drawable-xhdpi/img_tangem_for_everyone.webp similarity index 100% rename from app/src/main/res/drawable-xhdpi/img_tangem_for_everyone.webp rename to core/ui/src/main/res/drawable-xhdpi/img_tangem_for_everyone.webp diff --git a/app/src/main/res/drawable-xxhdpi/img_meet_tangem.webp b/core/ui/src/main/res/drawable-xxhdpi/img_meet_tangem.webp similarity index 100% rename from app/src/main/res/drawable-xxhdpi/img_meet_tangem.webp rename to core/ui/src/main/res/drawable-xxhdpi/img_meet_tangem.webp diff --git a/app/src/main/res/drawable-xxhdpi/img_revolutionary_wallet.webp b/core/ui/src/main/res/drawable-xxhdpi/img_revolutionary_wallet.webp similarity index 100% rename from app/src/main/res/drawable-xxhdpi/img_revolutionary_wallet.webp rename to core/ui/src/main/res/drawable-xxhdpi/img_revolutionary_wallet.webp diff --git a/app/src/main/res/drawable-xxhdpi/img_tangem_for_everyone.webp b/core/ui/src/main/res/drawable-xxhdpi/img_tangem_for_everyone.webp similarity index 100% rename from app/src/main/res/drawable-xxhdpi/img_tangem_for_everyone.webp rename to core/ui/src/main/res/drawable-xxhdpi/img_tangem_for_everyone.webp diff --git a/app/src/main/res/drawable-xxxhdpi/img_meet_tangem.webp b/core/ui/src/main/res/drawable-xxxhdpi/img_meet_tangem.webp similarity index 100% rename from app/src/main/res/drawable-xxxhdpi/img_meet_tangem.webp rename to core/ui/src/main/res/drawable-xxxhdpi/img_meet_tangem.webp diff --git a/app/src/main/res/drawable-xxxhdpi/img_revolutionary_wallet.webp b/core/ui/src/main/res/drawable-xxxhdpi/img_revolutionary_wallet.webp similarity index 100% rename from app/src/main/res/drawable-xxxhdpi/img_revolutionary_wallet.webp rename to core/ui/src/main/res/drawable-xxxhdpi/img_revolutionary_wallet.webp diff --git a/app/src/main/res/drawable-xxxhdpi/img_tangem_for_everyone.webp b/core/ui/src/main/res/drawable-xxxhdpi/img_tangem_for_everyone.webp similarity index 100% rename from app/src/main/res/drawable-xxxhdpi/img_tangem_for_everyone.webp rename to core/ui/src/main/res/drawable-xxxhdpi/img_tangem_for_everyone.webp diff --git a/app/src/main/res/drawable/currency0.webp b/core/ui/src/main/res/drawable/currency0.webp similarity index 100% rename from app/src/main/res/drawable/currency0.webp rename to core/ui/src/main/res/drawable/currency0.webp diff --git a/app/src/main/res/drawable/currency1.webp b/core/ui/src/main/res/drawable/currency1.webp similarity index 100% rename from app/src/main/res/drawable/currency1.webp rename to core/ui/src/main/res/drawable/currency1.webp diff --git a/app/src/main/res/drawable/currency2.webp b/core/ui/src/main/res/drawable/currency2.webp similarity index 100% rename from app/src/main/res/drawable/currency2.webp rename to core/ui/src/main/res/drawable/currency2.webp diff --git a/app/src/main/res/drawable/currency3.webp b/core/ui/src/main/res/drawable/currency3.webp similarity index 100% rename from app/src/main/res/drawable/currency3.webp rename to core/ui/src/main/res/drawable/currency3.webp diff --git a/app/src/main/res/drawable/currency4.webp b/core/ui/src/main/res/drawable/currency4.webp similarity index 100% rename from app/src/main/res/drawable/currency4.webp rename to core/ui/src/main/res/drawable/currency4.webp diff --git a/app/src/main/res/drawable/dapps1.webp b/core/ui/src/main/res/drawable/dapps1.webp similarity index 100% rename from app/src/main/res/drawable/dapps1.webp rename to core/ui/src/main/res/drawable/dapps1.webp diff --git a/app/src/main/res/drawable/dapps2.webp b/core/ui/src/main/res/drawable/dapps2.webp similarity index 100% rename from app/src/main/res/drawable/dapps2.webp rename to core/ui/src/main/res/drawable/dapps2.webp diff --git a/app/src/main/res/drawable/dapps3.webp b/core/ui/src/main/res/drawable/dapps3.webp similarity index 100% rename from app/src/main/res/drawable/dapps3.webp rename to core/ui/src/main/res/drawable/dapps3.webp diff --git a/app/src/main/res/drawable/dapps4.webp b/core/ui/src/main/res/drawable/dapps4.webp similarity index 100% rename from app/src/main/res/drawable/dapps4.webp rename to core/ui/src/main/res/drawable/dapps4.webp diff --git a/app/src/main/res/drawable/dapps5.webp b/core/ui/src/main/res/drawable/dapps5.webp similarity index 100% rename from app/src/main/res/drawable/dapps5.webp rename to core/ui/src/main/res/drawable/dapps5.webp diff --git a/app/src/main/res/drawable/ic_tangem_logo.xml b/core/ui/src/main/res/drawable/ic_tangem_logo.xml similarity index 100% rename from app/src/main/res/drawable/ic_tangem_logo.xml rename to core/ui/src/main/res/drawable/ic_tangem_logo.xml diff --git a/app/src/main/res/drawable/img_card_placeholder_wallet_2.webp b/core/ui/src/main/res/drawable/img_card_placeholder_wallet_2.webp similarity index 100% rename from app/src/main/res/drawable/img_card_placeholder_wallet_2.webp rename to core/ui/src/main/res/drawable/img_card_placeholder_wallet_2.webp diff --git a/core/utils/src/main/java/com/tangem/utils/extensions/Int.kt b/core/utils/src/main/java/com/tangem/utils/extensions/Int.kt deleted file mode 100644 index 357b26203c..0000000000 --- a/core/utils/src/main/java/com/tangem/utils/extensions/Int.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.tangem.utils.extensions - -/** -[REDACTED_AUTHOR] - */ -fun Int.isEven(): Boolean = this % 2 == 0 \ No newline at end of file diff --git a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/model/DisclaimerModel.kt b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/model/DisclaimerModel.kt index f96cb66625..1ea5597638 100644 --- a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/model/DisclaimerModel.kt +++ b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/model/DisclaimerModel.kt @@ -51,7 +51,7 @@ internal class DisclaimerModel @Inject constructor( } else { neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION) neverRequestPermissionUseCase(PUSH_PERMISSION) - router.replaceAll(AppRoute.Home) + router.replaceAll(AppRoute.Home()) } } } diff --git a/features/home/api/.gitignore b/features/home/api/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/features/home/api/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/features/home/api/build.gradle.kts b/features/home/api/build.gradle.kts new file mode 100644 index 0000000000..7f07d748d7 --- /dev/null +++ b/features/home/api/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + id("configuration") +} + +android { + namespace = "com.tangem.features.home.api" +} + +dependencies { + implementation(projects.core.decompose) + implementation(projects.core.ui) + + /** Common */ + implementation(projects.common.routing) +} \ No newline at end of file diff --git a/features/home/api/src/main/kotlin/com/tangem/features/home/api/HomeComponent.kt b/features/home/api/src/main/kotlin/com/tangem/features/home/api/HomeComponent.kt new file mode 100644 index 0000000000..05a94c2090 --- /dev/null +++ b/features/home/api/src/main/kotlin/com/tangem/features/home/api/HomeComponent.kt @@ -0,0 +1,17 @@ +package com.tangem.features.home.api + +import com.tangem.common.routing.entity.InitScreenLaunchMode +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.decompose.ComposableContentComponent + +interface HomeComponent : ComposableContentComponent { + + data class Params( + val launchMode: InitScreenLaunchMode = InitScreenLaunchMode.Standard, + ) + + interface Factory : ComponentFactory { + override fun create(context: AppComponentContext, params: Params): HomeComponent + } +} \ No newline at end of file diff --git a/features/home/impl/.gitignore b/features/home/impl/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/features/home/impl/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/features/home/impl/build.gradle.kts b/features/home/impl/build.gradle.kts new file mode 100644 index 0000000000..4a4f17d3bf --- /dev/null +++ b/features/home/impl/build.gradle.kts @@ -0,0 +1,69 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.kapt) + alias(deps.plugins.hilt.android) + id("configuration") +} + +android { + namespace = "com.tangem.features.home.impl" +} + +dependencies { + /** Api */ + implementation(projects.features.home.api) + implementation(projects.features.hotWallet.api) + + /** Core modules */ + implementation(projects.core.decompose) + implementation(projects.core.ui) + implementation(projects.core.res) + implementation(projects.core.analytics) + implementation(projects.core.analytics.models) + implementation(projects.core.navigation) + + /** Common */ + implementation(projects.common.routing) + + /** Domain */ + implementation(projects.domain.models) + implementation(projects.domain.core) + implementation(projects.domain.card) + implementation(projects.domain.settings) + implementation(projects.domain.tokens) + implementation(projects.domain.wallets) + implementation(projects.domain.wallets.models) + implementation(projects.domain.legacy) + implementation(projects.domain.feedback) + implementation(projects.domain.feedback.models) + + /** AndroidX libraries */ + implementation(deps.androidx.activity.compose) + implementation(deps.lifecycle.runtime.ktx) + + /** Compose libraries */ + implementation(deps.compose.ui) + implementation(deps.compose.ui.tooling) + implementation(deps.compose.foundation) + implementation(deps.compose.material3) + implementation(deps.compose.animation) + implementation(deps.compose.coil) + implementation(deps.decompose.ext.compose) + + /** Firebase */ + implementation(deps.firebase.analytics) + + /** Tangem libraries */ + implementation(tangemDeps.card.android) + implementation(tangemDeps.card.core) + implementation(tangemDeps.blockchain) + + /** Other libraries */ + implementation(deps.kotlin.immutable.collections) + implementation(deps.timber) + + /** DI */ + implementation(deps.hilt.android) + kapt(deps.hilt.kapt) +} \ No newline at end of file diff --git a/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/DefaultHomeComponent.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/DefaultHomeComponent.kt new file mode 100644 index 0000000000..7c210b9af7 --- /dev/null +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/DefaultHomeComponent.kt @@ -0,0 +1,40 @@ +package com.tangem.features.home.impl + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.features.home.api.HomeComponent +import com.tangem.features.home.impl.model.HomeModel +import com.tangem.features.home.impl.ui.Home +import com.tangem.features.hotwallet.HotWalletFeatureToggles +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +internal class DefaultHomeComponent @AssistedInject constructor( + @Assisted appComponentContext: AppComponentContext, + @Assisted params: HomeComponent.Params, + private val hotWalletFeatureToggles: HotWalletFeatureToggles, +) : HomeComponent, AppComponentContext by appComponentContext { + + private val model: HomeModel = getOrCreateModel(params) + + @Composable + override fun Content(modifier: Modifier) { + val state by model.uiState.collectAsStateWithLifecycle() + + Home( + state = state, + modifier = modifier, + isV2StoriesEnabled = hotWalletFeatureToggles.isHotWalletEnabled, + ) + } + + @AssistedFactory + interface Factory : HomeComponent.Factory { + override fun create(context: AppComponentContext, params: HomeComponent.Params): DefaultHomeComponent + } +} \ No newline at end of file diff --git a/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/analytics/AnalyticsParam.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/analytics/AnalyticsParam.kt new file mode 100644 index 0000000000..def2ff2645 --- /dev/null +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/analytics/AnalyticsParam.kt @@ -0,0 +1,9 @@ +package com.tangem.features.home.impl.analytics + +internal sealed class AnalyticsParam { + + sealed class CurrencyType(val value: String) { + class Blockchain(blockchain: com.tangem.blockchain.common.Blockchain) : CurrencyType(blockchain.currency) + class Token(token: com.tangem.blockchain.common.Token) : CurrencyType(token.symbol) + } +} \ No newline at end of file diff --git a/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/analytics/IntroductionProcess.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/analytics/IntroductionProcess.kt new file mode 100644 index 0000000000..6115389bfd --- /dev/null +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/analytics/IntroductionProcess.kt @@ -0,0 +1,14 @@ +package com.tangem.features.home.impl.analytics + +import com.tangem.core.analytics.models.AnalyticsEvent + +sealed class IntroductionProcess( + event: String, + params: Map = mapOf(), +) : AnalyticsEvent("Introduction Process", event, params) { + + object ScreenOpened : IntroductionProcess("Introduction Process Screen Opened") + object ButtonTokensList : IntroductionProcess("Button - Tokens List") + object ButtonBuyCards : IntroductionProcess("Button - Buy Cards") + object ButtonScanCard : IntroductionProcess("Button - Scan Card") +} \ No newline at end of file diff --git a/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/analytics/ParamCardCurrencyConverter.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/analytics/ParamCardCurrencyConverter.kt new file mode 100644 index 0000000000..16c104323b --- /dev/null +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/analytics/ParamCardCurrencyConverter.kt @@ -0,0 +1,23 @@ +package com.tangem.features.home.impl.analytics + +import com.tangem.blockchain.common.Blockchain +import com.tangem.domain.card.CardTypesResolver +import com.tangem.utils.converter.Converter +import com.tangem.core.analytics.models.AnalyticsParam as CoreAnalyticsParam + +internal class ParamCardCurrencyConverter : Converter { + + override fun convert(value: CardTypesResolver): CoreAnalyticsParam.WalletType? { + if (value.isMultiwalletAllowed()) return CoreAnalyticsParam.WalletType.MultiCurrency + + val type = when { + value.isTangemNote() -> AnalyticsParam.CurrencyType.Blockchain(value.getBlockchain()) + value.isTangemTwins() -> AnalyticsParam.CurrencyType.Blockchain(Blockchain.Bitcoin) + value.getBlockchain() != Blockchain.Unknown -> AnalyticsParam.CurrencyType.Blockchain(value.getBlockchain()) + value.getPrimaryToken() != null -> AnalyticsParam.CurrencyType.Token(value.getPrimaryToken()!!) + else -> null + } ?: return null + + return CoreAnalyticsParam.WalletType.SingleCurrency(type.value) + } +} \ No newline at end of file diff --git a/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/analytics/Shop.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/analytics/Shop.kt new file mode 100644 index 0000000000..632cc66ef2 --- /dev/null +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/analytics/Shop.kt @@ -0,0 +1,11 @@ +package com.tangem.features.home.impl.analytics + +import com.tangem.core.analytics.models.AnalyticsEvent + +internal sealed class Shop( + event: String, + params: Map = mapOf(), +) : AnalyticsEvent("Shop", event, params) { + + object ScreenOpened : Shop("Shop Screen Opened") +} \ No newline at end of file diff --git a/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/di/HomeFeatureModule.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/di/HomeFeatureModule.kt new file mode 100644 index 0000000000..cb6516bc9b --- /dev/null +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/di/HomeFeatureModule.kt @@ -0,0 +1,33 @@ +package com.tangem.features.home.impl.di + +import com.tangem.core.decompose.di.ModelComponent +import com.tangem.core.decompose.model.Model +import com.tangem.features.home.api.HomeComponent +import com.tangem.features.home.impl.DefaultHomeComponent +import com.tangem.features.home.impl.model.HomeModel +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal interface ComponentModule { + + @Binds + @Singleton + fun bindComponent(factory: DefaultHomeComponent.Factory): HomeComponent.Factory +} + +@Module +@InstallIn(ModelComponent::class) +internal interface ModelModule { + + @Binds + @IntoMap + @ClassKey(HomeModel::class) + fun provideModel(model: HomeModel): Model +} \ No newline at end of file diff --git a/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/model/HomeModel.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/model/HomeModel.kt new file mode 100644 index 0000000000..61614e3a62 --- /dev/null +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/model/HomeModel.kt @@ -0,0 +1,268 @@ +package com.tangem.features.home.impl.model + +import com.google.firebase.analytics.ktx.analytics +import com.google.firebase.ktx.Firebase +import com.tangem.common.core.TangemError +import com.tangem.common.core.TangemSdkError +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRoute.ManageTokens.Source +import com.tangem.common.routing.AppRouter +import com.tangem.common.routing.entity.InitScreenLaunchMode +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.Basic.SignedIn +import com.tangem.core.analytics.models.Basic.SignedIn.SignInType +import com.tangem.core.decompose.di.GlobalUiMessageSender +import com.tangem.core.decompose.di.ModelScoped +import com.tangem.core.decompose.model.Model +import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.core.decompose.navigation.Router +import com.tangem.core.decompose.ui.UiMessageSender +import com.tangem.core.navigation.url.UrlOpener +import com.tangem.core.ui.R +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.message.DialogMessage +import com.tangem.domain.card.ScanCardProcessor +import com.tangem.domain.card.common.util.cardTypesResolver +import com.tangem.domain.card.repository.CardSdkConfigRepository +import com.tangem.domain.models.scan.ScanResponse +import com.tangem.domain.settings.repositories.SettingsRepository +import com.tangem.domain.settings.usercountry.GetUserCountryUseCase +import com.tangem.domain.settings.usercountry.models.UserCountry +import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions +import com.tangem.domain.wallets.builder.ColdUserWalletBuilder +import com.tangem.domain.wallets.usecase.SaveWalletUseCase +import com.tangem.domain.wallets.usecase.SelectWalletUseCase +import com.tangem.features.home.api.HomeComponent +import com.tangem.features.home.impl.analytics.IntroductionProcess +import com.tangem.features.home.impl.analytics.ParamCardCurrencyConverter +import com.tangem.features.home.impl.analytics.Shop +import com.tangem.features.home.impl.ui.state.HomeUM +import com.tangem.features.home.impl.ui.state.Stories +import com.tangem.features.home.impl.ui.state.getRestrictedStories +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import timber.log.Timber +import java.util.Locale +import javax.inject.Inject + +private const val HIDE_PROGRESS_DELAY = 400L + +@Suppress("LongParameterList") +@ModelScoped +internal class HomeModel @Inject constructor( + paramsContainer: ParamsContainer, + override val dispatchers: CoroutineDispatcherProvider, + private val scanCardProcessor: ScanCardProcessor, + private val saveWalletUseCase: SaveWalletUseCase, + private val cardSdkConfigRepository: CardSdkConfigRepository, + private val settingsRepository: SettingsRepository, + private val urlOpener: UrlOpener, + private val analyticsEventHandler: AnalyticsEventHandler, + private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory, + private val router: Router, + private val selectWalletUseCase: SelectWalletUseCase, + private val appRouter: AppRouter, + private val getUserCountryUseCase: GetUserCountryUseCase, + @GlobalUiMessageSender private val uiMessageSender: UiMessageSender, +) : Model() { + + val params = paramsContainer.require() + + private val _uiState = MutableStateFlow( + HomeUM( + scanInProgress = false, + stories = getRestrictedStories().toImmutableList(), + onScanClick = ::onScanClick, + onShopClick = ::onShopClick, + onSearchTokensClick = ::onSearchTokensClick, + onCreateNewWalletClick = ::onCreateNewWalletClick, + onAddExistingWalletClick = ::onAddExistingWalletClick, + ), + ) + + val uiState = _uiState.asStateFlow() + + init { + analyticsEventHandler.send(IntroductionProcess.ScreenOpened) + observeUserCountryChanges() + + when (params.launchMode) { + InitScreenLaunchMode.Standard -> Unit + InitScreenLaunchMode.WithCardScan -> scanCard() + } + } + + private fun observeUserCountryChanges() { + getUserCountryUseCase.invoke() + .distinctUntilChanged() + .filterNotNull() + .onEach { result -> + val userCountry = result.getOrNull() ?: UserCountry.Other(Locale.getDefault().country) + updateStoriesForCountry(userCountry) + } + .flowOn(dispatchers.io) + .launchIn(modelScope) + } + + private fun updateStoriesForCountry(userCountry: UserCountry) { + val stories = if (userCountry.needApplyFCARestrictions()) { + getRestrictedStories() + } else { + Stories.entries + } + + _uiState.update { + it.copy(stories = stories.toImmutableList()) + } + } + + private fun onScanClick() { + analyticsEventHandler.send(IntroductionProcess.ButtonScanCard) + scanCard() + } + + private fun onShopClick() { + analyticsEventHandler.send(IntroductionProcess.ButtonBuyCards) + analyticsEventHandler.send(Shop.ScreenOpened) + + Firebase.analytics.appInstanceId + .addOnSuccessListener { urlOpener.openUrl(url = "$NEW_BUY_WALLET_URL&app_instance_id=$it") } + .addOnFailureListener { urlOpener.openUrl(url = NEW_BUY_WALLET_URL) } + } + + private fun onSearchTokensClick() { + analyticsEventHandler.send(IntroductionProcess.ButtonTokensList) + router.push(AppRoute.ManageTokens(Source.STORIES)) + } + + private fun onCreateNewWalletClick() { + router.push(AppRoute.CreateWalletSelection) + } + + private fun onAddExistingWalletClick() { + router.push(AppRoute.AddExistingWallet) + } + + private fun scanCard() { + modelScope.launch { + setLoading(true) + + val shouldSaveAccessCodes = settingsRepository.shouldSaveAccessCodes() + cardSdkConfigRepository.setAccessCodeRequestPolicy( + isBiometricsRequestPolicy = shouldSaveAccessCodes, + ) + + val analyticsSource = AnalyticsParam.ScreensSources.Intro + + scanCardProcessor.scan( + analyticsSource = analyticsSource, + onProgressStateChange = { showProgress -> + if (!showProgress) { + delay(HIDE_PROGRESS_DELAY) + setLoading(false) + } else { + setLoading(true) + } + }, + onFailure = { error -> + handleScanError(error) + delay(HIDE_PROGRESS_DELAY) + setLoading(false) + }, + onSuccess = { scanResponse -> + proceedWithScanResponse(scanResponse) + }, + ) + } + } + + private suspend fun proceedWithScanResponse(scanResponse: ScanResponse) { + val userWallet = coldUserWalletBuilderFactory.create(scanResponse = scanResponse).build() + + if (userWallet == null) { + Timber.e("User wallet not created") + setLoading(false) + return + } + + saveWalletUseCase(userWallet).fold( + ifLeft = { + Timber.e(it.toString(), "Unable to save user wallet") + setLoading(false) + }, + ifRight = { + sendSignedInCardAnalyticsEvent(scanResponse) + + // Select the wallet using new mechanism + selectWalletUseCase(userWallet.walletId).fold( + ifLeft = { + Timber.e("Unable to select user wallet: $it") + setLoading(false) + }, + ifRight = { + delay(HIDE_PROGRESS_DELAY) + setLoading(false) + appRouter.replaceAll(AppRoute.Wallet) + }, + ) + }, + ) + } + + private fun sendSignedInCardAnalyticsEvent(scanResponse: ScanResponse) { + val currency = ParamCardCurrencyConverter().convert(value = scanResponse.cardTypesResolver) + if (currency != null) { + analyticsEventHandler.send( + SignedIn( + currency = currency, + batch = scanResponse.card.batchId, + signInType = SignInType.Card, + walletsCount = "1", + hasBackup = scanResponse.card.backupStatus?.isActive, + ), + ) + } + } + + private fun setLoading(isLoading: Boolean) { + _uiState.update { it.copy(scanInProgress = isLoading) } + } + + fun handleScanError(error: TangemError) { + when (error) { + is TangemSdkError.NfcFeatureIsUnavailable -> { + handleNfcFeatureUnavailable() + } + is TangemSdkError -> { + Timber.e(error, "Scan error occurred") + } + else -> { + Timber.e(error, "Error happened") + } + } + } + + private fun handleNfcFeatureUnavailable() { + uiMessageSender.send( + message = DialogMessage( + message = resourceReference(R.string.nfc_error_unavailable), + title = resourceReference(id = R.string.common_error), + ), + ) + } + + companion object { + const val NEW_BUY_WALLET_URL = "https://buy.tangem.com/?utm_source=tangem-app&utm_medium=app" + } +} \ No newline at end of file diff --git a/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/Home.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/Home.kt new file mode 100644 index 0000000000..24025e1627 --- /dev/null +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/Home.kt @@ -0,0 +1,35 @@ +package com.tangem.features.home.impl.ui + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.tangem.core.ui.components.SystemBarsIconsDisposable +import com.tangem.core.ui.res.TangemColorPalette +import com.tangem.core.ui.utils.ChangeRootBackgroundColorEffect +import com.tangem.features.home.impl.ui.compose.StoriesScreen +import com.tangem.features.home.impl.ui.compose.StoriesScreenV2 +import com.tangem.features.home.impl.ui.state.HomeUM + +@Composable +internal fun Home(state: HomeUM, isV2StoriesEnabled: Boolean, modifier: Modifier = Modifier) { + SystemBarsIconsDisposable(darkIcons = false) + + if (isV2StoriesEnabled) { + StoriesScreenV2( + modifier = modifier, + state = state, + onCreateNewWalletButtonClick = state.onCreateNewWalletClick, + onAddExistingWalletButtonClick = state.onAddExistingWalletClick, + onScanButtonClick = state.onScanClick, + ) + } else { + StoriesScreen( + modifier = modifier, + state = state, + onScanButtonClick = state.onScanClick, + onShopButtonClick = state.onShopClick, + onSearchTokensClick = state.onSearchTokensClick, + ) + } + + ChangeRootBackgroundColorEffect(TangemColorPalette.Black) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesAnimation.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/StoriesAnimation.kt similarity index 96% rename from app/src/main/java/com/tangem/tap/features/home/compose/StoriesAnimation.kt rename to features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/StoriesAnimation.kt index 5265c91db4..3bf071d96a 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesAnimation.kt +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/StoriesAnimation.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.home.compose +package com.tangem.features.home.impl.ui.compose import androidx.compose.animation.core.* import androidx.compose.foundation.Image @@ -19,8 +19,8 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp -import com.tangem.tap.common.compose.extensions.AnimatedValue -import com.tangem.tap.common.compose.extensions.toAnimatable +import com.tangem.core.ui.utils.AnimatedValue +import com.tangem.core.ui.utils.toAnimatable private const val SCALE_SWITCH_BARRIER = 1.15f diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/StoriesScreen.kt similarity index 94% rename from app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt rename to features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/StoriesScreen.kt index 3ccd4ba7fe..704485cb28 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/StoriesScreen.kt @@ -1,6 +1,6 @@ @file:Suppress("MagicNumber") -package com.tangem.tap.features.home.compose +package com.tangem.features.home.impl.ui.compose import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn @@ -23,24 +23,23 @@ import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameter import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.test.StoriesScreenTestTags -import com.tangem.tap.features.home.compose.content.* -import com.tangem.tap.features.home.compose.views.HomeButtons -import com.tangem.tap.features.home.compose.views.SearchCurrenciesButton -import com.tangem.tap.features.home.compose.views.StoriesProgressBar -import com.tangem.tap.features.home.redux.HomeState -import com.tangem.tap.features.home.redux.Stories -import com.tangem.wallet.R +import com.tangem.features.home.impl.ui.compose.content.* +import com.tangem.features.home.impl.ui.compose.views.HomeButtons +import com.tangem.features.home.impl.ui.compose.views.SearchCurrenciesButton +import com.tangem.features.home.impl.ui.compose.views.StoriesProgressBar +import com.tangem.features.home.impl.ui.state.Stories +import com.tangem.core.ui.R +import com.tangem.features.home.impl.ui.state.HomeUM import kotlin.math.max @Composable internal fun StoriesScreen( - homeState: MutableState, + state: HomeUM, onScanButtonClick: () -> Unit, onShopButtonClick: () -> Unit, onSearchTokensClick: () -> Unit, + modifier: Modifier = Modifier, ) { - val state = homeState.value - var currentStory by remember { mutableStateOf(state.firstStory) } val currentStoryIndex by rememberUpdatedState(newValue = state.stepOf(currentStory)) @@ -59,14 +58,14 @@ internal fun StoriesScreen( // todo refactor [REDACTED_TASK_KEY] StoriesScreenContent( - modifier = Modifier + modifier = modifier .fillMaxSize() .testTag(StoriesScreenTestTags.SCREEN_CONTAINER), config = StoriesScreenContentConfig( storiesSize = state.stories.lastIndex, currentStoryIndex = currentStoryIndex, currentStory = currentStory, - isScanInProgress = homeState.value.scanInProgress, + isScanInProgress = state.scanInProgress, onGoToPreviousStory = goToPreviousStory, onGoToNextStory = goToNextStory, onSearchTokensClick = onSearchTokensClick, diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreenV2.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/StoriesScreenV2.kt similarity index 94% rename from app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreenV2.kt rename to features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/StoriesScreenV2.kt index 5ba5f44315..0554472199 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreenV2.kt +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/StoriesScreenV2.kt @@ -1,6 +1,6 @@ @file:Suppress("MagicNumber") -package com.tangem.tap.features.home.compose +package com.tangem.features.home.impl.ui.compose import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -20,23 +20,22 @@ import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameter import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.test.StoriesScreenTestTags -import com.tangem.tap.features.home.compose.content.* -import com.tangem.tap.features.home.compose.views.HomeButtonsV2 -import com.tangem.tap.features.home.compose.views.StoriesProgressBar -import com.tangem.tap.features.home.redux.HomeState -import com.tangem.tap.features.home.redux.Stories -import com.tangem.wallet.R +import com.tangem.features.home.impl.ui.compose.content.* +import com.tangem.features.home.impl.ui.compose.views.HomeButtonsV2 +import com.tangem.features.home.impl.ui.compose.views.StoriesProgressBar +import com.tangem.features.home.impl.ui.state.Stories import kotlin.math.max +import com.tangem.core.ui.R +import com.tangem.features.home.impl.ui.state.HomeUM @Composable internal fun StoriesScreenV2( - homeState: MutableState, + state: HomeUM, onCreateNewWalletButtonClick: () -> Unit, onAddExistingWalletButtonClick: () -> Unit, onScanButtonClick: () -> Unit, + modifier: Modifier = Modifier, ) { - val state = homeState.value - var currentStory by remember { mutableStateOf(state.firstStory) } val currentStoryIndex by rememberUpdatedState(newValue = state.stepOf(currentStory)) @@ -55,14 +54,14 @@ internal fun StoriesScreenV2( // todo refactor [REDACTED_TASK_KEY] StoriesScreenContentV2( - modifier = Modifier + modifier = modifier .fillMaxSize() .testTag(StoriesScreenTestTags.SCREEN_CONTAINER), config = StoriesScreenContentV2Config( storiesSize = state.stories.lastIndex, currentStoryIndex = currentStoryIndex, currentStory = currentStory, - isScanInProgress = homeState.value.scanInProgress, + isScanInProgress = state.scanInProgress, onGoToPreviousStory = goToPreviousStory, onGoToNextStory = goToNextStory, onCreateNewWalletButtonClick = onCreateNewWalletButtonClick, diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/content/Content.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/content/Content.kt similarity index 96% rename from app/src/main/java/com/tangem/tap/features/home/compose/content/Content.kt rename to features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/content/Content.kt index 1635f234ad..33bf6db728 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/content/Content.kt +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/content/Content.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.home.compose.content +package com.tangem.features.home.impl.ui.compose.content import androidx.annotation.DrawableRes import androidx.compose.foundation.Image @@ -21,9 +21,9 @@ import com.tangem.core.ui.components.SpacerH32 import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme -import com.tangem.tap.features.home.compose.StoriesBottomImageAnimation -import com.tangem.tap.features.home.compose.StoriesTextAnimation -import com.tangem.wallet.R +import com.tangem.features.home.impl.ui.compose.StoriesBottomImageAnimation +import com.tangem.features.home.impl.ui.compose.StoriesTextAnimation +import com.tangem.core.ui.R @Composable fun StoriesRevolutionaryWallet() { diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/content/CurrenciesWeb3Content.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/content/CurrenciesWeb3Content.kt similarity index 93% rename from app/src/main/java/com/tangem/tap/features/home/compose/content/CurrenciesWeb3Content.kt rename to features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/content/CurrenciesWeb3Content.kt index ec1cb88957..66f639bc51 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/content/CurrenciesWeb3Content.kt +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/content/CurrenciesWeb3Content.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.home.compose.content +package com.tangem.features.home.impl.ui.compose.content import androidx.compose.foundation.background import androidx.compose.foundation.layout.* @@ -17,12 +17,10 @@ import androidx.compose.ui.unit.dp import com.tangem.core.ui.components.SpacerH12 import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme -import com.tangem.tap.common.compose.extensions.dpSize -import com.tangem.tap.common.compose.extensions.halfHeight -import com.tangem.tap.common.compose.extensions.toPx -import com.tangem.tap.common.extensions.isEven -import com.tangem.tap.features.home.compose.HorizontalSlidingImage -import com.tangem.wallet.R +import com.tangem.features.home.impl.ui.compose.HorizontalSlidingImage +import com.tangem.core.ui.R +import com.tangem.core.ui.utils.dpSize +import com.tangem.core.ui.utils.toPx @Composable fun StoriesCurrenciesContent(paused: Boolean, duration: Int) { @@ -138,4 +136,8 @@ private val BottomGradient: Brush = Brush.verticalGradient( TangemColorPalette.Black.copy(alpha = 0.95f), TangemColorPalette.Black, ), -) \ No newline at end of file +) + +fun DpSize.halfHeight(): Dp = this.height / 2 + +fun Int.isEven() = this and 1 == 0 \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/content/FirstStoriesContent.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/content/FirstStoriesContent.kt similarity index 94% rename from app/src/main/java/com/tangem/tap/features/home/compose/content/FirstStoriesContent.kt rename to features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/content/FirstStoriesContent.kt index 42fdcaf8fa..f7f6debd9c 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/content/FirstStoriesContent.kt +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/content/FirstStoriesContent.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.home.compose.content +package com.tangem.features.home.impl.ui.compose.content import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.LinearEasing @@ -25,8 +25,8 @@ import com.tangem.core.ui.components.SpacerH import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme -import com.tangem.tap.features.home.compose.StoriesTextAnimation -import com.tangem.wallet.R +import com.tangem.features.home.impl.ui.compose.StoriesTextAnimation +import com.tangem.core.ui.R @Suppress("LongMethod", "ComplexMethod", "MagicNumber") @Composable diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/content/FloatingCardsContent.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/content/FloatingCardsContent.kt similarity index 92% rename from app/src/main/java/com/tangem/tap/features/home/compose/content/FloatingCardsContent.kt rename to features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/content/FloatingCardsContent.kt index 6a010d28ed..e846781ec0 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/content/FloatingCardsContent.kt +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/content/FloatingCardsContent.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.home.compose.content +package com.tangem.features.home.impl.ui.compose.content import androidx.compose.foundation.Image import androidx.compose.foundation.layout.* @@ -6,10 +6,10 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.graphicsLayer -import com.tangem.tap.common.compose.extensions.AnimatedValue -import com.tangem.tap.common.compose.extensions.asImageBitmap -import com.tangem.tap.common.compose.extensions.toAnimatable -import com.tangem.wallet.R +import com.tangem.core.ui.R +import com.tangem.core.ui.utils.AnimatedValue +import com.tangem.core.ui.utils.asImageBitmap +import com.tangem.core.ui.utils.toAnimatable /** [REDACTED_AUTHOR] diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtons.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/views/HomeButtons.kt similarity index 97% rename from app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtons.kt rename to features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/views/HomeButtons.kt index eab6ba4ced..5b0615495b 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtons.kt +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/views/HomeButtons.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.home.compose.views +package com.tangem.features.home.impl.ui.compose.views import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -18,7 +18,7 @@ import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.test.StoriesScreenTestTags -import com.tangem.wallet.R +import com.tangem.core.ui.R @Composable internal fun HomeButtons( diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtonsV2.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/views/HomeButtonsV2.kt similarity index 98% rename from app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtonsV2.kt rename to features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/views/HomeButtonsV2.kt index dc2192b603..c68825891d 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtonsV2.kt +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/views/HomeButtonsV2.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.home.compose.views +package com.tangem.features.home.impl.ui.compose.views import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -19,7 +19,7 @@ import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.test.StoriesScreenTestTags -import com.tangem.wallet.R +import com.tangem.core.ui.R @Composable internal fun HomeButtonsV2( diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/views/SearchCurrenciesButton.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/views/SearchCurrenciesButton.kt similarity index 94% rename from app/src/main/java/com/tangem/tap/features/home/compose/views/SearchCurrenciesButton.kt rename to features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/views/SearchCurrenciesButton.kt index 4d70d5f2f6..0987e2193b 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/views/SearchCurrenciesButton.kt +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/views/SearchCurrenciesButton.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.home.compose.views +package com.tangem.features.home.impl.ui.compose.views import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -12,7 +12,7 @@ import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview -import com.tangem.wallet.R +import com.tangem.core.ui.R @Composable internal fun SearchCurrenciesButton(onClick: () -> Unit, modifier: Modifier = Modifier) { diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesButton.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/views/StoriesButton.kt similarity index 97% rename from app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesButton.kt rename to features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/views/StoriesButton.kt index 66fc2bd6f1..ddcc652d3f 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesButton.kt +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/views/StoriesButton.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.home.compose.views +package com.tangem.features.home.impl.ui.compose.views import androidx.compose.material3.ButtonColors import androidx.compose.runtime.Composable diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesProgressBar.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/views/StoriesProgressBar.kt similarity index 98% rename from app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesProgressBar.kt rename to features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/views/StoriesProgressBar.kt index ef3514bce4..cc6ea2107c 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesProgressBar.kt +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/views/StoriesProgressBar.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.home.compose.views +package com.tangem.features.home.impl.ui.compose.views import android.provider.Settings import androidx.compose.animation.core.Animatable diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/state/HomeUM.kt similarity index 58% rename from app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt rename to features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/state/HomeUM.kt index b1d45779ba..d727df8990 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/state/HomeUM.kt @@ -1,15 +1,16 @@ -package com.tangem.tap.features.home.redux +package com.tangem.features.home.impl.ui.state import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList -import org.rekotlin.StateType - -// todo refactor [REDACTED_TASK_KEY] -data class HomeState( - val scanInProgress: Boolean = false, - val stories: ImmutableList = getRestrictedStories().toImmutableList(), -) : StateType { +data class HomeUM( + val scanInProgress: Boolean, + val stories: ImmutableList, + val onScanClick: () -> Unit, + val onShopClick: () -> Unit, + val onSearchTokensClick: () -> Unit, + val onCreateNewWalletClick: () -> Unit, + val onAddExistingWalletClick: () -> Unit, +) { val firstStory: Stories get() = stories[0] fun stepOf(story: Stories): Int = stories.indexOf(story) diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt index ac205fd637..24dc8aaa95 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt @@ -206,7 +206,7 @@ internal class OnboardingEntryModel @Inject constructor( router.replaceAll(AppRoute.Wallet) } } else { - router.replaceAll(AppRoute.Home) + router.replaceAll(AppRoute.Home()) } } diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt index 520d031d69..dcdfa6c203 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt @@ -197,7 +197,7 @@ internal class WalletSettingsModel @Inject constructor( if (hasUserWallets) { router.pop() } else { - router.replaceAll(AppRoute.Home) + router.replaceAll(AppRoute.Home()) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletCardClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletCardClickIntents.kt index 2ecee965b3..466f15890e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletCardClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletCardClickIntents.kt @@ -102,7 +102,7 @@ internal class WalletCardClickIntentsImplementor @Inject constructor( } else { tokenListStore.clear() stateHolder.clear() - appRouter.replaceAll(AppRoute.Home) + appRouter.replaceAll(AppRoute.Home()) } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 2567421144..846fe77ccb 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -75,7 +75,7 @@ internal class DefaultWalletRouter @Inject constructor( } override fun openStoriesScreen() { - router.push(AppRoute.Home) + router.push(AppRoute.Home()) } override fun isWalletLastScreen(): Boolean { diff --git a/features/welcome/api/src/main/kotlin/com/tangem/features/welcome/WelcomeComponent.kt b/features/welcome/api/src/main/kotlin/com/tangem/features/welcome/WelcomeComponent.kt index 50b914548b..6d9044f216 100644 --- a/features/welcome/api/src/main/kotlin/com/tangem/features/welcome/WelcomeComponent.kt +++ b/features/welcome/api/src/main/kotlin/com/tangem/features/welcome/WelcomeComponent.kt @@ -1,5 +1,6 @@ package com.tangem.features.welcome +import com.tangem.common.routing.entity.InitScreenLaunchMode import com.tangem.common.routing.entity.SerializableIntent import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.decompose.ComposableContentComponent @@ -7,6 +8,7 @@ import com.tangem.core.ui.decompose.ComposableContentComponent interface WelcomeComponent : ComposableContentComponent { data class Params( + val launchMode: InitScreenLaunchMode, val intent: SerializableIntent?, ) diff --git a/settings.gradle.kts b/settings.gradle.kts index 0550f19562..9eb66f4052 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -183,6 +183,9 @@ include(":libs:tangem-sdk-api") include(":features:onboarding-v2:api") include(":features:onboarding-v2:impl") +include(":features:home:api") +include(":features:home:impl") + include(":features:referral:api") include(":features:referral:data") include(":features:referral:domain") From 177da9798efe833fe57151f2233fa7c9b375ced0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 30 Jul 2025 13:45:45 +0400 Subject: [PATCH 18/53] Updated on 2026-08-14 --- .../MockYieldBalanceWrapperDTOFactory.kt | 2 +- .../test/data/staking/MockYieldDTOFactory.kt | 2 +- .../ui/tokens/TokenItemStateConverter.kt | 2 +- .../models/request/ActionRequestBody.kt | 2 +- .../token/converter/BalanceTypeConverter.kt | 2 +- .../PendingActionConstraintsConverter.kt | 4 ++-- .../token/converter/PendingActionConverter.kt | 2 +- .../converter/StakingActionTypeConverter.kt | 2 +- .../converter/StakingNetworkTypeConverter.kt | 2 +- .../token/converter/YieldBalanceConverter.kt | 10 ++++---- ...kenConverter.kt => YieldTokenConverter.kt} | 10 ++++---- .../data/staking/DefaultStakingRepository.kt | 10 ++++---- .../data/staking/converters/YieldConverter.kt | 12 +++++----- .../transaction/GasEstimateConverter.kt | 4 ++-- .../multi/DefaultMultiYieldBalanceFetcher.kt | 2 +- .../multi/DefaultMultiYieldBalanceProducer.kt | 2 +- .../DefaultSingleYieldBalanceProducer.kt | 2 +- .../store/DefaultYieldsBalancesStore.kt | 4 ++-- .../data/staking/store/YieldsBalancesStore.kt | 4 ++-- .../YieldBalanceRequestBodyAddressFactory.kt | 2 +- .../utils/YieldBalanceRequestBodyFactory.kt | 2 +- .../tangem/data/staking/YieldBalanceExt.kt | 2 +- .../DefaultMultiYieldBalanceFetcherTest.kt | 2 +- .../DefaultMultiYieldBalanceProducerTest.kt | 4 ++-- .../DefaultSingleYieldBalanceProducerTest.kt | 4 ++-- .../store/YieldsBalancesStoreGetMethodTest.kt | 2 +- .../YieldsBalancesStoreInitializationTest.kt | 2 +- .../YieldsBalancesStoreUpdateMethodsTest.kt | 4 ++-- .../DefaultCurrencyChecksRepository.kt | 2 +- domain/models/build.gradle.kts | 2 ++ .../domain/models/staking}/NetworkType.kt | 5 +++- .../domain/models/staking}/StakingID.kt | 2 +- .../domain/models/staking}/YieldBalance.kt | 3 +-- .../models/staking}/YieldBalanceItem.kt | 6 ++--- .../domain/models/staking/YieldToken.kt | 15 ++++++++++++ .../staking}/action/StakingActionType.kt | 5 +++- .../domain/staking/model/stakekit/Yield.kt | 23 +++++-------------- .../model/stakekit/action/StakingAction.kt | 1 + .../stakekit/transaction/ActionParams.kt | 6 ++--- .../transaction/StakingGasEstimate.kt | 4 ++-- .../transaction/StakingTransaction.kt | 2 +- .../domain/staking/FetchActionsUseCase.kt | 2 +- .../GetActionRequirementAmountUseCase.kt | 2 +- .../InvalidatePendingTransactionsUseCase.kt | 16 ++++++++----- .../tangem/domain/staking/StakingIdFactory.kt | 2 +- .../analytics/StakingAnalyticsEvent.kt | 2 +- .../staking/multi/MultiYieldBalanceFetcher.kt | 2 +- .../multi/MultiYieldBalanceProducer.kt | 2 +- .../multi/MultiYieldBalanceSupplier.kt | 2 +- .../staking/repositories/StakingRepository.kt | 2 +- .../single/SingleYieldBalanceFetcher.kt | 2 +- .../single/SingleYieldBalanceProducer.kt | 4 ++-- .../single/SingleYieldBalanceSupplier.kt | 2 +- .../domain/staking/utils/YieldBalanceExt.kt | 4 ++-- .../domain/staking/StakingIdFactoryTest.kt | 2 +- .../tokens/model/CryptoCurrencyStatus.kt | 2 +- .../BaseCurrencyStatusOperations.kt | 4 ++-- .../CachedCurrenciesStatusesOperations.kt | 4 ++-- .../operations/CurrencyStatusOperations.kt | 2 +- .../TokenListFiatBalanceOperations.kt | 2 +- .../operations/TokenListSortingOperations.kt | 2 +- .../utils/CurrencyStatusProxyCreator.kt | 4 ++-- .../tokens/wallet/WalletBalanceFetcherTest.kt | 2 +- .../analytics/utils/StakingAnalyticSender.kt | 2 +- .../impl/presentation/model/StakingModel.kt | 7 +++++- .../state/InnerYieldBalanceState.kt | 4 ++++ .../impl/presentation/state/StakingUiState.kt | 2 +- ...StakingActionSelectionBottomSheetConfig.kt | 2 +- .../state/converters/BalanceItemConverter.kt | 10 ++++---- .../RewardsValidatorStateConverter.kt | 6 ++--- .../converters/YieldBalancesConverter.kt | 6 ++++- .../helpers/StakingFeeTransactionLoader.kt | 2 +- .../state/helpers/StakingTransactionSender.kt | 4 ++-- .../previewdata/InitialStakingStatePreview.kt | 4 ++-- .../SetConfirmationStateInitTransformer.kt | 4 ++-- .../SetInitialDataStateTransformer.kt | 2 +- ...howActionSelectorBottomSheetTransformer.kt | 2 +- .../StakingInfoNotificationsFactory.kt | 6 ++--- .../ValidatorSelectChangeTransformer.kt | 2 +- .../state/utils/StakingPendingActionUtils.kt | 6 ++--- .../ui/StakingInitialInfoContent.kt | 4 ++-- .../StakingActionSelectorBottomSheet.kt | 4 ++-- ...TokenDetailsBalanceSelectStateConverter.kt | 2 +- .../TokenDetailsLoadedBalanceConverter.kt | 2 +- .../TokenDetailsStakingInfoConverter.kt | 4 ++-- .../CryptoCurrencyToDraggableItemConverter.kt | 2 +- 86 files changed, 183 insertions(+), 154 deletions(-) rename core/datasource/src/main/java/com/tangem/datasource/local/token/converter/{TokenConverter.kt => YieldTokenConverter.kt} (77%) rename domain/{staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit => models/src/main/kotlin/com/tangem/domain/models/staking}/NetworkType.kt (90%) rename domain/{staking/models/src/main/kotlin/com/tangem/domain/staking/model => models/src/main/kotlin/com/tangem/domain/models/staking}/StakingID.kt (75%) rename domain/{staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit => models/src/main/kotlin/com/tangem/domain/models/staking}/YieldBalance.kt (95%) rename domain/{staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit => models/src/main/kotlin/com/tangem/domain/models/staking}/YieldBalanceItem.kt (95%) create mode 100644 domain/models/src/main/kotlin/com/tangem/domain/models/staking/YieldToken.kt rename domain/{staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit => models/src/main/kotlin/com/tangem/domain/models/staking}/action/StakingActionType.kt (91%) diff --git a/common/test/src/main/java/com/tangem/common/test/data/staking/MockYieldBalanceWrapperDTOFactory.kt b/common/test/src/main/java/com/tangem/common/test/data/staking/MockYieldBalanceWrapperDTOFactory.kt index 1616a4c847..bb2343344e 100644 --- a/common/test/src/main/java/com/tangem/common/test/data/staking/MockYieldBalanceWrapperDTOFactory.kt +++ b/common/test/src/main/java/com/tangem/common/test/data/staking/MockYieldBalanceWrapperDTOFactory.kt @@ -5,7 +5,7 @@ import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO import com.tangem.datasource.api.stakekit.models.response.model.NetworkTypeDTO import com.tangem.datasource.api.stakekit.models.response.model.TokenDTO import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO -import com.tangem.domain.staking.model.StakingID +import com.tangem.domain.models.staking.StakingID import java.math.BigDecimal /** diff --git a/common/test/src/main/java/com/tangem/common/test/data/staking/MockYieldDTOFactory.kt b/common/test/src/main/java/com/tangem/common/test/data/staking/MockYieldDTOFactory.kt index 189705d948..c58ff8edc0 100644 --- a/common/test/src/main/java/com/tangem/common/test/data/staking/MockYieldDTOFactory.kt +++ b/common/test/src/main/java/com/tangem/common/test/data/staking/MockYieldDTOFactory.kt @@ -4,7 +4,7 @@ import com.tangem.datasource.api.stakekit.models.response.model.AddressArgumentD import com.tangem.datasource.api.stakekit.models.response.model.NetworkTypeDTO import com.tangem.datasource.api.stakekit.models.response.model.TokenDTO import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO -import com.tangem.domain.staking.model.StakingID +import com.tangem.domain.models.staking.StakingID import java.math.BigDecimal /** diff --git a/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt index 1ddbfffe90..da96ebf46d 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt @@ -13,7 +13,7 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.format.bigdecimal.percent import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.StatusSource -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.utils.StringsSigns.DASH_SIGN diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/request/ActionRequestBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/request/ActionRequestBody.kt index efd6c85900..c7ab58a7fd 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/request/ActionRequestBody.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/request/ActionRequestBody.kt @@ -5,7 +5,7 @@ import com.squareup.moshi.JsonClass import com.tangem.datasource.api.stakekit.models.request.ConstructTransactionRequestBody.GasArgs import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO import com.tangem.datasource.api.stakekit.models.response.model.TokenDTO -import com.tangem.domain.staking.model.stakekit.action.StakingActionType +import com.tangem.domain.models.staking.action.StakingActionType @JsonClass(generateAdapter = true) data class PendingActionRequestBody( diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/BalanceTypeConverter.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/BalanceTypeConverter.kt index a1e9edcb49..772c349e0a 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/BalanceTypeConverter.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/BalanceTypeConverter.kt @@ -1,7 +1,7 @@ package com.tangem.datasource.local.token.converter import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO.BalanceTypeDTO -import com.tangem.domain.staking.model.stakekit.BalanceType +import com.tangem.domain.models.staking.BalanceType import com.tangem.utils.converter.Converter internal object BalanceTypeConverter : Converter { diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/PendingActionConstraintsConverter.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/PendingActionConstraintsConverter.kt index 2284023080..129ecf3eda 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/PendingActionConstraintsConverter.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/PendingActionConstraintsConverter.kt @@ -1,8 +1,8 @@ package com.tangem.datasource.local.token.converter import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO -import com.tangem.domain.staking.model.stakekit.PendingAction -import com.tangem.domain.staking.model.stakekit.PendingActionConstraints +import com.tangem.domain.models.staking.PendingAction +import com.tangem.domain.models.staking.PendingActionConstraints import com.tangem.utils.converter.Converter internal object PendingActionConstraintsConverter : diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/PendingActionConverter.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/PendingActionConverter.kt index 716d5fc960..161bc9885c 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/PendingActionConverter.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/PendingActionConverter.kt @@ -1,7 +1,7 @@ package com.tangem.datasource.local.token.converter import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO -import com.tangem.domain.staking.model.stakekit.PendingAction +import com.tangem.domain.models.staking.PendingAction import com.tangem.utils.converter.Converter internal object PendingActionConverter : Converter { diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/StakingActionTypeConverter.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/StakingActionTypeConverter.kt index d95777148f..f5300b3327 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/StakingActionTypeConverter.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/StakingActionTypeConverter.kt @@ -1,7 +1,7 @@ package com.tangem.datasource.local.token.converter import com.tangem.datasource.api.stakekit.models.response.model.action.StakingActionTypeDTO -import com.tangem.domain.staking.model.stakekit.action.StakingActionType +import com.tangem.domain.models.staking.action.StakingActionType import com.tangem.utils.converter.Converter @Suppress("CyclomaticComplexMethod") diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/StakingNetworkTypeConverter.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/StakingNetworkTypeConverter.kt index e69821a778..adc411b5e5 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/StakingNetworkTypeConverter.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/StakingNetworkTypeConverter.kt @@ -1,7 +1,7 @@ package com.tangem.datasource.local.token.converter import com.tangem.datasource.api.stakekit.models.response.model.NetworkTypeDTO -import com.tangem.domain.staking.model.stakekit.NetworkType +import com.tangem.domain.models.staking.NetworkType import com.tangem.utils.converter.TwoWayConverter @Suppress("CyclomaticComplexMethod", "LongMethod") diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/YieldBalanceConverter.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/YieldBalanceConverter.kt index 5d86e9a0be..828449590f 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/YieldBalanceConverter.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/YieldBalanceConverter.kt @@ -3,10 +3,10 @@ package com.tangem.datasource.local.token.converter import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO import com.tangem.domain.models.StatusSource -import com.tangem.domain.staking.model.StakingID -import com.tangem.domain.staking.model.stakekit.BalanceItem -import com.tangem.domain.staking.model.stakekit.YieldBalance -import com.tangem.domain.staking.model.stakekit.YieldBalanceItem +import com.tangem.domain.models.staking.BalanceItem +import com.tangem.domain.models.staking.StakingID +import com.tangem.domain.models.staking.YieldBalance +import com.tangem.domain.models.staking.YieldBalanceItem import com.tangem.utils.converter.Converter import kotlinx.datetime.Instant @@ -43,7 +43,7 @@ class YieldBalanceConverter( return BalanceItem( groupId = item.groupId, - token = TokenConverter.convert(item.tokenDTO), + token = YieldTokenConverter.convert(item.tokenDTO), type = BalanceTypeConverter.convert(item.type), amount = item.amount, rawCurrencyId = item.tokenDTO.coinGeckoId, diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/TokenConverter.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/YieldTokenConverter.kt similarity index 77% rename from core/datasource/src/main/java/com/tangem/datasource/local/token/converter/TokenConverter.kt rename to core/datasource/src/main/java/com/tangem/datasource/local/token/converter/YieldTokenConverter.kt index 4a363aa324..4c4e1934df 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/TokenConverter.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/YieldTokenConverter.kt @@ -1,13 +1,13 @@ package com.tangem.datasource.local.token.converter import com.tangem.datasource.api.stakekit.models.response.model.TokenDTO -import com.tangem.domain.staking.model.stakekit.Token +import com.tangem.domain.models.staking.YieldToken import com.tangem.utils.converter.TwoWayConverter -object TokenConverter : TwoWayConverter { +object YieldTokenConverter : TwoWayConverter { - override fun convert(value: TokenDTO): Token { - return Token( + override fun convert(value: TokenDTO): YieldToken { + return YieldToken( name = value.name, network = StakingNetworkTypeConverter.convert(value.network), symbol = value.symbol, @@ -19,7 +19,7 @@ object TokenConverter : TwoWayConverter { ) } - override fun convertBack(value: Token): TokenDTO { + override fun convertBack(value: YieldToken): TokenDTO { return TokenDTO( name = value.name, network = StakingNetworkTypeConverter.convertBack(value.network), diff --git a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt index dfb797e129..0dad5046fe 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt @@ -29,22 +29,22 @@ import com.tangem.datasource.api.stakekit.models.response.model.action.StakingAc import com.tangem.datasource.api.stakekit.models.response.model.transaction.tron.TronStakeKitTransaction import com.tangem.datasource.local.token.StakingYieldsStore import com.tangem.datasource.local.token.converter.StakingNetworkTypeConverter -import com.tangem.datasource.local.token.converter.TokenConverter +import com.tangem.datasource.local.token.converter.YieldTokenConverter import com.tangem.domain.card.common.TapWorkarounds.isWallet2 import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network +import com.tangem.domain.models.staking.NetworkType +import com.tangem.domain.models.staking.YieldBalance +import com.tangem.domain.models.staking.action.StakingActionType import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.staking.model.StakingEntryInfo import com.tangem.domain.staking.model.StakingIntegrationID -import com.tangem.domain.staking.model.stakekit.NetworkType import com.tangem.domain.staking.model.stakekit.Yield -import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.staking.model.stakekit.action.StakingAction import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus -import com.tangem.domain.staking.model.stakekit.action.StakingActionType import com.tangem.domain.staking.model.stakekit.transaction.ActionParams import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction @@ -399,7 +399,7 @@ internal class DefaultStakingRepository( ), args = ActionRequestBodyArgs( amount = params.amount.toPlainString(), - inputToken = TokenConverter.convertBack(params.token), + inputToken = YieldTokenConverter.convertBack(params.token), validatorAddress = params.validatorAddress, validatorAddresses = listOf(params.validatorAddress), // check on other networks tronResource = getTronResource(network), diff --git a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt index 9d8c056e83..60fe5f9e3b 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt @@ -4,7 +4,7 @@ import com.tangem.datasource.api.stakekit.models.response.model.AddressArgumentD import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO.MetadataDTO.RewardScheduleDTO import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO.ValidatorDTO.ValidatorStatusDTO -import com.tangem.datasource.local.token.converter.TokenConverter +import com.tangem.datasource.local.token.converter.YieldTokenConverter import com.tangem.domain.staking.model.stakekit.AddressArgument import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.Yield.Metadata.RewardSchedule @@ -24,8 +24,8 @@ internal object YieldConverter : Converter { override fun convert(value: YieldDTO): Yield { return Yield( id = value.id.asMandatory("id"), - token = TokenConverter.convert(value.token.asMandatory("token")), - tokens = value.tokens.asMandatory("tokens").map(TokenConverter::convert), + token = YieldTokenConverter.convert(value.token.asMandatory("token")), + tokens = value.tokens.asMandatory("tokens").map(YieldTokenConverter::convert), args = convertArgs(value.args.asMandatory("args")), status = convertStatus(value.status.asMandatory("status")), apy = value.apy.asMandatory("apy"), @@ -91,9 +91,9 @@ internal object YieldConverter : Converter { logoUri = metadataDTO.logoUri.asMandatory("logoUri"), description = metadataDTO.description.asMandatory("description"), documentation = metadataDTO.documentation, - gasFeeToken = TokenConverter.convert(metadataDTO.gasFeeTokenDTO.asMandatory("gasFeeTokenDTO")), - token = TokenConverter.convert(metadataDTO.tokenDTO.asMandatory("tokenDTO")), - tokens = metadataDTO.tokensDTO.asMandatory("tokensDTO").map(TokenConverter::convert), + gasFeeToken = YieldTokenConverter.convert(metadataDTO.gasFeeTokenDTO.asMandatory("gasFeeTokenDTO")), + token = YieldTokenConverter.convert(metadataDTO.tokenDTO.asMandatory("tokenDTO")), + tokens = metadataDTO.tokensDTO.asMandatory("tokensDTO").map(YieldTokenConverter::convert), type = metadataDTO.type.asMandatory("type"), rewardSchedule = convertRewardSchedule(metadataDTO.rewardSchedule.asMandatory("rewardSchedule")), cooldownPeriod = metadataDTO.cooldownPeriod?.let { convertPeriod(it) }, diff --git a/data/staking/src/main/java/com/tangem/data/staking/converters/transaction/GasEstimateConverter.kt b/data/staking/src/main/java/com/tangem/data/staking/converters/transaction/GasEstimateConverter.kt index a9fb76fc0a..64ed35b3e1 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/converters/transaction/GasEstimateConverter.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/converters/transaction/GasEstimateConverter.kt @@ -1,7 +1,7 @@ package com.tangem.data.staking.converters.transaction import com.tangem.datasource.api.stakekit.models.response.model.transaction.StakingGasEstimateDTO -import com.tangem.datasource.local.token.converter.TokenConverter +import com.tangem.datasource.local.token.converter.YieldTokenConverter import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate import com.tangem.utils.converter.Converter @@ -10,7 +10,7 @@ internal object GasEstimateConverter : Converter().configureEach { } dependencies { + api(projects.domain.core) api(projects.domain.visa.models) api(projects.core.utils) @@ -19,6 +20,7 @@ dependencies { implementation(tangemDeps.hot.core) implementation(deps.moshi.kotlin) implementation(deps.moshi.adapters) + implementation(deps.kotlin.datetime) implementation(deps.kotlin.serialization) ksp(deps.moshi.kotlin.codegen) implementation(deps.arrow.core) diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/NetworkType.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/staking/NetworkType.kt similarity index 90% rename from domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/NetworkType.kt rename to domain/models/src/main/kotlin/com/tangem/domain/models/staking/NetworkType.kt index 2733a30688..3e0921c351 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/NetworkType.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/staking/NetworkType.kt @@ -1,5 +1,8 @@ -package com.tangem.domain.staking.model.stakekit +package com.tangem.domain.models.staking +import kotlinx.serialization.Serializable + +@Serializable enum class NetworkType { AVALANCHE_C, AVALANCHE_ATOMIC, diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/StakingID.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/staking/StakingID.kt similarity index 75% rename from domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/StakingID.kt rename to domain/models/src/main/kotlin/com/tangem/domain/models/staking/StakingID.kt index 59618e15b0..5ed3a0b3ce 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/StakingID.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/staking/StakingID.kt @@ -1,4 +1,4 @@ -package com.tangem.domain.staking.model +package com.tangem.domain.models.staking import kotlinx.serialization.Serializable diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/staking/YieldBalance.kt similarity index 95% rename from domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt rename to domain/models/src/main/kotlin/com/tangem/domain/models/staking/YieldBalance.kt index 1dd03af2b9..3badeed6e2 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/staking/YieldBalance.kt @@ -1,7 +1,6 @@ -package com.tangem.domain.staking.model.stakekit +package com.tangem.domain.models.staking import com.tangem.domain.models.StatusSource -import com.tangem.domain.staking.model.StakingID import kotlinx.serialization.Serializable /** diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalanceItem.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/staking/YieldBalanceItem.kt similarity index 95% rename from domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalanceItem.kt rename to domain/models/src/main/kotlin/com/tangem/domain/models/staking/YieldBalanceItem.kt index b5bbd086b5..0f59dfe679 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalanceItem.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/staking/YieldBalanceItem.kt @@ -1,7 +1,7 @@ -package com.tangem.domain.staking.model.stakekit +package com.tangem.domain.models.staking import com.tangem.domain.core.serialization.SerializedBigDecimal -import com.tangem.domain.staking.model.stakekit.action.StakingActionType +import com.tangem.domain.models.staking.action.StakingActionType import kotlinx.datetime.Instant import kotlinx.serialization.Serializable @@ -14,7 +14,7 @@ data class YieldBalanceItem( @Serializable data class BalanceItem( val groupId: String, - val token: Token, + val token: YieldToken, val type: BalanceType, val amount: SerializedBigDecimal, val rawCurrencyId: String?, diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/staking/YieldToken.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/staking/YieldToken.kt new file mode 100644 index 0000000000..18954a5c1f --- /dev/null +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/staking/YieldToken.kt @@ -0,0 +1,15 @@ +package com.tangem.domain.models.staking + +import kotlinx.serialization.Serializable + +@Serializable +data class YieldToken( + val name: String, + val network: NetworkType, + val symbol: String, + val decimals: Int, + val address: String?, + val coinGeckoId: String?, + val logoURI: String?, + val isPoints: Boolean?, +) \ No newline at end of file diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/action/StakingActionType.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/staking/action/StakingActionType.kt similarity index 91% rename from domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/action/StakingActionType.kt rename to domain/models/src/main/kotlin/com/tangem/domain/models/staking/action/StakingActionType.kt index 467bb051aa..d255b23852 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/action/StakingActionType.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/staking/action/StakingActionType.kt @@ -1,5 +1,8 @@ -package com.tangem.domain.staking.model.stakekit.action +package com.tangem.domain.models.staking.action +import kotlinx.serialization.Serializable + +@Serializable enum class StakingActionType { STAKE, UNSTAKE, diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt index 063bcce89a..75a9e20b8f 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt @@ -1,13 +1,14 @@ package com.tangem.domain.staking.model.stakekit import com.tangem.domain.core.serialization.SerializedBigDecimal +import com.tangem.domain.models.staking.YieldToken import kotlinx.serialization.Serializable @Serializable data class Yield( val id: String, - val token: Token, - val tokens: List, + val token: YieldToken, + val tokens: List, val args: Args, val status: Status, val apy: SerializedBigDecimal, @@ -93,9 +94,9 @@ data class Yield( val logoUri: String, val description: String, val documentation: String?, - val gasFeeToken: Token, - val token: Token, - val tokens: List, + val gasFeeToken: YieldToken, + val token: YieldToken, + val tokens: List, val type: String, val rewardSchedule: RewardSchedule, val cooldownPeriod: Period?, @@ -145,18 +146,6 @@ data class Yield( } } -@Serializable -data class Token( - val name: String, - val network: NetworkType, - val symbol: String, - val decimals: Int, - val address: String?, - val coinGeckoId: String?, - val logoURI: String?, - val isPoints: Boolean?, -) - @Serializable data class AddressArgument( val required: Boolean, diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/action/StakingAction.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/action/StakingAction.kt index be1545d6da..bfe0503203 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/action/StakingAction.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/action/StakingAction.kt @@ -1,5 +1,6 @@ package com.tangem.domain.staking.model.stakekit.action +import com.tangem.domain.models.staking.action.StakingActionType import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction import org.joda.time.DateTime import java.math.BigDecimal diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/transaction/ActionParams.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/transaction/ActionParams.kt index 605ed69fa5..1bf184eded 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/transaction/ActionParams.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/transaction/ActionParams.kt @@ -1,8 +1,8 @@ package com.tangem.domain.staking.model.stakekit.transaction -import com.tangem.domain.staking.model.stakekit.Token +import com.tangem.domain.models.staking.YieldToken +import com.tangem.domain.models.staking.action.StakingActionType import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType -import com.tangem.domain.staking.model.stakekit.action.StakingActionType import java.math.BigDecimal data class ActionParams( @@ -11,7 +11,7 @@ data class ActionParams( val amount: BigDecimal, val address: String, val validatorAddress: String, - val token: Token, + val token: YieldToken, val publicKey: String? = null, val passthrough: String? = null, val type: StakingActionType? = null, diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/transaction/StakingGasEstimate.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/transaction/StakingGasEstimate.kt index 585ae1edb5..7f8562f492 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/transaction/StakingGasEstimate.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/transaction/StakingGasEstimate.kt @@ -1,10 +1,10 @@ package com.tangem.domain.staking.model.stakekit.transaction -import com.tangem.domain.staking.model.stakekit.Token +import com.tangem.domain.models.staking.YieldToken import java.math.BigDecimal data class StakingGasEstimate( val amount: BigDecimal, - val token: Token, + val token: YieldToken, val gasLimit: String?, ) \ No newline at end of file diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/transaction/StakingTransaction.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/transaction/StakingTransaction.kt index 4222203a21..058a7d1e6d 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/transaction/StakingTransaction.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/transaction/StakingTransaction.kt @@ -1,6 +1,6 @@ package com.tangem.domain.staking.model.stakekit.transaction -import com.tangem.domain.staking.model.stakekit.NetworkType +import com.tangem.domain.models.staking.NetworkType data class StakingTransaction( val id: String, diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/FetchActionsUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/FetchActionsUseCase.kt index bf3fb63499..f44adef4ae 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/FetchActionsUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/FetchActionsUseCase.kt @@ -2,7 +2,7 @@ package com.tangem.domain.staking import arrow.core.Either import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.staking.model.stakekit.NetworkType +import com.tangem.domain.models.staking.NetworkType import com.tangem.domain.staking.model.stakekit.StakingError import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus import com.tangem.domain.staking.repositories.StakingActionRepository diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/GetActionRequirementAmountUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/GetActionRequirementAmountUseCase.kt index ccbb011f35..c3c236025c 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/GetActionRequirementAmountUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/GetActionRequirementAmountUseCase.kt @@ -1,7 +1,7 @@ package com.tangem.domain.staking import com.tangem.domain.staking.model.StakingIntegrationID -import com.tangem.domain.staking.model.stakekit.action.StakingActionType +import com.tangem.domain.models.staking.action.StakingActionType import java.math.BigDecimal class GetActionRequirementAmountUseCase { diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt index 2b1a377476..f24cac1f74 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt @@ -1,10 +1,14 @@ package com.tangem.domain.staking import arrow.core.Either -import com.tangem.domain.staking.model.stakekit.* +import com.tangem.domain.models.staking.BalanceItem +import com.tangem.domain.models.staking.BalanceType +import com.tangem.domain.models.staking.NetworkType +import com.tangem.domain.models.staking.YieldToken +import com.tangem.domain.models.staking.action.StakingActionType +import com.tangem.domain.staking.model.stakekit.StakingError import com.tangem.domain.staking.model.stakekit.action.StakingAction import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus -import com.tangem.domain.staking.model.stakekit.action.StakingActionType import com.tangem.domain.staking.repositories.StakingErrorResolver import com.tangem.utils.extensions.isEqualTo import java.math.BigDecimal @@ -17,7 +21,7 @@ class InvalidatePendingTransactionsUseCase( operator fun invoke( balanceItems: List, stakingActions: List, - token: Token, + token: YieldToken, ): Either> { return Either.catch { val balancesToDisplay = mergeBalancesAndProcessingActions( @@ -34,7 +38,7 @@ class InvalidatePendingTransactionsUseCase( private fun mergeBalancesAndProcessingActions( realBalances: List, processingActions: List, - token: Token, + token: YieldToken, ): List { val balances = realBalances.toMutableList() @@ -87,7 +91,7 @@ class InvalidatePendingTransactionsUseCase( private fun addStubStakedPendingTransaction( balances: MutableList, action: StakingAction, - token: Token, + token: YieldToken, ) { balances.add( BalanceItem( @@ -153,7 +157,7 @@ class InvalidatePendingTransactionsUseCase( return index to action.amount } - private fun doPostProcessing(balances: MutableList, action: StakingAction, token: Token) { + private fun doPostProcessing(balances: MutableList, action: StakingAction, token: YieldToken) { val validatorAddress = action.validatorAddress ?: action.validatorAddresses?.firstOrNull() if (token.network == NetworkType.TON && validatorAddress != null) { for (index in balances.indices) { diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/StakingIdFactory.kt b/domain/staking/src/main/java/com/tangem/domain/staking/StakingIdFactory.kt index a830af4484..c8648ad64c 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/StakingIdFactory.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/StakingIdFactory.kt @@ -6,7 +6,7 @@ import arrow.core.raise.ensureNotNull import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.staking.model.StakingID +import com.tangem.domain.models.staking.StakingID import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.walletmanager.WalletManagersFacade diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/analytics/StakingAnalyticsEvent.kt b/domain/staking/src/main/java/com/tangem/domain/staking/analytics/StakingAnalyticsEvent.kt index 063e0e9f70..f8376e3707 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/analytics/StakingAnalyticsEvent.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/analytics/StakingAnalyticsEvent.kt @@ -4,7 +4,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.domain.staking.analytics.StakingAnalyticsEvent.ButtonRewards.addIfValueIsNotNull import com.tangem.domain.staking.model.stakekit.StakingError -import com.tangem.domain.staking.model.stakekit.action.StakingActionType +import com.tangem.domain.models.staking.action.StakingActionType sealed class StakingAnalyticsEvent( event: String, diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/multi/MultiYieldBalanceFetcher.kt b/domain/staking/src/main/java/com/tangem/domain/staking/multi/MultiYieldBalanceFetcher.kt index 17330db5b0..041755dbc8 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/multi/MultiYieldBalanceFetcher.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/multi/MultiYieldBalanceFetcher.kt @@ -2,7 +2,7 @@ package com.tangem.domain.staking.multi import com.tangem.domain.core.flow.FlowFetcher import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.staking.model.StakingID +import com.tangem.domain.models.staking.StakingID /** * Fetcher of yields balances diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/multi/MultiYieldBalanceProducer.kt b/domain/staking/src/main/java/com/tangem/domain/staking/multi/MultiYieldBalanceProducer.kt index b5b2446198..7b9357cb24 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/multi/MultiYieldBalanceProducer.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/multi/MultiYieldBalanceProducer.kt @@ -1,7 +1,7 @@ package com.tangem.domain.staking.multi import com.tangem.domain.core.flow.FlowProducer -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.models.wallet.UserWalletId /** diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/multi/MultiYieldBalanceSupplier.kt b/domain/staking/src/main/java/com/tangem/domain/staking/multi/MultiYieldBalanceSupplier.kt index 4c6cbed1f3..6d6f106113 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/multi/MultiYieldBalanceSupplier.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/multi/MultiYieldBalanceSupplier.kt @@ -2,7 +2,7 @@ package com.tangem.domain.staking.multi import com.tangem.domain.core.flow.FlowCachingSupplier import com.tangem.domain.core.flow.FlowProducer -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.YieldBalance /** * Supplier of all yield balances for selected wallet [MultiYieldBalanceProducer.Params] diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt index 3be0818547..efbbae2c38 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt @@ -8,7 +8,7 @@ import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.staking.model.StakingEntryInfo -import com.tangem.domain.staking.model.stakekit.NetworkType +import com.tangem.domain.models.staking.NetworkType import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingAction import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceFetcher.kt b/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceFetcher.kt index 789c93584a..61a7ee90c6 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceFetcher.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceFetcher.kt @@ -2,7 +2,7 @@ package com.tangem.domain.staking.single import com.tangem.domain.core.flow.FlowFetcher import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.staking.model.StakingID +import com.tangem.domain.models.staking.StakingID /** * Fetcher of yield balance diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceProducer.kt b/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceProducer.kt index 7fea7e7014..9d33907751 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceProducer.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceProducer.kt @@ -2,8 +2,8 @@ package com.tangem.domain.staking.single import com.tangem.domain.core.flow.FlowProducer import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.staking.model.StakingID -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.StakingID +import com.tangem.domain.models.staking.YieldBalance /** * Producer of yield balance for selected wallet [UserWalletId] diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceSupplier.kt b/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceSupplier.kt index f7509dc980..4d93e733a1 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceSupplier.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/single/SingleYieldBalanceSupplier.kt @@ -2,7 +2,7 @@ package com.tangem.domain.staking.single import com.tangem.domain.core.flow.FlowCachingSupplier import com.tangem.domain.core.flow.FlowProducer -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.YieldBalance /** * Supplier of yield balance for selected wallet [SingleYieldBalanceProducer.Params] diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/utils/YieldBalanceExt.kt b/domain/staking/src/main/java/com/tangem/domain/staking/utils/YieldBalanceExt.kt index fc7a759b50..f6b6a96d42 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/utils/YieldBalanceExt.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/utils/YieldBalanceExt.kt @@ -1,7 +1,7 @@ package com.tangem.domain.staking.utils -import com.tangem.domain.staking.model.stakekit.BalanceType -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.BalanceType +import com.tangem.domain.models.staking.YieldBalance import com.tangem.lib.crypto.BlockchainUtils import java.math.BigDecimal diff --git a/domain/staking/src/test/kotlin/com/tangem/domain/staking/StakingIdFactoryTest.kt b/domain/staking/src/test/kotlin/com/tangem/domain/staking/StakingIdFactoryTest.kt index cd77c25e9e..09f2670173 100644 --- a/domain/staking/src/test/kotlin/com/tangem/domain/staking/StakingIdFactoryTest.kt +++ b/domain/staking/src/test/kotlin/com/tangem/domain/staking/StakingIdFactoryTest.kt @@ -9,7 +9,7 @@ import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory import com.tangem.common.test.utils.ProvideTestModels import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.staking.model.StakingID +import com.tangem.domain.models.staking.StakingID import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.walletmanager.WalletManagersFacade import io.mockk.clearMocks diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt index 65d534c54b..3fc15a0f52 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt @@ -5,7 +5,7 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.getResultStatusSource import com.tangem.domain.models.network.NetworkAddress import com.tangem.domain.models.network.TxInfo -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.YieldBalance import java.math.BigDecimal /** diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt index 70f695f4ef..abf478099e 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt @@ -17,9 +17,9 @@ import com.tangem.domain.quotes.QuotesRepository import com.tangem.domain.quotes.single.SingleQuoteStatusProducer import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier import com.tangem.domain.staking.StakingIdFactory -import com.tangem.domain.staking.model.StakingID +import com.tangem.domain.models.staking.StakingID import com.tangem.domain.staking.model.isStakingSupported -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.multi.MultiYieldBalanceProducer import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier import com.tangem.domain.staking.single.SingleYieldBalanceProducer diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt index 797a76ca5f..9d0dfe2461 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt @@ -25,9 +25,9 @@ import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher import com.tangem.domain.quotes.single.SingleQuoteStatusProducer import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier import com.tangem.domain.staking.StakingIdFactory -import com.tangem.domain.staking.model.StakingID +import com.tangem.domain.models.staking.StakingID import com.tangem.domain.staking.model.StakingIntegrationID -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier import com.tangem.domain.staking.single.SingleYieldBalanceProducer diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt index ca1b7b0fcb..da8de5018f 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt @@ -4,7 +4,7 @@ import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.quote.QuoteStatus -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.tokens.model.CryptoCurrencyStatus import java.math.BigDecimal diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt index ec1e5a826d..2bd4ff898a 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt @@ -4,7 +4,7 @@ import arrow.core.NonEmptyList import com.tangem.domain.models.StatusSource import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.getResultStatusSource -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.lib.crypto.BlockchainUtils diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt index 72382a701c..bec82f8e2a 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt @@ -10,7 +10,7 @@ import arrow.core.toNonEmptyListOrNull import com.tangem.domain.models.TokensSortType import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.network.Network -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkGroup diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt index 53b7db96ff..08138e4e05 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt @@ -7,9 +7,9 @@ import arrow.core.toNonEmptySetOrNull import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.quote.QuoteStatus -import com.tangem.domain.staking.model.StakingID +import com.tangem.domain.models.staking.StakingID import com.tangem.domain.staking.model.StakingIntegrationID -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error import com.tangem.domain.tokens.operations.CurrencyStatusOperations diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/wallet/WalletBalanceFetcherTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/wallet/WalletBalanceFetcherTest.kt index f7cc8a5633..1a8e1bf3f0 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/wallet/WalletBalanceFetcherTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/wallet/WalletBalanceFetcherTest.kt @@ -12,7 +12,7 @@ import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher import com.tangem.domain.staking.StakingIdFactory -import com.tangem.domain.staking.model.StakingID +import com.tangem.domain.models.staking.StakingID import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.tokens.repository.CurrenciesRepository diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/utils/StakingAnalyticSender.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/utils/StakingAnalyticSender.kt index 3c56d06c66..65ffcfdedb 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/utils/StakingAnalyticSender.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/utils/StakingAnalyticSender.kt @@ -8,7 +8,7 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.staking.analytics.StakeScreenSource import com.tangem.domain.staking.analytics.StakingAnalyticsEvent import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType -import com.tangem.domain.staking.model.stakekit.action.StakingActionType +import com.tangem.domain.models.staking.action.StakingActionType import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.* diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt index 96e5279cb3..90a4867e0c 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt @@ -29,6 +29,11 @@ import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.BlockchainErrorInfo import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.staking.BalanceItem +import com.tangem.domain.models.staking.BalanceType +import com.tangem.domain.models.staking.PendingAction +import com.tangem.domain.models.staking.RewardBlockType +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.requireColdWallet @@ -40,7 +45,7 @@ import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.staking.model.stakekit.* import com.tangem.domain.staking.model.stakekit.action.StakingAction import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType -import com.tangem.domain.staking.model.stakekit.action.StakingActionType +import com.tangem.domain.models.staking.action.StakingActionType import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction import com.tangem.domain.staking.utils.getValidatorsCount import com.tangem.domain.tokens.* diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerYieldBalanceState.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerYieldBalanceState.kt index a4b3ea96fd..36a4a319f5 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerYieldBalanceState.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerYieldBalanceState.kt @@ -2,6 +2,10 @@ package com.tangem.features.staking.impl.presentation.state import androidx.compose.runtime.Immutable import com.tangem.core.ui.extensions.TextReference +import com.tangem.domain.models.staking.BalanceType +import com.tangem.domain.models.staking.PendingAction +import com.tangem.domain.models.staking.PendingActionConstraints +import com.tangem.domain.models.staking.RewardBlockType import com.tangem.domain.staking.model.stakekit.* import kotlinx.collections.immutable.ImmutableList import java.math.BigDecimal diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt index e4bc6089b6..3f8262760b 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt @@ -9,7 +9,7 @@ import com.tangem.core.ui.components.list.RoundedListWithDividersItemData import com.tangem.core.ui.event.StateEvent import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig -import com.tangem.domain.staking.model.stakekit.PendingAction +import com.tangem.domain.models.staking.PendingAction import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.features.staking.impl.presentation.state.bottomsheet.InfoType diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/bottomsheet/StakingActionSelectionBottomSheetConfig.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/bottomsheet/StakingActionSelectionBottomSheetConfig.kt index 873ba40422..6601a859f6 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/bottomsheet/StakingActionSelectionBottomSheetConfig.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/bottomsheet/StakingActionSelectionBottomSheetConfig.kt @@ -2,7 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.bottomsheet import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.extensions.TextReference -import com.tangem.domain.staking.model.stakekit.PendingAction +import com.tangem.domain.models.staking.PendingAction internal data class StakingActionSelectionBottomSheetConfig( val title: TextReference, diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt index bdc0f43c77..9476521ace 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt @@ -6,12 +6,12 @@ import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.staking.model.stakekit.BalanceItem -import com.tangem.domain.staking.model.stakekit.BalanceType -import com.tangem.domain.staking.model.stakekit.BalanceType.Companion.isClickable +import com.tangem.domain.models.staking.BalanceItem +import com.tangem.domain.models.staking.BalanceType +import com.tangem.domain.models.staking.BalanceType.Companion.isClickable import com.tangem.domain.staking.model.stakekit.Yield -import com.tangem.domain.staking.model.stakekit.YieldBalance -import com.tangem.domain.staking.model.stakekit.action.StakingActionType +import com.tangem.domain.models.staking.YieldBalance +import com.tangem.domain.models.staking.action.StakingActionType import com.tangem.domain.staking.utils.getRewardStakingBalance import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.R diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/RewardsValidatorStateConverter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/RewardsValidatorStateConverter.kt index 4fefa8ca21..af1a4bde08 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/RewardsValidatorStateConverter.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/RewardsValidatorStateConverter.kt @@ -6,10 +6,10 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.staking.model.stakekit.BalanceItem -import com.tangem.domain.staking.model.stakekit.BalanceType +import com.tangem.domain.models.staking.BalanceItem +import com.tangem.domain.models.staking.BalanceType import com.tangem.domain.staking.model.stakekit.Yield -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.BalanceState import com.tangem.features.staking.impl.presentation.state.StakingStates diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt index f82c1ece1f..0ed2a3814c 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt @@ -5,8 +5,12 @@ import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.staking.BalanceItem +import com.tangem.domain.models.staking.BalanceType +import com.tangem.domain.models.staking.RewardBlockType +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.model.stakekit.* -import com.tangem.domain.staking.model.stakekit.action.StakingActionType +import com.tangem.domain.models.staking.action.StakingActionType import com.tangem.domain.staking.utils.getRewardStakingBalance import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingFeeTransactionLoader.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingFeeTransactionLoader.kt index fe8e42f2a8..94e68d2aad 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingFeeTransactionLoader.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingFeeTransactionLoader.kt @@ -9,7 +9,7 @@ import com.tangem.common.extensions.isZero import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.staking.EstimateGasUseCase -import com.tangem.domain.staking.model.stakekit.PendingAction +import com.tangem.domain.models.staking.PendingAction import com.tangem.domain.staking.model.stakekit.StakingError import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt index 74cfa8e53b..f2a1574d06 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt @@ -10,8 +10,8 @@ import com.tangem.domain.staking.GetStakingTransactionsUseCase import com.tangem.domain.staking.SaveUnsubmittedHashUseCase import com.tangem.domain.staking.SubmitHashUseCase import com.tangem.domain.staking.model.SubmitHashData -import com.tangem.domain.staking.model.stakekit.NetworkType -import com.tangem.domain.staking.model.stakekit.PendingAction +import com.tangem.domain.models.staking.NetworkType +import com.tangem.domain.models.staking.PendingAction import com.tangem.domain.staking.model.stakekit.StakingError import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/InitialStakingStatePreview.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/InitialStakingStatePreview.kt index d7af6496e2..da70fc8533 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/InitialStakingStatePreview.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/InitialStakingStatePreview.kt @@ -4,8 +4,8 @@ import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfi import com.tangem.core.ui.components.list.RoundedListWithDividersItemData import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.stringReference -import com.tangem.domain.staking.model.stakekit.BalanceType -import com.tangem.domain.staking.model.stakekit.RewardBlockType +import com.tangem.domain.models.staking.BalanceType +import com.tangem.domain.models.staking.RewardBlockType import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.features.staking.impl.R import com.tangem.features.staking.impl.presentation.state.BalanceState diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateInitTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateInitTransformer.kt index ba40e2960c..99b2bd7041 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateInitTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateInitTransformer.kt @@ -2,10 +2,10 @@ package com.tangem.features.staking.impl.presentation.state.transformers import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.staking.model.StakingApproval -import com.tangem.domain.staking.model.stakekit.PendingAction +import com.tangem.domain.models.staking.PendingAction import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType -import com.tangem.domain.staking.model.stakekit.action.StakingActionType +import com.tangem.domain.models.staking.action.StakingActionType import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.* import com.tangem.features.staking.impl.presentation.state.utils.isCompositePendingActions diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt index 82f48a612c..e2e24ff112 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt @@ -14,7 +14,7 @@ import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.format.bigdecimal.percent import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.staking.model.stakekit.BalanceItem +import com.tangem.domain.models.staking.BalanceItem import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ShowActionSelectorBottomSheetTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ShowActionSelectorBottomSheetTransformer.kt index c70861712b..07077a8575 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ShowActionSelectorBottomSheetTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ShowActionSelectorBottomSheetTransformer.kt @@ -2,7 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.extensions.resourceReference -import com.tangem.domain.staking.model.stakekit.PendingAction +import com.tangem.domain.models.staking.PendingAction import com.tangem.features.staking.impl.R import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.features.staking.impl.presentation.state.bottomsheet.StakingActionSelectionBottomSheetConfig diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt index 10e064d094..ee2c810e32 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt @@ -4,11 +4,11 @@ import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.ui.extensions.pluralReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.wrappedList -import com.tangem.domain.staking.model.stakekit.BalanceType +import com.tangem.domain.models.staking.BalanceType import com.tangem.domain.staking.model.stakekit.Yield -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType -import com.tangem.domain.staking.model.stakekit.action.StakingActionType +import com.tangem.domain.models.staking.action.StakingActionType import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.R import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/validator/ValidatorSelectChangeTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/validator/ValidatorSelectChangeTransformer.kt index f9f71dd51e..d9948ed3b8 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/validator/ValidatorSelectChangeTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/validator/ValidatorSelectChangeTransformer.kt @@ -2,7 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers.validat import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType -import com.tangem.domain.staking.model.stakekit.action.StakingActionType +import com.tangem.domain.models.staking.action.StakingActionType import com.tangem.features.staking.impl.presentation.state.StakingStates import com.tangem.features.staking.impl.presentation.state.StakingStep import com.tangem.features.staking.impl.presentation.state.StakingUiState diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/utils/StakingPendingActionUtils.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/utils/StakingPendingActionUtils.kt index da281e339d..6ef20b93a9 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/utils/StakingPendingActionUtils.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/utils/StakingPendingActionUtils.kt @@ -3,9 +3,9 @@ package com.tangem.features.staking.impl.presentation.state.utils import com.tangem.core.ui.R import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference -import com.tangem.domain.staking.model.stakekit.BalanceType -import com.tangem.domain.staking.model.stakekit.PendingAction -import com.tangem.domain.staking.model.stakekit.action.StakingActionType +import com.tangem.domain.models.staking.BalanceType +import com.tangem.domain.models.staking.PendingAction +import com.tangem.domain.models.staking.action.StakingActionType import com.tangem.features.staking.impl.presentation.state.BalanceState import com.tangem.lib.crypto.BlockchainUtils.isBSC import com.tangem.lib.crypto.BlockchainUtils.isCardano diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt index 1cccaf852d..462c5d22c0 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt @@ -42,8 +42,8 @@ import com.tangem.core.ui.format.bigdecimal.percent import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview -import com.tangem.domain.staking.model.stakekit.BalanceType -import com.tangem.domain.staking.model.stakekit.RewardBlockType +import com.tangem.domain.models.staking.BalanceType +import com.tangem.domain.models.staking.RewardBlockType import com.tangem.features.staking.impl.R import com.tangem.features.staking.impl.presentation.model.StakingClickIntents import com.tangem.features.staking.impl.presentation.state.BalanceState diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/bottomsheet/StakingActionSelectorBottomSheet.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/bottomsheet/StakingActionSelectorBottomSheet.kt index 9a1c63bc5b..ae9fc0d984 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/bottomsheet/StakingActionSelectorBottomSheet.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/bottomsheet/StakingActionSelectorBottomSheet.kt @@ -17,8 +17,8 @@ import com.tangem.core.ui.components.inputrow.InputRowDefault import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview -import com.tangem.domain.staking.model.stakekit.PendingAction -import com.tangem.domain.staking.model.stakekit.action.StakingActionType +import com.tangem.domain.models.staking.PendingAction +import com.tangem.domain.models.staking.action.StakingActionType import com.tangem.features.staking.impl.R import com.tangem.features.staking.impl.presentation.state.bottomsheet.StakingActionSelectionBottomSheetConfig import com.tangem.features.staking.impl.presentation.state.utils.getPendingActionTitle diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsBalanceSelectStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsBalanceSelectStateConverter.kt index ac4af1dc72..c2f97a9c09 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsBalanceSelectStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsBalanceSelectStateConverter.kt @@ -4,7 +4,7 @@ import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.feature.tokendetails.presentation.tokendetails.state.* diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt index 75fd755d3b..da60bfba2b 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt @@ -9,7 +9,7 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.format.bigdecimal.* import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.StatusSource -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.tokens.model.CryptoCurrencyStatus diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStakingInfoConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStakingInfoConverter.kt index 82a04112f4..2a7dfc2ef6 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStakingInfoConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStakingInfoConverter.kt @@ -10,8 +10,8 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.staking.model.StakingEntryInfo -import com.tangem.domain.staking.model.stakekit.RewardBlockType -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.RewardBlockType +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.utils.getRewardStakingBalance import com.tangem.domain.staking.utils.getTotalStakingBalance import com.tangem.domain.tokens.model.CryptoCurrencyStatus diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt index 87c169926c..037bed6176 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt @@ -7,7 +7,7 @@ import com.tangem.core.ui.format.bigdecimal.BigDecimalFormatConstants import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem From 011df1a797df1bf6ad2ab2be2e5608705c29836a Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 30 Jul 2025 14:49:45 +0400 Subject: [PATCH 19/53] Updated on 2026-08-14 --- .../exchangeServices/DefaultRampManager.kt | 2 +- .../converters/AmountCurrencyTransformer.kt | 2 +- .../converters/AmountReduceByTransformer.kt | 2 +- .../converters/AmountReduceToTransformer.kt | 2 +- .../converters/AmountStateConverter.kt | 2 +- .../converters/MaxEnterAmountConverter.kt | 2 +- .../field/AmountBoundaryUpdateTransformer.kt | 2 +- .../field/AmountFieldChangeTransformer.kt | 2 +- .../converters/field/AmountFieldConverter.kt | 2 +- .../AmountFieldSetMaxAmountTransformer.kt | 2 +- .../ui/notifications/NotificationsFactory.kt | 2 +- .../ui/tokens/TokenItemStateConverter.kt | 2 +- .../CryptoCurrencyToIconStateConverter.kt | 2 +- .../onramp/models/OnrampTransactionDTO.kt | 2 +- .../data/swap/DefaultSwapRepositoryV2.kt | 2 +- .../repository/DefaultCurrenciesRepository.kt | 2 +- .../DefaultCurrencyChecksRepository.kt | 2 +- .../domain/exchange/RampStateManager.kt | 2 +- .../domain/markets/TokenMarketParams.kt | 2 +- .../models/currency}/CryptoCurrencyStatus.kt | 117 ++++++++++++------ .../domain/models/network/NetworkAddress.kt | 6 + .../tangem/domain/models/network/TxInfo.kt | 58 ++++++++- .../serialization/BigDecimalSerializer.kt | 2 +- .../serialization/BigIntegerSerializer.kt | 2 +- .../serialization/SerializedBigDecimal.kt | 2 +- .../serialization/SerializedBigInteger.kt | 2 +- .../domain/models/staking/YieldBalanceItem.kt | 2 +- .../com/tangem/domain/nft/models/NFTAsset.kt | 2 +- .../tangem/domain/nft/models/NFTSalePrice.kt | 2 +- .../domain/onramp/model/OnrampAmount.kt | 2 +- .../onramp/model/cache/OnrampTransaction.kt | 4 +- .../domain/onramp/GetLegacyTopUpUrlUseCase.kt | 4 +- .../domain/staking/model/stakekit/Yield.kt | 2 +- .../domain/swap/models/SwapCurrencies.kt | 2 +- .../domain/swap/models/SwapPairModel.kt | 2 +- .../tangem/domain/swap/SwapRepositoryV2.kt | 2 +- .../domain/swap/usecase/GetSwapDataUseCase.kt | 2 +- .../swap/usecase/GetSwapPairsUseCase.kt | 4 +- .../usecase/GetSwapSupportedPairsUseCase.kt | 2 +- .../swap/usecase/SelectInitialPairUseCase.kt | 2 +- .../usecase/SwapTransactionSentUseCase.kt | 4 +- ...AllWalletsCryptoCurrencyStatusesUseCase.kt | 4 +- ...GetBalanceNotEnoughForFeeWarningUseCase.kt | 4 +- .../tokens/GetCryptoCurrencyActionsUseCase.kt | 6 +- .../domain/tokens/GetCurrencyCheckUseCase.kt | 4 +- .../tokens/GetCurrencyWarningsUseCase.kt | 4 +- ...tFeePaidCryptoCurrencyStatusSyncUseCase.kt | 4 +- .../GetMinimumTransactionAmountSyncUseCase.kt | 4 +- .../GetMultiCryptoCurrencyStatusUseCase.kt | 4 +- .../tokens/GetNetworkCoinStatusUseCase.kt | 10 +- .../GetSingleCryptoCurrencyStatusUseCase.kt | 4 +- .../domain/tokens/GetTokenListUseCase.kt | 4 +- .../tokens/GetWalletTotalBalanceUseCase.kt | 2 +- .../tokens/actions/BaseActionsFactory.kt | 2 +- .../tokens/actions/CommonActionsFactory.kt | 6 +- .../actions/OutdatedDataActionsFactory.kt | 4 +- .../actions/UnreachableActionsFactory.kt | 4 +- .../domain/tokens/legacy/TradeCryptoAction.kt | 2 +- .../domain/tokens/model/NetworkGroup.kt | 1 + .../domain/tokens/model/TokenActionsState.kt | 3 +- .../tangem/domain/tokens/model/TokenList.kt | 1 + .../BaseCurrenciesStatusesOperations.kt | 2 +- .../BaseCurrencyStatusOperations.kt | 6 +- .../CachedCurrenciesStatusesOperations.kt | 6 +- .../operations/CurrencyStatusOperations.kt | 2 +- .../TokenListFiatBalanceOperations.kt | 2 +- .../tokens/operations/TokenListOperations.kt | 2 +- .../operations/TokenListSortingOperations.kt | 2 +- .../tokens/repository/CurrenciesRepository.kt | 4 +- .../repository/CurrencyChecksRepository.kt | 4 +- .../utils/CurrencyStatusProxyCreator.kt | 4 +- .../domain/tokens/mock/MockTokenLists.kt | 2 +- .../domain/tokens/mock/MockTokensStates.kt | 2 +- .../repository/MockCurrenciesRepository.kt | 4 +- .../portfolio/impl/loader/PortfolioData.kt | 2 +- .../impl/model/MyPortfolioUMFactory.kt | 2 +- .../impl/model/PortfolioTokenUMConverter.kt | 4 +- .../impl/model/TokensPortfolioUMConverter.kt | 2 +- .../topup/model/OnboardingNoteTopUpModel.kt | 4 +- .../impl/model/OnboardingNoteCommonState.kt | 2 +- .../v2/twin/impl/model/OnboardingTwinModel.kt | 4 +- .../onramp/hottokens/HotCryptoComponent.kt | 2 +- .../selecttoken/model/OnrampOperationModel.kt | 4 +- .../AvailableSwapPairsComponent.kt | 2 +- .../LoadingTokenListItemConverter.kt | 2 +- .../SetLoadingTokenItemsTransformer.kt | 2 +- .../SetNoAvailablePairsTransformer.kt | 2 +- .../model/AvailableSwapPairsModel.kt | 2 +- .../swap/model/SwapSelectTokensModel.kt | 2 +- .../tokenlist/OnrampTokenListComponent.kt | 2 +- .../UpdateTokenItemsTransformer.kt | 2 +- .../OnrampTokenItemStateConverterFactory.kt | 2 +- .../tokenlist/model/OnrampTokenListModel.kt | 2 +- .../send/v2/api/SendNotificationsComponent.kt | 4 +- .../send/v2/api/params/FeeSelectorParams.kt | 2 +- .../feeSelector/utils/FeeCalculationUtils.kt | 2 +- .../utils/FeeCalculationUtilsTest.kt | 2 +- .../model/transformers/FeeItemConverter.kt | 2 +- .../FeeSelectorCustomFieldConverter.kt | 2 +- ...eeSelectorCustomValueChangedTransformer.kt | 2 +- .../FeeSelectorLoadedTransformer.kt | 2 +- .../send/v2/send/DefaultSendComponent.kt | 2 +- .../v2/send/confirm/SendConfirmComponent.kt | 4 +- .../features/send/v2/send/model/SendModel.kt | 2 +- .../confirm/NFTSendConfirmComponent.kt | 10 +- .../send/v2/sendnft/model/NFTSendModel.kt | 2 +- .../amount/SendAmountComponentParams.kt | 4 +- .../amount/model/SendAmountModel.kt | 2 +- .../fee/SendFeeComponentParams.kt | 4 +- .../subcomponents/fee/model/FeeCalculation.kt | 2 +- .../fee/model/converters/FeeConverter.kt | 4 +- .../converters/SendFeeCustomFieldConverter.kt | 4 +- .../bitcoin/BitcoinCustomFeeConverter.kt | 2 +- .../ethereum/EthereumCustomFeeConverter.kt | 4 +- .../ethereum/EthereumEIPCustomFeeConverter.kt | 4 +- .../EthereumLegacyCustomFeeConverter.kt | 4 +- .../custom/kaspa/KaspaCustomFeeConverter.kt | 4 +- .../SendFeeCustomAutoFixTransformer.kt | 6 +- .../SendFeeCustomValueChangeTransformer.kt | 6 +- .../SendFeeInitialStateTransformer.kt | 2 +- .../transformers/SendFeeLoadedTransformer.kt | 2 +- .../transformers/SendFeeSelectTransformer.kt | 6 +- .../notifications/model/NotificationsModel.kt | 2 +- .../analytics/utils/StakingAnalyticSender.kt | 4 +- .../impl/presentation/model/StakingModel.kt | 13 +- .../state/converters/BalanceItemConverter.kt | 4 +- .../RewardsValidatorStateConverter.kt | 4 +- .../converters/YieldBalancesConverter.kt | 4 +- .../state/helpers/StakingBalanceUpdater.kt | 4 +- .../helpers/StakingFeeTransactionLoader.kt | 6 +- .../state/helpers/StakingTransactionSender.kt | 8 +- .../transformers/SetAmountDataTransformer.kt | 6 +- .../SetConfirmationStateAssentTransformer.kt | 2 +- ...etConfirmationStateCompletedTransformer.kt | 2 +- .../SetConfirmationStateInitTransformer.kt | 6 +- ...ConfirmationStateResetAssentTransformer.kt | 2 +- .../SetInitialDataStateTransformer.kt | 4 +- .../amount/AmountChangeStateTransformer.kt | 2 +- .../AmountCurrencyChangeStateTransformer.kt | 2 +- .../amount/AmountMaxValueStateTransformer.kt | 2 +- .../amount/AmountReduceByStateTransformer.kt | 2 +- .../amount/AmountReduceToStateTransformer.kt | 2 +- .../AmountRequirementStateTransformer.kt | 2 +- .../amount/AmountRoundToIntegerTransformer.kt | 2 +- ...firmationStateAssentApprovalTransformer.kt | 2 +- .../ShowApprovalBottomSheetTransformer.kt | 2 +- .../AddStakingNotificationsTransformer.kt | 2 +- .../StakingInfoNotificationsFactory.kt | 6 +- .../state/utils/FeeCalculation.kt | 2 +- .../impl/amount/SwapAmountComponentParams.kt | 4 +- .../v2/impl/amount/entity/SwapAmountUM.kt | 2 +- .../v2/impl/amount/model/SwapAmountModel.kt | 2 +- .../impl/amount/model/SwapAmountQuoteUtils.kt | 2 +- .../converter/SwapAmountFieldConverter.kt | 2 +- .../SwapAmountPrimaryReadyStateTransformer.kt | 4 +- ...wapAmountSecondaryReadyStateTransformer.kt | 4 +- .../SwapAmountUpdateBalanceTransformer.kt | 2 +- .../ui/preview/SwapAmountContentPreview.kt | 2 +- .../swap/v2/impl/common/ConfirmData.kt | 2 +- .../confirm/SendWithSwapConfirmComponent.kt | 2 +- .../confirm/model/SendWithSwapConfirmModel.kt | 2 +- .../confirm/model/SwapTransactionSender.kt | 2 +- .../sendviaswap/model/SendWithSwapModel.kt | 2 +- .../domain/models/domain/SwapPairLeast.kt | 2 +- .../swap/domain/models/ui/SwapState.kt | 2 +- .../DefaultInitialToCurrencyResolver.kt | 2 +- .../swap/domain/InitialToCurrencyResolver.kt | 2 +- .../feature/swap/domain/SwapInteractor.kt | 2 +- .../feature/swap/domain/SwapInteractorImpl.kt | 6 +- .../swap/converters/TokensDataConverter.kt | 2 +- .../tangem/feature/swap/model/SwapModel.kt | 8 +- .../swap/model/SwapNotificationsFactory.kt | 2 +- .../swap/model/SwapProcessDataState.kt | 2 +- .../feature/swap/models/SwapStateHolder.kt | 2 +- .../tangem/feature/swap/ui/StateBuilder.kt | 2 +- ...kenDetailsCurrencyStatusAnalyticsSender.kt | 2 +- .../tokendetails/model/TokenDetailsModel.kt | 2 +- ...TokenDetailsBalanceSelectStateConverter.kt | 2 +- .../TokenDetailsLoadedBalanceConverter.kt | 2 +- ...nDetailsOnrampTransactionStateConverter.kt | 2 +- .../TokenDetailsStakingInfoConverter.kt | 6 +- .../state/factory/TokenDetailsStateFactory.kt | 2 +- .../factory/express/ExpressStatusFactory.kt | 4 +- .../factory/express/OnrampStatusFactory.kt | 4 +- .../txhistory/model/TxHistoryModel.kt | 4 +- .../intents/WalletContentClickIntents.kt | 6 +- .../WalletCurrencyActionsClickIntents.kt | 6 +- .../CryptoCurrencyToDraggableItemConverter.kt | 2 +- .../router/DefaultWalletRouter.kt | 6 +- .../presentation/router/InnerWalletRouter.kt | 2 +- .../utils/TokenListAnalyticsSender.kt | 2 +- .../domain/GetMultiWalletWarningsFactory.kt | 2 +- .../domain/GetSingleWalletWarningsFactory.kt | 6 +- .../presentation/wallet/domain/UseCaseExt.kt | 6 +- .../wallet/domain/WalletWithFundsChecker.kt | 2 +- .../SetExpressStatusesTransformer.kt | 8 +- .../SetPrimaryCurrencyTransformer.kt | 2 +- .../transformers/SetVisaInfoTransformer.kt | 10 +- .../MultiWalletCurrencyActionsConverter.kt | 8 +- .../SingleWalletCardStateConverter.kt | 2 +- .../SingleWalletMarketPriceConverter.kt | 2 +- .../SingleWalletOnrampTransactionConverter.kt | 2 +- .../converter/TokenListStateConverter.kt | 2 +- .../subscribers/PrimaryCurrencySubscriber.kt | 4 +- .../SingleWalletExpressStatusesSubscriber.kt | 4 +- .../wallet/subscribers/TxHistorySubscriber.kt | 4 +- .../model/WcSendTransactionModel.kt | 2 +- 207 files changed, 453 insertions(+), 362 deletions(-) rename domain/{tokens/models/src/main/java/com/tangem/domain/tokens/model => models/src/main/kotlin/com/tangem/domain/models/currency}/CryptoCurrencyStatus.kt (68%) rename domain/{core/src/main/kotlin/com/tangem/domain/core => models/src/main/kotlin/com/tangem/domain/models}/serialization/BigDecimalSerializer.kt (93%) rename domain/{core/src/main/kotlin/com/tangem/domain/core => models/src/main/kotlin/com/tangem/domain/models}/serialization/BigIntegerSerializer.kt (93%) rename domain/{core/src/main/kotlin/com/tangem/domain/core => models/src/main/kotlin/com/tangem/domain/models}/serialization/SerializedBigDecimal.kt (77%) rename domain/{core/src/main/kotlin/com/tangem/domain/core => models/src/main/kotlin/com/tangem/domain/models}/serialization/SerializedBigInteger.kt (77%) diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/DefaultRampManager.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/DefaultRampManager.kt index be5a5a9705..bfeee05b09 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/DefaultRampManager.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/DefaultRampManager.kt @@ -13,9 +13,9 @@ import com.tangem.domain.core.lce.Lce import com.tangem.domain.exchange.ExpressAvailabilityState import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.utils.Provider diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountCurrencyTransformer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountCurrencyTransformer.kt index cdacdd3ce7..7406013c29 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountCurrencyTransformer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountCurrencyTransformer.kt @@ -4,7 +4,7 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import com.tangem.common.ui.amountScreen.models.AmountState -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.utils.isNullOrZero import com.tangem.utils.transformer.Transformer diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceByTransformer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceByTransformer.kt index e52898094a..244ea5b4c0 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceByTransformer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceByTransformer.kt @@ -14,7 +14,7 @@ import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseBigDecimal -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.utils.isNullOrZero import com.tangem.utils.transformer.Transformer import java.math.BigDecimal diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceToTransformer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceToTransformer.kt index 7e5cf0ab8d..cb91187d95 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceToTransformer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceToTransformer.kt @@ -15,7 +15,7 @@ import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.utils.isNullOrZero import com.tangem.utils.transformer.Transformer import java.math.BigDecimal diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt index a38f516c70..937f94852b 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt @@ -16,7 +16,7 @@ import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.utils.Provider import com.tangem.utils.converter.Converter import com.tangem.utils.isNullOrZero diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/MaxEnterAmountConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/MaxEnterAmountConverter.kt index dce1957361..045c704421 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/MaxEnterAmountConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/MaxEnterAmountConverter.kt @@ -1,7 +1,7 @@ package com.tangem.common.ui.amountScreen.converters import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.utils.converter.Converter /** diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountBoundaryUpdateTransformer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountBoundaryUpdateTransformer.kt index a689b4501a..8761c5abb0 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountBoundaryUpdateTransformer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountBoundaryUpdateTransformer.kt @@ -9,7 +9,7 @@ import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.utils.transformer.Transformer /** diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldChangeTransformer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldChangeTransformer.kt index cbfc6907e8..4a490037ae 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldChangeTransformer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldChangeTransformer.kt @@ -16,7 +16,7 @@ import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseToBigDecimal -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.utils.isNullOrZero import com.tangem.utils.transformer.Transformer import java.math.BigDecimal diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldConverter.kt index f19fad6bb4..9c42eeac7e 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldConverter.kt @@ -9,9 +9,9 @@ import com.tangem.common.ui.amountScreen.models.AmountFieldModel import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.tokens.model.Amount import com.tangem.domain.tokens.model.AmountType -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.convertToAmount import com.tangem.utils.Provider import com.tangem.utils.converter.Converter diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldSetMaxAmountTransformer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldSetMaxAmountTransformer.kt index 72dcac8ac9..a8c20c0745 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldSetMaxAmountTransformer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldSetMaxAmountTransformer.kt @@ -12,7 +12,7 @@ import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseBigDecimal -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.utils.extensions.isZero import com.tangem.utils.transformer.Transformer import java.math.BigDecimal diff --git a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt index 5e3fb48b92..73f9baa570 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt @@ -11,7 +11,7 @@ import com.tangem.core.ui.format.bigdecimal.uncapped import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.domain.transaction.error.GetFeeError diff --git a/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt index da96ebf46d..9c80a6a1b7 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt @@ -13,9 +13,9 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.format.bigdecimal.percent import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.StatusSource +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.utils.StringsSigns.DASH_SIGN import com.tangem.utils.converter.Converter import com.tangem.utils.extensions.isZero diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/converter/CryptoCurrencyToIconStateConverter.kt b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/converter/CryptoCurrencyToIconStateConverter.kt index be2a3778ff..152ff9caf6 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/converter/CryptoCurrencyToIconStateConverter.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/converter/CryptoCurrencyToIconStateConverter.kt @@ -5,7 +5,7 @@ import com.tangem.core.ui.extensions.getTintForTokenIcon import com.tangem.core.ui.extensions.networkIconResId import com.tangem.core.ui.extensions.tryGetBackgroundForTokenIcon import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.utils.converter.Converter /** diff --git a/data/onramp/src/main/java/com/tangem/data/onramp/models/OnrampTransactionDTO.kt b/data/onramp/src/main/java/com/tangem/data/onramp/models/OnrampTransactionDTO.kt index 582fbead56..95beb54a57 100644 --- a/data/onramp/src/main/java/com/tangem/data/onramp/models/OnrampTransactionDTO.kt +++ b/data/onramp/src/main/java/com/tangem/data/onramp/models/OnrampTransactionDTO.kt @@ -4,7 +4,7 @@ import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import com.tangem.datasource.api.onramp.models.response.Status import com.tangem.datasource.api.onramp.models.response.model.OnrampCurrencyDTO -import com.tangem.domain.core.serialization.SerializedBigDecimal +import com.tangem.domain.models.serialization.SerializedBigDecimal import com.tangem.domain.models.wallet.UserWalletId /** diff --git a/data/swap/src/main/java/com/tangem/data/swap/DefaultSwapRepositoryV2.kt b/data/swap/src/main/java/com/tangem/data/swap/DefaultSwapRepositoryV2.kt index 51f04d6a97..46346745a5 100644 --- a/data/swap/src/main/java/com/tangem/data/swap/DefaultSwapRepositoryV2.kt +++ b/data/swap/src/main/java/com/tangem/data/swap/DefaultSwapRepositoryV2.kt @@ -22,6 +22,7 @@ import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.express.models.ExpressProviderType import com.tangem.domain.express.models.ExpressRateType import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.quotes.single.SingleQuoteStatusFetcher @@ -29,7 +30,6 @@ import com.tangem.domain.quotes.single.SingleQuoteStatusProducer import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier import com.tangem.domain.swap.SwapRepositoryV2 import com.tangem.domain.swap.models.* -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.utils.CurrencyStatusProxyCreator import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.CoroutineScope diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt index 6687fab2f7..190429d257 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt @@ -22,9 +22,9 @@ import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.core.error.DataError import com.tangem.domain.demo.models.DemoConfig import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.* -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.FeePaidCurrency import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.walletmanager.WalletManagersFacade diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt index db4a2cabb1..f374404cbe 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt @@ -7,11 +7,11 @@ import com.tangem.blockchain.common.ReserveAmountProvider import com.tangem.blockchain.common.UtxoAmountLimitProvider import com.tangem.data.tokens.converters.UtxoConverter import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.staking.utils.getTotalStakingBalance -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.CurrencyAmount import com.tangem.domain.tokens.model.blockchains.UtxoAmountLimit import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning diff --git a/domain/legacy/src/main/java/com/tangem/domain/exchange/RampStateManager.kt b/domain/legacy/src/main/java/com/tangem/domain/exchange/RampStateManager.kt index dd4d2ae6d7..3ac8a33c7f 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/exchange/RampStateManager.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/exchange/RampStateManager.kt @@ -3,9 +3,9 @@ package com.tangem.domain.exchange import arrow.core.Either import com.tangem.domain.core.lce.Lce import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import kotlinx.coroutines.flow.Flow diff --git a/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/TokenMarketParams.kt b/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/TokenMarketParams.kt index 12ab5b67e1..01cff0386d 100644 --- a/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/TokenMarketParams.kt +++ b/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/TokenMarketParams.kt @@ -1,7 +1,7 @@ package com.tangem.domain.markets -import com.tangem.domain.core.serialization.SerializedBigDecimal import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.serialization.SerializedBigDecimal import kotlinx.serialization.Serializable @Serializable diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/currency/CryptoCurrencyStatus.kt similarity index 68% rename from domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt rename to domain/models/src/main/kotlin/com/tangem/domain/models/currency/CryptoCurrencyStatus.kt index 3fc15a0f52..64e478276e 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/currency/CryptoCurrencyStatus.kt @@ -1,12 +1,12 @@ -package com.tangem.domain.tokens.model +package com.tangem.domain.models.currency import com.tangem.domain.models.StatusSource -import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.getResultStatusSource import com.tangem.domain.models.network.NetworkAddress import com.tangem.domain.models.network.TxInfo +import com.tangem.domain.models.serialization.SerializedBigDecimal import com.tangem.domain.models.staking.YieldBalance -import java.math.BigDecimal +import kotlinx.serialization.Serializable /** * Represents the status of a cryptocurrency asset within a network. @@ -18,6 +18,7 @@ import java.math.BigDecimal * @property currency The details of the cryptocurrency asset, including its type, name, symbol, and other properties. * @property value The current status of the cryptocurrency, reflecting its state within the network. */ +@Serializable data class CryptoCurrencyStatus( val currency: CryptoCurrency, val value: Value, @@ -28,36 +29,40 @@ data class CryptoCurrencyStatus( * * @property isError Indicates whether this status represents an error status. */ - sealed class Value(val isError: Boolean) { + @Serializable + sealed interface Value { + + val isError: Boolean /** The amount of the cryptocurrency. */ - open val amount: BigDecimal? = null + val amount: SerializedBigDecimal? get() = null /** The fiat equivalent of the cryptocurrency's amount. */ - open val fiatAmount: BigDecimal? = null + val fiatAmount: SerializedBigDecimal? get() = null /** The exchange rate used for converting the cryptocurrency amount to fiat. */ - open val fiatRate: BigDecimal? = null + val fiatRate: SerializedBigDecimal? get() = null /** The change in price of the cryptocurrency. */ - open val priceChange: BigDecimal? = null + val priceChange: SerializedBigDecimal? get() = null /** Indicates if there are any transactions in progress related to the cryptocurrency network. */ - open val hasCurrentNetworkTransactions: Boolean = false + val hasCurrentNetworkTransactions: Boolean get() = false /** The pending cryptocurrency transactions. */ - open val pendingTransactions: Set = emptySet() + val pendingTransactions: Set get() = emptySet() /** The network address */ - open val networkAddress: NetworkAddress? = null + val networkAddress: NetworkAddress? get() = null /** Staking yield balance */ - open val yieldBalance: YieldBalance? = null + val yieldBalance: YieldBalance? get() = null /** Sources */ - open val sources: Sources = Sources() + val sources: Sources get() = Sources() } + @Serializable data class Sources( val networkSource: StatusSource = StatusSource.ACTUAL, val quoteSource: StatusSource = StatusSource.ACTUAL, @@ -70,7 +75,11 @@ data class CryptoCurrencyStatus( } /** Represents the Loading state of a cryptocurrency, typically while fetching its details. */ - data object Loading : Value(isError = false) + @Serializable + data object Loading : Value { + + override val isError: Boolean = false + } /** * Represents a state where the cryptocurrency is not reachable. @@ -79,23 +88,35 @@ data class CryptoCurrencyStatus( * @property fiatRate The exchange rate used for converting the cryptocurrency amount to fiat. * @property networkAddress The network address */ + @Serializable data class Unreachable( - override val priceChange: BigDecimal?, - override val fiatRate: BigDecimal?, + override val priceChange: SerializedBigDecimal?, + override val fiatRate: SerializedBigDecimal?, override val networkAddress: NetworkAddress?, - ) : Value(isError = true) + ) : Value { + + override val isError: Boolean = true + } /** Represents a state where the cryptocurrency's network amount not found. */ + @Serializable data class NoAmount( - override val priceChange: BigDecimal?, - override val fiatRate: BigDecimal?, - ) : Value(isError = true) + override val priceChange: SerializedBigDecimal?, + override val fiatRate: SerializedBigDecimal?, + ) : Value { + + override val isError: Boolean = true + } /** Represents a state where the cryptocurrency's derivation is missed. */ + @Serializable data class MissedDerivation( - override val priceChange: BigDecimal?, - override val fiatRate: BigDecimal?, - ) : Value(isError = true) + override val priceChange: SerializedBigDecimal?, + override val fiatRate: SerializedBigDecimal?, + ) : Value { + + override val isError: Boolean = true + } /** * Represents a state where there is no account associated with the cryptocurrency @@ -103,16 +124,18 @@ data class CryptoCurrencyStatus( * @property amountToCreateAccount base reserve amount for account creation * @property sources sources of data */ + @Serializable data class NoAccount( - val amountToCreateAccount: BigDecimal, - override val fiatAmount: BigDecimal?, - override val priceChange: BigDecimal?, - override val fiatRate: BigDecimal?, + val amountToCreateAccount: SerializedBigDecimal, + override val fiatAmount: SerializedBigDecimal?, + override val priceChange: SerializedBigDecimal?, + override val fiatRate: SerializedBigDecimal?, override val networkAddress: NetworkAddress, override val sources: Sources, - ) : Value(isError = false) { + ) : Value { - override val amount: BigDecimal = BigDecimal.ZERO + override val isError: Boolean = false + override val amount: SerializedBigDecimal? = SerializedBigDecimal.ZERO } /** @@ -127,17 +150,21 @@ data class CryptoCurrencyStatus( * @property pendingTransactions The current cryptocurrency transactions. * @property sources sources of data */ + @Serializable data class Loaded( - override val amount: BigDecimal, - override val fiatAmount: BigDecimal, - override val fiatRate: BigDecimal, - override val priceChange: BigDecimal, + override val amount: SerializedBigDecimal, + override val fiatAmount: SerializedBigDecimal, + override val fiatRate: SerializedBigDecimal, + override val priceChange: SerializedBigDecimal, override val yieldBalance: YieldBalance?, override val hasCurrentNetworkTransactions: Boolean, override val pendingTransactions: Set, override val networkAddress: NetworkAddress, override val sources: Sources, - ) : Value(isError = false) + ) : Value { + + override val isError: Boolean = false + } /** * Represents a Custom state of a cryptocurrency, typically used for user-defined tokens. @@ -150,17 +177,21 @@ data class CryptoCurrencyStatus( * cryptocurrency network. * @property pendingTransactions The current cryptocurrency transactions. */ + @Serializable data class Custom( - override val amount: BigDecimal, - override val fiatAmount: BigDecimal?, - override val fiatRate: BigDecimal?, - override val priceChange: BigDecimal?, + override val amount: SerializedBigDecimal, + override val fiatAmount: SerializedBigDecimal?, + override val fiatRate: SerializedBigDecimal?, + override val priceChange: SerializedBigDecimal?, override val yieldBalance: YieldBalance?, override val hasCurrentNetworkTransactions: Boolean, override val pendingTransactions: Set, override val networkAddress: NetworkAddress, override val sources: Sources, - ) : Value(isError = false) + ) : Value { + + override val isError: Boolean = false + } /** * Represents a state where the cryptocurrency is available, but there is no current quote available for it. @@ -170,12 +201,16 @@ data class CryptoCurrencyStatus( * cryptocurrency network. * @property pendingTransactions The current cryptocurrency transactions. */ + @Serializable data class NoQuote( - override val amount: BigDecimal, + override val amount: SerializedBigDecimal, override val yieldBalance: YieldBalance?, override val hasCurrentNetworkTransactions: Boolean, override val pendingTransactions: Set, override val networkAddress: NetworkAddress, override val sources: Sources, - ) : Value(isError = false) + ) : Value { + + override val isError: Boolean = false + } } \ No newline at end of file diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/network/NetworkAddress.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/network/NetworkAddress.kt index 0fef921e21..f32f27b752 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/network/NetworkAddress.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/network/NetworkAddress.kt @@ -1,6 +1,9 @@ package com.tangem.domain.models.network +import kotlinx.serialization.Serializable + /** Represents a network address */ +@Serializable sealed class NetworkAddress { /** The default or currently selected network address */ @@ -14,6 +17,7 @@ sealed class NetworkAddress { * * @property defaultAddress the static network address */ + @Serializable data class Single(override val defaultAddress: Address) : NetworkAddress() { override val availableAddresses: Set
= setOf(defaultAddress) @@ -25,6 +29,7 @@ sealed class NetworkAddress { * @property defaultAddress the currently selected or default network address * @property availableAddresses the set of available network addresses to choose from */ + @Serializable data class Selectable( override val defaultAddress: Address, override val availableAddresses: Set
, @@ -41,6 +46,7 @@ sealed class NetworkAddress { * @property value string representation of the address * @property type address type */ + @Serializable data class Address( val value: String, val type: Type, diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/network/TxInfo.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/network/TxInfo.kt index f2d47d0f00..d5bb077099 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/network/TxInfo.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/network/TxInfo.kt @@ -1,6 +1,7 @@ package com.tangem.domain.models.network -import java.math.BigDecimal +import com.tangem.domain.models.serialization.SerializedBigDecimal +import kotlinx.serialization.Serializable /** * Represents information about a transaction. Do not use it for sending transactions. @@ -15,6 +16,7 @@ import java.math.BigDecimal * @property type transaction type * @property amount transaction amount */ +@Serializable data class TxInfo( val txHash: String, val timestampInMillis: Long, @@ -24,10 +26,11 @@ data class TxInfo( val interactionAddressType: InteractionAddressType?, val status: TransactionStatus, val type: TransactionType, - val amount: BigDecimal, + val amount: SerializedBigDecimal, ) { /** Destination type*/ + @Serializable sealed class DestinationType { /** @@ -35,6 +38,7 @@ data class TxInfo( * * @property addressType address type */ + @Serializable data class Single(val addressType: AddressType) : DestinationType() /** @@ -42,21 +46,29 @@ data class TxInfo( * * @property addressTypes addresses types */ + @Serializable data class Multiple(val addressTypes: List) : DestinationType() } /** Address type */ + @Serializable sealed class AddressType { /** Address value */ abstract val address: String + @Serializable data class User(override val address: String) : AddressType() + + @Serializable data class Contract(override val address: String) : AddressType() + + @Serializable data class Validator(override val address: String) : AddressType() } /** Source type */ + @Serializable sealed class SourceType { /** @@ -64,6 +76,7 @@ data class TxInfo( * * @property address address */ + @Serializable data class Single(val address: String) : SourceType() /** @@ -71,38 +84,79 @@ data class TxInfo( * * @property addresses addresses */ + @Serializable data class Multiple(val addresses: List) : SourceType() } /** Transaction type */ + @Serializable sealed interface TransactionType { + + @Serializable data object Transfer : TransactionType + + @Serializable data object Approve : TransactionType + + @Serializable data object Swap : TransactionType + + @Serializable data object UnknownOperation : TransactionType + + @Serializable data class Operation(val name: String) : TransactionType + @Serializable sealed interface Staking : TransactionType { + + @Serializable data class Vote(val validatorAddress: String) : Staking + + @Serializable data object ClaimRewards : Staking + + @Serializable data object Stake : Staking + + @Serializable data object Unstake : Staking + + @Serializable data object Withdraw : Staking + + @Serializable data object Restake : Staking } } /** Transaction status */ + @Serializable sealed class TransactionStatus { + + @Serializable data object Failed : TransactionStatus() + + @Serializable data object Unconfirmed : TransactionStatus() + + @Serializable data object Confirmed : TransactionStatus() } + @Serializable sealed class InteractionAddressType { + + @Serializable data class Validator(val address: String) : InteractionAddressType() + + @Serializable data class User(val address: String) : InteractionAddressType() + + @Serializable data class Contract(val address: String) : InteractionAddressType() + + @Serializable data class Multiple(val addresses: List) : InteractionAddressType() } } \ No newline at end of file diff --git a/domain/core/src/main/kotlin/com/tangem/domain/core/serialization/BigDecimalSerializer.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/serialization/BigDecimalSerializer.kt similarity index 93% rename from domain/core/src/main/kotlin/com/tangem/domain/core/serialization/BigDecimalSerializer.kt rename to domain/models/src/main/kotlin/com/tangem/domain/models/serialization/BigDecimalSerializer.kt index 639de2993b..af3fb7323d 100644 --- a/domain/core/src/main/kotlin/com/tangem/domain/core/serialization/BigDecimalSerializer.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/serialization/BigDecimalSerializer.kt @@ -1,4 +1,4 @@ -package com.tangem.domain.core.serialization +package com.tangem.domain.models.serialization import kotlinx.serialization.KSerializer import kotlinx.serialization.descriptors.PrimitiveKind diff --git a/domain/core/src/main/kotlin/com/tangem/domain/core/serialization/BigIntegerSerializer.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/serialization/BigIntegerSerializer.kt similarity index 93% rename from domain/core/src/main/kotlin/com/tangem/domain/core/serialization/BigIntegerSerializer.kt rename to domain/models/src/main/kotlin/com/tangem/domain/models/serialization/BigIntegerSerializer.kt index 93394b1e0c..f9be82fe0b 100644 --- a/domain/core/src/main/kotlin/com/tangem/domain/core/serialization/BigIntegerSerializer.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/serialization/BigIntegerSerializer.kt @@ -1,4 +1,4 @@ -package com.tangem.domain.core.serialization +package com.tangem.domain.models.serialization import kotlinx.serialization.KSerializer import kotlinx.serialization.descriptors.PrimitiveKind diff --git a/domain/core/src/main/kotlin/com/tangem/domain/core/serialization/SerializedBigDecimal.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/serialization/SerializedBigDecimal.kt similarity index 77% rename from domain/core/src/main/kotlin/com/tangem/domain/core/serialization/SerializedBigDecimal.kt rename to domain/models/src/main/kotlin/com/tangem/domain/models/serialization/SerializedBigDecimal.kt index 3243892b18..c75fe1644b 100644 --- a/domain/core/src/main/kotlin/com/tangem/domain/core/serialization/SerializedBigDecimal.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/serialization/SerializedBigDecimal.kt @@ -1,4 +1,4 @@ -package com.tangem.domain.core.serialization +package com.tangem.domain.models.serialization import kotlinx.serialization.Serializable import java.math.BigDecimal diff --git a/domain/core/src/main/kotlin/com/tangem/domain/core/serialization/SerializedBigInteger.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/serialization/SerializedBigInteger.kt similarity index 77% rename from domain/core/src/main/kotlin/com/tangem/domain/core/serialization/SerializedBigInteger.kt rename to domain/models/src/main/kotlin/com/tangem/domain/models/serialization/SerializedBigInteger.kt index 8fbbda7eea..d5a6847870 100644 --- a/domain/core/src/main/kotlin/com/tangem/domain/core/serialization/SerializedBigInteger.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/serialization/SerializedBigInteger.kt @@ -1,4 +1,4 @@ -package com.tangem.domain.core.serialization +package com.tangem.domain.models.serialization import kotlinx.serialization.Serializable import java.math.BigInteger diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/staking/YieldBalanceItem.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/staking/YieldBalanceItem.kt index 0f59dfe679..f5aef4acfa 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/staking/YieldBalanceItem.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/staking/YieldBalanceItem.kt @@ -1,6 +1,6 @@ package com.tangem.domain.models.staking -import com.tangem.domain.core.serialization.SerializedBigDecimal +import com.tangem.domain.models.serialization.SerializedBigDecimal import com.tangem.domain.models.staking.action.StakingActionType import kotlinx.datetime.Instant import kotlinx.serialization.Serializable diff --git a/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTAsset.kt b/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTAsset.kt index f83aec887a..0fdd408cc5 100644 --- a/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTAsset.kt +++ b/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTAsset.kt @@ -1,8 +1,8 @@ package com.tangem.domain.nft.models -import com.tangem.domain.core.serialization.SerializedBigInteger import com.tangem.domain.models.StatusSource import com.tangem.domain.models.network.Network +import com.tangem.domain.models.serialization.SerializedBigInteger import kotlinx.serialization.Serializable @Serializable diff --git a/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTSalePrice.kt b/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTSalePrice.kt index 43bc8a4a29..bac263726a 100644 --- a/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTSalePrice.kt +++ b/domain/nft/models/src/main/kotlin/com/tangem/domain/nft/models/NFTSalePrice.kt @@ -1,6 +1,6 @@ package com.tangem.domain.nft.models -import com.tangem.domain.core.serialization.SerializedBigDecimal +import com.tangem.domain.models.serialization.SerializedBigDecimal import kotlinx.serialization.Serializable @Serializable diff --git a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampAmount.kt b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampAmount.kt index a6ffb9df16..29e09d2151 100644 --- a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampAmount.kt +++ b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampAmount.kt @@ -1,6 +1,6 @@ package com.tangem.domain.onramp.model -import com.tangem.domain.core.serialization.SerializedBigDecimal +import com.tangem.domain.models.serialization.SerializedBigDecimal import kotlinx.serialization.Serializable @Serializable diff --git a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/cache/OnrampTransaction.kt b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/cache/OnrampTransaction.kt index fb76a29bea..352c2d75e2 100644 --- a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/cache/OnrampTransaction.kt +++ b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/cache/OnrampTransaction.kt @@ -2,10 +2,10 @@ package com.tangem.domain.onramp.model.cache import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.tangem.domain.core.serialization.SerializedBigDecimal +import com.tangem.domain.models.serialization.SerializedBigDecimal +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.onramp.model.OnrampCurrency import com.tangem.domain.onramp.model.OnrampStatus -import com.tangem.domain.models.wallet.UserWalletId /** * Model for local storing onramp transaction diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/GetLegacyTopUpUrlUseCase.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/GetLegacyTopUpUrlUseCase.kt index ab5a2807a0..1a2b6103e1 100644 --- a/domain/onramp/src/main/java/com/tangem/domain/onramp/GetLegacyTopUpUrlUseCase.kt +++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/GetLegacyTopUpUrlUseCase.kt @@ -2,9 +2,9 @@ package com.tangem.domain.onramp import arrow.core.Either import arrow.core.raise.either -import com.tangem.domain.onramp.repositories.LegacyTopUpRepository +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.onramp.model.error.OnrampError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.onramp.repositories.LegacyTopUpRepository class GetLegacyTopUpUrlUseCase( private val legacyTopUpRepository: LegacyTopUpRepository, diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt index 75a9e20b8f..093fd13bef 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt @@ -1,6 +1,6 @@ package com.tangem.domain.staking.model.stakekit -import com.tangem.domain.core.serialization.SerializedBigDecimal +import com.tangem.domain.models.serialization.SerializedBigDecimal import com.tangem.domain.models.staking.YieldToken import kotlinx.serialization.Serializable diff --git a/domain/swap/models/src/main/java/com/tangem/domain/swap/models/SwapCurrencies.kt b/domain/swap/models/src/main/java/com/tangem/domain/swap/models/SwapCurrencies.kt index acfaaf4d46..81eb91fe42 100644 --- a/domain/swap/models/src/main/java/com/tangem/domain/swap/models/SwapCurrencies.kt +++ b/domain/swap/models/src/main/java/com/tangem/domain/swap/models/SwapCurrencies.kt @@ -1,7 +1,7 @@ package com.tangem.domain.swap.models import com.tangem.domain.express.models.ExpressProvider -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus /** * Model of currencies available to swap diff --git a/domain/swap/models/src/main/java/com/tangem/domain/swap/models/SwapPairModel.kt b/domain/swap/models/src/main/java/com/tangem/domain/swap/models/SwapPairModel.kt index 5e8e0479f9..863d43017e 100644 --- a/domain/swap/models/src/main/java/com/tangem/domain/swap/models/SwapPairModel.kt +++ b/domain/swap/models/src/main/java/com/tangem/domain/swap/models/SwapPairModel.kt @@ -1,7 +1,7 @@ package com.tangem.domain.swap.models import com.tangem.domain.express.models.ExpressProvider -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus /** * Domain layer representation of SwapPair data network model. diff --git a/domain/swap/src/main/java/com/tangem/domain/swap/SwapRepositoryV2.kt b/domain/swap/src/main/java/com/tangem/domain/swap/SwapRepositoryV2.kt index d3a0b037c7..6fa54f8771 100644 --- a/domain/swap/src/main/java/com/tangem/domain/swap/SwapRepositoryV2.kt +++ b/domain/swap/src/main/java/com/tangem/domain/swap/SwapRepositoryV2.kt @@ -4,12 +4,12 @@ import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.express.models.ExpressProviderType import com.tangem.domain.express.models.ExpressRateType import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.models.SwapDataModel import com.tangem.domain.swap.models.SwapPairModel import com.tangem.domain.swap.models.SwapQuoteModel import com.tangem.domain.swap.models.SwapStatusModel -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import java.math.BigDecimal /** diff --git a/domain/swap/src/main/java/com/tangem/domain/swap/usecase/GetSwapDataUseCase.kt b/domain/swap/src/main/java/com/tangem/domain/swap/usecase/GetSwapDataUseCase.kt index 6370564cc2..ac60b660e2 100644 --- a/domain/swap/src/main/java/com/tangem/domain/swap/usecase/GetSwapDataUseCase.kt +++ b/domain/swap/src/main/java/com/tangem/domain/swap/usecase/GetSwapDataUseCase.kt @@ -5,11 +5,11 @@ import com.tangem.domain.express.models.ExpressError import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.express.models.ExpressRateType import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.SwapErrorResolver import com.tangem.domain.swap.SwapRepositoryV2 import com.tangem.domain.swap.models.SwapDataModel -import com.tangem.domain.tokens.model.CryptoCurrencyStatus @Suppress("LongParameterList") class GetSwapDataUseCase( diff --git a/domain/swap/src/main/java/com/tangem/domain/swap/usecase/GetSwapPairsUseCase.kt b/domain/swap/src/main/java/com/tangem/domain/swap/usecase/GetSwapPairsUseCase.kt index 55fe6a22b4..5dfe8cda5e 100644 --- a/domain/swap/src/main/java/com/tangem/domain/swap/usecase/GetSwapPairsUseCase.kt +++ b/domain/swap/src/main/java/com/tangem/domain/swap/usecase/GetSwapPairsUseCase.kt @@ -3,14 +3,14 @@ package com.tangem.domain.swap.usecase import arrow.core.Either import com.tangem.domain.express.models.ExpressProviderType import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.SwapErrorResolver import com.tangem.domain.swap.SwapRepositoryV2 import com.tangem.domain.swap.models.SwapCryptoCurrency import com.tangem.domain.swap.models.SwapCurrencies import com.tangem.domain.swap.models.SwapCurrenciesGroup import com.tangem.domain.swap.models.SwapPairModel -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.models.wallet.UserWallet /** * Get list of swap pairs diff --git a/domain/swap/src/main/java/com/tangem/domain/swap/usecase/GetSwapSupportedPairsUseCase.kt b/domain/swap/src/main/java/com/tangem/domain/swap/usecase/GetSwapSupportedPairsUseCase.kt index db527b65a1..5f923070f5 100644 --- a/domain/swap/src/main/java/com/tangem/domain/swap/usecase/GetSwapSupportedPairsUseCase.kt +++ b/domain/swap/src/main/java/com/tangem/domain/swap/usecase/GetSwapSupportedPairsUseCase.kt @@ -3,6 +3,7 @@ package com.tangem.domain.swap.usecase import arrow.core.Either import com.tangem.domain.express.models.ExpressProviderType import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.SwapErrorResolver import com.tangem.domain.swap.SwapRepositoryV2 @@ -10,7 +11,6 @@ import com.tangem.domain.swap.models.SwapCryptoCurrency import com.tangem.domain.swap.models.SwapCurrencies import com.tangem.domain.swap.models.SwapCurrenciesGroup import com.tangem.domain.swap.models.SwapPairModel -import com.tangem.domain.tokens.model.CryptoCurrencyStatus /** * Returns pais diff --git a/domain/swap/src/main/java/com/tangem/domain/swap/usecase/SelectInitialPairUseCase.kt b/domain/swap/src/main/java/com/tangem/domain/swap/usecase/SelectInitialPairUseCase.kt index ed6c8a1d27..dcfd5cffb4 100644 --- a/domain/swap/src/main/java/com/tangem/domain/swap/usecase/SelectInitialPairUseCase.kt +++ b/domain/swap/src/main/java/com/tangem/domain/swap/usecase/SelectInitialPairUseCase.kt @@ -1,13 +1,13 @@ package com.tangem.domain.swap.usecase import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.SwapTransactionRepository import com.tangem.domain.swap.models.SwapCurrencies import com.tangem.domain.swap.models.SwapCurrenciesGroup import com.tangem.domain.swap.models.SwapDirection import com.tangem.domain.swap.models.getGroupWithDirection -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.utils.extensions.orZero /** diff --git a/domain/swap/src/main/java/com/tangem/domain/swap/usecase/SwapTransactionSentUseCase.kt b/domain/swap/src/main/java/com/tangem/domain/swap/usecase/SwapTransactionSentUseCase.kt index e254c348c4..5cebffc055 100644 --- a/domain/swap/src/main/java/com/tangem/domain/swap/usecase/SwapTransactionSentUseCase.kt +++ b/domain/swap/src/main/java/com/tangem/domain/swap/usecase/SwapTransactionSentUseCase.kt @@ -3,6 +3,8 @@ package com.tangem.domain.swap.usecase import arrow.core.Either import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.express.models.ExpressProviderType.Companion.shouldStoreSwapTransaction +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.SwapErrorResolver import com.tangem.domain.swap.SwapRepositoryV2 import com.tangem.domain.swap.SwapTransactionRepository @@ -10,8 +12,6 @@ import com.tangem.domain.swap.models.SwapDataTransactionModel import com.tangem.domain.swap.models.SwapStatus import com.tangem.domain.swap.models.SwapStatusModel import com.tangem.domain.swap.models.SwapTransactionModel -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.models.wallet.UserWallet @Suppress("LongParameterList") class SwapTransactionSentUseCase( diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetAllWalletsCryptoCurrencyStatusesUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetAllWalletsCryptoCurrencyStatusesUseCase.kt index 0d6b285977..9bba447d40 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetAllWalletsCryptoCurrencyStatusesUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetAllWalletsCryptoCurrencyStatusesUseCase.kt @@ -2,13 +2,13 @@ package com.tangem.domain.tokens import arrow.core.Either import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.tokens.error.mapper.mapToCurrencyError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations import com.tangem.domain.tokens.repository.CurrenciesRepository -import com.tangem.domain.models.wallet.UserWallet import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetBalanceNotEnoughForFeeWarningUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetBalanceNotEnoughForFeeWarningUseCase.kt index 807e118321..bc8e4fa72d 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetBalanceNotEnoughForFeeWarningUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetBalanceNotEnoughForFeeWarningUseCase.kt @@ -2,11 +2,11 @@ package com.tangem.domain.tokens import arrow.core.Either import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.tokens.model.FeePaidCurrency import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.domain.tokens.repository.CurrenciesRepository -import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.withContext import java.math.BigDecimal diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt index e1b0f65499..e219fbf94c 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt @@ -3,6 +3,9 @@ package com.tangem.domain.tokens import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.promo.PromoRepository import com.tangem.domain.promo.models.StoryContent import com.tangem.domain.promo.models.StoryContentIds @@ -12,11 +15,8 @@ import com.tangem.domain.tokens.actions.CommonActionsFactory import com.tangem.domain.tokens.actions.MissedDerivationsActionsFactory import com.tangem.domain.tokens.actions.OutdatedDataActionsFactory import com.tangem.domain.tokens.actions.UnreachableActionsFactory -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenActionsState import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt index 329794c3ae..43c65d397a 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt @@ -1,10 +1,10 @@ package com.tangem.domain.tokens import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck import com.tangem.domain.tokens.repository.CurrencyChecksRepository -import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.withContext import java.math.BigDecimal diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt index eb1f7d4bd6..6b64410367 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt @@ -3,8 +3,9 @@ package com.tangem.domain.tokens import com.tangem.blockchainsdk.utils.isNeedToCreateAccountWithoutReserve import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.tokens.model.CurrencyAmount import com.tangem.domain.tokens.model.FeePaidCurrency import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning @@ -15,7 +16,6 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.transaction.models.AssetRequirementsCondition import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.domain.models.wallet.UserWalletId import com.tangem.lib.crypto.BlockchainUtils import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetFeePaidCryptoCurrencyStatusSyncUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetFeePaidCryptoCurrencyStatusSyncUseCase.kt index 640a3f0726..77bb61fb5e 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetFeePaidCryptoCurrencyStatusSyncUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetFeePaidCryptoCurrencyStatusSyncUseCase.kt @@ -2,12 +2,12 @@ package com.tangem.domain.tokens import arrow.core.Either import arrow.core.raise.either +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.FeePaidCurrency import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations import com.tangem.domain.tokens.repository.CurrenciesRepository -import com.tangem.domain.models.wallet.UserWalletId class GetFeePaidCryptoCurrencyStatusSyncUseCase( internal val currenciesRepository: CurrenciesRepository, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMinimumTransactionAmountSyncUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMinimumTransactionAmountSyncUseCase.kt index 72ee9c9df4..71f50c03d7 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMinimumTransactionAmountSyncUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMinimumTransactionAmountSyncUseCase.kt @@ -2,9 +2,9 @@ package com.tangem.domain.tokens import arrow.core.Either import arrow.core.raise.either -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.repository.CurrencyChecksRepository +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.tokens.repository.CurrencyChecksRepository import java.math.BigDecimal class GetMinimumTransactionAmountSyncUseCase( diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMultiCryptoCurrencyStatusUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMultiCryptoCurrencyStatusUseCase.kt index 34faf60620..63b74c9631 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMultiCryptoCurrencyStatusUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMultiCryptoCurrencyStatusUseCase.kt @@ -1,12 +1,12 @@ package com.tangem.domain.tokens import arrow.core.Either +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.error.mapper.mapToTokenListError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations -import com.tangem.domain.models.wallet.UserWalletId import kotlinx.coroutines.flow.Flow class GetMultiCryptoCurrencyStatusUseCase( diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkCoinStatusUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkCoinStatusUseCase.kt index 5179b54d12..89a9baade6 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkCoinStatusUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkCoinStatusUseCase.kt @@ -2,15 +2,15 @@ package com.tangem.domain.tokens import arrow.core.Either import com.tangem.domain.card.common.util.cardTypesResolver +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network -import com.tangem.domain.tokens.error.CurrencyStatusError -import com.tangem.domain.tokens.error.mapper.mapToCurrencyError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations -import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.isMultiCurrency +import com.tangem.domain.tokens.error.CurrencyStatusError +import com.tangem.domain.tokens.error.mapper.mapToCurrencyError +import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations +import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetSingleCryptoCurrencyStatusUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetSingleCryptoCurrencyStatusUseCase.kt index 1bd4c4f678..369303c3cb 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetSingleCryptoCurrencyStatusUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetSingleCryptoCurrencyStatusUseCase.kt @@ -2,12 +2,12 @@ package com.tangem.domain.tokens import arrow.core.Either import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.tokens.error.mapper.mapToCurrencyError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations -import com.tangem.domain.models.wallet.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt index fde614c26c..a7b6edcee0 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt @@ -4,14 +4,14 @@ import com.tangem.domain.core.lce.LceFlow import com.tangem.domain.core.utils.lceError import com.tangem.domain.core.utils.lceLoading import com.tangem.domain.core.utils.toLce +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.error.mapper.mapToTokenListError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.tokens.operations.BaseCurrenciesStatusesOperations import com.tangem.domain.tokens.operations.TokenListOperations import com.tangem.domain.tokens.repository.CurrenciesRepository -import com.tangem.domain.models.wallet.UserWalletId import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.map diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetWalletTotalBalanceUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetWalletTotalBalanceUseCase.kt index c3a08ac9a3..1d02262cc7 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetWalletTotalBalanceUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetWalletTotalBalanceUseCase.kt @@ -9,9 +9,9 @@ import com.tangem.domain.core.lce.lce import com.tangem.domain.core.utils.lceContent import com.tangem.domain.core.utils.lceLoading import com.tangem.domain.models.TotalFiatBalance +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.operations.BaseCurrenciesStatusesOperations import com.tangem.domain.tokens.operations.TokenListFiatBalanceOperations import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/BaseActionsFactory.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/BaseActionsFactory.kt index 6feda808cf..5863373ebf 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/BaseActionsFactory.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/BaseActionsFactory.kt @@ -2,11 +2,11 @@ package com.tangem.domain.tokens.actions import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.NetworkAddress import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.staking.model.StakingAvailability -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.TokenActionsState.ActionState import com.tangem.domain.transaction.models.AssetRequirementsCondition diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactory.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactory.kt index 41890a7320..522690bc10 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactory.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactory.kt @@ -2,13 +2,13 @@ package com.tangem.domain.tokens.actions import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.staking.model.StakingAvailability -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.TokenActionsState.ActionState import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.UserWalletId import kotlinx.coroutines.Deferred import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/OutdatedDataActionsFactory.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/OutdatedDataActionsFactory.kt index f38522a4f7..adf28072be 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/OutdatedDataActionsFactory.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/OutdatedDataActionsFactory.kt @@ -2,12 +2,12 @@ package com.tangem.domain.tokens.actions import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.models.StatusSource +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.staking.model.StakingAvailability -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.TokenActionsState.ActionState import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.domain.models.wallet.UserWallet import kotlinx.coroutines.Deferred import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/UnreachableActionsFactory.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/UnreachableActionsFactory.kt index ded293f444..c41c4826e4 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/UnreachableActionsFactory.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/UnreachableActionsFactory.kt @@ -1,11 +1,11 @@ package com.tangem.domain.tokens.actions import com.tangem.domain.exchange.RampStateManager -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.TokenActionsState.ActionState import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.domain.models.wallet.UserWallet import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/legacy/TradeCryptoAction.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/legacy/TradeCryptoAction.kt index b7f00f81cb..1cf1651c9d 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/legacy/TradeCryptoAction.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/legacy/TradeCryptoAction.kt @@ -1,6 +1,6 @@ package com.tangem.domain.tokens.legacy -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import org.rekotlin.Action sealed class TradeCryptoAction : Action { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkGroup.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkGroup.kt index 19aae81b6d..f771dd1795 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkGroup.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkGroup.kt @@ -1,5 +1,6 @@ package com.tangem.domain.tokens.model +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network /** diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenActionsState.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenActionsState.kt index d822bddd38..306a8cba00 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenActionsState.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenActionsState.kt @@ -1,7 +1,8 @@ package com.tangem.domain.tokens.model -import com.tangem.domain.staking.model.stakekit.Yield +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.staking.model.stakekit.Yield data class TokenActionsState( val walletId: UserWalletId, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenList.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenList.kt index 1ade11835e..5cd12a6b8b 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenList.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenList.kt @@ -3,6 +3,7 @@ package com.tangem.domain.tokens.model import com.tangem.domain.models.StatusSource import com.tangem.domain.models.TokensSortType import com.tangem.domain.models.TotalFiatBalance +import com.tangem.domain.models.currency.CryptoCurrencyStatus import java.math.BigDecimal /** diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrenciesStatusesOperations.kt index 11ad1a755f..d3842665be 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrenciesStatusesOperations.kt @@ -1,9 +1,9 @@ package com.tangem.domain.tokens.operations import com.tangem.domain.core.lce.LceFlow +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus /** * Base operations for working with currencies statuses diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt index abf478099e..e39261d0a7 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt @@ -5,9 +5,12 @@ import arrow.core.raise.* import com.tangem.blockchainsdk.utils.toBlockchain import com.tangem.domain.core.utils.EitherFlow import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.quote.QuoteStatus +import com.tangem.domain.models.staking.StakingID +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.networks.multi.MultiNetworkStatusProducer import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier @@ -17,9 +20,7 @@ import com.tangem.domain.quotes.QuotesRepository import com.tangem.domain.quotes.single.SingleQuoteStatusProducer import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier import com.tangem.domain.staking.StakingIdFactory -import com.tangem.domain.models.staking.StakingID import com.tangem.domain.staking.model.isStakingSupported -import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.multi.MultiYieldBalanceProducer import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier import com.tangem.domain.staking.single.SingleYieldBalanceProducer @@ -27,7 +28,6 @@ import com.tangem.domain.staking.single.SingleYieldBalanceSupplier import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesProducer import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier import com.tangem.domain.tokens.TokensFeatureToggles -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.utils.CurrencyStatusProxyCreator diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt index 9d0dfe2461..fcce30d9fd 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt @@ -11,9 +11,12 @@ import com.tangem.domain.core.utils.EitherFlow import com.tangem.domain.core.utils.lceContent import com.tangem.domain.core.utils.lceError import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.quote.QuoteStatus +import com.tangem.domain.models.staking.StakingID +import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier @@ -25,9 +28,7 @@ import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher import com.tangem.domain.quotes.single.SingleQuoteStatusProducer import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier import com.tangem.domain.staking.StakingIdFactory -import com.tangem.domain.models.staking.StakingID import com.tangem.domain.staking.model.StakingIntegrationID -import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier import com.tangem.domain.staking.single.SingleYieldBalanceProducer @@ -35,7 +36,6 @@ import com.tangem.domain.staking.single.SingleYieldBalanceSupplier import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier import com.tangem.domain.tokens.TokensFeatureToggles import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.utils.extractAddress diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt index da8de5018f..c1dc1ffb33 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt @@ -2,10 +2,10 @@ package com.tangem.domain.tokens.operations import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.quote.QuoteStatus import com.tangem.domain.models.staking.YieldBalance -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import java.math.BigDecimal internal class CurrencyStatusOperations( diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt index 2bd4ff898a..198890fd63 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt @@ -3,10 +3,10 @@ package com.tangem.domain.tokens.operations import arrow.core.NonEmptyList import com.tangem.domain.models.StatusSource import com.tangem.domain.models.TotalFiatBalance +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.getResultStatusSource import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.lib.crypto.BlockchainUtils import com.tangem.utils.extensions.orZero import java.math.BigDecimal diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt index 0d853bbf2c..d2c9b899b3 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt @@ -6,8 +6,8 @@ import arrow.core.raise.either import arrow.core.raise.withError import com.tangem.domain.models.TokensSortType import com.tangem.domain.models.TotalFiatBalance +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.tokens.repository.CurrenciesRepository import kotlinx.coroutines.flow.* diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt index bec82f8e2a..e6288914b2 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt @@ -9,10 +9,10 @@ import arrow.core.raise.ensureNotNull import arrow.core.toNonEmptyListOrNull import com.tangem.domain.models.TokensSortType import com.tangem.domain.models.TotalFiatBalance +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList import com.tangem.utils.extensions.orZero diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt index 28522fdc9d..6dd77dfdc2 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt @@ -3,11 +3,11 @@ package com.tangem.domain.tokens.repository import com.tangem.domain.card.CardTypesResolver import com.tangem.domain.core.error.DataError import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.model.FeePaidCurrency import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.tokens.model.FeePaidCurrency import kotlinx.coroutines.flow.Flow /** diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt index a971f7d8be..8351958b10 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt @@ -1,12 +1,12 @@ package com.tangem.domain.tokens.repository import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.tokens.model.CurrencyAmount import com.tangem.domain.tokens.model.blockchains.UtxoAmountLimit import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning -import com.tangem.domain.models.wallet.UserWalletId import java.math.BigDecimal interface CurrencyChecksRepository { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt index 08138e4e05..bad702ce42 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt @@ -5,12 +5,12 @@ import arrow.core.NonEmptyList import arrow.core.raise.either import arrow.core.toNonEmptySetOrNull import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.quote.QuoteStatus import com.tangem.domain.models.staking.StakingID -import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.models.staking.YieldBalance -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error import com.tangem.domain.tokens.operations.CurrencyStatusOperations diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt index a380587679..1d660f1061 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt @@ -5,10 +5,10 @@ import arrow.core.toNonEmptyListOrNull import com.tangem.domain.models.StatusSource import com.tangem.domain.models.TokensSortType import com.tangem.domain.models.TotalFiatBalance +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.tokens.mock.MockNetworksGroups.failedNetworksGroups import com.tangem.domain.tokens.mock.MockNetworksGroups.loadedNetworksGroups import com.tangem.domain.tokens.mock.MockNetworksGroups.sortedNetworksGroups -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList import java.math.BigDecimal diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt index fddd277f37..555289ed3d 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt @@ -1,11 +1,11 @@ package com.tangem.domain.tokens.mock import arrow.core.nonEmptyListOf +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.NetworkAddress import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.quote.QuoteStatus import com.tangem.domain.models.quote.fold -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import java.math.BigDecimal @Suppress("MemberVisibilityCanBePrivate") diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt index b408c3975e..8a0f5fa136 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt @@ -5,11 +5,11 @@ import arrow.core.getOrElse import com.tangem.domain.card.CardTypesResolver import com.tangem.domain.core.error.DataError import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.model.FeePaidCurrency import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.tokens.model.FeePaidCurrency import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.first diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/loader/PortfolioData.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/loader/PortfolioData.kt index 253d74b0ce..ac34715c4d 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/loader/PortfolioData.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/loader/PortfolioData.kt @@ -3,10 +3,10 @@ package com.tangem.features.markets.portfolio.impl.loader import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.core.lce.Lce import com.tangem.domain.models.TotalFiatBalance +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenActionsState /** diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MyPortfolioUMFactory.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MyPortfolioUMFactory.kt index caa873be75..5c5bb59b96 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MyPortfolioUMFactory.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MyPortfolioUMFactory.kt @@ -3,7 +3,7 @@ package com.tangem.features.markets.portfolio.impl.model import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.domain.markets.TokenMarketInfo import com.tangem.domain.models.ArtworkModel -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.isMultiCurrency diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/PortfolioTokenUMConverter.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/PortfolioTokenUMConverter.kt index 8601ed1193..e1acbcf4e1 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/PortfolioTokenUMConverter.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/PortfolioTokenUMConverter.kt @@ -4,10 +4,10 @@ import com.tangem.common.ui.tokens.TokenItemStateConverter import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.core.ui.extensions.stringReference import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.TokenActionsState -import com.tangem.domain.models.wallet.UserWallet import com.tangem.features.markets.portfolio.impl.loader.PortfolioData import com.tangem.features.markets.portfolio.impl.ui.state.PortfolioTokenUM import com.tangem.features.markets.portfolio.impl.ui.state.QuickActionUM diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokensPortfolioUMConverter.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokensPortfolioUMConverter.kt index 85478242c5..9e5aba6a0a 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokensPortfolioUMConverter.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokensPortfolioUMConverter.kt @@ -2,7 +2,7 @@ package com.tangem.features.markets.portfolio.impl.model import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.features.markets.portfolio.impl.loader.PortfolioData import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/note/impl/child/topup/model/OnboardingNoteTopUpModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/note/impl/child/topup/model/OnboardingNoteTopUpModel.kt index 01f73e63a2..4f0a65a810 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/note/impl/child/topup/model/OnboardingNoteTopUpModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/note/impl/child/topup/model/OnboardingNoteTopUpModel.kt @@ -13,19 +13,19 @@ import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.exchange.RampStateManager +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.NetworkAddress import com.tangem.domain.models.scan.ScanResponse +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.onramp.GetLegacyTopUpUrlUseCase import com.tangem.domain.tokens.FetchCurrencyStatusUseCase import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.TokensFeatureToggles -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent import com.tangem.domain.tokens.wallet.WalletBalanceFetcher import com.tangem.domain.wallets.builder.ColdUserWalletBuilder -import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.wallets.usecase.SaveWalletUseCase import com.tangem.features.onboarding.v2.common.analytics.OnboardingEvent import com.tangem.features.onboarding.v2.note.impl.child.topup.OnboardingNoteTopUpComponent diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/note/impl/model/OnboardingNoteCommonState.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/note/impl/model/OnboardingNoteCommonState.kt index c1cd153231..704923459b 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/note/impl/model/OnboardingNoteCommonState.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/note/impl/model/OnboardingNoteCommonState.kt @@ -1,8 +1,8 @@ package com.tangem.features.onboarding.v2.note.impl.model import com.tangem.core.ui.components.artwork.ArtworkUM +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet internal data class OnboardingNoteCommonState( diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/model/OnboardingTwinModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/model/OnboardingTwinModel.kt index fa3a235361..2e3a94788a 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/model/OnboardingTwinModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/model/OnboardingTwinModel.kt @@ -30,20 +30,20 @@ import com.tangem.domain.common.TwinCardNumber import com.tangem.domain.common.getTwinCardNumber import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.FeedbackEmailType +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network import com.tangem.domain.models.scan.ScanResponse +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase import com.tangem.domain.onramp.GetLegacyTopUpUrlUseCase import com.tangem.domain.tokens.FetchCurrencyStatusUseCase import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.TokensFeatureToggles -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent import com.tangem.domain.tokens.wallet.WalletBalanceFetcher import com.tangem.domain.wallets.builder.ColdUserWalletBuilder import com.tangem.domain.wallets.builder.UserWalletIdBuilder import com.tangem.domain.wallets.legacy.UserWalletsListManager -import com.tangem.domain.models.wallet.UserWallet import com.tangem.features.onboarding.v2.common.analytics.OnboardingEvent import com.tangem.features.onboarding.v2.common.ui.interruptBackupDialog import com.tangem.features.onboarding.v2.impl.R diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/HotCryptoComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/HotCryptoComponent.kt index a416668d6f..0cdec24a2b 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/HotCryptoComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/HotCryptoComponent.kt @@ -2,7 +2,7 @@ package com.tangem.features.onramp.hottokens 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.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId /** diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/model/OnrampOperationModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/model/OnrampOperationModel.kt index 73b0f0a25a..d889c54e08 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/model/OnrampOperationModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/model/OnrampOperationModel.kt @@ -17,12 +17,12 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.exchange.RampStateManager +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.onramp.model.OnrampSource import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.tokens.legacy.TradeCryptoAction -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason -import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.selecttoken.OnrampOperationComponent.Params diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/AvailableSwapPairsComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/AvailableSwapPairsComponent.kt index 38a7a486de..35f44bbb33 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/AvailableSwapPairsComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/AvailableSwapPairsComponent.kt @@ -4,8 +4,8 @@ import androidx.compose.runtime.Stable import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import kotlinx.coroutines.flow.StateFlow /** Token list component that present list of available tokens for swap */ diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/entity/converters/LoadingTokenListItemConverter.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/entity/converters/LoadingTokenListItemConverter.kt index 056bbaf84f..55e336791d 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/entity/converters/LoadingTokenListItemConverter.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/entity/converters/LoadingTokenListItemConverter.kt @@ -3,7 +3,7 @@ package com.tangem.features.onramp.swap.availablepairs.entity.converters import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.utils.converter.Converter /** diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/entity/transformers/SetLoadingTokenItemsTransformer.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/entity/transformers/SetLoadingTokenItemsTransformer.kt index b1a335134f..ccf527232b 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/entity/transformers/SetLoadingTokenItemsTransformer.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/entity/transformers/SetLoadingTokenItemsTransformer.kt @@ -1,6 +1,6 @@ package com.tangem.features.onramp.swap.availablepairs.entity.transformers -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.onramp.swap.availablepairs.entity.converters.LoadingTokenListItemConverter import com.tangem.features.onramp.tokenlist.entity.TokenListUM import com.tangem.features.onramp.tokenlist.entity.TokenListUMTransformer diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/entity/transformers/SetNoAvailablePairsTransformer.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/entity/transformers/SetNoAvailablePairsTransformer.kt index 7e9c537c9e..d46f06637e 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/entity/transformers/SetNoAvailablePairsTransformer.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/entity/transformers/SetNoAvailablePairsTransformer.kt @@ -4,7 +4,7 @@ import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.onramp.tokenlist.entity.TokenListUM import com.tangem.features.onramp.tokenlist.entity.TokenListUMTransformer import com.tangem.features.onramp.tokenlist.entity.utils.OnrampTokenItemStateConverterFactory diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt index 12a9b31e16..0fe2bad477 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt @@ -16,8 +16,8 @@ import com.tangem.domain.core.utils.lceContent import com.tangem.domain.core.utils.lceError import com.tangem.domain.core.utils.lceLoading import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus 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.feature.swap.domain.GetAvailablePairsUseCase diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/model/SwapSelectTokensModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/model/SwapSelectTokensModel.kt index 52a717c63e..5472e0e61c 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/model/SwapSelectTokensModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/model/SwapSelectTokensModel.kt @@ -9,7 +9,7 @@ import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.onramp.component.SwapSelectTokensComponent import com.tangem.features.onramp.swap.entity.SwapSelectTokensController import com.tangem.features.onramp.swap.entity.SwapSelectTokensUM diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/OnrampTokenListComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/OnrampTokenListComponent.kt index e7daefe8c7..94ba06e247 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/OnrampTokenListComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/OnrampTokenListComponent.kt @@ -4,7 +4,7 @@ import androidx.compose.runtime.Stable import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.core.ui.decompose.ComposableContentComponent -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId import com.tangem.features.onramp.tokenlist.entity.OnrampOperation diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/transformer/UpdateTokenItemsTransformer.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/transformer/UpdateTokenItemsTransformer.kt index 2deda8e910..70d687f450 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/transformer/UpdateTokenItemsTransformer.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/transformer/UpdateTokenItemsTransformer.kt @@ -7,7 +7,7 @@ import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.tokenlist.entity.TokenListUM import com.tangem.features.onramp.tokenlist.entity.TokenListUMTransformer diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/utils/OnrampTokenItemStateConverterFactory.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/utils/OnrampTokenItemStateConverterFactory.kt index b54f7d0386..f693f02e79 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/utils/OnrampTokenItemStateConverterFactory.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/utils/OnrampTokenItemStateConverterFactory.kt @@ -8,7 +8,7 @@ import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIco import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.core.ui.extensions.stringReference import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus /** [REDACTED_AUTHOR] diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt index c9e0a59aee..8e0f99d7fd 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt @@ -13,12 +13,12 @@ import com.tangem.domain.core.lce.Lce import com.tangem.domain.core.utils.getOrElse import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.models.TotalFiatBalance +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.settings.usercountry.GetUserCountryUseCase import com.tangem.domain.settings.usercountry.models.UserCountry import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.wallets.usecase.GetWalletsUseCase diff --git a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/SendNotificationsComponent.kt b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/SendNotificationsComponent.kt index 1cef61c576..90823b9c00 100644 --- a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/SendNotificationsComponent.kt +++ b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/SendNotificationsComponent.kt @@ -6,9 +6,9 @@ import com.tangem.blockchain.common.transaction.Fee import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.transaction.error.GetFeeError +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.transaction.error.GetFeeError import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.flow.StateFlow import java.math.BigDecimal diff --git a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/params/FeeSelectorParams.kt b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/params/FeeSelectorParams.kt index 94c63eef08..785002a836 100644 --- a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/params/FeeSelectorParams.kt +++ b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/params/FeeSelectorParams.kt @@ -4,7 +4,7 @@ import arrow.core.Either import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.core.ui.extensions.TextReference -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.transaction.error.GetFeeError import com.tangem.features.send.v2.api.callbacks.FeeSelectorModelCallback import com.tangem.features.send.v2.api.entity.FeeSelectorUM diff --git a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/feeSelector/utils/FeeCalculationUtils.kt b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/feeSelector/utils/FeeCalculationUtils.kt index 181267b68a..3c251c609b 100644 --- a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/feeSelector/utils/FeeCalculationUtils.kt +++ b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/feeSelector/utils/FeeCalculationUtils.kt @@ -3,7 +3,7 @@ package com.tangem.features.send.v2.api.subcomponents.feeSelector.utils import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.send.v2.api.entity.FeeItem import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.utils.extensions.isZero diff --git a/features/send-v2/api/src/test/java/com/tangem/features/send/v2/api/subcomponents/feeSelector/utils/FeeCalculationUtilsTest.kt b/features/send-v2/api/src/test/java/com/tangem/features/send/v2/api/subcomponents/feeSelector/utils/FeeCalculationUtilsTest.kt index 59ea19d75c..76958ee240 100644 --- a/features/send-v2/api/src/test/java/com/tangem/features/send/v2/api/subcomponents/feeSelector/utils/FeeCalculationUtilsTest.kt +++ b/features/send-v2/api/src/test/java/com/tangem/features/send/v2/api/subcomponents/feeSelector/utils/FeeCalculationUtilsTest.kt @@ -1,7 +1,7 @@ package com.tangem.features.send.v2.api.subcomponents.feeSelector.utils import com.google.common.truth.Truth.assertThat -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import io.mockk.mockk import org.junit.jupiter.api.Test import java.math.BigDecimal diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeItemConverter.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeItemConverter.kt index 864d53807d..139d171f9a 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeItemConverter.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeItemConverter.kt @@ -3,7 +3,7 @@ package com.tangem.features.send.v2.feeselector.model.transformers import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.send.v2.api.entity.FeeItem import com.tangem.features.send.v2.api.params.FeeSelectorParams import com.tangem.features.send.v2.feeselector.model.FeeSelectorIntents diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorCustomFieldConverter.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorCustomFieldConverter.kt index 7a2265d453..258312e8a0 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorCustomFieldConverter.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorCustomFieldConverter.kt @@ -4,7 +4,7 @@ import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.send.v2.feeselector.model.FeeSelectorIntents diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorCustomValueChangedTransformer.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorCustomValueChangedTransformer.kt index d1c3b9c9e0..9f826d2181 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorCustomValueChangedTransformer.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorCustomValueChangedTransformer.kt @@ -1,7 +1,7 @@ package com.tangem.features.send.v2.feeselector.model.transformers import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.send.v2.api.entity.FeeItem import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.send.v2.feeselector.model.FeeSelectorIntents diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorLoadedTransformer.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorLoadedTransformer.kt index b2b45f8a90..684d7f6611 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorLoadedTransformer.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorLoadedTransformer.kt @@ -4,7 +4,7 @@ import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.send.v2.api.entity.* import com.tangem.features.send.v2.api.params.FeeSelectorParams import com.tangem.features.send.v2.feeselector.model.FeeSelectorIntents diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt index c0d9b98bd2..29b3573aa4 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt @@ -20,7 +20,7 @@ import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.decompose.navigation.inner.InnerRouter import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.extensions.resourceReference -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.send.v2.api.FeeSelectorBlockComponent import com.tangem.features.send.v2.api.SendComponent import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt index e98b5f4d20..114939b743 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt @@ -11,9 +11,9 @@ import com.tangem.core.decompose.context.child import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.transaction.error.GetFeeError +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.transaction.error.GetFeeError import com.tangem.features.send.v2.api.FeeSelectorBlockComponent import com.tangem.features.send.v2.api.SendNotificationsComponent import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt index 333bfe1c2a..d23c93d631 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt @@ -23,6 +23,7 @@ import com.tangem.domain.feedback.SaveBlockchainErrorUseCase import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.BlockchainErrorInfo import com.tangem.domain.feedback.models.FeedbackEmailType +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.isMultiCurrency import com.tangem.domain.models.wallet.requireColdWallet @@ -32,7 +33,6 @@ import com.tangem.domain.qrscanning.usecases.ParseQrCodeUseCase import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.error.CurrencyStatusError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.CreateTransferTransactionUseCase import com.tangem.domain.transaction.usecase.GetFeeUseCase diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/NFTSendConfirmComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/NFTSendConfirmComponent.kt index fb7ed7c2b5..cc0fe9a936 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/NFTSendConfirmComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/NFTSendConfirmComponent.kt @@ -11,20 +11,20 @@ import com.tangem.core.decompose.context.child import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.nft.models.NFTAsset -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.transaction.error.GetFeeError +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.nft.models.NFTAsset +import com.tangem.domain.transaction.error.GetFeeError import com.tangem.features.nft.component.NFTDetailsBlockComponent import com.tangem.features.send.v2.api.SendNotificationsComponent -import com.tangem.features.send.v2.common.CommonSendRoute import com.tangem.features.send.v2.api.entity.PredefinedValues +import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams.DestinationBlockParams +import com.tangem.features.send.v2.common.CommonSendRoute import com.tangem.features.send.v2.common.ui.state.ConfirmUM import com.tangem.features.send.v2.sendnft.confirm.model.NFTSendConfirmModel import com.tangem.features.send.v2.sendnft.confirm.ui.NFTSendConfirmContent import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM import com.tangem.features.send.v2.subcomponents.destination.DefaultSendDestinationBlockComponent -import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams.DestinationBlockParams import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponentParams import com.tangem.features.send.v2.subcomponents.notifications.DefaultSendNotificationsComponent diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt index 873a2c545e..287366db87 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt @@ -20,10 +20,10 @@ import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.BlockchainErrorInfo import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.tokens.* -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.CreateNFTTransferTransactionUseCase import com.tangem.domain.transaction.usecase.GetFeeUseCase diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountComponentParams.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountComponentParams.kt index d212be79aa..925d3780df 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountComponentParams.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountComponentParams.kt @@ -3,11 +3,11 @@ package com.tangem.features.send.v2.subcomponents.amount import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.features.send.v2.common.CommonSendRoute import com.tangem.features.send.v2.api.entity.PredefinedValues +import com.tangem.features.send.v2.common.CommonSendRoute import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponent.ModelCallback import kotlinx.coroutines.flow.StateFlow diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt index 58c464e03a..28b1e1cc38 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt @@ -22,10 +22,10 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.exchange.RampStateManager +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.isMultiCurrency import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.features.send.v2.api.SendFeatureToggles diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/SendFeeComponentParams.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/SendFeeComponentParams.kt index bdf1038ebe..dcd8c7ca34 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/SendFeeComponentParams.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/SendFeeComponentParams.kt @@ -3,9 +3,9 @@ package com.tangem.features.send.v2.subcomponents.fee import arrow.core.Either import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.transaction.error.GetFeeError +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.transaction.error.GetFeeError import com.tangem.features.send.v2.common.CommonSendRoute import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM import kotlinx.coroutines.flow.Flow diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/FeeCalculation.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/FeeCalculation.kt index 939bb73a64..11d3e108d9 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/FeeCalculation.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/FeeCalculation.kt @@ -3,7 +3,7 @@ package com.tangem.features.send.v2.subcomponents.fee.model import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType import com.tangem.utils.extensions.isZero diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/FeeConverter.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/FeeConverter.kt index d66b9376e2..7891a1fd2c 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/FeeConverter.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/FeeConverter.kt @@ -3,9 +3,9 @@ package com.tangem.features.send.v2.subcomponents.fee.model.converters import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM +import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.ImmutableList diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/SendFeeCustomFieldConverter.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/SendFeeCustomFieldConverter.kt index 820e44dbb4..344269f818 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/SendFeeCustomFieldConverter.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/SendFeeCustomFieldConverter.kt @@ -4,12 +4,12 @@ import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.bitcoin.BitcoinCustomFeeConverter import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.kaspa.KaspaCustomFeeConverter -import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM import com.tangem.utils.converter.TwoWayConverter import kotlinx.collections.immutable.ImmutableList diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/bitcoin/BitcoinCustomFeeConverter.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/bitcoin/BitcoinCustomFeeConverter.kt index efb7fee254..821aac24fc 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/bitcoin/BitcoinCustomFeeConverter.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/bitcoin/BitcoinCustomFeeConverter.kt @@ -10,7 +10,7 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.subcomponents.fee.model.checkExceedBalance diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/ethereum/EthereumCustomFeeConverter.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/ethereum/EthereumCustomFeeConverter.kt index b16dd562a8..85cea0393c 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/ethereum/EthereumCustomFeeConverter.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/ethereum/EthereumCustomFeeConverter.kt @@ -9,10 +9,10 @@ import com.tangem.common.ui.amountScreen.utils.getFiatReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.subcomponents.fee.model.checkExceedBalance -import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/ethereum/EthereumEIPCustomFeeConverter.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/ethereum/EthereumEIPCustomFeeConverter.kt index 6a380e8e4c..c72ed83f63 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/ethereum/EthereumEIPCustomFeeConverter.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/ethereum/EthereumEIPCustomFeeConverter.kt @@ -10,7 +10,8 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.subcomponents.fee.model.checkExceedBalance import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter.Companion.ETHEREUM_GAS_UNIT @@ -18,7 +19,6 @@ import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.eth import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter.Companion.GAS_DECIMALS import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter.Companion.GIGA_DECIMALS import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.setEmpty -import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/ethereum/EthereumLegacyCustomFeeConverter.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/ethereum/EthereumLegacyCustomFeeConverter.kt index 759cf62c33..de65e73033 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/ethereum/EthereumLegacyCustomFeeConverter.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/ethereum/EthereumLegacyCustomFeeConverter.kt @@ -10,7 +10,8 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.subcomponents.fee.model.checkExceedBalance import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter.Companion.ETHEREUM_GAS_UNIT @@ -18,7 +19,6 @@ import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.eth import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter.Companion.GAS_DECIMALS import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.ethereum.EthereumCustomFeeConverter.Companion.GIGA_DECIMALS import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.setEmpty -import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/kaspa/KaspaCustomFeeConverter.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/kaspa/KaspaCustomFeeConverter.kt index c3dd23ba75..66607e3750 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/kaspa/KaspaCustomFeeConverter.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/converters/custom/kaspa/KaspaCustomFeeConverter.kt @@ -10,10 +10,10 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.subcomponents.fee.model.converters.custom.CustomFeeConverter -import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeCustomAutoFixTransformer.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeCustomAutoFixTransformer.kt index 65d07205da..495efa22af 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeCustomAutoFixTransformer.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeCustomAutoFixTransformer.kt @@ -1,12 +1,12 @@ package com.tangem.features.send.v2.subcomponents.fee.model.transformers import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents +import com.tangem.features.send.v2.subcomponents.fee.model.converters.SendFeeCustomFieldConverter import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM -import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents -import com.tangem.features.send.v2.subcomponents.fee.model.converters.SendFeeCustomFieldConverter import com.tangem.utils.transformer.Transformer internal class SendFeeCustomAutoFixTransformer( diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeCustomValueChangeTransformer.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeCustomValueChangeTransformer.kt index 5d95767e59..1569cd7d3b 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeCustomValueChangeTransformer.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeCustomValueChangeTransformer.kt @@ -1,11 +1,11 @@ package com.tangem.features.send.v2.subcomponents.fee.model.transformers import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM -import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents import com.tangem.features.send.v2.subcomponents.fee.model.converters.SendFeeCustomFieldConverter +import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM +import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM import com.tangem.utils.transformer.Transformer internal class SendFeeCustomValueChangeTransformer( diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeInitialStateTransformer.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeInitialStateTransformer.kt index 2a2b88a7d2..498ced2b6b 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeInitialStateTransformer.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeInitialStateTransformer.kt @@ -2,7 +2,7 @@ package com.tangem.features.send.v2.subcomponents.fee.model.transformers import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM import com.tangem.lib.crypto.BlockchainUtils.isTron diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeLoadedTransformer.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeLoadedTransformer.kt index 89a69d49f0..3c16c06146 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeLoadedTransformer.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeLoadedTransformer.kt @@ -3,7 +3,7 @@ package com.tangem.features.send.v2.subcomponents.fee.model.transformers import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents import com.tangem.features.send.v2.subcomponents.fee.model.converters.FeeConverter import com.tangem.features.send.v2.subcomponents.fee.model.converters.SendFeeCustomFieldConverter diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeSelectTransformer.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeSelectTransformer.kt index 6ae4b1b0d3..853aff08fe 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeSelectTransformer.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/transformers/SendFeeSelectTransformer.kt @@ -1,12 +1,12 @@ package com.tangem.features.send.v2.subcomponents.fee.model.transformers import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents +import com.tangem.features.send.v2.subcomponents.fee.model.converters.FeeConverter import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM -import com.tangem.features.send.v2.subcomponents.fee.model.converters.FeeConverter -import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeClickIntents import com.tangem.utils.transformer.Transformer internal class SendFeeSelectTransformer( diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/notifications/model/NotificationsModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/notifications/model/NotificationsModel.kt index 8f2dabbed3..da00a9307b 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/notifications/model/NotificationsModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/notifications/model/NotificationsModel.kt @@ -25,12 +25,12 @@ import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.notifications.GetTronFeeNotificationShowCountUseCase import com.tangem.domain.notifications.IncrementNotificationsShowCountUseCase import com.tangem.domain.tokens.GetBalanceNotEnoughForFeeWarningUseCase import com.tangem.domain.tokens.GetCurrencyCheckUseCase import com.tangem.domain.tokens.IsAmountSubtractAvailableUseCase -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck import com.tangem.domain.transaction.usecase.ValidateTransactionUseCase import com.tangem.domain.utils.convertToSdkAmount diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/utils/StakingAnalyticSender.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/utils/StakingAnalyticSender.kt index 65ffcfdedb..131d64fb11 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/utils/StakingAnalyticSender.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/utils/StakingAnalyticSender.kt @@ -5,11 +5,11 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.Basic import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.staking.action.StakingActionType import com.tangem.domain.staking.analytics.StakeScreenSource import com.tangem.domain.staking.analytics.StakingAnalyticsEvent import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType -import com.tangem.domain.models.staking.action.StakingActionType -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.* internal class StakingAnalyticSender( diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt index 90a4867e0c..d5efc4972b 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt @@ -29,11 +29,9 @@ import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.BlockchainErrorInfo import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.models.staking.BalanceItem -import com.tangem.domain.models.staking.BalanceType -import com.tangem.domain.models.staking.PendingAction -import com.tangem.domain.models.staking.RewardBlockType -import com.tangem.domain.models.staking.YieldBalance +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.staking.* +import com.tangem.domain.models.staking.action.StakingActionType import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.requireColdWallet @@ -42,14 +40,13 @@ import com.tangem.domain.staking.analytics.StakeScreenSource import com.tangem.domain.staking.analytics.StakingAnalyticsEvent import com.tangem.domain.staking.model.StakingApproval import com.tangem.domain.staking.model.StakingIntegrationID -import com.tangem.domain.staking.model.stakekit.* +import com.tangem.domain.staking.model.stakekit.StakingError +import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingAction import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType -import com.tangem.domain.models.staking.action.StakingActionType import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction import com.tangem.domain.staking.utils.getValidatorsCount import com.tangem.domain.tokens.* -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.CreateApprovalTransactionUseCase import com.tangem.domain.transaction.usecase.GetAllowanceUseCase diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt index 9476521ace..15cc4b24ae 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt @@ -6,14 +6,14 @@ import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.staking.BalanceItem import com.tangem.domain.models.staking.BalanceType import com.tangem.domain.models.staking.BalanceType.Companion.isClickable -import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.models.staking.action.StakingActionType +import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.utils.getRewardStakingBalance -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.R import com.tangem.features.staking.impl.presentation.state.BalanceState import com.tangem.lib.crypto.BlockchainUtils diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/RewardsValidatorStateConverter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/RewardsValidatorStateConverter.kt index af1a4bde08..3643ba7e27 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/RewardsValidatorStateConverter.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/RewardsValidatorStateConverter.kt @@ -6,11 +6,11 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.staking.BalanceItem import com.tangem.domain.models.staking.BalanceType -import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.models.staking.YieldBalance -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.features.staking.impl.presentation.state.BalanceState import com.tangem.features.staking.impl.presentation.state.StakingStates import com.tangem.utils.Provider diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt index 0ed2a3814c..4251a689ff 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt @@ -5,14 +5,14 @@ import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.staking.BalanceItem import com.tangem.domain.models.staking.BalanceType import com.tangem.domain.models.staking.RewardBlockType import com.tangem.domain.models.staking.YieldBalance -import com.tangem.domain.staking.model.stakekit.* import com.tangem.domain.models.staking.action.StakingActionType +import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.utils.getRewardStakingBalance -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState import com.tangem.features.staking.impl.presentation.state.YieldReward import com.tangem.lib.crypto.BlockchainUtils.isStakingRewardUnavailable diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt index 0cd6d2468c..3defda6f74 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt @@ -1,14 +1,14 @@ package com.tangem.features.staking.impl.presentation.state.helpers +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.staking.FetchActionsUseCase import com.tangem.domain.staking.FetchStakingYieldBalanceUseCase import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus import com.tangem.domain.tokens.FetchCurrencyStatusUseCase import com.tangem.domain.tokens.FetchPendingTransactionsUseCase -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase -import com.tangem.domain.models.wallet.UserWallet import com.tangem.features.txhistory.entity.TxHistoryContentUpdateEmitter import com.tangem.utils.coroutines.DelayedWork import dagger.assisted.Assisted diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingFeeTransactionLoader.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingFeeTransactionLoader.kt index 94e68d2aad..c54feee187 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingFeeTransactionLoader.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingFeeTransactionLoader.kt @@ -8,20 +8,20 @@ import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.common.extensions.isZero import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.staking.EstimateGasUseCase +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.staking.PendingAction +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.staking.EstimateGasUseCase import com.tangem.domain.staking.model.stakekit.StakingError import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.domain.staking.model.stakekit.transaction.ActionParams import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.staking.getCurrentToken import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.CreateApprovalTransactionUseCase import com.tangem.domain.transaction.usecase.GetFeeUseCase import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase -import com.tangem.domain.models.wallet.UserWallet import com.tangem.features.staking.impl.presentation.state.StakingStateController import com.tangem.features.staking.impl.presentation.state.StakingStates import com.tangem.features.staking.impl.presentation.state.utils.isCompositePendingActions diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt index f2a1574d06..8223f07a1d 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt @@ -5,13 +5,15 @@ import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.TransactionSender import com.tangem.blockchain.common.transaction.Fee import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.staking.NetworkType +import com.tangem.domain.models.staking.PendingAction +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.staking.GetConstructedStakingTransactionUseCase import com.tangem.domain.staking.GetStakingTransactionsUseCase import com.tangem.domain.staking.SaveUnsubmittedHashUseCase import com.tangem.domain.staking.SubmitHashUseCase import com.tangem.domain.staking.model.SubmitHashData -import com.tangem.domain.models.staking.NetworkType -import com.tangem.domain.models.staking.PendingAction import com.tangem.domain.staking.model.stakekit.StakingError import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType @@ -19,14 +21,12 @@ import com.tangem.domain.staking.model.stakekit.transaction.ActionParams import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction import com.tangem.domain.staking.model.stakekit.transaction.StakingTransactionStatus import com.tangem.domain.staking.model.stakekit.transaction.StakingTransactionType -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.staking.getCurrentToken import com.tangem.domain.transaction.error.SendTransactionError import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase import com.tangem.domain.transaction.usecase.SendTransactionUseCase import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase import com.tangem.domain.utils.convertToSdkAmount -import com.tangem.domain.models.wallet.UserWallet import com.tangem.features.staking.impl.presentation.state.FeeState import com.tangem.features.staking.impl.presentation.state.StakingStateController import com.tangem.features.staking.impl.presentation.state.StakingStates diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetAmountDataTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetAmountDataTransformer.kt index d4ca71cd71..936fea5c1b 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetAmountDataTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetAmountDataTransformer.kt @@ -7,12 +7,12 @@ import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIco import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.features.staking.impl.R -import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.features.staking.impl.presentation.model.StakingClickIntents +import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.utils.Provider import com.tangem.utils.transformer.Transformer diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateAssentTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateAssentTransformer.kt index c9a8054cc5..73b8c296f2 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateAssentTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateAssentTransformer.kt @@ -2,7 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers import com.tangem.blockchain.common.transaction.Fee import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.FeeState import com.tangem.features.staking.impl.presentation.state.StakingStates import com.tangem.features.staking.impl.presentation.state.StakingUiState diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateCompletedTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateCompletedTransformer.kt index 7190ad5ae1..7eb0589c34 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateCompletedTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateCompletedTransformer.kt @@ -1,7 +1,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers import com.tangem.core.ui.extensions.TextReference -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.InnerConfirmationStakingState import com.tangem.features.staking.impl.presentation.state.StakingStates import com.tangem.features.staking.impl.presentation.state.StakingUiState diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateInitTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateInitTransformer.kt index 99b2bd7041..7fb48b2a61 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateInitTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateInitTransformer.kt @@ -1,12 +1,12 @@ package com.tangem.features.staking.impl.presentation.state.transformers import com.tangem.core.ui.extensions.TextReference -import com.tangem.domain.staking.model.StakingApproval +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.staking.PendingAction +import com.tangem.domain.models.staking.action.StakingActionType +import com.tangem.domain.staking.model.StakingApproval import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType -import com.tangem.domain.models.staking.action.StakingActionType -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.* import com.tangem.features.staking.impl.presentation.state.utils.isCompositePendingActions import com.tangem.features.staking.impl.presentation.state.utils.isTronStakedBalance diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateResetAssentTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateResetAssentTransformer.kt index 327ec4ae8e..db0da4876d 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateResetAssentTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateResetAssentTransformer.kt @@ -1,6 +1,6 @@ package com.tangem.features.staking.impl.presentation.state.transformers -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.InnerConfirmationStakingState import com.tangem.features.staking.impl.presentation.state.StakingStates import com.tangem.features.staking.impl.presentation.state.StakingUiState diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt index e2e24ff112..9a319caa8a 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt @@ -14,10 +14,10 @@ import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.format.bigdecimal.percent import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.staking.BalanceItem -import com.tangem.domain.staking.model.stakekit.Yield -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.features.staking.impl.R import com.tangem.features.staking.impl.presentation.model.StakingClickIntents import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountChangeStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountChangeStateTransformer.kt index e905b0b0a5..0603ec9e7f 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountChangeStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountChangeStateTransformer.kt @@ -3,9 +3,9 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTransformer import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.utils.transformer.Transformer diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountCurrencyChangeStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountCurrencyChangeStateTransformer.kt index 0410bf8745..9631543416 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountCurrencyChangeStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountCurrencyChangeStateTransformer.kt @@ -1,7 +1,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount import com.tangem.common.ui.amountScreen.converters.AmountCurrencyTransformer -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.utils.transformer.Transformer diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountMaxValueStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountMaxValueStateTransformer.kt index 318d0c6522..cee87196ee 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountMaxValueStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountMaxValueStateTransformer.kt @@ -3,9 +3,9 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter import com.tangem.common.ui.amountScreen.converters.field.AmountFieldSetMaxAmountTransformer import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.utils.transformer.Transformer diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceByStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceByStateTransformer.kt index f5e92f66ec..ce81a3c55c 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceByStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceByStateTransformer.kt @@ -3,7 +3,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer.ReduceByData import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.utils.transformer.Transformer diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceToStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceToStateTransformer.kt index 5f7b5e5e57..026ab044df 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceToStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceToStateTransformer.kt @@ -2,7 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount import com.tangem.common.ui.amountScreen.converters.AmountReduceToTransformer import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.utils.transformer.Transformer import java.math.BigDecimal diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountRequirementStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountRequirementStateTransformer.kt index 321db52473..f24052ac95 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountRequirementStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountRequirementStateTransformer.kt @@ -11,10 +11,10 @@ import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseBigDecimal +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.staking.model.stakekit.AddressArgument import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.R import com.tangem.lib.crypto.BlockchainUtils.isTron import com.tangem.utils.extensions.isPositive diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountRoundToIntegerTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountRoundToIntegerTransformer.kt index d2b2e7fe50..26deb1759b 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountRoundToIntegerTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountRoundToIntegerTransformer.kt @@ -2,7 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.core.ui.utils.parseBigDecimal -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.utils.transformer.Transformer import java.math.RoundingMode diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/approval/SetConfirmationStateAssentApprovalTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/approval/SetConfirmationStateAssentApprovalTransformer.kt index 0729792529..235472ef08 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/approval/SetConfirmationStateAssentApprovalTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/approval/SetConfirmationStateAssentApprovalTransformer.kt @@ -2,7 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers.approva import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.FeeState import com.tangem.features.staking.impl.presentation.state.InnerConfirmationStakingState import com.tangem.features.staking.impl.presentation.state.StakingStates diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/approval/ShowApprovalBottomSheetTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/approval/ShowApprovalBottomSheetTransformer.kt index be9aac4f36..c70f2da7a7 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/approval/ShowApprovalBottomSheetTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/approval/ShowApprovalBottomSheetTransformer.kt @@ -9,7 +9,7 @@ import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.staking.impl.R import com.tangem.features.staking.impl.presentation.state.FeeState import com.tangem.features.staking.impl.presentation.state.StakingStates diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/AddStakingNotificationsTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/AddStakingNotificationsTransformer.kt index 122420439f..0dbf76f8ad 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/AddStakingNotificationsTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/AddStakingNotificationsTransformer.kt @@ -14,11 +14,11 @@ import com.tangem.core.ui.extensions.networkIconResId import com.tangem.core.ui.extensions.stringReference import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.staking.model.stakekit.StakingError import com.tangem.domain.staking.model.stakekit.StakingErrors import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.domain.transaction.error.GetFeeError diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt index ee2c810e32..8959e90124 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt @@ -4,12 +4,12 @@ import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.ui.extensions.pluralReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.wrappedList +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.staking.BalanceType -import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.models.staking.YieldBalance -import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.domain.models.staking.action.StakingActionType -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.staking.model.stakekit.Yield +import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.features.staking.impl.R import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState import com.tangem.features.staking.impl.presentation.state.StakingNotification diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/utils/FeeCalculation.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/utils/FeeCalculation.kt index b4d42058e1..3f99aba73d 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/utils/FeeCalculation.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/utils/FeeCalculation.kt @@ -1,6 +1,6 @@ package com.tangem.features.staking.impl.presentation.state.utils -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.lib.crypto.BlockchainUtils.isTron import java.math.BigDecimal import java.math.MathContext diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt index c5e112b2f4..109c84dd81 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt @@ -3,9 +3,9 @@ package com.tangem.features.swap.v2.impl.amount import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.express.models.ExpressProviderType import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.swap.models.SwapDirection -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.swap.models.SwapDirection import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt index 26267a4710..fea9701d97 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt @@ -6,9 +6,9 @@ import com.tangem.core.ui.components.atoms.text.TextEllipsis import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.express.models.ExpressRateType +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.swap.models.SwapCurrencies import com.tangem.domain.swap.models.SwapDirection -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM import kotlinx.collections.immutable.ImmutableList diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt index 23c478c056..f1d6be3848 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt @@ -17,6 +17,7 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.express.models.ExpressError import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.settings.usercountry.GetUserCountryUseCase import com.tangem.domain.settings.usercountry.models.UserCountry import com.tangem.domain.swap.models.SwapCurrencies @@ -27,7 +28,6 @@ import com.tangem.domain.swap.models.getGroupWithDirection import com.tangem.domain.swap.usecase.GetSwapQuoteUseCase import com.tangem.domain.swap.usecase.SelectInitialPairUseCase import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.transaction.usecase.GetAllowanceUseCase import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger import com.tangem.features.swap.v2.api.choosetoken.SwapChooseTokenNetworkListener diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountQuoteUtils.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountQuoteUtils.kt index c4869a1825..06fb3dc71b 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountQuoteUtils.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountQuoteUtils.kt @@ -4,8 +4,8 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.format.bigdecimal.percent +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.swap.models.SwapDirection -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountFieldConverter.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountFieldConverter.kt index 8745ee371d..f29f727166 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountFieldConverter.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountFieldConverter.kt @@ -10,9 +10,9 @@ import com.tangem.core.ui.extensions.* import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.models.SwapDirection -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.swap.v2.impl.R import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt index 2b25b3314b..9aeeb9f2d7 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt @@ -3,10 +3,10 @@ package com.tangem.features.swap.v2.impl.amount.model.transformers import com.tangem.common.ui.amountScreen.AmountScreenClickIntents import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.express.models.ExpressRateType +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.models.SwapCurrencies import com.tangem.domain.swap.models.SwapDirection -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.models.wallet.UserWallet import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt index c4ba35f561..9f54cc94b6 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt @@ -3,10 +3,10 @@ package com.tangem.features.swap.v2.impl.amount.model.transformers import com.tangem.common.ui.amountScreen.AmountScreenClickIntents import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.express.models.ExpressRateType +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.models.SwapCurrencies import com.tangem.domain.swap.models.SwapDirection -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.models.wallet.UserWallet import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM import com.tangem.features.swap.v2.impl.amount.model.converter.SwapAmountFieldConverter diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountUpdateBalanceTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountUpdateBalanceTransformer.kt index dde5dd9c4c..5ed86c33d1 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountUpdateBalanceTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountUpdateBalanceTransformer.kt @@ -3,7 +3,7 @@ package com.tangem.features.swap.v2.impl.amount.model.transformers import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTransformer import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM import com.tangem.utils.transformer.Transformer diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt index d7fd561378..e7bf485c35 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt @@ -9,10 +9,10 @@ import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.express.models.ExpressProviderType import com.tangem.domain.express.models.ExpressRateType import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network import com.tangem.domain.swap.models.SwapCurrencies import com.tangem.domain.swap.models.SwapDirection -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/ConfirmData.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/ConfirmData.kt index d902dee096..995f10c9ae 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/ConfirmData.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/ConfirmData.kt @@ -2,7 +2,7 @@ package com.tangem.features.swap.v2.impl.common import com.tangem.blockchain.common.transaction.Fee import com.tangem.domain.express.models.ExpressRateType -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.transaction.error.GetFeeError import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM import java.math.BigDecimal diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt index 1d02dcce0c..47b374269f 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt @@ -10,9 +10,9 @@ import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.models.SwapDirection -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.v2.api.FeeSelectorBlockComponent import com.tangem.features.send.v2.api.SendNotificationsComponent import com.tangem.features.send.v2.api.entity.PredefinedValues diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt index d4a416b177..847f6d100b 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt @@ -16,10 +16,10 @@ import com.tangem.core.decompose.navigation.Router import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.express.models.ExpressProviderType import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.settings.IsSendTapHelpEnabledUseCase import com.tangem.domain.swap.models.SwapDirection.Companion.withSwapDirection import com.tangem.domain.tokens.IsAmountSubtractAvailableUseCase -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.EstimateFeeUseCase import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SwapTransactionSender.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SwapTransactionSender.kt index ace6ba9272..f6c595b273 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SwapTransactionSender.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SwapTransactionSender.kt @@ -5,12 +5,12 @@ import com.tangem.blockchain.common.transaction.Fee import com.tangem.domain.express.models.ExpressError import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.express.models.ExpressProviderType +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.models.SwapDataModel import com.tangem.domain.swap.models.SwapDataTransactionModel import com.tangem.domain.swap.usecase.GetSwapDataUseCase import com.tangem.domain.swap.usecase.SwapTransactionSentUseCase -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.transaction.error.SendTransactionError import com.tangem.domain.transaction.usecase.CreateTransferTransactionUseCase import com.tangem.domain.transaction.usecase.SendTransactionUseCase diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/model/SendWithSwapModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/model/SendWithSwapModel.kt index d8a7e41bde..e370965fe4 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/model/SendWithSwapModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/model/SendWithSwapModel.kt @@ -13,13 +13,13 @@ import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.express.models.ExpressError import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.isMultiCurrency import com.tangem.domain.swap.models.SwapDirection import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.error.CurrencyStatusError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponent diff --git a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapPairLeast.kt b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapPairLeast.kt index abbcf27ea2..bf97fe58d9 100644 --- a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapPairLeast.kt +++ b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapPairLeast.kt @@ -2,7 +2,7 @@ package com.tangem.feature.swap.domain.models.domain import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import java.math.BigDecimal /** diff --git a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt index be89782000..e8e10f7165 100644 --- a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt +++ b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt @@ -1,7 +1,7 @@ package com.tangem.feature.swap.domain.models.ui import com.tangem.blockchain.common.transaction.Fee -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck import com.tangem.feature.swap.domain.models.ExpressDataError import com.tangem.feature.swap.domain.models.SwapAmount diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/DefaultInitialToCurrencyResolver.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/DefaultInitialToCurrencyResolver.kt index 85fc60140b..b71ed02569 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/DefaultInitialToCurrencyResolver.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/DefaultInitialToCurrencyResolver.kt @@ -1,7 +1,7 @@ package com.tangem.feature.swap.domain import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.feature.swap.domain.models.ui.TokensDataStateExpress import com.tangem.feature.swap.domain.models.ui.getGroupWithReverse diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/InitialToCurrencyResolver.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/InitialToCurrencyResolver.kt index 3c27003179..c62a113adc 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/InitialToCurrencyResolver.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/InitialToCurrencyResolver.kt @@ -1,7 +1,7 @@ package com.tangem.feature.swap.domain import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.feature.swap.domain.models.ui.TokensDataStateExpress diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractor.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractor.kt index 673cc0e7fc..3646a0d6b5 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractor.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractor.kt @@ -1,7 +1,7 @@ package com.tangem.feature.swap.domain import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId import com.tangem.feature.swap.domain.models.SwapAmount import com.tangem.feature.swap.domain.models.domain.IncludeFeeInAmount diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index f0fbc9fbcc..cc8f64ca53 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -18,12 +18,14 @@ import com.tangem.domain.appcurrency.extenstions.unwrap import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network import com.tangem.domain.models.quote.QuoteStatus +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.quotes.QuotesRepository import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher import com.tangem.domain.tokens.* -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.FeePaidCurrency import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck import com.tangem.domain.tokens.repository.CurrenciesRepository @@ -31,8 +33,6 @@ import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.* import com.tangem.domain.utils.convertToSdkAmount -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.feature.swap.domain.api.SwapRepository import com.tangem.feature.swap.domain.models.ExpressDataError diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt index 450234b855..d31fdcffe5 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt @@ -7,7 +7,7 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.feature.swap.domain.models.domain.CryptoCurrencySwapInfo import com.tangem.feature.swap.models.CurrenciesGroupWithFromCurrency import com.tangem.feature.swap.models.SwapSelectTokenStateHolder diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt index d0b4f81377..a748df6f52 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt @@ -29,7 +29,11 @@ import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.BlockchainErrorInfo import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.promo.GetStoryContentUseCase import com.tangem.domain.promo.ShouldShowStoriesUseCase import com.tangem.domain.promo.models.StoryContentIds @@ -40,11 +44,7 @@ import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.feature.swap.analytics.StoriesEvents import com.tangem.feature.swap.analytics.SwapEvents diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapNotificationsFactory.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapNotificationsFactory.kt index 69f4309c55..36018d5189 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapNotificationsFactory.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapNotificationsFactory.kt @@ -11,7 +11,7 @@ import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.feature.swap.domain.models.ExpressDataError import com.tangem.feature.swap.domain.models.SwapAmount import com.tangem.feature.swap.domain.models.domain.IncludeFeeInAmount diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapProcessDataState.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapProcessDataState.kt index 86a8a33195..604203a8f7 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapProcessDataState.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapProcessDataState.kt @@ -1,6 +1,6 @@ package com.tangem.feature.swap.model -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.feature.swap.domain.models.domain.SwapDataModel import com.tangem.feature.swap.domain.models.domain.SwapProvider import com.tangem.feature.swap.domain.models.ui.RequestApproveStateData diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt index a041840318..2364dbd0a6 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt @@ -10,7 +10,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.event.StateEvent import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.extensions.TextReference -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.feature.swap.domain.models.ui.PriceImpact import com.tangem.feature.swap.models.states.FeeItemState import com.tangem.feature.swap.models.states.ProviderState diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index ad13b81ea0..9f250ee99b 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -18,8 +18,8 @@ import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.promo.models.StoryContent -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.feature.swap.converters.SwapTransactionErrorStateConverter import com.tangem.feature.swap.converters.TokensDataConverter import com.tangem.feature.swap.domain.models.ExpressDataError diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/analytics/TokenDetailsCurrencyStatusAnalyticsSender.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/analytics/TokenDetailsCurrencyStatusAnalyticsSender.kt index 9a17d9be3a..04b8498da4 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/analytics/TokenDetailsCurrencyStatusAnalyticsSender.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/analytics/TokenDetailsCurrencyStatusAnalyticsSender.kt @@ -4,8 +4,8 @@ import arrow.core.Either import arrow.core.getOrElse import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.tokens.error.CurrencyStatusError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus internal class TokenDetailsCurrencyStatusAnalyticsSender( private val analyticsEventHandler: AnalyticsEventHandler, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt index 33f4a326c7..f5352430b4 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt @@ -36,6 +36,7 @@ import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.NetworkAddress import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId @@ -49,7 +50,6 @@ import com.tangem.domain.staking.GetYieldUseCase import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.tokens.* import com.tangem.domain.tokens.legacy.TradeCryptoAction -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.TokenActionsState import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsBalanceSelectStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsBalanceSelectStateConverter.kt index c2f97a9c09..95139be280 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsBalanceSelectStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsBalanceSelectStateConverter.kt @@ -4,9 +4,9 @@ import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.feature.tokendetails.presentation.tokendetails.state.* import com.tangem.feature.tokendetails.presentation.tokendetails.state.utils.getBalance import com.tangem.utils.Provider diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt index da60bfba2b..03fbaa9901 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt @@ -9,10 +9,10 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.format.bigdecimal.* import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.StatusSource +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance import com.tangem.domain.tokens.error.CurrencyStatusError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents import com.tangem.feature.tokendetails.presentation.tokendetails.state.BalanceType import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockState diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsOnrampTransactionStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsOnrampTransactionStateConverter.kt index a18b9be6e1..ec37185065 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsOnrampTransactionStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsOnrampTransactionStateConverter.kt @@ -16,9 +16,9 @@ import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday import com.tangem.core.ui.utils.toTimeFormat import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.onramp.model.OnrampStatus import com.tangem.domain.onramp.model.cache.OnrampTransaction -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents import com.tangem.features.tokendetails.impl.R diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStakingInfoConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStakingInfoConverter.kt index 2a7dfc2ef6..f6fb474788 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStakingInfoConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStakingInfoConverter.kt @@ -8,13 +8,13 @@ import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.staking.model.StakingAvailability -import com.tangem.domain.staking.model.StakingEntryInfo +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.staking.RewardBlockType import com.tangem.domain.models.staking.YieldBalance +import com.tangem.domain.staking.model.StakingAvailability +import com.tangem.domain.staking.model.StakingEntryInfo import com.tangem.domain.staking.utils.getRewardStakingBalance import com.tangem.domain.staking.utils.getTotalStakingBalance -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents import com.tangem.feature.tokendetails.presentation.tokendetails.state.IconState import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingBlockUM diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt index 251bfb5dfd..22365b4892 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt @@ -15,6 +15,7 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.NetworkAddress import com.tangem.domain.models.network.TxInfo @@ -23,7 +24,6 @@ import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.staking.model.StakingEntryInfo import com.tangem.domain.tokens.error.CurrencyStatusError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.TokenActionsState import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt index 5b3760c7f4..46a530ddbe 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt @@ -6,11 +6,11 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent -import com.tangem.domain.models.wallet.UserWallet import com.tangem.feature.swap.domain.models.domain.ExchangeStatus import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt index c646572718..2e91b8b73d 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt @@ -6,15 +6,15 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.datasource.local.swaptx.ExpressAnalyticsStatus import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.onramp.GetOnrampStatusUseCase import com.tangem.domain.onramp.GetOnrampTransactionsUseCase import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase import com.tangem.domain.onramp.OnrampUpdateTransactionStatusUseCase import com.tangem.domain.onramp.model.OnrampStatus import com.tangem.domain.onramp.model.OnrampStatus.Status.* -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent -import com.tangem.domain.models.wallet.UserWallet import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsOnrampTransactionStateConverter diff --git a/features/txhistory/impl/src/main/kotlin/com/tangem/features/txhistory/model/TxHistoryModel.kt b/features/txhistory/impl/src/main/kotlin/com/tangem/features/txhistory/model/TxHistoryModel.kt index 1cf9437129..b5586fdb53 100644 --- a/features/txhistory/impl/src/main/kotlin/com/tangem/features/txhistory/model/TxHistoryModel.kt +++ b/features/txhistory/impl/src/main/kotlin/com/tangem/features/txhistory/model/TxHistoryModel.kt @@ -8,14 +8,14 @@ import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.navigation.url.UrlOpener import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.card.common.util.cardTypesResolver +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.error.CurrencyStatusError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.txhistory.models.TxHistoryStateError import com.tangem.domain.txhistory.repository.TxHistoryRepositoryV2 import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase -import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.features.txhistory.component.TxHistoryComponent import com.tangem.features.txhistory.converter.TxHistoryItemToTransactionStateConverter diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt index 06cea48302..cc5a440140 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt @@ -4,17 +4,17 @@ import arrow.core.getOrElse import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ModelScoped +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.isLocked import com.tangem.domain.nft.analytics.NFTAnalyticsEvent import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.settings.ShouldShowMarketsTooltipUseCase import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.TokensAction -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenActionsState import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.isLocked import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.feature.wallet.presentation.wallet.domain.OnrampStatusFactory import com.tangem.feature.wallet.presentation.wallet.domain.unwrap diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletCurrencyActionsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletCurrencyActionsClickIntents.kt index b58f51c8ef..29c08244a6 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletCurrencyActionsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletCurrencyActionsClickIntents.kt @@ -30,8 +30,11 @@ import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.markets.TokenMarketParams import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.NetworkAddress +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.onramp.model.OnrampSource import com.tangem.domain.promo.GetStoryContentUseCase import com.tangem.domain.promo.models.StoryContentIds @@ -41,15 +44,12 @@ import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.IsCryptoCurrencyCoinCouldHideUseCase import com.tangem.domain.tokens.RemoveCurrencyUseCase import com.tangem.domain.tokens.legacy.TradeCryptoAction -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.AVAILABLE import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.toReasonAnalyticsText import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.feature.wallet.impl.R diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt index 037bed6176..21f42375ef 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt @@ -7,9 +7,9 @@ import com.tangem.core.ui.format.bigdecimal.BigDecimalFormatConstants import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupHeaderId import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getTokenItemId diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 846fe77ccb..8c39cfc9e5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -6,12 +6,12 @@ import com.tangem.common.routing.AppRoute.ManageTokens.Source import com.tangem.common.routing.AppRouter import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.navigation.url.UrlOpener +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.redux.ReduxStateHolder -import com.tangem.domain.redux.StateDialog -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.redux.ReduxStateHolder +import com.tangem.domain.redux.StateDialog import com.tangem.feature.wallet.navigation.WalletRoute import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig import kotlinx.coroutines.channels.BufferOverflow diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index c33e3a6652..b1670c0110 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -2,8 +2,8 @@ package com.tangem.feature.wallet.presentation.router import androidx.compose.runtime.Stable import com.arkivanov.decompose.router.slot.SlotNavigation +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.feature.wallet.navigation.WalletRoute diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/TokenListAnalyticsSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/TokenListAnalyticsSender.kt index 1ce39be795..9c7d278dd0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/TokenListAnalyticsSender.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/TokenListAnalyticsSender.kt @@ -13,10 +13,10 @@ import com.tangem.domain.analytics.CheckIsWalletToppedUpUseCase import com.tangem.domain.analytics.model.WalletBalanceState import com.tangem.domain.models.StatusSource import com.tangem.domain.models.TotalFiatBalance +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.isMultiCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt index 2e1f9c25f6..24cd9574d3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt @@ -8,12 +8,12 @@ import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.models.StatusSource import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.promo.ShouldShowPromoWalletUseCase import com.tangem.domain.promo.models.PromoId import com.tangem.domain.settings.IsReadyToShowRateAppUseCase import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.wallets.models.SeedPhraseNotificationsStatus import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetSingleWalletWarningsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetSingleWalletWarningsFactory.kt index ab95132fed..64d80d9754 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetSingleWalletWarningsFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetSingleWalletWarningsFactory.kt @@ -6,15 +6,15 @@ import com.tangem.domain.card.CardTypesResolver import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.models.StatusSource +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.settings.IsReadyToShowRateAppUseCase import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.error.CurrencyStatusError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase -import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.* diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/UseCaseExt.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/UseCaseExt.kt index 3e917b7417..58da9fca4d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/UseCaseExt.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/UseCaseExt.kt @@ -1,11 +1,11 @@ package com.tangem.feature.wallet.presentation.wallet.domain import arrow.core.Either -import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase -import com.tangem.domain.tokens.error.CurrencyStatusError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase +import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import kotlinx.coroutines.flow.* import timber.log.Timber diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletWithFundsChecker.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletWithFundsChecker.kt index 586234c6d6..9f31104f3a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletWithFundsChecker.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletWithFundsChecker.kt @@ -1,8 +1,8 @@ package com.tangem.feature.wallet.presentation.wallet.domain import com.tangem.common.extensions.isZero +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList import javax.inject.Inject diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetExpressStatusesTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetExpressStatusesTransformer.kt index 822e377075..b3ebdd5713 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetExpressStatusesTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetExpressStatusesTransformer.kt @@ -1,16 +1,16 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig +import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.onramp.model.cache.OnrampTransaction -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM +import com.tangem.domain.onramp.model.cache.OnrampTransaction +import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.SingleWalletOnrampTransactionConverter -import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import kotlinx.collections.immutable.toPersistentList import timber.log.Timber diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetPrimaryCurrencyTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetPrimaryCurrencyTransformer.kt index 4c6f769a5e..4e5b37f5d9 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetPrimaryCurrencyTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetPrimaryCurrencyTransformer.kt @@ -2,7 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetVisaInfoTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetVisaInfoTransformer.kt index 1201f8807a..2accc6f5b7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetVisaInfoTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetVisaInfoTransformer.kt @@ -9,16 +9,12 @@ import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.card.common.util.getCardsCount -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.visa.exception.RefreshTokenExpiredException import com.tangem.domain.visa.model.VisaCurrency -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state.model.BalancesAndLimitsBlockState -import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo -import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents -import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton +import com.tangem.feature.wallet.presentation.wallet.state.model.* import com.tangem.utils.extensions.isZero import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCurrencyActionsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCurrencyActionsConverter.kt index 98c83885fa..97a13ed361 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCurrencyActionsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCurrencyActionsConverter.kt @@ -3,13 +3,13 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers.convert import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.card.common.util.cardTypesResolver -import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.model.TokenActionsState +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason +import com.tangem.domain.tokens.model.TokenActionsState +import com.tangem.feature.wallet.child.wallet.model.intents.WalletCurrencyActionsClickIntents import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonConfig -import com.tangem.feature.wallet.child.wallet.model.intents.WalletCurrencyActionsClickIntents import com.tangem.utils.converter.Converter import com.tangem.utils.isNullOrZero import kotlinx.collections.immutable.ImmutableList diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletCardStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletCardStateConverter.kt index 4df4316649..fe8a06fbc8 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletCardStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletCardStateConverter.kt @@ -5,7 +5,7 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.card.common.util.getCardsCount import com.tangem.domain.models.StatusSource -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletMarketPriceConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletMarketPriceConverter.kt index 3611aa8119..8dadf5a69e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletMarketPriceConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletMarketPriceConverter.kt @@ -8,7 +8,7 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.format.bigdecimal.percent import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.utils.converter.Converter internal class SingleWalletMarketPriceConverter( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletOnrampTransactionConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletOnrampTransactionConverter.kt index 5b487cb700..73813e73d3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletOnrampTransactionConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletOnrampTransactionConverter.kt @@ -15,9 +15,9 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday import com.tangem.core.ui.utils.toTimeFormat import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.onramp.model.OnrampStatus import com.tangem.domain.onramp.model.cache.OnrampTransaction -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.impl.R diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt index 9a3c8407b4..83cdcb49cb 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt @@ -7,8 +7,8 @@ import com.tangem.core.ui.extensions.wrappedList import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.models.TotalFiatBalance +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/PrimaryCurrencySubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/PrimaryCurrencySubscriber.kt index 1fa58e9e7d..4532a34ec2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/PrimaryCurrencySubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/PrimaryCurrencySubscriber.kt @@ -7,11 +7,11 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.error.CurrencyStatusError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.models.wallet.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetPrimaryCurrencyTransformer diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletExpressStatusesSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletExpressStatusesSubscriber.kt index 7600075e6a..a2d9cbdd89 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletExpressStatusesSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletExpressStatusesSubscriber.kt @@ -5,13 +5,13 @@ import arrow.core.getOrElse import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.onramp.GetOnrampTransactionsUseCase import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase import com.tangem.domain.onramp.model.cache.OnrampTransaction import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.error.CurrencyStatusError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.models.wallet.UserWallet import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetExpressStatusesTransformer diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TxHistorySubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TxHistorySubscriber.kt index df61988921..e5deefbf1f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TxHistorySubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TxHistorySubscriber.kt @@ -5,14 +5,14 @@ import androidx.paging.cachedIn import androidx.paging.map import arrow.core.Either import com.tangem.domain.card.common.util.cardTypesResolver +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.TxInfo +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.domain.txhistory.models.TxHistoryStateError import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase -import com.tangem.domain.models.wallet.UserWallet import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.presentation.wallet.domain.collectLatest import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt index 463c89fc5a..fc2dac78e4 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt @@ -17,11 +17,11 @@ import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.ui.clipboard.ClipboardManager import com.tangem.core.ui.extensions.stringReference import com.tangem.domain.core.lce.Lce +import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.tokens.GetNetworkCoinStatusUseCase import com.tangem.domain.tokens.error.CurrencyStatusError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.GetFeeUseCase import com.tangem.domain.walletconnect.WcAnalyticEvents From 4c51193221e547bf3446ef20efef4eb49f247559 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 30 Jul 2025 17:42:54 +0400 Subject: [PATCH 20/53] Updated on 2026-08-14 --- .../com/tangem/domain/models/StatusSource.kt | 3 + .../tangem/domain/models/TotalFiatBalance.kt | 9 +- .../domain/models/tokenlist/TokenList.kt | 88 +++++++++++++++++++ .../domain/tokens/GetTokenListUseCase.kt | 2 +- .../tokens/ToggleTokenListGroupingUseCase.kt | 2 +- .../tokens/ToggleTokenListSortingUseCase.kt | 2 +- .../domain/tokens/error/TokenListError.kt | 2 +- .../domain/tokens/model/NetworkGroup.kt | 17 ---- .../tangem/domain/tokens/model/TokenList.kt | 66 -------------- .../tokens/operations/TokenListOperations.kt | 2 +- .../operations/TokenListSortingOperations.kt | 4 +- .../domain/tokens/mock/MockNetworksGroups.kt | 2 +- .../domain/tokens/mock/MockTokenLists.kt | 2 +- .../model/AvailableSwapPairsModel.kt | 4 +- .../tokenlist/model/OnrampTokenListModel.kt | 2 +- .../model/OrganizeTokensModel.kt | 2 +- .../OrganizeTokensStateHolder.kt | 2 +- .../utils/CryptoCurrenciesIdsResolver.kt | 2 +- .../utils/common/TokenListOperations.kt | 2 +- .../converter/TokenListToStateConverter.kt | 2 +- .../NetworkGroupToDraggableItemsConverter.kt | 2 +- .../items/TokenListToListStateConverter.kt | 2 +- .../utils/TokenListAnalyticsSender.kt | 2 +- .../domain/GetMultiWalletWarningsFactory.kt | 2 +- .../domain/MultiWalletTokenListStore.kt | 4 +- .../wallet/domain/WalletWithFundsChecker.kt | 2 +- .../transformers/SetTokenListTransformer.kt | 4 +- .../converter/TokenListStateConverter.kt | 4 +- .../subscribers/BasicTokenListSubscriber.kt | 2 +- .../MultiWalletTokenListSubscriber.kt | 2 +- .../SingleWalletWithTokenListSubscriber.kt | 4 +- 31 files changed, 130 insertions(+), 117 deletions(-) create mode 100644 domain/models/src/main/kotlin/com/tangem/domain/models/tokenlist/TokenList.kt delete mode 100644 domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkGroup.kt delete mode 100644 domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenList.kt diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/StatusSource.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/StatusSource.kt index 73edfce845..641ec0eaa7 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/StatusSource.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/StatusSource.kt @@ -1,10 +1,13 @@ package com.tangem.domain.models +import kotlinx.serialization.Serializable + /** * Source of the status of any loaded data * [REDACTED_AUTHOR] */ +@Serializable enum class StatusSource { /** diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/TotalFiatBalance.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/TotalFiatBalance.kt index e7b9ccdc03..374956a548 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/TotalFiatBalance.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/TotalFiatBalance.kt @@ -1,22 +1,26 @@ package com.tangem.domain.models -import java.math.BigDecimal +import com.tangem.domain.models.serialization.SerializedBigDecimal +import kotlinx.serialization.Serializable /** * Represents the possible states of the fiat balance, including loading, failure, or a loaded amount */ +@Serializable sealed interface TotalFiatBalance { /** * Represents the loading state of the fiat balance. * This state indicates that the fiat balance is currently being retrieved or calculated. */ + @Serializable data object Loading : TotalFiatBalance /** * Represents the failure state of the fiat balance. * This state indicates that an attempt to retrieve or calculate the fiat balance has failed. */ + @Serializable data object Failed : TotalFiatBalance /** @@ -25,8 +29,9 @@ sealed interface TotalFiatBalance { * @property amount the loaded fiat balance amount * @property isAllAmountsSummarized indicates whether the amount includes a summary of all underlying amounts */ + @Serializable data class Loaded( - val amount: BigDecimal, + val amount: SerializedBigDecimal, val isAllAmountsSummarized: Boolean, val source: StatusSource, ) : TotalFiatBalance diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/tokenlist/TokenList.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/tokenlist/TokenList.kt new file mode 100644 index 0000000000..bec343c4df --- /dev/null +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/tokenlist/TokenList.kt @@ -0,0 +1,88 @@ +package com.tangem.domain.models.tokenlist + +import com.tangem.domain.models.StatusSource +import com.tangem.domain.models.TokensSortType +import com.tangem.domain.models.TotalFiatBalance +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.network.Network +import com.tangem.domain.models.serialization.SerializedBigDecimal +import kotlinx.serialization.Serializable + +/** + * Represents a list of cryptocurrency tokens, which can be grouped by network or ungrouped. + * + * The tokens can be represented in two forms: either grouped by the network or as an ungrouped collection. + * Additional details like the total fiat balance and the sorting type can be associated with the list. + */ +@Serializable +sealed interface TokenList { + + /** The total fiat balance across all tokens */ + val totalFiatBalance: TotalFiatBalance + + /** The criteria used for sorting the tokens */ + val sortedBy: TokensSortType + + /** + * Represents tokens that are grouped by their network + * + * @property totalFiatBalance the total fiat balance across all groups + * @property sortedBy the criteria used for sorting the tokens within the groups + * @property groups a list of network groups containing tokens + */ + @Serializable + data class GroupedByNetwork( + override val totalFiatBalance: TotalFiatBalance, + override val sortedBy: TokensSortType, + val groups: List, + ) : TokenList { + + /** + * Represents a group of cryptocurrencies associated with a specific network + * + * @property network the blockchain network associated with the group + * @property currencies a list of cryptocurrency statuses that belong to the network + */ + @Serializable + data class NetworkGroup( + val network: Network, + val currencies: List, + ) + } + + /** + * Represents tokens that are not grouped by any specific criteria. + * + * @property totalFiatBalance the total fiat balance across all groups + * @property sortedBy the criteria used for sorting the tokens within the groups + * @property currencies a list of cryptocurrency statuses + */ + @Serializable + data class Ungrouped( + override val totalFiatBalance: TotalFiatBalance, + override val sortedBy: TokensSortType, + val currencies: List, + ) : TokenList + + /** Represents a state where the token list is empty */ + @Serializable + data object Empty : TokenList { + + override val totalFiatBalance: TotalFiatBalance = TotalFiatBalance.Loaded( + amount = SerializedBigDecimal.ZERO, + isAllAmountsSummarized = true, + source = StatusSource.ACTUAL, + ) + + override val sortedBy: TokensSortType = TokensSortType.NONE + } + + /** Get flatten list of cryptocurrency status [CryptoCurrencyStatus] */ + fun flattenCurrencies(): List { + return when (this) { + is GroupedByNetwork -> groups.flatMap(GroupedByNetwork.NetworkGroup::currencies) + is Ungrouped -> currencies + is Empty -> emptyList() + } + } +} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt index a7b6edcee0..c1d7cd8cc8 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt @@ -5,10 +5,10 @@ import com.tangem.domain.core.utils.lceError import com.tangem.domain.core.utils.lceLoading import com.tangem.domain.core.utils.toLce import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.error.mapper.mapToTokenListError -import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.tokens.operations.BaseCurrenciesStatusesOperations import com.tangem.domain.tokens.operations.TokenListOperations import com.tangem.domain.tokens.repository.CurrenciesRepository diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListGroupingUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListGroupingUseCase.kt index 936723bc4d..b0b9ac0e7c 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListGroupingUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListGroupingUseCase.kt @@ -6,9 +6,9 @@ import arrow.core.raise.either import arrow.core.raise.ensure import arrow.core.raise.withError import com.tangem.domain.models.TotalFiatBalance +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.tokens.error.TokenListSortingError import com.tangem.domain.tokens.error.mapper.mapToTokenListSortingError -import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.tokens.operations.TokenListSortingOperations import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.withContext diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListSortingUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListSortingUseCase.kt index e282aed62d..9283f50615 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListSortingUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListSortingUseCase.kt @@ -7,9 +7,9 @@ import arrow.core.raise.ensure import arrow.core.raise.withError import com.tangem.domain.models.TokensSortType import com.tangem.domain.models.TotalFiatBalance +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.tokens.error.TokenListSortingError import com.tangem.domain.tokens.error.mapper.mapToTokenListSortingError -import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.tokens.operations.TokenListSortingOperations import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.withContext diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/TokenListError.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/TokenListError.kt index 5332319220..e1afb549a0 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/TokenListError.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/TokenListError.kt @@ -1,6 +1,6 @@ package com.tangem.domain.tokens.error -import com.tangem.domain.tokens.model.TokenList +import com.tangem.domain.models.tokenlist.TokenList sealed class TokenListError { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkGroup.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkGroup.kt deleted file mode 100644 index f771dd1795..0000000000 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkGroup.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.tangem.domain.tokens.model - -import com.tangem.domain.models.currency.CryptoCurrencyStatus -import com.tangem.domain.models.network.Network - -/** - * Represents a group of cryptocurrencies associated with a specific network. - * - * This class encapsulates a collection of cryptocurrency statuses, all of which are part of the same blockchain network. - * - * @property network The blockchain network associated with the group. - * @property currencies A list of cryptocurrency statuses that belong to the network. - */ -data class NetworkGroup( - val network: Network, - val currencies: List, -) \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenList.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenList.kt deleted file mode 100644 index 5cd12a6b8b..0000000000 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenList.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.tangem.domain.tokens.model - -import com.tangem.domain.models.StatusSource -import com.tangem.domain.models.TokensSortType -import com.tangem.domain.models.TotalFiatBalance -import com.tangem.domain.models.currency.CryptoCurrencyStatus -import java.math.BigDecimal - -/** - * Represents a list of cryptocurrency tokens, which can be grouped by network or ungrouped. - * - * The tokens can be represented in two forms: either grouped by the network or as an ungrouped collection. - * Additional details like the total fiat balance and the sorting type can be associated with the list. - * - * @property totalFiatBalance The total fiat balance across all tokens, which could be in a loading state, failed, or loaded with a specific amount. - * @property sortedBy The criteria used for sorting the tokens. - */ -sealed class TokenList { - open val totalFiatBalance: TotalFiatBalance = TotalFiatBalance.Loading - open val sortedBy: TokensSortType = TokensSortType.NONE - - /** - * Represents tokens that are grouped by their network. - * - * @property groups A list of network groups containing tokens. - * @property totalFiatBalance The total fiat balance across all groups. - * @property sortedBy The criteria used for sorting the tokens within the groups. - */ - data class GroupedByNetwork( - val groups: List, - override val totalFiatBalance: TotalFiatBalance, - override val sortedBy: TokensSortType, - ) : TokenList() - - /** - * Represents tokens that are not grouped by any specific criteria. - * - * @property currencies A list of cryptocurrency statuses. - * @property totalFiatBalance The total fiat balance across all currencies. - * @property sortedBy The criteria used for sorting the currencies. - */ - data class Ungrouped( - val currencies: List, - override val totalFiatBalance: TotalFiatBalance, - override val sortedBy: TokensSortType, - ) : TokenList() - - /** Represents a state where the token list is empty. */ - data object Empty : TokenList() { - - override val totalFiatBalance: TotalFiatBalance = TotalFiatBalance.Loaded( - amount = BigDecimal.ZERO, - isAllAmountsSummarized = true, - source = StatusSource.ACTUAL, - ) - } - - /** Get flatten list of cryptocurrency status [CryptoCurrencyStatus] */ - fun flattenCurrencies(): List { - return when (this) { - is GroupedByNetwork -> groups.flatMap(NetworkGroup::currencies) - is Ungrouped -> currencies - is Empty -> emptyList() - } - } -} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt index d2c9b899b3..01eac8c1bb 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt @@ -7,8 +7,8 @@ import arrow.core.raise.withError import com.tangem.domain.models.TokensSortType import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.tokens.repository.CurrenciesRepository import kotlinx.coroutines.flow.* diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt index e6288914b2..7669211249 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt @@ -12,9 +12,9 @@ import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.Network import com.tangem.domain.models.staking.YieldBalance +import com.tangem.domain.models.tokenlist.TokenList +import com.tangem.domain.models.tokenlist.TokenList.GroupedByNetwork.NetworkGroup import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance -import com.tangem.domain.tokens.model.NetworkGroup -import com.tangem.domain.tokens.model.TokenList import com.tangem.utils.extensions.orZero import java.math.BigDecimal diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworksGroups.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworksGroups.kt index d69fa589b0..6cc7313c8f 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworksGroups.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworksGroups.kt @@ -2,7 +2,7 @@ package com.tangem.domain.tokens.mock import arrow.core.nonEmptyListOf import arrow.core.toNonEmptyListOrNull -import com.tangem.domain.tokens.model.NetworkGroup +import com.tangem.domain.models.tokenlist.TokenList.GroupedByNetwork.NetworkGroup @Suppress("MemberVisibilityCanBePrivate") internal object MockNetworksGroups { diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt index 1d660f1061..caa2c13c6f 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt @@ -6,10 +6,10 @@ import com.tangem.domain.models.StatusSource import com.tangem.domain.models.TokensSortType import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.tokens.mock.MockNetworksGroups.failedNetworksGroups import com.tangem.domain.tokens.mock.MockNetworksGroups.loadedNetworksGroups import com.tangem.domain.tokens.mock.MockNetworksGroups.sortedNetworksGroups -import com.tangem.domain.tokens.model.TokenList import java.math.BigDecimal @Suppress("MemberVisibilityCanBePrivate") diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt index 0fe2bad477..18f0ca746d 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt @@ -17,8 +17,8 @@ import com.tangem.domain.core.utils.lceError import com.tangem.domain.core.utils.lceLoading import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.tokens.GetTokenListUseCase -import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.feature.swap.domain.GetAvailablePairsUseCase import com.tangem.feature.swap.domain.models.domain.LeastTokenInfo @@ -200,7 +200,7 @@ internal class AvailableSwapPairsModel @Inject constructor( .collectLatest { selectedStatus -> val networkInfo = selectedStatus.toLeastTokenInfo() - val isAlreadyLoaded = availablePairsByNetworkFlow.value[networkInfo]?.isContent() ?: false + val isAlreadyLoaded = availablePairsByNetworkFlow.value[networkInfo]?.isContent() == true if (isAlreadyLoaded) return@collectLatest val statuses = tokenListFlow.firstOrNull() ?: return@collectLatest diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt index 8e0f99d7fd..d2ce88c2d0 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt @@ -14,13 +14,13 @@ import com.tangem.domain.core.utils.getOrElse import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.settings.usercountry.GetUserCountryUseCase import com.tangem.domain.settings.usercountry.models.UserCountry import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason -import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/organizetokens/model/OrganizeTokensModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/organizetokens/model/OrganizeTokensModel.kt index 7aeebcd81e..4f7dfafd03 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/organizetokens/model/OrganizeTokensModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/organizetokens/model/OrganizeTokensModel.kt @@ -12,12 +12,12 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.core.lce.Lce import com.tangem.domain.models.TokensSortType +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.tokens.ApplyTokenListSortingUseCase import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.ToggleTokenListGroupingUseCase import com.tangem.domain.tokens.ToggleTokenListSortingUseCase import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.child.organizetokens.OrganizeTokensComponent import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensIntents import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensStateHolder diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt index 8b112a4dd6..193ed15375 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt @@ -3,9 +3,9 @@ package com.tangem.feature.wallet.presentation.organizetokens import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.event.triggeredEvent import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.error.TokenListSortingError -import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.InProgressStateConverter diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/CryptoCurrenciesIdsResolver.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/CryptoCurrenciesIdsResolver.kt index fc72339e53..74bec371ac 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/CryptoCurrenciesIdsResolver.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/CryptoCurrenciesIdsResolver.kt @@ -1,7 +1,7 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.TokenList +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/TokenListOperations.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/TokenListOperations.kt index 4ff807e1f8..8600d8beaf 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/TokenListOperations.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/TokenListOperations.kt @@ -1,7 +1,7 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.common import com.tangem.domain.models.TokensSortType -import com.tangem.domain.tokens.model.TokenList +import com.tangem.domain.models.tokenlist.TokenList internal fun TokenList.disableSortingByBalance(): TokenList { return when (this) { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/TokenListToStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/TokenListToStateConverter.kt index 12a713c0ff..d561e3b262 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/TokenListToStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/TokenListToStateConverter.kt @@ -1,7 +1,7 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.converter import com.tangem.domain.models.TokensSortType -import com.tangem.domain.tokens.model.TokenList +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items.TokenListToListStateConverter diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/NetworkGroupToDraggableItemsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/NetworkGroupToDraggableItemsConverter.kt index 0205df476a..73ecfe3f79 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/NetworkGroupToDraggableItemsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/NetworkGroupToDraggableItemsConverter.kt @@ -1,6 +1,6 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items -import com.tangem.domain.tokens.model.NetworkGroup +import com.tangem.domain.models.tokenlist.TokenList.GroupedByNetwork.NetworkGroup import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupHeaderId import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupPlaceholder diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/TokenListToListStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/TokenListToListStateConverter.kt index 455e896da9..770d63c0f4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/TokenListToListStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/TokenListToListStateConverter.kt @@ -1,6 +1,6 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items -import com.tangem.domain.tokens.model.TokenList +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState import com.tangem.feature.wallet.presentation.organizetokens.utils.common.uniteItems diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/TokenListAnalyticsSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/TokenListAnalyticsSender.kt index 9c7d278dd0..bf5b27d5ea 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/TokenListAnalyticsSender.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/TokenListAnalyticsSender.kt @@ -14,10 +14,10 @@ import com.tangem.domain.analytics.model.WalletBalanceState import com.tangem.domain.models.StatusSource import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.isMultiCurrency -import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt index 24cd9574d3..4427d9de7e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt @@ -9,12 +9,12 @@ import com.tangem.domain.models.StatusSource import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.promo.ShouldShowPromoWalletUseCase import com.tangem.domain.promo.models.PromoId import com.tangem.domain.settings.IsReadyToShowRateAppUseCase import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.wallets.models.SeedPhraseNotificationsStatus import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/MultiWalletTokenListStore.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/MultiWalletTokenListStore.kt index 167deff237..1aa129675c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/MultiWalletTokenListStore.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/MultiWalletTokenListStore.kt @@ -2,10 +2,10 @@ package com.tangem.feature.wallet.presentation.wallet.domain import com.tangem.core.decompose.di.ModelScoped import com.tangem.domain.core.lce.LceFlow +import com.tangem.domain.models.tokenlist.TokenList +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.TokenList -import com.tangem.domain.models.wallet.UserWalletId import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ensureActive import kotlinx.coroutines.flow.SharingStarted diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletWithFundsChecker.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletWithFundsChecker.kt index 9f31104f3a..6561164dd0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletWithFundsChecker.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletWithFundsChecker.kt @@ -2,8 +2,8 @@ package com.tangem.feature.wallet.presentation.wallet.domain import com.tangem.common.extensions.isZero import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase -import com.tangem.domain.tokens.model.TokenList import javax.inject.Inject internal class WalletWithFundsChecker @Inject constructor( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformer.kt index 54ff2afed0..e8b3b2446f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformer.kt @@ -1,15 +1,15 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.TokenList +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.models.wallet.UserWallet +import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCardStateConverter import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TokenListStateConverter import com.tangem.feature.wallet.presentation.wallet.state.utils.enableButtons -import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import timber.log.Timber internal class SetTokenListTransformer( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt index 83cdcb49cb..ec332f336e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt @@ -8,9 +8,9 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.tokenlist.TokenList +import com.tangem.domain.models.tokenlist.TokenList.GroupedByNetwork.NetworkGroup import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.tokens.model.NetworkGroup -import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt index e98689e34e..08c415f5b9 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt @@ -6,10 +6,10 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.core.lce.Lce import com.tangem.domain.core.lce.LceFlow import com.tangem.domain.core.utils.getOrElse +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt index e9dbc7eb6f..b719923f57 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt @@ -6,11 +6,11 @@ import com.tangem.domain.core.lce.LceFlow import com.tangem.domain.models.TokensSortType import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.tokens.ApplyTokenListSortingUseCase import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletWithTokenListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletWithTokenListSubscriber.kt index e93d8962f6..80027a5ec1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletWithTokenListSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletWithTokenListSubscriber.kt @@ -3,10 +3,10 @@ package com.tangem.feature.wallet.presentation.wallet.subscribers import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.core.lce.Lce import com.tangem.domain.core.lce.LceFlow +import com.tangem.domain.models.tokenlist.TokenList +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.TokenList -import com.tangem.domain.models.wallet.UserWallet import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore From c8a181c829601ac4b4e4b60e910508ee45fd4496 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 30 Jul 2025 12:29:05 +0500 Subject: [PATCH 21/53] Updated on 2026-08-14 --- .../swap/DefaultSwapTransactionRepository.kt | 57 +++++++++---------- .../transaction/SavedSwapStatusConverter.kt | 6 +- .../SavedSwapTransactionConverter.kt | 17 +++++- .../swap/models/SwapTransactionListDTO.kt | 5 +- .../tangem/data/swap/models/SwapTxTypeDTO.kt | 13 +++++ .../swap/models/SwapTransactionListModel.kt | 1 + .../tangem/domain/swap/models/SwapTxType.kt | 6 ++ .../domain/swap/SwapTransactionRepository.kt | 6 +- .../usecase/SwapTransactionSentUseCase.kt | 24 ++++---- .../confirm/model/SwapTransactionSender.kt | 2 + .../swap/DefaultSwapTransactionRepository.kt | 32 ++++++++--- .../SavedSwapTransactionListConverter.kt | 25 ++++---- .../domain/SavedSwapTransactionListModel.kt | 14 ++++- 13 files changed, 135 insertions(+), 73 deletions(-) create mode 100644 data/swap/src/main/java/com/tangem/data/swap/models/SwapTxTypeDTO.kt create mode 100644 domain/swap/models/src/main/java/com/tangem/domain/swap/models/SwapTxType.kt diff --git a/data/swap/src/main/java/com/tangem/data/swap/DefaultSwapTransactionRepository.kt b/data/swap/src/main/java/com/tangem/data/swap/DefaultSwapTransactionRepository.kt index c2d3527060..072cf92212 100644 --- a/data/swap/src/main/java/com/tangem/data/swap/DefaultSwapTransactionRepository.kt +++ b/data/swap/src/main/java/com/tangem/data/swap/DefaultSwapTransactionRepository.kt @@ -13,7 +13,7 @@ import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.datasource.local.preferences.utils.getObjectList import com.tangem.datasource.local.preferences.utils.getObjectListSync -import com.tangem.datasource.local.preferences.utils.getObjectMapSync +import com.tangem.datasource.local.preferences.utils.getObjectMap import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId @@ -24,9 +24,8 @@ import com.tangem.domain.swap.models.SwapTransactionModel import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.extensions.addOrReplace import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.withContext internal class DefaultSwapTransactionRepository( private val appPreferencesStore: AppPreferencesStore, @@ -95,36 +94,34 @@ internal class DefaultSwapTransactionRepository( } } - override suspend fun getTransactions( + override fun getTransactions( userWallet: UserWallet, cryptoCurrencyId: CryptoCurrency.ID, - ): Flow?> { - return withContext(dispatchers.io) { - val txStatuses = appPreferencesStore.getObjectMapSync( - key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY, - ) - appPreferencesStore.getObjectList( - key = PreferencesKeys.SWAP_TRANSACTIONS_KEY, - ).map { savedTransactions -> - val currencyTxs = savedTransactions - ?.filter { - it.userWalletId == userWallet.walletId.stringValue && - ( - it.toCryptoCurrencyId == cryptoCurrencyId.value || - it.fromCryptoCurrencyId == cryptoCurrencyId.value - ) - } + ): Flow?> = combine( + flow = appPreferencesStore.getObjectList( + key = PreferencesKeys.SWAP_TRANSACTIONS_KEY, + ), + flow2 = appPreferencesStore.getObjectMap( + key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY, + ), + ) { savedTransactions, txStatuses -> + val currencyTxs = savedTransactions + ?.filter { + it.userWalletId == userWallet.walletId.stringValue && + ( + it.toCryptoCurrencyId == cryptoCurrencyId.value || + it.fromCryptoCurrencyId == cryptoCurrencyId.value + ) + } - currencyTxs?.mapNotNull { - listConverter.convertBack( - value = it, - userWallet = userWallet, - txStatuses = txStatuses, - ) - } - }.flowOn(dispatchers.io) + currencyTxs?.mapNotNull { + listConverter.convertBack( + value = it, + userWallet = userWallet, + txStatuses = txStatuses, + ) } - } + }.flowOn(dispatchers.default) override suspend fun removeTransaction( userWalletId: UserWalletId, @@ -188,7 +185,7 @@ internal class DefaultSwapTransactionRepository( ) val updatesMap = savedMap.toMutableMap() - updatesMap[txId] = savedStatusConverter.convertBack( + updatesMap[txId] = savedStatusConverter.convert( status.copy( refundTokensResponse = refundTokenCurrency?.let { userTokensResponseFactory.createResponseToken(refundTokenCurrency) diff --git a/data/swap/src/main/java/com/tangem/data/swap/converter/transaction/SavedSwapStatusConverter.kt b/data/swap/src/main/java/com/tangem/data/swap/converter/transaction/SavedSwapStatusConverter.kt index c3d8f36903..20c6016104 100644 --- a/data/swap/src/main/java/com/tangem/data/swap/converter/transaction/SavedSwapStatusConverter.kt +++ b/data/swap/src/main/java/com/tangem/data/swap/converter/transaction/SavedSwapStatusConverter.kt @@ -6,9 +6,9 @@ import com.tangem.domain.swap.models.SwapStatus import com.tangem.domain.swap.models.SwapStatusModel import com.tangem.utils.converter.TwoWayConverter -internal class SavedSwapStatusConverter : TwoWayConverter { +internal class SavedSwapStatusConverter : TwoWayConverter { - override fun convert(value: SwapStatusDTO) = SwapStatusModel( + override fun convertBack(value: SwapStatusDTO) = SwapStatusModel( providerId = value.providerId, status = SwapStatus.entries.firstOrNull { it.name.lowercase() == value.status?.name?.lowercase() @@ -22,7 +22,7 @@ internal class SavedSwapStatusConverter : TwoWayConverter?> diff --git a/domain/swap/src/main/java/com/tangem/domain/swap/usecase/SwapTransactionSentUseCase.kt b/domain/swap/src/main/java/com/tangem/domain/swap/usecase/SwapTransactionSentUseCase.kt index 5cebffc055..8d6869366a 100644 --- a/domain/swap/src/main/java/com/tangem/domain/swap/usecase/SwapTransactionSentUseCase.kt +++ b/domain/swap/src/main/java/com/tangem/domain/swap/usecase/SwapTransactionSentUseCase.kt @@ -8,10 +8,7 @@ import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.SwapErrorResolver import com.tangem.domain.swap.SwapRepositoryV2 import com.tangem.domain.swap.SwapTransactionRepository -import com.tangem.domain.swap.models.SwapDataTransactionModel -import com.tangem.domain.swap.models.SwapStatus -import com.tangem.domain.swap.models.SwapStatusModel -import com.tangem.domain.swap.models.SwapTransactionModel +import com.tangem.domain.swap.models.* @Suppress("LongParameterList") class SwapTransactionSentUseCase( @@ -28,15 +25,8 @@ class SwapTransactionSentUseCase( provider: ExpressProvider, txHash: String, timestamp: Long, + swapTxType: SwapTxType, ) = Either.catch { - swapRepositoryV2.swapTransactionSent( - userWallet = userWallet, - fromCryptoCurrencyStatus = fromCryptoCurrencyStatus, - toAddress = swapDataTransactionModel.txTo, - txId = swapDataTransactionModel.txId, - txHash = txHash, - txExtraId = swapDataTransactionModel.txExtraId, - ) if (provider.type.shouldStoreSwapTransaction()) { swapTransactionRepository.storeTransaction( userWalletId = userWallet.walletId, @@ -56,12 +46,22 @@ class SwapTransactionSentUseCase( txExternalId = (swapDataTransactionModel as? SwapDataTransactionModel.CEX)?.externalTxId, averageDuration = null, ), + swapTxType = swapTxType, ), ) } + swapTransactionRepository.storeLastSwappedCryptoCurrencyId( userWalletId = userWallet.walletId, cryptoCurrencyId = toCryptoCurrencyStatus.currency.id, ) + swapRepositoryV2.swapTransactionSent( + userWallet = userWallet, + fromCryptoCurrencyStatus = fromCryptoCurrencyStatus, + toAddress = swapDataTransactionModel.txTo, + txId = swapDataTransactionModel.txId, + txHash = txHash, + txExtraId = swapDataTransactionModel.txExtraId, + ) }.mapLeft(swapErrorResolver::resolve) } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SwapTransactionSender.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SwapTransactionSender.kt index f6c595b273..458d1ebd4e 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SwapTransactionSender.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SwapTransactionSender.kt @@ -9,6 +9,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.swap.models.SwapDataModel import com.tangem.domain.swap.models.SwapDataTransactionModel +import com.tangem.domain.swap.models.SwapTxType import com.tangem.domain.swap.usecase.GetSwapDataUseCase import com.tangem.domain.swap.usecase.SwapTransactionSentUseCase import com.tangem.domain.transaction.error.SendTransactionError @@ -158,6 +159,7 @@ internal class SwapTransactionSender @AssistedInject constructor( provider = provider, txHash = txHash, timestamp = timestamp, + swapTxType = SwapTxType.SendWithSwap, ) onSendSuccess(txHash, timestamp, swapData) }, diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt index e5c25f522c..d4fec5e8ed 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt @@ -92,21 +92,37 @@ internal class DefaultSwapTransactionRepository( key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY, ), ) { savedTransactions, txStatuses -> - val currencyTxs = savedTransactions?.filter { - it.userWalletId == userWallet.walletId.stringValue && - ( - it.toCryptoCurrencyId == cryptoCurrencyId.value || - it.fromCryptoCurrencyId == cryptoCurrencyId.value - ) + + val currencyToTxs = savedTransactions?.filter { + val isUserWallet = it.userWalletId == userWallet.walletId.stringValue + val toCurrency = it.toCryptoCurrencyId == cryptoCurrencyId.value + isUserWallet && toCurrency } - currencyTxs?.mapNotNull { + val currencyFromTxs = savedTransactions?.filter { + val isUserWallet = it.userWalletId == userWallet.walletId.stringValue + val fromCurrency = it.fromCryptoCurrencyId == cryptoCurrencyId.value + isUserWallet && fromCurrency + } + + val toTxs = currencyToTxs?.mapNotNull { + converter.convertBack( + value = it, + userWallet = userWallet, + txStatuses = txStatuses, + onFilter = { it.swapTxTypeDTO == SwapTxTypeDTO.Swap }, + ) + }.orEmpty() + + val fromTxs = currencyFromTxs?.mapNotNull { converter.convertBack( value = it, userWallet = userWallet, txStatuses = txStatuses, ) - } + }.orEmpty() + + fromTxs + toTxs } .flowOn(dispatchers.default) } diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SavedSwapTransactionListConverter.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SavedSwapTransactionListConverter.kt index 4735f63fdc..abef06ede4 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SavedSwapTransactionListConverter.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SavedSwapTransactionListConverter.kt @@ -34,6 +34,7 @@ internal class SavedSwapTransactionListConverter( value: SavedSwapTransactionListModelInner, userWallet: UserWallet, txStatuses: Map, + onFilter: (SavedSwapTransactionModel) -> Boolean = { true }, ): SavedSwapTransactionListModel? { val fromToken = value.fromTokensResponse val toToken = value.toTokensResponse @@ -50,17 +51,19 @@ internal class SavedSwapTransactionListConverter( ) ?: return null return SavedSwapTransactionListModel( - transactions = value.transactions.map { tx -> - val status = txStatuses[tx.txId] - val refundCurrency = status?.refundTokensResponse?.let { id -> - responseCryptoCurrenciesFactory.createCurrency( - responseToken = id, - userWallet = userWallet, - ) - } - val statusWithRefundCurrency = status?.copy(refundCurrency = refundCurrency) - tx.copy(status = statusWithRefundCurrency) - }, + transactions = value.transactions + .filter(onFilter) + .map { tx -> + val status = txStatuses[tx.txId] + val refundCurrency = status?.refundTokensResponse?.let { id -> + responseCryptoCurrenciesFactory.createCurrency( + responseToken = id, + userWallet = userWallet, + ) + } + val statusWithRefundCurrency = status?.copy(refundCurrency = refundCurrency) + tx.copy(status = statusWithRefundCurrency) + }, userWalletId = value.userWalletId, fromCryptoCurrencyId = value.fromCryptoCurrencyId, toCryptoCurrencyId = value.toCryptoCurrencyId, diff --git a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/SavedSwapTransactionListModel.kt b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/SavedSwapTransactionListModel.kt index 68fea21eff..94742cbac9 100644 --- a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/SavedSwapTransactionListModel.kt +++ b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/SavedSwapTransactionListModel.kt @@ -50,4 +50,16 @@ data class SavedSwapTransactionModel( val provider: SwapProvider, @Json(name = "status") val status: ExchangeStatusModel? = null, -) \ No newline at end of file + @Json(name = "swapTxType") + val swapTxTypeDTO: SwapTxTypeDTO? = SwapTxTypeDTO.Swap, +) + +// TODO refactor to use separate models to store +@JsonClass(generateAdapter = false) +enum class SwapTxTypeDTO { + @Json(name = "Swap") + Swap, + + @Json(name = "SendWithSwap") + SendWithSwap, +} \ No newline at end of file From 50c1623bfa6da0fee35f0eaeb702fddcb414ffb2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 31 Jul 2025 14:54:44 +0500 Subject: [PATCH 22/53] Updated on 2026-08-14 --- .../com/tangem/core/ui/components/appbar/TangemTopAppBar.kt | 4 ++-- .../tangem/core/ui/components/notifications/Notification.kt | 4 ++-- .../tangem/features/onramp/main/ui/OnrampProviderContent.kt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/TangemTopAppBar.kt b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/TangemTopAppBar.kt index c963651693..c936234a4b 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/TangemTopAppBar.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/TangemTopAppBar.kt @@ -129,7 +129,7 @@ fun TangemTopAppBar( TopAppBarButton( button = endButton, tint = iconTint, - modifier = modifier.testTag(TopAppBarTestTags.MORE_BUTTON), + modifier = Modifier.testTag(TopAppBarTestTags.MORE_BUTTON), ) } }, @@ -175,7 +175,7 @@ fun TangemTopAppBar( TopAppBarButton( button = startButton, tint = iconTint, - modifier = modifier.testTag(TopAppBarTestTags.CLOSE_BUTTON), + modifier = Modifier.testTag(TopAppBarTestTags.CLOSE_BUTTON), ) } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt index 1e8c2f342d..1e2279b8c2 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt @@ -208,7 +208,7 @@ internal fun TextsBlock( text = titleText, color = titleColor, style = TangemTheme.typography.button, - modifier = modifier.testTag(NotificationTestTags.TITLE), + modifier = Modifier.testTag(NotificationTestTags.TITLE), ) SpacerH(height = TangemTheme.dimens.spacing2) @@ -218,7 +218,7 @@ internal fun TextsBlock( text = subtitle.resolveReference(), color = subtitleColor, style = TangemTheme.typography.caption2, - modifier = modifier.testTag(NotificationTestTags.TEXT), + modifier = Modifier.testTag(NotificationTestTags.TEXT), ) } } diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/ui/OnrampProviderContent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/ui/OnrampProviderContent.kt index 8d2bf768e6..3693715b68 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/ui/OnrampProviderContent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/ui/OnrampProviderContent.kt @@ -62,7 +62,7 @@ private fun OnrampProviderBlock(state: OnrampProviderBlockUM.Content, modifier: }, color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.body2, - modifier = modifier.testTag(BuyTokenDetailsScreenTestTags.PROVIDER_TITLE), + modifier = Modifier.testTag(BuyTokenDetailsScreenTestTags.PROVIDER_TITLE), ) Text( text = buildAnnotatedString { @@ -72,7 +72,7 @@ private fun OnrampProviderBlock(state: OnrampProviderBlockUM.Content, modifier: }, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, - modifier = modifier.testTag(BuyTokenDetailsScreenTestTags.PROVIDER_TEXT), + modifier = Modifier.testTag(BuyTokenDetailsScreenTestTags.PROVIDER_TEXT), ) } AnimatedVisibility( From 02f6e52f46248028c662f51f12a5da92c56424b5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 1 Aug 2025 09:53:17 +0500 Subject: [PATCH 23/53] Updated on 2026-08-14 --- .../entry/AddExistingWalletModel.kt | 15 +- .../routing/AddExistingWalletChildFactory.kt | 3 + .../entry/routing/AddExistingWalletRoute.kt | 7 +- .../port/AddExistingWalletImportComponent.kt | 3 +- .../model/AddExistingWalletImportModel.kt | 30 +++- .../port/ui/AddExistingWalletImportContent.kt | 5 +- .../ManualBackupCompletedComponent.kt | 4 +- .../completed/ManualBackupCompletedModel.kt | 2 +- .../setaccesscode/AccessCodeComponent.kt | 26 +--- .../setaccesscode/AccessCodeModel.kt | 51 ++++++- .../setaccesscode/entity/AccessCodeUM.kt | 2 + .../hotwallet/setaccesscode/ui/AccessCode.kt | 136 ++++++++++++++++++ .../setaccesscode/ui/AccessCodeEnter.kt | 106 -------------- .../setaccesscode/ui/AccessCodeLayout.kt | 51 ------- 14 files changed, 244 insertions(+), 197 deletions(-) create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCode.kt delete mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCodeEnter.kt delete mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCodeLayout.kt diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt index 1d192ffae5..b7eb62a719 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt @@ -9,6 +9,7 @@ import com.tangem.common.routing.AppRoute import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.navigation.Router +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.settings.ShouldAskPermissionUseCase import com.tangem.features.hotwallet.addexistingwallet.im.port.AddExistingWalletImportComponent import com.tangem.features.hotwallet.addexistingwallet.entry.routing.AddExistingWalletRoute @@ -78,23 +79,23 @@ internal class AddExistingWalletModel @Inject constructor( } inner class AddExistingWalletImportModelCallbacks : AddExistingWalletImportComponent.ModelCallbacks { - override fun onWalletImported() { - stackNavigation.replaceCurrent(AddExistingWalletRoute.BackupCompleted) + override fun onWalletImported(userWalletId: UserWalletId) { + stackNavigation.replaceCurrent(AddExistingWalletRoute.BackupCompleted(userWalletId)) } } inner class ManualBackupCompletedComponentModelCallbacks : ManualBackupCompletedComponent.ModelCallbacks { - override fun onContinueClick() { - stackNavigation.replaceCurrent(AddExistingWalletRoute.SetAccessCode) + override fun onContinueClick(userWalletId: UserWalletId) { + stackNavigation.replaceCurrent(AddExistingWalletRoute.SetAccessCode(userWalletId)) } } inner class AccessCodeModelCallbacks : AccessCodeComponent.ModelCallbacks { - override fun onAccessCodeSet(accessCode: String) { - stackNavigation.push(AddExistingWalletRoute.ConfirmAccessCode(accessCode)) + override fun onAccessCodeSet(userWalletId: UserWalletId, accessCode: String) { + stackNavigation.push(AddExistingWalletRoute.ConfirmAccessCode(userWalletId, accessCode)) } - override fun onAccessCodeConfirmed() { + override fun onAccessCodeConfirmed(userWalletId: UserWalletId) { navigateToPushNotificationsOrNext() } } diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt index 90ffa797d0..2b68c1b132 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt @@ -37,6 +37,7 @@ internal class AddExistingWalletChildFactory @Inject constructor( is AddExistingWalletRoute.BackupCompleted -> ManualBackupCompletedComponent( context = childContext, params = ManualBackupCompletedComponent.Params( + userWalletId = route.userWalletId, callbacks = model.manualBackupCompletedComponentModelCallbacks, ), ) @@ -44,6 +45,7 @@ internal class AddExistingWalletChildFactory @Inject constructor( context = childContext, params = AccessCodeComponent.Params( isConfirmMode = false, + userWalletId = route.userWalletId, callbacks = model.accessCodeModelCallbacks, ), ) @@ -52,6 +54,7 @@ internal class AddExistingWalletChildFactory @Inject constructor( params = AccessCodeComponent.Params( isConfirmMode = true, accessCodeToConfirm = route.accessCode, + userWalletId = route.userWalletId, callbacks = model.accessCodeModelCallbacks, ), ) diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletRoute.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletRoute.kt index f1102b80eb..c5f3009aac 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletRoute.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletRoute.kt @@ -1,6 +1,7 @@ package com.tangem.features.hotwallet.addexistingwallet.entry.routing import com.tangem.core.decompose.navigation.Route +import com.tangem.domain.models.wallet.UserWalletId import kotlinx.serialization.Serializable internal sealed class AddExistingWalletRoute : Route { @@ -12,13 +13,13 @@ internal sealed class AddExistingWalletRoute : Route { object Import : AddExistingWalletRoute() @Serializable - object BackupCompleted : AddExistingWalletRoute() + data class BackupCompleted(val userWalletId: UserWalletId) : AddExistingWalletRoute() @Serializable - object SetAccessCode : AddExistingWalletRoute() + data class SetAccessCode(val userWalletId: UserWalletId) : AddExistingWalletRoute() @Serializable - data class ConfirmAccessCode(val accessCode: String) : AddExistingWalletRoute() + data class ConfirmAccessCode(val userWalletId: UserWalletId, val accessCode: String) : AddExistingWalletRoute() @Serializable object PushNotifications : AddExistingWalletRoute() diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/AddExistingWalletImportComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/AddExistingWalletImportComponent.kt index afd76f5987..096bd395a0 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/AddExistingWalletImportComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/AddExistingWalletImportComponent.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.features.hotwallet.addexistingwallet.im.port.model.AddExistingWalletImportModel import com.tangem.features.hotwallet.addexistingwallet.im.port.ui.AddExistingWalletImportContent import dagger.assisted.Assisted @@ -28,7 +29,7 @@ internal class AddExistingWalletImportComponent @AssistedInject constructor( } interface ModelCallbacks { - fun onWalletImported() + fun onWalletImported(userWalletId: UserWalletId) } data class Params( diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/model/AddExistingWalletImportModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/model/AddExistingWalletImportModel.kt index 2f70fa2273..f7664bfa55 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/model/AddExistingWalletImportModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/model/AddExistingWalletImportModel.kt @@ -4,13 +4,19 @@ import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.crypto.bip39.Mnemonic +import com.tangem.domain.wallets.builder.HotUserWalletBuilder +import com.tangem.domain.wallets.usecase.SaveWalletUseCase import com.tangem.features.hotwallet.MnemonicRepository import com.tangem.features.hotwallet.addexistingwallet.im.port.AddExistingWalletImportComponent import com.tangem.features.hotwallet.addexistingwallet.im.port.entity.AddExistingWalletImportUM +import com.tangem.hot.sdk.TangemHotSdk +import com.tangem.hot.sdk.model.HotAuth import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import timber.log.Timber import javax.inject.Inject @ModelScoped @@ -18,6 +24,9 @@ internal class AddExistingWalletImportModel @Inject constructor( paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, private val mnemonicRepository: MnemonicRepository, + private val tangemHotSdk: TangemHotSdk, + private val hotUserWalletBuilderFactory: HotUserWalletBuilder.Factory, + private val saveUserWalletUseCase: SaveWalletUseCase, ) : Model() { private val params: AddExistingWalletImportComponent.Params = paramsContainer.require() @@ -44,7 +53,24 @@ internal class AddExistingWalletImportModel @Inject constructor( @Suppress("UnusedPrivateMember") private fun importWallet(mnemonic: Mnemonic, passphrase: String?) { - // TODO implement importing seed phrase - params.callbacks.onWalletImported() + modelScope.launch { + uiState.update { + it.copy(createWalletProgress = true) + } + + runCatching { + val hotWalletId = tangemHotSdk.importWallet(mnemonic, passphrase?.toCharArray(), HotAuth.NoAuth) + val hotUserWalletBuilder = hotUserWalletBuilderFactory.create(hotWalletId) + val userWallet = hotUserWalletBuilder.build() + saveUserWalletUseCase(userWallet) + params.callbacks.onWalletImported(userWallet.walletId) + }.onFailure { + Timber.e(it) + + uiState.update { + it.copy(createWalletProgress = false) + } + } + } } } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/ui/AddExistingWalletImportContent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/ui/AddExistingWalletImportContent.kt index 22cb0309c7..33e7872bb8 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/ui/AddExistingWalletImportContent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/ui/AddExistingWalletImportContent.kt @@ -35,7 +35,7 @@ import com.tangem.core.ui.R import com.tangem.core.ui.components.Keyboard import com.tangem.core.ui.components.Notifier import com.tangem.core.ui.components.OutlineTextFieldWithIcon -import com.tangem.core.ui.components.PrimaryButtonIconEnd +import com.tangem.core.ui.components.PrimaryButton import com.tangem.core.ui.components.TangemTextFieldsDefault import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.keyboardAsState @@ -91,12 +91,11 @@ internal fun AddExistingWalletImportContent(state: AddExistingWalletImportUM, mo ) } - PrimaryButtonIconEnd( + PrimaryButton( modifier = Modifier .padding(16.dp) .fillMaxWidth(), text = stringResourceSafe(id = R.string.common_import), - iconResId = R.drawable.ic_tangem_24, enabled = state.createWalletEnabled, showProgress = state.createWalletProgress, onClick = state.createWalletClick, diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/completed/ManualBackupCompletedComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/completed/ManualBackupCompletedComponent.kt index 43f8c076d3..bd40e6bb1a 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/completed/ManualBackupCompletedComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/completed/ManualBackupCompletedComponent.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.features.hotwallet.manualbackup.completed.ui.ManualBackupCompletedContent import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -27,10 +28,11 @@ internal class ManualBackupCompletedComponent @AssistedInject constructor( } interface ModelCallbacks { - fun onContinueClick() + fun onContinueClick(userWalletId: UserWalletId) } data class Params( + val userWalletId: UserWalletId, val callbacks: ModelCallbacks, ) } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/completed/ManualBackupCompletedModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/completed/ManualBackupCompletedModel.kt index 0646d7646b..d0fcfe7333 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/completed/ManualBackupCompletedModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/completed/ManualBackupCompletedModel.kt @@ -20,7 +20,7 @@ internal class ManualBackupCompletedModel @Inject constructor( internal val uiState: StateFlow field = MutableStateFlow( ManualBackupCompletedUM( - onContinueClick = params.callbacks::onContinueClick, + onContinueClick = { params.callbacks.onContinueClick(params.userWalletId) }, ), ) } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/AccessCodeComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/AccessCodeComponent.kt index 4200b68d42..6cd119cdfb 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/AccessCodeComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/AccessCodeComponent.kt @@ -8,9 +8,8 @@ import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect -import com.tangem.core.ui.extensions.stringResourceSafe -import com.tangem.features.hotwallet.setaccesscode.ui.AccessCodeLayout -import com.tangem.core.res.R +import com.tangem.features.hotwallet.setaccesscode.ui.AccessCode +import com.tangem.domain.models.wallet.UserWalletId import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -28,32 +27,21 @@ internal class AccessCodeComponent @AssistedInject constructor( DisableScreenshotsDisposableEffect() - AccessCodeLayout( + AccessCode( modifier = modifier, - accessCode = state.accessCode, - onAccessCodeChange = state.onAccessCodeChange, - accessCodeLength = state.accessCodeLength, - reEnterAccessCodeState = params.isConfirmMode, - buttonText = stringResourceSafe( - if (params.isConfirmMode) { - R.string.common_confirm - } else { - R.string.common_continue - }, - ), - onButtonClick = state.onButtonClick, - buttonEnabled = state.buttonEnabled, + state = state, ) } interface ModelCallbacks { - fun onAccessCodeSet(accessCode: String) - fun onAccessCodeConfirmed() + fun onAccessCodeSet(userWalletId: UserWalletId, accessCode: String) + fun onAccessCodeConfirmed(userWalletId: UserWalletId) } data class Params( val isConfirmMode: Boolean, val accessCodeToConfirm: String? = null, + val userWalletId: UserWalletId, val callbacks: ModelCallbacks, ) diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/AccessCodeModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/AccessCodeModel.kt index d978ba32f6..75210c3f64 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/AccessCodeModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/AccessCodeModel.kt @@ -1,14 +1,24 @@ package com.tangem.features.hotwallet.setaccesscode import androidx.compose.runtime.Stable +import arrow.core.getOrElse import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.wallets.usecase.GetUserWalletUseCase +import com.tangem.domain.wallets.usecase.SaveWalletUseCase import com.tangem.features.hotwallet.setaccesscode.entity.AccessCodeUM +import com.tangem.hot.sdk.TangemHotSdk +import com.tangem.hot.sdk.model.HotAuth +import com.tangem.hot.sdk.model.UnlockHotWallet import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import timber.log.Timber import javax.inject.Inject @Stable @@ -16,6 +26,9 @@ import javax.inject.Inject internal class AccessCodeModel @Inject constructor( paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, + private val getUserWalletUseCase: GetUserWalletUseCase, + private val saveWalletUseCase: SaveWalletUseCase, + private val tangemHotSdk: TangemHotSdk, ) : Model() { private val params = paramsContainer.require() @@ -26,7 +39,9 @@ internal class AccessCodeModel @Inject constructor( private fun getInitialState() = AccessCodeUM( accessCode = "", onAccessCodeChange = ::onAccessCodeChange, + isConfirmMode = params.isConfirmMode, buttonEnabled = false, + buttonInProgress = false, onButtonClick = ::onButtonClick, ) @@ -44,10 +59,40 @@ internal class AccessCodeModel @Inject constructor( } private fun onButtonClick() { - if (params.isConfirmMode) { - params.callbacks.onAccessCodeConfirmed() + if (!params.isConfirmMode) { + params.callbacks.onAccessCodeSet(params.userWalletId, uiState.value.accessCode) } else { - params.callbacks.onAccessCodeSet(uiState.value.accessCode) + params.accessCodeToConfirm?.let { + setCode(params.userWalletId, it) + } + } + } + + private fun setCode(userWalletId: UserWalletId, accessCode: String) { + modelScope.launch { + uiState.update { + it.copy(buttonInProgress = true) + } + + runCatching { + val userWallet = getUserWalletUseCase(userWalletId) + .getOrElse { error("User wallet with id $userWalletId not found") } + if (userWallet is UserWallet.Hot) { + val unlockHotWallet = UnlockHotWallet(userWallet.hotWalletId, HotAuth.NoAuth) + val updatedHotWalletId = tangemHotSdk.changeAuth( + unlockHotWallet = unlockHotWallet, + auth = HotAuth.Password(accessCode.toCharArray()), + ) + saveWalletUseCase(userWallet.copy(hotWalletId = updatedHotWalletId)) + params.callbacks.onAccessCodeConfirmed(params.userWalletId) + } + }.onFailure { + Timber.e(it) + + uiState.update { + it.copy(buttonInProgress = false) + } + } } } } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/entity/AccessCodeUM.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/entity/AccessCodeUM.kt index 836a816175..9141f7f4c5 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/entity/AccessCodeUM.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/entity/AccessCodeUM.kt @@ -5,7 +5,9 @@ import com.tangem.features.hotwallet.setaccesscode.ACCESS_CODE_LENGTH internal data class AccessCodeUM( val accessCode: String, val onAccessCodeChange: (String) -> Unit, + val isConfirmMode: Boolean, val buttonEnabled: Boolean, + val buttonInProgress: Boolean, val onButtonClick: () -> Unit, ) { val accessCodeLength: Int = ACCESS_CODE_LENGTH diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCode.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCode.kt new file mode 100644 index 0000000000..923b475395 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCode.kt @@ -0,0 +1,136 @@ +package com.tangem.features.hotwallet.setaccesscode.ui + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.tangem.core.res.R +import com.tangem.core.ui.components.PrimaryButton +import com.tangem.core.ui.components.fields.PinTextField +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.features.hotwallet.setaccesscode.entity.AccessCodeUM + +@Suppress("LongParameterList", "LongMethod") +@Composable +internal fun AccessCode(state: AccessCodeUM, modifier: Modifier = Modifier) { + Column( + modifier = modifier + .fillMaxSize() + .navigationBarsPadding(), + ) { + Column( + modifier = Modifier + .padding(top = 16.dp) + .weight(1f) + .fillMaxSize() + .background(TangemTheme.colors.background.primary) + .padding(horizontal = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + modifier = Modifier + .padding(top = 56.dp) + .align(Alignment.CenterHorizontally), + text = if (state.isConfirmMode) { + stringResourceSafe(R.string.access_code_confirm_title) + } else { + stringResourceSafe(R.string.access_code_create_title) + }, + style = TangemTheme.typography.h2, + color = TangemTheme.colors.text.primary1, + textAlign = TextAlign.Center, + ) + Text( + modifier = Modifier + .padding(16.dp) + .align(Alignment.CenterHorizontally), + text = if (state.isConfirmMode) { + stringResourceSafe(R.string.access_code_confirm_description) + } else { + stringResourceSafe( + R.string.access_code_create_description, + state.accessCodeLength, + ) + }, + style = TangemTheme.typography.body1, + color = TangemTheme.colors.text.secondary, + textAlign = TextAlign.Center, + ) + + Box( + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp), + contentAlignment = Alignment.Center, + ) { + PinTextField( + length = state.accessCodeLength, + isPasswordVisual = true, + value = state.accessCode, + onValueChange = state.onAccessCodeChange, + ) + } + } + + PrimaryButton( + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 16.dp, bottom = 16.dp) + .imePadding(), + text = stringResourceSafe( + if (state.isConfirmMode) { + R.string.common_confirm + } else { + R.string.common_continue + }, + ), + onClick = state.onButtonClick, + enabled = state.buttonEnabled, + showProgress = state.buttonInProgress, + ) + } +} + +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun PreviewSet() { + TangemThemePreview { + AccessCode( + state = AccessCodeUM( + accessCode = "", + onAccessCodeChange = {}, + isConfirmMode = false, + buttonEnabled = false, + buttonInProgress = false, + onButtonClick = {}, + ), + ) + } +} + +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun PreviewConfirm() { + TangemThemePreview { + AccessCode( + state = AccessCodeUM( + accessCode = "123456", + onAccessCodeChange = {}, + isConfirmMode = true, + buttonEnabled = true, + buttonInProgress = false, + onButtonClick = {}, + ), + ) + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCodeEnter.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCodeEnter.kt deleted file mode 100644 index 62e4388d9a..0000000000 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCodeEnter.kt +++ /dev/null @@ -1,106 +0,0 @@ -package com.tangem.features.hotwallet.setaccesscode.ui - -import android.content.res.Configuration -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.tangem.core.res.R -import com.tangem.core.ui.components.fields.PinTextField -import com.tangem.core.ui.extensions.stringResourceSafe -import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemThemePreview - -@Composable -internal fun AccessCodeEnter( - accessCode: String, - onAccessCodeChange: (String) -> Unit, - accessCodeLength: Int, - reEnterAccessCodeState: Boolean, - modifier: Modifier = Modifier, -) { - Column( - modifier = modifier - .fillMaxSize() - .background(TangemTheme.colors.background.primary) - .padding(horizontal = 16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text( - modifier = Modifier - .padding(top = 56.dp) - .align(Alignment.CenterHorizontally), - text = if (reEnterAccessCodeState) { - stringResourceSafe(R.string.access_code_confirm_title) - } else { - stringResourceSafe(R.string.access_code_create_title) - }, - style = TangemTheme.typography.h2, - color = TangemTheme.colors.text.primary1, - textAlign = TextAlign.Center, - ) - Text( - modifier = Modifier - .padding(16.dp) - .align(Alignment.CenterHorizontally), - text = if (reEnterAccessCodeState) { - stringResourceSafe(R.string.access_code_confirm_description) - } else { - stringResourceSafe( - R.string.access_code_create_description, - accessCodeLength, - ) - }, - style = TangemTheme.typography.body1, - color = TangemTheme.colors.text.secondary, - textAlign = TextAlign.Center, - ) - - Box( - modifier = Modifier - .fillMaxWidth() - .padding(top = 8.dp), - contentAlignment = Alignment.Center, - ) { - PinTextField( - length = accessCodeLength, - isPasswordVisual = true, - value = accessCode, - onValueChange = onAccessCodeChange, - ) - } - } -} - -@Preview(showBackground = true, widthDp = 360) -@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) -@Composable -private fun Preview() { - TangemThemePreview { - AccessCodeEnter( - accessCode = "123456", - onAccessCodeChange = {}, - accessCodeLength = 6, - reEnterAccessCodeState = false, - ) - } -} - -@Preview(showBackground = true, widthDp = 360) -@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) -@Composable -private fun Preview2() { - TangemThemePreview { - AccessCodeEnter( - accessCode = "123456", - onAccessCodeChange = {}, - accessCodeLength = 6, - reEnterAccessCodeState = true, - ) - } -} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCodeLayout.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCodeLayout.kt deleted file mode 100644 index aee581c976..0000000000 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setaccesscode/ui/AccessCodeLayout.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.tangem.features.hotwallet.setaccesscode.ui - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.imePadding -import androidx.compose.foundation.layout.navigationBarsPadding -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.tangem.core.ui.components.PrimaryButton - -@Suppress("LongParameterList") -@Composable -internal fun AccessCodeLayout( - accessCode: String, - onAccessCodeChange: (String) -> Unit, - accessCodeLength: Int, - reEnterAccessCodeState: Boolean, - buttonText: String, - onButtonClick: () -> Unit, - buttonEnabled: Boolean, - modifier: Modifier = Modifier, -) { - Column( - modifier = modifier - .fillMaxSize() - .navigationBarsPadding(), - ) { - AccessCodeEnter( - modifier = Modifier - .padding(top = 16.dp) - .weight(1f), - accessCode = accessCode, - onAccessCodeChange = onAccessCodeChange, - accessCodeLength = accessCodeLength, - reEnterAccessCodeState = reEnterAccessCodeState, - ) - - PrimaryButton( - modifier = Modifier - .fillMaxWidth() - .padding(start = 16.dp, end = 16.dp, bottom = 16.dp) - .imePadding(), - text = buttonText, - onClick = onButtonClick, - enabled = buttonEnabled, - ) - } -} \ No newline at end of file From d0f1e7c6c45defc554fedd3f6d380b7a47df546e Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 1 Aug 2025 14:12:12 +0300 Subject: [PATCH 24/53] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/MainActivity.kt | 12 +++- .../features/tester/api/KeyEventObserver.kt | 13 ++++ .../features/tester/api/TesterMenuLauncher.kt | 9 +-- .../tester/di/TesterMenuLauncherModule.kt | 10 +-- .../tester/presentation/TesterActivity.kt | 2 + .../presentation/menu/ui/TesterMenuScreen.kt | 4 +- .../navigation/DefaultTesterMenuLauncher.kt | 46 ++----------- .../navigation/ShakeEventListener.kt | 51 --------------- .../VolumeButtonDoublePressObserver.kt | 64 +++++++++++++++++++ 9 files changed, 105 insertions(+), 106 deletions(-) create mode 100644 features/tester/api/src/main/java/com/tangem/features/tester/api/KeyEventObserver.kt delete mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/ShakeEventListener.kt create mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/VolumeButtonDoublePressObserver.kt diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 7be16eab0a..526a308532 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -6,6 +6,7 @@ import android.content.pm.ActivityInfo import android.content.res.Configuration import android.os.Build import android.os.Bundle +import android.view.KeyEvent import android.view.MotionEvent import android.view.WindowManager import androidx.activity.SystemBarStyle @@ -241,7 +242,7 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { lifecycle.addObserver(defaultDeviceFlipDetector) if (BuildConfig.TESTER_MENU_ENABLED) { - lifecycle.addObserver(testerMenuLauncher.launchOnShakeObserver) + lifecycle.addObserver(testerMenuLauncher.launchOnKeyEventObserver) } } @@ -417,10 +418,17 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { override fun dispatchTouchEvent(event: MotionEvent): Boolean { val result = WindowObscurationObserver.dispatchTouchEvent(event, analyticsEventsHandler) - return if (result) super.dispatchTouchEvent(event) else false } + override fun dispatchKeyEvent(event: KeyEvent): Boolean { + return if (BuildConfig.TESTER_MENU_ENABLED) { + testerMenuLauncher.launchOnKeyEventObserver.dispatchKeyEvent(event) || super.dispatchKeyEvent(event) + } else { + super.dispatchKeyEvent(event) + } + } + private fun navigateToInitialScreenIfNeeded(intentWhichStartedActivity: Intent?) { val backStack = appRouterConfig.stack ?: emptyList() // TODO move inital navigation to navigation component ([REDACTED_JIRA]) diff --git a/features/tester/api/src/main/java/com/tangem/features/tester/api/KeyEventObserver.kt b/features/tester/api/src/main/java/com/tangem/features/tester/api/KeyEventObserver.kt new file mode 100644 index 0000000000..d431c7cd85 --- /dev/null +++ b/features/tester/api/src/main/java/com/tangem/features/tester/api/KeyEventObserver.kt @@ -0,0 +1,13 @@ +package com.tangem.features.tester.api + +import android.view.KeyEvent +import androidx.lifecycle.DefaultLifecycleObserver + +/** + * Interface for observing to key events in a lifecycle-aware manner. + * Implementations should handle key events and return true if the event was consumed. + */ +interface KeyEventObserver : DefaultLifecycleObserver { + + fun dispatchKeyEvent(event: KeyEvent): Boolean +} \ No newline at end of file diff --git a/features/tester/api/src/main/java/com/tangem/features/tester/api/TesterMenuLauncher.kt b/features/tester/api/src/main/java/com/tangem/features/tester/api/TesterMenuLauncher.kt index a6338eaa73..b16f8b366d 100644 --- a/features/tester/api/src/main/java/com/tangem/features/tester/api/TesterMenuLauncher.kt +++ b/features/tester/api/src/main/java/com/tangem/features/tester/api/TesterMenuLauncher.kt @@ -1,7 +1,5 @@ package com.tangem.features.tester.api -import androidx.lifecycle.DefaultLifecycleObserver - /** * Interface for launching the tester menu * @@ -9,6 +7,9 @@ import androidx.lifecycle.DefaultLifecycleObserver */ interface TesterMenuLauncher { - /** Observer for detecting shake events and launching the tester menu */ - val launchOnShakeObserver: DefaultLifecycleObserver + /** + * Observer for key events to open the tester menu. + * Implementations should handle key events and return true if the event was consumed. + */ + val launchOnKeyEventObserver: KeyEventObserver } \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/di/TesterMenuLauncherModule.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/di/TesterMenuLauncherModule.kt index 859469177f..48e8ef8bfd 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/di/TesterMenuLauncherModule.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/di/TesterMenuLauncherModule.kt @@ -6,15 +6,15 @@ import com.tangem.features.tester.api.TesterMenuLauncher import dagger.Module import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent +import dagger.hilt.android.components.ActivityComponent +import dagger.hilt.android.qualifiers.ActivityContext @Module -@InstallIn(SingletonComponent::class) +@InstallIn(ActivityComponent::class) internal object TesterMenuLauncherModule { @Provides - fun provideTesterMenuLauncher(@ApplicationContext context: Context): TesterMenuLauncher { - return DefaultTesterMenuLauncher(context = context) + fun provideTesterMenuLauncher(@ActivityContext context: Context): TesterMenuLauncher { + return DefaultTesterMenuLauncher(context) } } \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/TesterActivity.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/TesterActivity.kt index 3c28e87b46..8cd7fd94f1 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/TesterActivity.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/TesterActivity.kt @@ -1,5 +1,6 @@ package com.tangem.feature.tester.presentation +import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -92,6 +93,7 @@ internal class TesterActivity : ComposeActivity() { innerTesterRouter.open(route) }, ), + modifier = Modifier.systemBarsPadding(), ) } diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/ui/TesterMenuScreen.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/ui/TesterMenuScreen.kt index da2e67b08d..b369752bd1 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/ui/TesterMenuScreen.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/menu/ui/TesterMenuScreen.kt @@ -31,11 +31,11 @@ import kotlinx.collections.immutable.toImmutableList */ @OptIn(ExperimentalFoundationApi::class) @Composable -internal fun TesterMenuScreen(state: TesterMenuUM) { +internal fun TesterMenuScreen(state: TesterMenuUM, modifier: Modifier = Modifier) { BackHandler(onBack = state.onBackClick) LazyColumn( - modifier = Modifier + modifier = modifier .fillMaxSize() .background(TangemTheme.colors.background.primary), ) { diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/DefaultTesterMenuLauncher.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/DefaultTesterMenuLauncher.kt index fbd8a860ff..1c1fc90ee1 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/DefaultTesterMenuLauncher.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/DefaultTesterMenuLauncher.kt @@ -1,53 +1,15 @@ package com.tangem.feature.tester.presentation.navigation import android.content.Context -import android.content.Intent -import android.hardware.Sensor -import android.hardware.SensorManager -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.LifecycleOwner -import com.tangem.feature.tester.presentation.TesterActivity import com.tangem.features.tester.api.TesterMenuLauncher /** - * Default implementation of [TesterMenuLauncher] that listens for shake events using the device's accelerometer. - * When a shake is detected, it opens the tester menu. + * Default implementation of [TesterMenuLauncher] that listens for double-press events + * of the volume down button. When a double press is detected, it opens the tester menu. * - * @param context the application context used to access system services - * -[REDACTED_AUTHOR] + * @param context the application context used to launch the tester menu */ internal class DefaultTesterMenuLauncher(private val context: Context) : TesterMenuLauncher { - override val launchOnShakeObserver: DefaultLifecycleObserver by lazy(LazyThreadSafetyMode.NONE) { - createObserver(context) - } - - private fun createObserver(context: Context): DefaultLifecycleObserver { - return object : DefaultLifecycleObserver { - private val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager - private val accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) - private val shakeEventListener = ShakeEventListener(action = ::openTesterMenu) - - override fun onResume(owner: LifecycleOwner) { - accelerometer?.let { - sensorManager.registerListener( - /* listener = */ shakeEventListener, - /* sensor = */ it, - /* samplingPeriodUs = */ SensorManager.SENSOR_DELAY_NORMAL, - ) - } - } - - override fun onPause(owner: LifecycleOwner) { - sensorManager.unregisterListener(shakeEventListener) - } - } - } - - private fun openTesterMenu() { - val intent = Intent(context, TesterActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - - context.startActivity(intent) - } + override val launchOnKeyEventObserver = VolumeButtonDoublePressObserver(context) } \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/ShakeEventListener.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/ShakeEventListener.kt deleted file mode 100644 index 133628c9ac..0000000000 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/ShakeEventListener.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.tangem.feature.tester.presentation.navigation - -import android.hardware.Sensor -import android.hardware.SensorEvent -import android.hardware.SensorEventListener -import android.hardware.SensorManager -import kotlin.math.sqrt - -/** - * Listener for device shake events. - * - * This class implements [SensorEventListener] and is used to detect device shaking based on accelerometer data. - * When a shake is detected, the provided action is invoked. - * - * @property action lambda function to be called when a shake is detected - * -[REDACTED_AUTHOR] - */ -internal class ShakeEventListener(private val action: () -> Unit) : SensorEventListener { - - private var lastShakeTime = 0L - - override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) = Unit - - override fun onSensorChanged(event: SensorEvent?) { - if (event?.sensor?.type != Sensor.TYPE_ACCELEROMETER) return - - val acceleration = calculateAcceleration(event = event) - - val currentTime = System.currentTimeMillis() - val currentShakeInterval = currentTime - lastShakeTime - - if (acceleration > SHAKE_THRESHOLD && currentShakeInterval > SHAKE_INTERVAL_MS) { - lastShakeTime = currentTime - action() - } - } - - private fun calculateAcceleration(event: SensorEvent): Float { - val (x, y, z) = event.toXYZ() - - return sqrt(x * x + y * y + z * z) - SensorManager.GRAVITY_EARTH - } - - private fun SensorEvent.toXYZ() = Triple(values[0], values[1], values[2]) - - private companion object { - private const val SHAKE_THRESHOLD: Float = 12f - private const val SHAKE_INTERVAL_MS: Long = 1000 - } -} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/VolumeButtonDoublePressObserver.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/VolumeButtonDoublePressObserver.kt new file mode 100644 index 0000000000..c683121653 --- /dev/null +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/VolumeButtonDoublePressObserver.kt @@ -0,0 +1,64 @@ + +package com.tangem.feature.tester.presentation.navigation + +import android.content.Context +import android.content.Intent +import android.os.SystemClock +import android.view.KeyEvent +import androidx.lifecycle.LifecycleOwner +import com.tangem.feature.tester.presentation.TesterActivity +import com.tangem.features.tester.api.KeyEventObserver + +/** + * A key event observer that listens for volume down button presses to open the tester menu. + * It requires two consecutive volume down presses within a specified interval to trigger the menu. + */ +internal class VolumeButtonDoublePressObserver(private val context: Context) : KeyEventObserver { + + private var lastVolumeDownTime = 0L + private var volumeDownCount = 0 + private var isReady = false + + override fun onResume(owner: LifecycleOwner) { + isReady = true + } + + override fun onPause(owner: LifecycleOwner) { + isReady = false + } + + /** + * Returns true if the tester menu was opened. + */ + override fun dispatchKeyEvent(event: KeyEvent): Boolean { + if (!isReady) return false + + if (event.action == KeyEvent.ACTION_DOWN && event.keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { + val now = SystemClock.elapsedRealtime() + volumeDownCount = if (now - lastVolumeDownTime <= DOUBLE_PRESS_INTERVAL_MS) { + volumeDownCount + 1 + } else { + 1 + } + lastVolumeDownTime = now + + if (volumeDownCount == REQUIRED_PRESS_COUNT) { + volumeDownCount = 0 + openTesterMenu(context) + return true + } + } + + return false + } + + private fun openTesterMenu(context: Context) { + val intent = Intent(context, TesterActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(intent) + } + + companion object { + private const val DOUBLE_PRESS_INTERVAL_MS = 300L + private const val REQUIRED_PRESS_COUNT = 2 + } +} \ No newline at end of file From 71003c43dbe6e504ec347b580c6bbb0abd45a213 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 1 Aug 2025 16:49:44 +0300 Subject: [PATCH 25/53] Updated on 2026-08-14 --- .../common/extensions/BaseTestCaseExt.kt | 15 +++ .../tangem/screens/DisclaimerPageObject.kt | 12 ++ .../kotlin/com/tangem/tests/StoriesTest.kt | 11 +- .../com/tangem/tests/TermsOfServiceTest.kt | 106 ++++++++++++++++++ .../core/ui/test/DisclaimerScreenTestTags.kt | 1 + .../disclaimer/impl/ui/DisclaimerScreen.kt | 8 +- 6 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 app/src/androidTest/kotlin/com/tangem/common/extensions/BaseTestCaseExt.kt create mode 100644 app/src/androidTest/kotlin/com/tangem/tests/TermsOfServiceTest.kt diff --git a/app/src/androidTest/kotlin/com/tangem/common/extensions/BaseTestCaseExt.kt b/app/src/androidTest/kotlin/com/tangem/common/extensions/BaseTestCaseExt.kt new file mode 100644 index 0000000000..bff5addff5 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/common/extensions/BaseTestCaseExt.kt @@ -0,0 +1,15 @@ +package com.tangem.common.extensions + +import com.tangem.common.BaseTestCase + +fun BaseTestCase.swipeToCloseApp() { + + device.uiDevice.swipe( + device.uiDevice.displayWidth / 2, + device.uiDevice.displayHeight / 2, + device.uiDevice.displayWidth / 2, + device.uiDevice.displayHeight / 30, + 15 + ) + +} \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/DisclaimerPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/DisclaimerPageObject.kt index fa12033630..9e43faf303 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/DisclaimerPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/DisclaimerPageObject.kt @@ -3,9 +3,12 @@ package com.tangem.screens import androidx.compose.ui.test.SemanticsNodeInteractionsProvider import com.tangem.common.BaseTestCase import com.tangem.core.ui.test.DisclaimerScreenTestTags +import com.tangem.core.ui.test.TopAppBarTestTags +import com.tangem.features.disclaimer.impl.R import io.github.kakaocup.compose.node.element.ComposeScreen import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen import io.github.kakaocup.compose.node.element.KNode +import io.github.kakaocup.kakao.common.utilities.getResourceString class DisclaimerPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : ComposeScreen( @@ -13,6 +16,15 @@ class DisclaimerPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) viewBuilderAction = { hasTestTag(DisclaimerScreenTestTags.SCREEN_CONTAINER) } ) { + val title: KNode = child { + hasTestTag(TopAppBarTestTags.TITLE) + hasText(getResourceString(R.string.disclaimer_title)) + } + + val webView: KNode = child { + hasTestTag(DisclaimerScreenTestTags.WEB_VIEW) + } + val acceptButton: KNode = child { hasTestTag(DisclaimerScreenTestTags.ACCEPT_BUTTON) } diff --git a/app/src/androidTest/kotlin/com/tangem/tests/StoriesTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/StoriesTest.kt index 8fe8ee6203..63a4706cb0 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/StoriesTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/StoriesTest.kt @@ -3,18 +3,19 @@ package com.tangem.tests import android.content.Intent.ACTION_VIEW import com.tangem.common.BaseTestCase import com.tangem.common.extensions.clickWithAssertion -import com.tangem.screens.DisclaimerTestScreen -import com.tangem.screens.StoriesTestScreen -import com.tangem.tap.features.home.redux.HomeMiddleware.NEW_BUY_WALLET_URL +import com.tangem.screens.onDisclaimerScreen +import com.tangem.screens.onStoriesScreen import dagger.hilt.android.testing.HiltAndroidTest import io.github.kakaocup.kakao.intent.KIntent +import org.junit.Test @HiltAndroidTest class StoriesTest : BaseTestCase() { - // @Test + @Test fun clickOnOrderButtonTest() = setupHooks().run { + val buyWalletUrl = "https://buy.tangem.com/?utm_source=tangem-app&utm_medium=app" onDisclaimerScreen { step("Click on 'Accept' button") { acceptButton.clickWithAssertion() @@ -27,7 +28,7 @@ class StoriesTest : BaseTestCase() { step("Assert: browser opened") { val expectedIntent = KIntent { hasAction(ACTION_VIEW) - hasData { toString().startsWith(NEW_BUY_WALLET_URL) } + hasData { toString().startsWith(buyWalletUrl) } } expectedIntent.intended() device.uiDevice.pressBack() diff --git a/app/src/androidTest/kotlin/com/tangem/tests/TermsOfServiceTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/TermsOfServiceTest.kt new file mode 100644 index 0000000000..0e596d4528 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/tests/TermsOfServiceTest.kt @@ -0,0 +1,106 @@ +package com.tangem.tests + +import androidx.test.InstrumentationRegistry.getTargetContext +import com.tangem.common.BaseTestCase +import com.tangem.common.extensions.clickWithAssertion +import com.tangem.common.extensions.swipeToCloseApp +import com.tangem.screens.onDisclaimerScreen +import com.tangem.screens.onStoriesScreen +import dagger.hilt.android.testing.HiltAndroidTest +import io.qameta.allure.kotlin.AllureId +import io.qameta.allure.kotlin.junit4.DisplayName +import org.junit.Test + +@HiltAndroidTest +class TermsOfServiceTest : BaseTestCase() { + + @AllureId("3573") + @DisplayName("ToS: success acceptance") + @Test + fun validateTermsOfServiceScreenTest() { + setupHooks().run { + val tosUrl = "https://tangem.com/tangem_tos.html" + step("Assert title of 'Disclaimer screen' is displayed") { + onDisclaimerScreen { title.assertIsDisplayed() } + } + step("Assert title of 'Disclaimer screen' is displayed") { + onDisclaimerScreen { webView.assertIsDisplayed() } + } + step("Verify WebView loads correct URL") { + onDisclaimerScreen { + webView.assertContentDescriptionContains(value = tosUrl, substring = true) + } + } + step("Click on 'Accept' button") { + onDisclaimerScreen { acceptButton.clickWithAssertion() } + } + step("Assert 'Stories' screen is opened") { + onStoriesScreen { + scanButton.assertIsDisplayed() + orderButton.assertIsDisplayed()} + } + } + } + + @AllureId("3574") + @DisplayName("ToS: accept after app restart") + @Test + fun acceptTermsOfServiceAfterAppRestart() { + val packageName = getTargetContext().packageName + setupHooks().run { + val tosUrl = "https://tangem.com/tangem_tos.html" + step("Assert title of 'Disclaimer screen' is displayed") { + onDisclaimerScreen { title.assertIsDisplayed() } + } + step("Assert WebView of 'Disclaimer screen' is displayed") { + onDisclaimerScreen { webView.assertIsDisplayed() } + } + step("Verify WebView loads correct URL") { + onDisclaimerScreen { + webView.assertContentDescriptionContains(value = tosUrl, substring = true) + } + } + step("'Accept' button is displayed") { + onDisclaimerScreen { acceptButton.assertIsDisplayed() } + } + step("Open recent apps") { + device.uiDevice.pressRecentApps() + } + step("Stop app by swipe") { + swipeToCloseApp() + } + step("Launch app") { + device.apps.launch(packageName) + } + step("Assert title of 'Disclaimer screen' is displayed") { + onDisclaimerScreen { title.assertIsDisplayed() } + } + step("Assert WebView of 'Disclaimer screen' is displayed") { + onDisclaimerScreen { webView.assertIsDisplayed() } + } + step("Verify WebView loads correct URL") { + onDisclaimerScreen { + webView.assertContentDescriptionContains(value = tosUrl, substring = true) + } + } + step("Click on 'Accept' button") { + onDisclaimerScreen { acceptButton.clickWithAssertion() } + } + step("Open recent apps") { + device.uiDevice.pressRecentApps() + } + step("Stop app by swipe") { + swipeToCloseApp() + } + step("Launch app") { + device.apps.launch(packageName) + } + step("Assert 'Stories' screen is opened") { + onStoriesScreen { + scanButton.assertIsDisplayed() + orderButton.assertIsDisplayed()} + } + } + } + +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/DisclaimerScreenTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/DisclaimerScreenTestTags.kt index b1355ed20a..e5716b476f 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/test/DisclaimerScreenTestTags.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/test/DisclaimerScreenTestTags.kt @@ -3,4 +3,5 @@ package com.tangem.core.ui.test object DisclaimerScreenTestTags { const val SCREEN_CONTAINER = "DISCLAIMER_SCREEN_CONTAINER" const val ACCEPT_BUTTON = "DISCLAIMER_SCREEN_ACCEPT_BUTTON" + const val WEB_VIEW = "DISCLAIMER_SCREEN_WEB_VIEW" } \ No newline at end of file diff --git a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/ui/DisclaimerScreen.kt b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/ui/DisclaimerScreen.kt index 4a68dba57e..46e17998f8 100644 --- a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/ui/DisclaimerScreen.kt +++ b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/ui/DisclaimerScreen.kt @@ -14,6 +14,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.testTag +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.isGranted @@ -111,7 +113,11 @@ private fun DisclaimerContent(url: String) { state = webViewState, modifier = Modifier .fillMaxSize() - .background(TangemTheme.colors.background.primary), + .background(TangemTheme.colors.background.primary) + .testTag(DisclaimerScreenTestTags.WEB_VIEW) + .semantics { + contentDescription = "WebView URL: ${webViewState.content.getCurrentUrl() ?: url}" + }, captureBackPresses = false, navigator = webViewNavigator, onCreated = WebView::applySafeSettings, From 3cd5c708c9609538c11bdc449a4d448570c03206 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 1 Aug 2025 15:46:06 +0400 Subject: [PATCH 26/53] Updated on 2026-08-14 --- .../tangem/domain/models/account/Account.kt | 127 ++++++++++++ .../tangem/domain/models/account/AccountId.kt | 14 ++ .../domain/models/account/AccountName.kt | 59 ++++++ .../models/account/CryptoPortfolioIcon.kt | 83 +++----- .../domain/models/account/AccountNameTest.kt | 67 +++++++ .../domain/models/account/AccountTest.kt | 187 ++++++++++++++++++ .../models/account/CryptoPortfolioIconTest.kt | 172 ++++++++-------- 7 files changed, 564 insertions(+), 145 deletions(-) create mode 100644 domain/models/src/main/kotlin/com/tangem/domain/models/account/Account.kt create mode 100644 domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountId.kt create mode 100644 domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountName.kt create mode 100644 domain/models/src/test/kotlin/com/tangem/domain/models/account/AccountNameTest.kt create mode 100644 domain/models/src/test/kotlin/com/tangem/domain/models/account/AccountTest.kt diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/account/Account.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/account/Account.kt new file mode 100644 index 0000000000..398930b72e --- /dev/null +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/account/Account.kt @@ -0,0 +1,127 @@ +package com.tangem.domain.models.account + +import arrow.core.Either +import arrow.core.raise.either +import arrow.core.raise.ensure +import com.tangem.domain.models.TokensGroupType +import com.tangem.domain.models.TokensSortType +import com.tangem.domain.models.account.Account.CryptoPortfolio.Error.AccountNameError +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.wallet.UserWalletId + +/** + * Represents an account + * +[REDACTED_AUTHOR] + */ +sealed interface Account { + + /** Unique identifier of the account */ + val accountId: AccountId + + /** Name of the account */ + val name: AccountName + + /** The identifier of the user wallet associated with the account */ + val userWalletId: UserWalletId + get() = accountId.userWalletId + + /** + * Represents a crypto portfolio account + * + * @property accountId unique identifier of the account + * @property name name of the account + * @property icon icon representing the account + * @property derivationIndex index used for derivation of the account + * @property isArchived indicates whether the account is archived + * @property cryptoCurrencyList list of tokens associated with the account + */ + data class CryptoPortfolio private constructor( + override val accountId: AccountId, + override val name: AccountName, + val icon: CryptoPortfolioIcon, + val derivationIndex: Int, + val isArchived: Boolean, + val cryptoCurrencyList: CryptoCurrencyList, + ) : Account { + + /** Indicates if the account is the main account */ + val isMainAccount: Boolean + get() = derivationIndex == 0 + + /** Number of tokens in the account */ + val tokensCount: Int + get() = cryptoCurrencyList.currencies.size + + /** Number of distinct networks in the account */ + val networksCount: Int + get() = cryptoCurrencyList.currencies.map(CryptoCurrency::network).distinct().size + + /** + * Represents a list of tokens in the account + * + * @property currencies set of cryptocurrencies in the account + * @property sortType sorting type for the tokens + * @property groupType grouping type for the tokens + */ + data class CryptoCurrencyList( + val currencies: Set, + val sortType: TokensSortType, + val groupType: TokensGroupType, + ) + + /** + * Represents possible errors when creating a crypto portfolio account + */ + sealed interface Error { + + /** Error indicating that the account name is blank */ + data class AccountNameError(val cause: AccountName.Error) : Error { + override fun toString(): String = cause.toString() + } + + /** Error indicating that the derivation index is negative */ + data object NegativeDerivationIndex : Error { + override fun toString(): String = "${this::class.simpleName}: Derivation index must be non-negative" + } + } + + companion object { + + /** + * Constructor for creating a [CryptoPortfolio] instance + * + * @param accountId unique identifier of the account + * @param name name of the account + * @param accountIcon icon representing the account + * @param derivationIndex index used for derivation of the account + * @param isArchived indicates whether the account is archived + * @param cryptoCurrencyList list of tokens associated with the account + */ + @Suppress("LongParameterList") + operator fun invoke( + accountId: AccountId, + name: String, + accountIcon: CryptoPortfolioIcon, + derivationIndex: Int, + isArchived: Boolean, + cryptoCurrencyList: CryptoCurrencyList, + ): Either { + return either { + val accountName = AccountName(name).mapLeft(::AccountNameError).bind() + + ensure(derivationIndex >= 0) { Error.NegativeDerivationIndex } + + CryptoPortfolio( + accountId = accountId, + name = accountName, + icon = accountIcon, + derivationIndex = derivationIndex, + isArchived = isArchived, + cryptoCurrencyList = cryptoCurrencyList, + ) + } + } + } + } +} \ No newline at end of file diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountId.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountId.kt new file mode 100644 index 0000000000..2f5728579c --- /dev/null +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountId.kt @@ -0,0 +1,14 @@ +package com.tangem.domain.models.account + +import com.tangem.domain.models.wallet.UserWalletId + +/** + * Represents a unique identifier for an account + * + * @property value a unique string value that distinguishes this account + * @property userWalletId the identifier of the user wallet associated with the account + */ +data class AccountId( + val value: String, + val userWalletId: UserWalletId, +) \ No newline at end of file diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountName.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountName.kt new file mode 100644 index 0000000000..e51a8e5a30 --- /dev/null +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountName.kt @@ -0,0 +1,59 @@ +package com.tangem.domain.models.account + +import arrow.core.Either +import arrow.core.raise.either +import arrow.core.raise.ensure + +/** + * Represents an account name + * + * @property value the validated account name as a string + * +[REDACTED_AUTHOR] + */ +data class AccountName private constructor( + val value: String, +) { + + /** + * Represents possible validation errors + */ + sealed interface Error { + + /** + * Error indicating that the account name is blank + */ + data object Empty : Error { + override fun toString(): String = "${Empty::class.simpleName}: Account name cannot be blank" + } + + /** + * Error indicating that the account name exceeds the maximum allowed length + */ + data object ExceedsMaxLength : Error { + override fun toString(): String { + return "${ExceedsMaxLength::class.simpleName}: Account name cannot exceed $MAX_LENGTH characters" + } + } + } + + companion object { + + private const val MAX_LENGTH = 20 + + /** + * Factory method to create an `AccountName` instance. + * Validates the input string to ensure it is not blank and does not exceed the maximum length. + * + * @param value the input string representing the account name + */ + operator fun invoke(value: String): Either = either { + val trimmedValue = value.trim() + + ensure(trimmedValue.isNotBlank()) { Error.Empty } + ensure(trimmedValue.length <= MAX_LENGTH) { Error.ExceedsMaxLength } + + AccountName(value = trimmedValue) + } + } +} \ No newline at end of file diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/account/CryptoPortfolioIcon.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/account/CryptoPortfolioIcon.kt index 9846695785..15b906020b 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/account/CryptoPortfolioIcon.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/account/CryptoPortfolioIcon.kt @@ -1,48 +1,26 @@ package com.tangem.domain.models.account -import com.tangem.domain.models.account.CryptoPortfolioIcon.Companion.ofCustomAccount +import com.tangem.domain.models.account.CryptoPortfolioIcon.Companion.ofDefaultCustomAccount import com.tangem.domain.models.account.CryptoPortfolioIcon.Companion.ofMainAccount +import com.tangem.domain.models.wallet.UserWalletId import kotlinx.serialization.Serializable /** * Represents an icon for an [Account.CryptoPortfolio] account * - * @property type the type of the account icon + * @property value the type of the account icon * @property color the color of the account icon * - * @constructor [ofMainAccount], [ofCustomAccount] + * @constructor [ofMainAccount], [ofDefaultCustomAccount] * [REDACTED_AUTHOR] */ @Serializable data class CryptoPortfolioIcon private constructor( - val type: Type, + val value: Icon, val color: Color, ) { - /** - * Represents the type of an account icon. Can either be a specific [Icon] or a [Symbol] - */ - @Serializable - sealed interface Type { - - /** - * Represents a specific predefined icon type - * - * @property value the predefined [Icon] of the icon - */ - @Serializable - data class Icon(val value: CryptoPortfolioIcon.Icon) : Type - - /** - * Represents an icon with a letter - * - * @property value the letter used as the icon - */ - @Serializable - data class Symbol(val value: Char) : Type - } - /** * Enum class representing the icons of accounts */ @@ -91,59 +69,44 @@ data class CryptoPortfolioIcon private constructor( companion object { - private val defaultMainAccountType: Icon = Icon.Star - private val defaultMainAccountColor: Color = Color.Azure + private val defaultMainAccountIcon: Icon = Icon.Star + private val excludedCustomAccountIcons: Set = setOf(Icon.Letter, Icon.Star) + private const val HASH_MULTIPLIER = 31 /** - * Creates an [CryptoPortfolioIcon] for the Main account, ensuring the color is not in the excluded set. + * Creating a [CryptoPortfolioIcon] for the Main account with default values. + * The color is derived from the [UserWalletId]. * - * @param exclude excluded colors that are already used for main accounts + * @param userWalletId the ID of the user wallet */ - fun ofMainAccount(exclude: Set): CryptoPortfolioIcon { - val isDefaultColorBusy = defaultMainAccountColor in exclude + fun ofMainAccount(userWalletId: UserWalletId): CryptoPortfolioIcon { + val colors = Color.entries + val hash = userWalletId.value.fold(0) { acc, byte -> acc * HASH_MULTIPLIER + byte } - val color = if (isDefaultColorBusy) { - val colorsWithExcluded = Color.entries - exclude + val index = (hash and Int.MAX_VALUE) % colors.size + val color = colors[index] - val availableColors = if (colorsWithExcluded.isNotEmpty()) { - colorsWithExcluded - } else { - Color.entries - } - - availableColors.random() - } else { - defaultMainAccountColor - } - - return CryptoPortfolioIcon( - type = Type.Icon(value = defaultMainAccountType), - color = color, - ) + return CryptoPortfolioIcon(value = defaultMainAccountIcon, color = color) } /** * Creates an [CryptoPortfolioIcon] for a user account based on the account name - * - * @param accountName the name of the account, used to determine the letter for the icon */ - fun ofCustomAccount(accountName: String): CryptoPortfolioIcon { + fun ofDefaultCustomAccount(): CryptoPortfolioIcon { + val icon = (Icon.entries - excludedCustomAccountIcons).random() val color = Color.entries.random() - return CryptoPortfolioIcon( - type = Type.Symbol(value = accountName.first()), - color = color, - ) + return CryptoPortfolioIcon(value = icon, color = color) } /** * Creates a [CryptoPortfolioIcon] for a user account with a specific type and color * - * @param type the type of the account icon + * @param value the icon of the account * @param color the color of the account icon */ - fun ofCustomAccount(type: Type, color: Color): CryptoPortfolioIcon { - return CryptoPortfolioIcon(type = type, color = color) + fun ofCustomAccount(value: Icon, color: Color): CryptoPortfolioIcon { + return CryptoPortfolioIcon(value = value, color = color) } } } \ No newline at end of file diff --git a/domain/models/src/test/kotlin/com/tangem/domain/models/account/AccountNameTest.kt b/domain/models/src/test/kotlin/com/tangem/domain/models/account/AccountNameTest.kt new file mode 100644 index 0000000000..d68bc46ab9 --- /dev/null +++ b/domain/models/src/test/kotlin/com/tangem/domain/models/account/AccountNameTest.kt @@ -0,0 +1,67 @@ +package com.tangem.domain.models.account + +import arrow.core.Either +import arrow.core.left +import com.google.common.truth.Truth +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource + +/** +[REDACTED_AUTHOR] + */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class AccountNameTest { + + @ParameterizedTest + @MethodSource("provideTestModels") + fun invoke(model: InvokeTestModel) { + // Act + val actual = AccountName(value = model.value) + + // Assert + actual + .onRight { + val expected = model.expected.getOrNull()!! + Truth.assertThat(it).isEqualTo(expected) + } + .onLeft { + val expected = model.expected.leftOrNull()!! + Truth.assertThat(it).isEqualTo(expected) + } + } + + private fun provideTestModels() = listOf( + InvokeTestModel( + value = "", + expected = AccountName.Error.Empty.left(), + ), + InvokeTestModel( + value = " ", + expected = AccountName.Error.Empty.left(), + ), + InvokeTestModel( + value = "a".repeat(21), + expected = AccountName.Error.ExceedsMaxLength.left(), + ), + "a".repeat(20).let { value -> + InvokeTestModel( + value = value, + expected = AccountName(value = value), + ) + }, + InvokeTestModel( + value = " name ", + expected = AccountName(value = "name"), + ), + InvokeTestModel( + value = "Main Account", + expected = AccountName(value = "Main Account"), + ), + ) + + data class InvokeTestModel( + val value: String, + val expected: Either, + ) +} \ No newline at end of file diff --git a/domain/models/src/test/kotlin/com/tangem/domain/models/account/AccountTest.kt b/domain/models/src/test/kotlin/com/tangem/domain/models/account/AccountTest.kt new file mode 100644 index 0000000000..d94c1a6bdf --- /dev/null +++ b/domain/models/src/test/kotlin/com/tangem/domain/models/account/AccountTest.kt @@ -0,0 +1,187 @@ +package com.tangem.domain.models.account + +import com.google.common.truth.Truth +import com.tangem.domain.models.TokensGroupType +import com.tangem.domain.models.TokensSortType +import com.tangem.domain.models.account.Account.CryptoPortfolio.CryptoCurrencyList +import com.tangem.domain.models.account.Account.CryptoPortfolio.Error.AccountNameError +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.wallet.UserWalletId +import io.mockk.every +import io.mockk.mockk +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance + +/** +[REDACTED_AUTHOR] + */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class AccountTest { + + @Test + fun `Account userWalletId`() { + // Arrange + val userWalletId = UserWalletId("011") + + // Act + val actual = createCryptoPortfolioStub(userWalletId = userWalletId).userWalletId + + // Assert + Truth.assertThat(actual).isEqualTo(userWalletId) + } + + @Test + fun `CryptoPortfolio isMainAccount`() { + // Arrange + val derivationIndex0 = 0 + val derivationIndex1 = 1 + + // Act + val actual1 = createCryptoPortfolioStub(derivationIndex = derivationIndex0) + .isMainAccount + + val actual2 = createCryptoPortfolioStub(derivationIndex = derivationIndex1) + .isMainAccount + + // Assert + Truth.assertThat(actual1).isTrue() + Truth.assertThat(actual2).isFalse() + } + + @Test + fun `CryptoPortfolio tokensCount`() { + // Arrange + val emptyCurrencies = emptySet() + val filledCurrencies = setOf(mockk()) + + // Act + val actual1 = createCryptoPortfolioStub(currencies = emptyCurrencies) + .tokensCount + + val actual2 = createCryptoPortfolioStub(currencies = filledCurrencies) + .tokensCount + + // Assert + Truth.assertThat(actual1).isEqualTo(0) + Truth.assertThat(actual2).isEqualTo(1) + } + + @Test + fun `CryptoPortfolio networksCount`() { + // Arrange + val emptyCurrencies = emptySet() + val filledCurrencies = setOf( + mockk { + every { network } returns mockk() + }, + ) + + // Act + val actual1 = createCryptoPortfolioStub(currencies = emptyCurrencies) + .networksCount + + val actual2 = createCryptoPortfolioStub(currencies = filledCurrencies) + .networksCount + + // Assert + Truth.assertThat(actual1).isEqualTo(0) + Truth.assertThat(actual2).isEqualTo(1) + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class CreateCryptoPortfolio { + + @Test + fun `invoke returns AccountNameError`() { + // Arrange + val name = "" + + // Act + val actual = Account.CryptoPortfolio( + accountId = mockk(), + name = name, + accountIcon = mockk(), + derivationIndex = 0, + isArchived = false, + cryptoCurrencyList = mockk(), + ) + .leftOrNull()!! + + // Assert + val expected = AccountNameError(cause = AccountName.Error.Empty) + Truth.assertThat(actual).isEqualTo(expected) + } + + @Test + fun `invoke returns NegativeDerivationIndex`() { + // Arrange + val derivationIndex = -1 + + // Act + val actual = Account.CryptoPortfolio( + accountId = mockk(), + name = "Test Account", + accountIcon = mockk(), + derivationIndex = derivationIndex, + isArchived = false, + cryptoCurrencyList = mockk(), + ) + .leftOrNull()!! + + // Assert + val expected = Account.CryptoPortfolio.Error.NegativeDerivationIndex + Truth.assertThat(actual).isEqualTo(expected) + } + + @Test + fun `invoke returns CryptoPortfolio`() { + // Act + val actual = Account.CryptoPortfolio( + accountId = AccountId( + value = "value", + userWalletId = UserWalletId("011"), + ), + name = "Test Account", + accountIcon = CryptoPortfolioIcon.ofMainAccount(userWalletId = UserWalletId("011")), + derivationIndex = 0, + isArchived = false, + cryptoCurrencyList = CryptoCurrencyList( + currencies = emptySet(), + sortType = TokensSortType.NONE, + groupType = TokensGroupType.NONE, + ), + ) + .getOrNull()!! + + // Assert + val expected = createCryptoPortfolioStub() + Truth.assertThat(actual).isEqualTo(expected) + } + } + + private fun createCryptoPortfolioStub( + userWalletId: UserWalletId = UserWalletId("011"), + name: String = "Test Account", + derivationIndex: Int = 0, + currencies: Set = emptySet(), + ): Account.CryptoPortfolio { + return Account.CryptoPortfolio.invoke( + accountId = AccountId( + value = "value", + userWalletId = userWalletId, + ), + name = name, + accountIcon = CryptoPortfolioIcon.ofMainAccount(userWalletId), + derivationIndex = derivationIndex, + isArchived = false, + cryptoCurrencyList = CryptoCurrencyList( + currencies = currencies, + sortType = TokensSortType.NONE, + groupType = TokensGroupType.NONE, + ), + ) + .getOrNull()!! + } +} \ No newline at end of file diff --git a/domain/models/src/test/kotlin/com/tangem/domain/models/account/CryptoPortfolioIconTest.kt b/domain/models/src/test/kotlin/com/tangem/domain/models/account/CryptoPortfolioIconTest.kt index 3706e6921d..ec55aabdcd 100644 --- a/domain/models/src/test/kotlin/com/tangem/domain/models/account/CryptoPortfolioIconTest.kt +++ b/domain/models/src/test/kotlin/com/tangem/domain/models/account/CryptoPortfolioIconTest.kt @@ -1,13 +1,14 @@ package com.tangem.domain.models.account import com.google.common.truth.Truth -import com.tangem.domain.models.account.CryptoPortfolioIcon.* +import com.tangem.domain.models.account.CryptoPortfolioIcon.Color +import com.tangem.domain.models.account.CryptoPortfolioIcon.Icon +import com.tangem.domain.models.wallet.UserWalletId import io.mockk.every import io.mockk.mockkObject import io.mockk.unmockkObject -import io.mockk.verify +import io.mockk.verifyOrder import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource @@ -23,159 +24,160 @@ class CryptoPortfolioIconTest { @TestInstance(TestInstance.Lifecycle.PER_CLASS) inner class OfMainAccount { - @Test - fun `ofMainAccount with empty exclude`() { - // Act - val actual = CryptoPortfolioIcon.ofMainAccount(exclude = emptySet()) - - // Assert - val expectedColor = Color.Azure - Truth.assertThat(actual.color).isEqualTo(expectedColor) - - val expectedType = Type.Icon(value = Icon.Star) - Truth.assertThat(actual.type).isEqualTo(expectedType) - } - @ParameterizedTest @MethodSource("provideTestModels") fun ofMainAccount(model: OfMainAccountModel) { - // Arrange - mockkObject(Random.Default) - - val size = (Color.entries.size - model.exclude.size).takeIf { it > 0 } ?: Color.entries.size - every { Random.nextInt(size) } returns model.randomNextInt - // Act - val actual = CryptoPortfolioIcon.ofMainAccount(exclude = model.exclude) + val actual = CryptoPortfolioIcon.ofMainAccount(userWalletId = model.userWalletId) // Assert val expectedColor = model.expectedColor Truth.assertThat(actual.color).isEqualTo(expectedColor) - val expectedType = Type.Icon(value = Icon.Star) - Truth.assertThat(actual.type).isEqualTo(expectedType) - - verify(exactly = 1) { Random.nextInt(size) } - - unmockkObject(Random.Default) + val expectedIcon = Icon.Star + Truth.assertThat(actual.value).isEqualTo(expectedIcon) } private fun provideTestModels() = listOf( - // If the default color is already occupied (present in the exclude set), a random color from the - // remaining available colors will be selected for the main account icon. OfMainAccountModel( - exclude = setOf(Color.Azure), - randomNextInt = 0, - expectedColor = Color.entries[1], + userWalletId = UserWalletId("1234567890abcdef"), + expectedColor = Color.Pattypan, ), OfMainAccountModel( - exclude = setOf(Color.Azure, Color.CaribbeanBlue), - randomNextInt = 0, - expectedColor = Color.entries[2], + userWalletId = UserWalletId("27163F47405CE73110837F24DF82607FF11C7AF9D78C93F409E4FEAFF3400C8F"), + expectedColor = Color.CandyGrapeFizz, ), - // If all colors are already occupied, a random one will be selected. OfMainAccountModel( - exclude = Color.entries.toSet(), - randomNextInt = 1, - expectedColor = Color.entries[1], + userWalletId = UserWalletId("64A3791C180584C700EBECD6EAB36CBC34643BB449BC87761104C09F41DBCF3D"), + expectedColor = Color.PalatinateBlue, + ), + OfMainAccountModel( + userWalletId = UserWalletId("01C061A99FCCEDA87933267EBAB3513592F83AD2E27BDA6EE5546BA96009D21F"), + expectedColor = Color.Pelati, + ), + OfMainAccountModel( + userWalletId = UserWalletId("6D387A8FA5D2AF95F601EBCA8736D73D2ED53159835D8C407FBD4BBB10290C8B"), + expectedColor = Color.CaribbeanBlue, + ), + OfMainAccountModel( + userWalletId = UserWalletId("33FCD9B9982C31648C235AE55A29212D567ECD3BA24BE4227D1A01897ADBC959"), + expectedColor = Color.SweetDesire, + ), + OfMainAccountModel( + userWalletId = UserWalletId("197C8C5AA59270F3E9E1F30799A007D193DA596E6DC24C37D002C2EC203C2A0B"), + expectedColor = Color.VitalGreen, + ), + OfMainAccountModel( + userWalletId = UserWalletId("ACF90C18393828958B5E795771F0692A00D3D7ADC092F726AB4A7E3116DD6E6E"), + expectedColor = Color.Pattypan, ), ) } data class OfMainAccountModel( - val exclude: Set, - val randomNextInt: Int, + val userWalletId: UserWalletId, val expectedColor: Color, ) @Nested @TestInstance(TestInstance.Lifecycle.PER_CLASS) - inner class OfCustomAccountBasedOnName { + inner class OfDefaultCustomAccount { @ParameterizedTest @MethodSource("provideTestModels") - fun ofCustomAccount(model: OfCustomAccountModel.BasedOnName) { + fun ofCustomAccount(model: OfDefaultCustomAccountModel) { // Arrange + val availableIcons = Icon.entries - setOf(Icon.Letter, Icon.Star) + mockkObject(Random.Default) - every { Random.nextInt(until = Color.entries.size) } returns model.randomNextInt + every { Random.nextInt(until = availableIcons.size) } returns model.randomIconIndex + every { Random.nextInt(until = Color.entries.size) } returns model.randomColorIndex // Act - val actual = CryptoPortfolioIcon.ofCustomAccount(accountName = model.accountName) + val actual = CryptoPortfolioIcon.ofDefaultCustomAccount() // Assert - val expectedColor = model.expectedColor - Truth.assertThat(actual.color).isEqualTo(expectedColor) + val expected = model.expected + Truth.assertThat(actual).isEqualTo(expected) - val expectedType = Type.Symbol(value = model.accountName.first()) - Truth.assertThat(actual.type).isEqualTo(expectedType) - - verify(exactly = 1) { Random.nextInt(until = Color.entries.size) } + verifyOrder { + Random.nextInt(until = availableIcons.size) + Random.nextInt(until = Color.entries.size) + } unmockkObject(Random.Default) } private fun provideTestModels() = listOf( - OfCustomAccountModel.BasedOnName( - accountName = "New account", - randomNextInt = 0, - expectedColor = Color.entries[0], + OfDefaultCustomAccountModel( + randomIconIndex = 0, + randomColorIndex = 0, + expected = CryptoPortfolioIcon.ofCustomAccount(value = Icon.User, color = Color.Azure), ), - OfCustomAccountModel.BasedOnName( - accountName = "Awesome", - randomNextInt = Color.entries.lastIndex, - expectedColor = Color.entries.last(), + OfDefaultCustomAccountModel( + randomIconIndex = 1, + randomColorIndex = 1, + expected = CryptoPortfolioIcon.ofCustomAccount(value = Icon.Family, color = Color.CaribbeanBlue), + ), + OfDefaultCustomAccountModel( + randomIconIndex = Icon.entries.lastIndex - 2, + randomColorIndex = Color.entries.lastIndex, + expected = CryptoPortfolioIcon.ofCustomAccount(value = Icon.Gift, color = Color.VitalGreen), ), ) } + data class OfDefaultCustomAccountModel( + val randomIconIndex: Int, + val randomColorIndex: Int, + val expected: CryptoPortfolioIcon, + ) + @Nested @TestInstance(TestInstance.Lifecycle.PER_CLASS) inner class OfCustomAccountWithTypeAndColor { @ParameterizedTest @MethodSource("provideTestModels") - fun ofCustomAccount(model: OfCustomAccountModel.WithTypeAndColor) { + fun ofCustomAccount(model: OfCustomAccountModel) { // Act - val actual = CryptoPortfolioIcon.ofCustomAccount(type = model.type, color = model.color) + val actual = CryptoPortfolioIcon.ofCustomAccount(value = model.icon, color = model.color) // Assert val expectedColor = model.expectedColor Truth.assertThat(actual.color).isEqualTo(expectedColor) val expectedType = model.expectedType - Truth.assertThat(actual.type).isEqualTo(expectedType) + Truth.assertThat(actual.value).isEqualTo(expectedType) } private fun provideTestModels() = listOf( - OfCustomAccountModel.WithTypeAndColor( - type = Type.Icon(value = Icon.User), + OfCustomAccountModel( + icon = Icon.User, color = Color.CaribbeanBlue, - expectedType = Type.Icon(value = Icon.User), + expectedType = Icon.User, expectedColor = Color.CaribbeanBlue, ), - OfCustomAccountModel.WithTypeAndColor( - type = Type.Symbol(value = 'A'), + OfCustomAccountModel( + icon = Icon.Letter, color = Color.DullLavender, - expectedType = Type.Symbol(value = 'A'), + expectedType = Icon.Letter, + expectedColor = Color.DullLavender, + ), + OfCustomAccountModel( + icon = Icon.Star, + color = Color.DullLavender, + expectedType = Icon.Star, expectedColor = Color.DullLavender, ), ) } - sealed interface OfCustomAccountModel { - - data class BasedOnName( - val accountName: String, - val randomNextInt: Int, - val expectedColor: Color, - ) : OfCustomAccountModel - - data class WithTypeAndColor( - val type: Type, - val color: Color, - val expectedType: Type, - val expectedColor: Color, - ) : OfCustomAccountModel - } + data class OfCustomAccountModel( + val icon: Icon, + val color: Color, + val expectedType: Icon, + val expectedColor: Color, + ) } \ No newline at end of file From 4be7763d712345178a4a2f9325a6c0006587435a Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Aug 2025 08:12:44 +0000 Subject: [PATCH 27/53] Updated on 2026-08-14 --- gradle/tangem_dependencies.toml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index 7250e5eb0c..e3fa514003 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,12 +5,14 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "releases-5.26.0-1123" +tangemBlockchainSdk = "develop-1125" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "releases-5.26.0-508" +tangemCardSdk = "develop-505" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem12" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ +tangemHotSdk = "develop-446" +#tangemHotSdk = "0.0.1" # Keep it! - used for local builds ^ @@ -18,6 +20,8 @@ tangemVico = "2.0.0-alpha.25-tangem12" blockchain = { module = "com.tangem:blockchain", version.ref = "tangemBlockchainSdk" } card-android = { module = "com.tangem.tangem-sdk-kotlin:android", version.ref = "tangemCardSdk" } card-core = { module = "com.tangem.tangem-sdk-kotlin:core", version.ref = "tangemCardSdk" } +hot-core = { module = "com.tangem.tangem-hot-sdk-kotlin:core", version.ref = "tangemHotSdk" } +hot-android = { module = "com.tangem.tangem-hot-sdk-kotlin:android", version.ref = "tangemHotSdk" } vico-compose = { group = "com.tangem.vico", name = "compose", version.ref = "tangemVico" } vico-compose-m3 = { group = "com.tangem.vico", name = "compose-m3", version.ref = "tangemVico" } From 49800731e347fe34a1751a4d3bf10b73f3cfd27e Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 29 Jul 2025 16:22:11 +0500 Subject: [PATCH 28/53] Updated on 2026-08-14 --- ...TransactionStatusStoreModule.kt => SwapStoreModule.kt} | 8 +++++--- .../{swaptx => swap}/DefaultSwapTransactionStatusStore.kt | 2 +- .../local/{swaptx => swap}/SwapTransactionStatusStore.kt | 2 +- .../state/factory/express/ExchangeStatusFactory.kt | 4 ++-- .../state/factory/express/OnrampStatusFactory.kt | 2 +- .../presentation/wallet/domain/OnrampStatusFactory.kt | 2 +- 6 files changed, 11 insertions(+), 9 deletions(-) rename core/datasource/src/main/java/com/tangem/datasource/di/{SwapTransactionStatusStoreModule.kt => SwapStoreModule.kt} (62%) rename core/datasource/src/main/java/com/tangem/datasource/local/{swaptx => swap}/DefaultSwapTransactionStatusStore.kt (91%) rename core/datasource/src/main/java/com/tangem/datasource/local/{swaptx => swap}/SwapTransactionStatusStore.kt (92%) diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/SwapTransactionStatusStoreModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/SwapStoreModule.kt similarity index 62% rename from core/datasource/src/main/java/com/tangem/datasource/di/SwapTransactionStatusStoreModule.kt rename to core/datasource/src/main/java/com/tangem/datasource/di/SwapStoreModule.kt index 25f71c60a0..edf8c36cc3 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/SwapTransactionStatusStoreModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/SwapStoreModule.kt @@ -1,8 +1,10 @@ package com.tangem.datasource.di import com.tangem.datasource.local.datastore.RuntimeDataStore -import com.tangem.datasource.local.swaptx.DefaultSwapTransactionStatusStore -import com.tangem.datasource.local.swaptx.SwapTransactionStatusStore +import com.tangem.datasource.local.swap.DefaultSwapBestRateAnimationStore +import com.tangem.datasource.local.swap.DefaultSwapTransactionStatusStore +import com.tangem.datasource.local.swap.SwapBestRateAnimationStore +import com.tangem.datasource.local.swap.SwapTransactionStatusStore import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -11,7 +13,7 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -object SwapTransactionStatusStoreModule { +object SwapStoreModule { @Provides @Singleton diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/DefaultSwapTransactionStatusStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/swap/DefaultSwapTransactionStatusStore.kt similarity index 91% rename from core/datasource/src/main/java/com/tangem/datasource/local/swaptx/DefaultSwapTransactionStatusStore.kt rename to core/datasource/src/main/java/com/tangem/datasource/local/swap/DefaultSwapTransactionStatusStore.kt index bb094d028f..87881b3768 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/DefaultSwapTransactionStatusStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/swap/DefaultSwapTransactionStatusStore.kt @@ -1,4 +1,4 @@ -package com.tangem.datasource.local.swaptx +package com.tangem.datasource.local.swap import com.tangem.datasource.local.datastore.core.StringKeyDataStore diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/SwapTransactionStatusStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/swap/SwapTransactionStatusStore.kt similarity index 92% rename from core/datasource/src/main/java/com/tangem/datasource/local/swaptx/SwapTransactionStatusStore.kt rename to core/datasource/src/main/java/com/tangem/datasource/local/swap/SwapTransactionStatusStore.kt index 033f71b6ac..af554bba1a 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/swaptx/SwapTransactionStatusStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/swap/SwapTransactionStatusStore.kt @@ -1,4 +1,4 @@ -package com.tangem.datasource.local.swaptx +package com.tangem.datasource.local.swap /** * Runtime cache for storing swap transactions statuses sent to analytics diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt index d418812ee0..27a03a1006 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt @@ -2,8 +2,8 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory. import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.datasource.local.swaptx.ExpressAnalyticsStatus -import com.tangem.datasource.local.swaptx.SwapTransactionStatusStore +import com.tangem.datasource.local.swap.ExpressAnalyticsStatus +import com.tangem.datasource.local.swap.SwapTransactionStatusStore import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.quote.QuoteStatus diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt index 2e91b8b73d..61a05030ba 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/OnrampStatusFactory.kt @@ -3,7 +3,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory. import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.datasource.local.swaptx.ExpressAnalyticsStatus +import com.tangem.datasource.local.swap.ExpressAnalyticsStatus import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/OnrampStatusFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/OnrampStatusFactory.kt index 6c985e4728..2af73a2491 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/OnrampStatusFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/OnrampStatusFactory.kt @@ -4,7 +4,7 @@ import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ModelScoped -import com.tangem.datasource.local.swaptx.ExpressAnalyticsStatus +import com.tangem.datasource.local.swap.ExpressAnalyticsStatus import com.tangem.domain.onramp.GetOnrampStatusUseCase import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase import com.tangem.domain.onramp.OnrampUpdateTransactionStatusUseCase From d664e6ecbb5cc689c4cacbe22df1b0bb077b9ffa Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 29 Jul 2025 16:26:32 +0500 Subject: [PATCH 29/53] Updated on 2026-08-14 --- .../tangem/datasource/di/SwapStoreModule.kt | 9 + .../swap/DefaultSwapBestRateAnimationStore.kt | 21 ++ .../local/swap/SwapBestRateAnimationStore.kt | 11 + features/swap-v2/impl/build.gradle.kts | 1 + .../impl/amount/SwapAmountBlockComponent.kt | 1 + .../v2/impl/amount/entity/SwapAmountUM.kt | 1 + .../v2/impl/amount/model/SwapAmountModel.kt | 14 ++ .../SwapAmountPrimaryReadyStateTransformer.kt | 2 + ...wapAmountSecondaryReadyStateTransformer.kt | 2 + .../SwapAmountSetQuotesTransformer.kt | 2 +- .../impl/amount/ui/SwapAmountBlockContent.kt | 10 +- .../ui/preview/SwapAmountContentPreview.kt | 2 + .../ui/SwapChooseProviderContent.kt | 230 +++++++++++++++--- 13 files changed, 267 insertions(+), 39 deletions(-) create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/swap/DefaultSwapBestRateAnimationStore.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/swap/SwapBestRateAnimationStore.kt diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/SwapStoreModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/SwapStoreModule.kt index edf8c36cc3..682bf87475 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/SwapStoreModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/SwapStoreModule.kt @@ -1,6 +1,7 @@ package com.tangem.datasource.di import com.tangem.datasource.local.datastore.RuntimeDataStore +import com.tangem.datasource.local.datastore.RuntimeSharedStore import com.tangem.datasource.local.swap.DefaultSwapBestRateAnimationStore import com.tangem.datasource.local.swap.DefaultSwapTransactionStatusStore import com.tangem.datasource.local.swap.SwapBestRateAnimationStore @@ -22,4 +23,12 @@ object SwapStoreModule { dataStore = RuntimeDataStore(), ) } + + @Provides + @Singleton + fun provideSwapBestRateAnimationStore(): SwapBestRateAnimationStore { + return DefaultSwapBestRateAnimationStore( + dataStore = RuntimeSharedStore(), + ) + } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/swap/DefaultSwapBestRateAnimationStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/swap/DefaultSwapBestRateAnimationStore.kt new file mode 100644 index 0000000000..4f207731c1 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/swap/DefaultSwapBestRateAnimationStore.kt @@ -0,0 +1,21 @@ +package com.tangem.datasource.local.swap + +import com.tangem.datasource.local.datastore.RuntimeSharedStore + +internal class DefaultSwapBestRateAnimationStore( + private val dataStore: RuntimeSharedStore, +) : SwapBestRateAnimationStore, RuntimeSharedStore by dataStore { + /** + * Returns flag indicating whether should show best rate animation in current session. + * Animation should appear once per session + * + * If true, reset flag to false + */ + override suspend fun getSyncOrNull(): Boolean { + val value = dataStore.getSyncOrNull() ?: true + if (value) { + dataStore.store(false) + } + return value + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/swap/SwapBestRateAnimationStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/swap/SwapBestRateAnimationStore.kt new file mode 100644 index 0000000000..7fc8e8a1d0 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/swap/SwapBestRateAnimationStore.kt @@ -0,0 +1,11 @@ +package com.tangem.datasource.local.swap + +/** + * Stores flag indicating whether should show best rate animation in current session. + * Animation should appear once per session + * + * If true, reset flag to false + */ +interface SwapBestRateAnimationStore { + suspend fun getSyncOrNull(): Boolean +} \ No newline at end of file diff --git a/features/swap-v2/impl/build.gradle.kts b/features/swap-v2/impl/build.gradle.kts index c1d107eaf9..5cb929fe7e 100644 --- a/features/swap-v2/impl/build.gradle.kts +++ b/features/swap-v2/impl/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { implementation(projects.core.ui) implementation(projects.core.navigation) implementation(projects.core.configToggles) + implementation(projects.core.datasource) /** Common */ implementation(projects.common.ui) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt index 5917c12bf3..0d6966910d 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt @@ -68,6 +68,7 @@ internal class SwapAmountBlockComponent( onInfoClick = model::onInfoClick, isClickEnabled = isClickEnabled, onClick = onClick, + onFinishAnimation = model::onFinishAnimation, onProviderSelectClick = { val amountUM = model.uiState.value as? SwapAmountUM.Content ?: return@SwapAmountBlockContent val selectedProvider = amountUM.selectedQuote.provider ?: return@SwapAmountBlockContent diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt index fea9701d97..4c077efd90 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt @@ -51,6 +51,7 @@ internal sealed class SwapAmountUM { // extra data val appCurrency: AppCurrency?, + val showBestRateAnimation: Boolean, ) : SwapAmountUM() } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt index f1d6be3848..149ccd0de1 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt @@ -13,6 +13,7 @@ import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.ui.extensions.resourceReference +import com.tangem.datasource.local.swap.SwapBestRateAnimationStore import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.express.models.ExpressError @@ -73,6 +74,7 @@ internal class SwapAmountModel @Inject constructor( private val getAllowanceUseCase: GetAllowanceUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getUserCountryUseCase: GetUserCountryUseCase, + private val swapBestRateAnimationStore: SwapBestRateAnimationStore, private val appRouter: AppRouter, private val swapAmountAlertFactory: SwapAmountAlertFactory, private val swapAlertFactory: SwapAlertFactory, @@ -96,6 +98,8 @@ internal class SwapAmountModel @Inject constructor( var userCountry: UserCountry = UserCountry.Other(Locale.getDefault().country) val bottomSheetNavigation: SlotNavigation = SlotNavigation() + var showBestRateAnimation: Boolean = false + val uiState: StateFlow field = MutableStateFlow(params.amountUM) @@ -107,6 +111,7 @@ internal class SwapAmountModel @Inject constructor( appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default } userCountry = getUserCountryUseCase.invokeSync().getOrNull() ?: UserCountry.Other(Locale.getDefault().country) + showBestRateAnimation = swapBestRateAnimationStore.getSyncOrNull() } configAmountNavigation() subscribeOnCryptoCurrencyStatusFlow() @@ -245,6 +250,12 @@ internal class SwapAmountModel @Inject constructor( } } + fun onFinishAnimation() { + uiState.update { + (it as? SwapAmountUM.Content)?.copy(showBestRateAnimation = false) ?: it + } + } + private fun confirmSendWithSwapClose() { val amountParams = params as? SwapAmountComponentParams.AmountParams ?: return val amountFieldData = uiState.value.primaryAmount.amountField as? AmountState.Data @@ -259,6 +270,7 @@ internal class SwapAmountModel @Inject constructor( swapDirection = swapDirection, clickIntents = this, isBalanceHidden = params.isBalanceHidingFlow.value, + showBestRateAnimation = showBestRateAnimation, ), ) } @@ -292,6 +304,7 @@ internal class SwapAmountModel @Inject constructor( swapDirection = swapDirection, clickIntents = this, isBalanceHidden = params.isBalanceHidingFlow.value, + showBestRateAnimation = showBestRateAnimation, ), ) } @@ -399,6 +412,7 @@ internal class SwapAmountModel @Inject constructor( swapDirection = swapDirection, clickIntents = this@SwapAmountModel, isBalanceHidden = params.isBalanceHidingFlow.value, + showBestRateAnimation = showBestRateAnimation, ), ) startLoadingQuotesTask(isSilentReload = false) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt index 9aeeb9f2d7..f6c07b51ce 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt @@ -23,6 +23,7 @@ internal class SwapAmountPrimaryReadyStateTransformer( private val clickIntents: AmountScreenClickIntents, private val swapDirection: SwapDirection, private val isBalanceHidden: Boolean, + private val showBestRateAnimation: Boolean, ) : Transformer { private val amountFieldConverter = SwapAmountFieldConverter( @@ -52,6 +53,7 @@ internal class SwapAmountPrimaryReadyStateTransformer( swapQuotes = persistentListOf(), selectedQuote = SwapQuoteUM.Empty, appCurrency = appCurrency, + showBestRateAnimation = showBestRateAnimation, ) } } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt index 9f54cc94b6..729519512d 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt @@ -24,6 +24,7 @@ internal class SwapAmountSecondaryReadyStateTransformer( private val clickIntents: AmountScreenClickIntents, private val swapDirection: SwapDirection, private val isBalanceHidden: Boolean, + private val showBestRateAnimation: Boolean, ) : Transformer { private val amountFieldConverter = SwapAmountFieldConverter( @@ -51,6 +52,7 @@ internal class SwapAmountSecondaryReadyStateTransformer( swapQuotes = persistentListOf(), selectedQuote = SwapQuoteUM.Empty, appCurrency = appCurrency, + showBestRateAnimation = showBestRateAnimation, ) } } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt index 614f1d8b53..665a6a37fb 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt @@ -29,7 +29,7 @@ internal class SwapAmountSetQuotesTransformer( val selectedQuote = if (isSilentReload) { prevState.selectedQuote } else { - bestQuote + (bestQuote as? SwapQuoteUM.Content)?.copy(diffPercent = DifferencePercent.Best) ?: bestQuote } val selectQuoteTransformer = SwapAmountSelectQuoteTransformer( diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountBlockContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountBlockContent.kt index 291627de98..e2a6522a74 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountBlockContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountBlockContent.kt @@ -37,8 +37,9 @@ import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM import com.tangem.features.swap.v2.impl.amount.ui.preview.SwapAmountContentPreview import com.tangem.features.swap.v2.impl.chooseprovider.ui.SwapChooseProviderContent +import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM -@Suppress("DestructuringDeclarationWithTooManyEntries") +@Suppress("DestructuringDeclarationWithTooManyEntries", "LongParameterList") @Composable internal fun SwapAmountBlockContent( amountUM: SwapAmountUM, @@ -46,6 +47,7 @@ internal fun SwapAmountBlockContent( onProviderSelectClick: () -> Unit, onInfoClick: () -> Unit, onClick: () -> Unit, + onFinishAnimation: () -> Unit, modifier: Modifier = Modifier, ) { if (amountUM !is SwapAmountUM.Content) return @@ -98,9 +100,14 @@ internal fun SwapAmountBlockContent( end.linkTo(parent.end) }, ) + val quoteContent = amountUM.selectedQuote as? SwapQuoteUM.Content + val isBestRate = quoteContent?.diffPercent is SwapQuoteUM.Content.DifferencePercent.Best SwapChooseProviderContent( + isBestRate = isBestRate, + showBestRateAnimation = amountUM.showBestRateAnimation, expressProvider = amountUM.selectedQuote.provider, onClick = onProviderSelectClick, + onFinishAnimation = onFinishAnimation, modifier = Modifier.constrainAs(provider) { top.linkTo(to.bottom) bottom.linkTo(parent.bottom) @@ -195,6 +202,7 @@ private fun SwapAmountBlockContent_Preview() { onProviderSelectClick = {}, onInfoClick = {}, onClick = {}, + onFinishAnimation = {}, ) } } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt index e7bf485c35..df8d9da742 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt @@ -88,6 +88,7 @@ internal data object SwapAmountContentPreview { secondaryCryptoCurrencyStatus = cryptoCurrencyStatus, swapRateType = ExpressRateType.Float, appCurrency = AppCurrency.Default, + showBestRateAnimation = false, ) val defaultState = SwapAmountUM.Content( @@ -124,5 +125,6 @@ internal data object SwapAmountContentPreview { secondaryCryptoCurrencyStatus = cryptoCurrencyStatus, swapRateType = ExpressRateType.Float, isPrimaryButtonEnabled = true, + showBestRateAnimation = false, ) } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderContent.kt index 38a3b4718c..2ef78b6506 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderContent.kt @@ -1,6 +1,9 @@ package com.tangem.features.swap.v2.impl.chooseprovider.ui import android.content.res.Configuration +import androidx.compose.animation.* +import androidx.compose.animation.core.MutableTransitionState +import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource @@ -11,6 +14,8 @@ import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.material3.ripple import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -21,6 +26,10 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.constraintlayout.compose.ConstrainedLayoutReference +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.ConstraintLayoutScope +import androidx.constraintlayout.compose.Visibility import coil.compose.SubcomposeAsyncImage import coil.request.ImageRequest import com.tangem.core.ui.components.RectangleShimmer @@ -33,9 +42,17 @@ import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.express.models.ExpressProviderType import com.tangem.domain.express.models.ExpressRateType import com.tangem.features.swap.v2.impl.R +import kotlinx.coroutines.delay @Composable -fun SwapChooseProviderContent(expressProvider: ExpressProvider?, onClick: () -> Unit, modifier: Modifier = Modifier) { +fun SwapChooseProviderContent( + expressProvider: ExpressProvider?, + isBestRate: Boolean, + showBestRateAnimation: Boolean, + onClick: () -> Unit, + onFinishAnimation: () -> Unit, + modifier: Modifier = Modifier, +) { Column( modifier = modifier.clickable( interactionSource = remember { MutableInteractionSource() }, @@ -48,57 +65,193 @@ fun SwapChooseProviderContent(expressProvider: ExpressProvider?, onClick: () -> color = TangemTheme.colors.stroke.primary, modifier = Modifier.padding(horizontal = 12.dp), ) - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = modifier.padding(12.dp), - ) { + Row(verticalAlignment = Alignment.CenterVertically) { Icon( painter = rememberVectorPainter( ImageVector.vectorResource(R.drawable.ic_stack_new_24), ), tint = TangemTheme.colors.icon.accent, contentDescription = null, + modifier = Modifier.padding(start = 12.dp, top = 12.dp, bottom = 12.dp), ) Text( text = stringResourceSafe(R.string.express_provider), style = TangemTheme.typography.body2, color = TangemTheme.colors.text.primary1, - modifier = Modifier.padding(start = 8.dp), + modifier = Modifier.padding(start = 8.dp, top = 12.dp, bottom = 12.dp), ) SpacerWMax() - SubcomposeAsyncImage( - modifier = modifier - .size(20.dp) - .clip(RoundedCornerShape(4.dp)), - model = ImageRequest.Builder(context = LocalContext.current) - .data(expressProvider?.imageLarge) - .crossfade(enable = true) - .allowHardware(false) - .build(), - loading = { RectangleShimmer(radius = 4.dp) }, - error = { - Box( - modifier = Modifier.background( - color = TangemColorPalette.Light1, - shape = RoundedCornerShape(4.dp), - ), - ) + ProviderInfo(expressProvider, isBestRate, showBestRateAnimation, onFinishAnimation) + } + } +} + +@Composable +private fun ProviderInfo( + expressProvider: ExpressProvider?, + isBestRate: Boolean, + showBestRateAnimation: Boolean, + onFinishAnimation: () -> Unit, +) { + ConstraintLayout { + val (imageRef, nameRef, iconRef) = createRefs() + SubcomposeAsyncImage( + model = ImageRequest.Builder(context = LocalContext.current) + .data(expressProvider?.imageLarge) + .crossfade(enable = true) + .allowHardware(false) + .build(), + loading = { RectangleShimmer(radius = 4.dp) }, + error = { + Box( + modifier = Modifier.background( + color = TangemColorPalette.Light1, + shape = RoundedCornerShape(4.dp), + ), + ) + }, + contentDescription = null, + modifier = Modifier + .size(20.dp) + .clip(RoundedCornerShape(4.dp)) + .constrainAs(imageRef) { + start.linkTo(parent.start) + top.linkTo(parent.top) + bottom.linkTo(parent.bottom) }, - contentDescription = null, - ) + ) + Text( + text = expressProvider?.name.orEmpty(), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier + .padding(start = 6.dp) + .constrainAs(nameRef) { + start.linkTo(imageRef.end) + top.linkTo(parent.top) + bottom.linkTo(parent.bottom) + }, + ) + Icon( + painter = rememberVectorPainter( + ImageVector.vectorResource(R.drawable.ic_chevron_24), + ), + tint = TangemTheme.colors.icon.informative, + contentDescription = null, + modifier = Modifier + .padding(start = 4.dp) + .constrainAs(iconRef) { + start.linkTo(nameRef.end) + top.linkTo(parent.top) + bottom.linkTo(parent.bottom) + end.linkTo(parent.end, 12.dp) + }, + ) + BestRateBadge( + showBestRateAnimation = showBestRateAnimation, + isBestRate = isBestRate, + ref = imageRef, + onFinishAnimation = onFinishAnimation, + ) + } +} + +@Suppress("MagicNumber", "LongMethod") +@Composable +private fun ConstraintLayoutScope.BestRateBadge( + showBestRateAnimation: Boolean, + isBestRate: Boolean, + ref: ConstrainedLayoutReference, + onFinishAnimation: () -> Unit, + modifier: Modifier = Modifier, +) { + val animateState = remember { MutableTransitionState(false) } + + LaunchedEffect(showBestRateAnimation) { + if (showBestRateAnimation) { + delay(600L) + animateState.targetState = true + delay(1_500L) + animateState.targetState = false + onFinishAnimation() + } + } + + val iconSize by animateDpAsState( + label = "iconSize", + targetValue = if (animateState.targetState) { + 12.dp + } else { + 8.dp + }, + ) + val iconVerticalPaddings by animateDpAsState( + label = "iconVerticalPaddings", + targetValue = if (animateState.targetState) { + 3.dp + } else { + 2.dp + }, + ) + val iconHorizontalPaddings by animateDpAsState( + label = "iconHorizontalPaddings", + targetValue = if (animateState.targetState) { + 4.dp + } else { + 2.dp + }, + ) + + val startMargin by animateDpAsState( + label = "startMargin", + targetValue = if (animateState.targetState) { + (-12).dp + } else { + (-10).dp + }, + ) + + val topMargin by animateDpAsState( + label = "topMargin", + targetValue = if (animateState.targetState) { + (-12).dp + } else { + (-9).dp + }, + ) + + Row( + modifier = modifier + .constrainAs(createRef()) { + start.linkTo(ref.end, startMargin) + top.linkTo(ref.bottom, topMargin) + visibility = if (isBestRate) Visibility.Visible else Visibility.Gone + } + .background(TangemTheme.colors.stroke.transparency, RoundedCornerShape(120.dp)) + .padding(1.5.dp) + .background(TangemTheme.colors.icon.accent, RoundedCornerShape(120.dp)), + ) { + Icon( + painter = rememberVectorPainter( + ImageVector.vectorResource(R.drawable.ic_rounded_star_24), + ), + contentDescription = null, + tint = TangemTheme.colors.icon.constant, + modifier = Modifier + .padding(iconHorizontalPaddings, iconVerticalPaddings) + .size(iconSize), + ) + AnimatedVisibility( + visibleState = animateState, + enter = expandIn() + fadeIn(), + exit = shrinkOut() + fadeOut(), + label = "textAnimation", + modifier = Modifier.padding(end = 6.dp), + ) { Text( - text = expressProvider?.name.orEmpty(), // todo provider error - style = TangemTheme.typography.body2, - color = TangemTheme.colors.text.tertiary, - modifier = Modifier.padding(start = 6.dp), - ) - Icon( - painter = rememberVectorPainter( - ImageVector.vectorResource(R.drawable.ic_chevron_24), - ), - tint = TangemTheme.colors.icon.informative, - contentDescription = null, - modifier = Modifier.padding(start = 4.dp), + text = stringResourceSafe(R.string.express_provider_best_rate), + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.text.constantWhite, ) } } @@ -114,6 +267,8 @@ private fun SwapChooseProviderContent_Preview() { modifier = Modifier.background(TangemTheme.colors.background.tertiary), ) { SwapChooseProviderContent( + isBestRate = true, + showBestRateAnimation = true, expressProvider = ExpressProvider( providerId = "changelly", rateTypes = listOf(ExpressRateType.Fixed), @@ -126,6 +281,7 @@ private fun SwapChooseProviderContent_Preview() { slippage = null, ), onClick = {}, + onFinishAnimation = {}, ) } } From 14df650f1a0250682acbc139913de4779d0a47c1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Aug 2025 12:52:08 +0300 Subject: [PATCH 30/53] Updated on 2026-08-14 --- .../tangem/tap/common/extensions/Analytics.kt | 2 +- .../tap/di/domain/TransactionDomainModule.kt | 14 +++++++++-- .../cardsettings/model/CardSettingsModel.kt | 3 ++- .../tangem/tap/features/main/MainViewModel.kt | 8 +----- .../welcome/redux/WelcomeMiddleware.kt | 2 +- .../moonpay/MoonPayService.kt | 8 +++--- .../DefaultManageTokensRepository.kt | 9 +++---- .../tangem/data/nft/DefaultNFTRepository.kt | 7 ++---- .../data/onramp/DefaultHotCryptoRepository.kt | 11 ++------ ...faultMultiWalletCryptoCurrenciesFetcher.kt | 3 +-- ...aultMultiWalletCryptoCurrenciesProducer.kt | 4 +-- .../derivations/MissedDerivationsFinder.kt | 2 +- .../data/wallets/hot/HotWalletAccessor.kt | 3 ++- .../domain/card/common/extensions/CardSdk.kt | 14 +++++++++++ ...FilterAvailableNetworksForWalletUseCase.kt | 24 ++++++------------ .../usecase/PrepareForSendUseCase.kt | 12 ++++++--- .../domain/transaction/usecase/SignUseCase.kt | 25 ++++++++++++------- .../wallets/builder/HotUserWalletBuilder.kt | 2 +- .../wallets/extension/UserWalletExtensions.kt | 3 ++- .../features/details/model/DetailsModel.kt | 20 +++++++++------ .../v2/visa/impl/model/OnboardingVisaModel.kt | 3 ++- .../selecttoken/model/OnrampOperationModel.kt | 5 ++-- .../tokenlist/model/OnrampTokenListModel.kt | 2 -- .../v2/send/confirm/model/SendConfirmModel.kt | 7 +++++- .../features/send/v2/send/model/SendModel.kt | 6 ++++- .../confirm/model/NFTSendConfirmModel.kt | 7 +++++- .../send/v2/sendnft/model/NFTSendModel.kt | 6 ++++- .../impl/presentation/model/StakingModel.kt | 4 +++ .../swap/v2/impl/common/SwapAlertFactory.kt | 6 ++++- .../tangem/feature/swap/model/SwapModel.kt | 9 +++++-- .../model/WalletSettingsModel.kt | 9 +++---- .../intents/WalletWarningsClickIntents.kt | 17 ++++++++++--- .../UpdateWalletCardsCountTransformer.kt | 5 ++-- .../state/utils/WalletLoadingStateFactory.kt | 4 +-- 34 files changed, 161 insertions(+), 105 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Analytics.kt b/app/src/main/java/com/tangem/tap/common/extensions/Analytics.kt index f2e7d342ef..e09ba24e78 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Analytics.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Analytics.kt @@ -24,7 +24,7 @@ fun Analytics.setContext(scanResponse: ScanResponse) { fun Analytics.setContext(userWallet: UserWallet) { setUserId(userWallet.walletId.stringValue) - // TODO add product type for hot ([REDACTED_TASK_KEY]) + // TODO add product type for hot ([REDACTED_TASK_KEY] [Hot Wallet] Analytics) if (userWallet is UserWallet.Cold) { addParamsInterceptor(LinkedCardContextInterceptor(userWallet.scanResponse)) diff --git a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt index 3538d33584..ffd34ca773 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt @@ -180,8 +180,13 @@ internal object TransactionDomainModule { fun providePrepareForSendUseCase( transactionRepository: TransactionRepository, cardSdkConfigRepository: CardSdkConfigRepository, + tangemHotWalletSignerFactory: TangemHotWalletSigner.Factory, ): PrepareForSendUseCase { - return PrepareForSendUseCase(transactionRepository, cardSdkConfigRepository) + return PrepareForSendUseCase( + transactionRepository = transactionRepository, + cardSdkConfigRepository = cardSdkConfigRepository, + getHotTransactionSigner = { tangemHotWalletSignerFactory.create(it) }, + ) } @Provides @@ -189,8 +194,13 @@ internal object TransactionDomainModule { fun provideSignUseCase( walletManagersFacade: WalletManagersFacade, cardSdkConfigRepository: CardSdkConfigRepository, + tangemHotWalletSignerFactory: TangemHotWalletSigner.Factory, ): SignUseCase { - return SignUseCase(cardSdkConfigRepository, walletManagersFacade) + return SignUseCase( + cardSdkConfigRepository = cardSdkConfigRepository, + walletManagersFacade = walletManagersFacade, + getHotTransactionSigner = { tangemHotWalletSignerFactory.create(it) }, + ) } @Provides diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/model/CardSettingsModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/model/CardSettingsModel.kt index e71a54fcdd..1d5b58407d 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/model/CardSettingsModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/model/CardSettingsModel.kt @@ -87,9 +87,10 @@ internal class CardSettingsModel @Inject constructor( val userWallet = getUserWalletUseCase(userWalletId) .getOrElse { error("User wallet $userWalletId not found") } + .requireColdWallet() cardSdkConfigRepository.isBiometricsRequestPolicy = - userWallet.requireColdWallet().scanResponse.card.isAccessCodeSet && // TODO [REDACTED_TASK_KEY] + userWallet.scanResponse.card.isAccessCodeSet && settingsRepository.shouldSaveAccessCodes() } } diff --git a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt index 6b5c655b9e..643710e4c2 100644 --- a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt @@ -24,8 +24,6 @@ import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.balancehiding.ListenToFlipsUseCase import com.tangem.domain.balancehiding.UpdateBalanceHidingSettingsUseCase import com.tangem.domain.common.LogConfig -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.notifications.GetApplicationIdUseCase import com.tangem.domain.notifications.SendPushTokenUseCase import com.tangem.domain.notifications.models.ApplicationId @@ -212,15 +210,11 @@ internal class MainViewModel @Inject constructor( } private fun makeSellExchangeService(environmentConfig: EnvironmentConfig): ExchangeService { - val cardProvider: () -> ScanResponse? = { - userWalletsListManager.selectedUserWalletSync?.requireColdWallet()?.scanResponse // TODO [REDACTED_TASK_KEY] - } - return MoonPayService( apiKey = environmentConfig.moonPayApiKey, secretKey = environmentConfig.moonPayApiSecretKey, logEnabled = LogConfig.network.moonPayService, - cardProvider = { cardProvider.invoke()?.card }, + userWalletProvider = { userWalletsListManager.selectedUserWalletSync }, ) } diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index 15c1ace4f6..37690ee484 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -134,7 +134,7 @@ internal class WelcomeMiddleware { } private fun sendSignedInAnalyticsEvent(userWallet: UserWallet, signInType: Basic.SignedIn.SignInType) { - // TODO [REDACTED_TASK_KEY] + // TODO [REDACTED_TASK_KEY] [Hot Wallet] Analytics if (userWallet !is UserWallet.Cold) { return diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt index f57f247e74..02bc4321ae 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt @@ -12,7 +12,7 @@ import com.tangem.domain.core.utils.lceContent import com.tangem.domain.core.utils.lceError import com.tangem.domain.core.utils.lceLoading import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.models.wallet.UserWallet import com.tangem.tap.domain.model.Currency import com.tangem.tap.network.exchangeServices.ExchangeService import com.tangem.tap.network.exchangeServices.ExchangeServiceInitializationStatus @@ -27,7 +27,7 @@ class MoonPayService( private val apiKey: String, private val secretKey: String, private val logEnabled: Boolean, - private val cardProvider: () -> CardDTO?, + private val userWalletProvider: () -> UserWallet?, ) : ExchangeService { override val initializationStatus: StateFlow @@ -103,8 +103,8 @@ class MoonPayService( } override fun availableForSell(currency: Currency): Boolean { - val card = cardProvider() ?: return false - val checkCardExchange = !card.isStart2Coin + val userWallet = userWalletProvider() ?: return false + val checkCardExchange = userWallet !is UserWallet.Cold || !userWallet.scanResponse.card.isStart2Coin if (!checkCardExchange) return false diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt index 20cdc81e55..18929af1a4 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt @@ -197,11 +197,10 @@ internal class DefaultManageTokensRepository( ) private fun getSupportedBlockchains(userWallet: UserWallet?): List { - return (userWallet as? UserWallet.Cold)?.scanResponse?.let { - it.card.supportedBlockchains(it.cardTypesResolver, excludedBlockchains) // TODO [REDACTED_TASK_KEY] - } ?: Blockchain.entries.filter { - !it.isTestnet() && it !in excludedBlockchains - } + return userWallet?.supportedBlockchains(excludedBlockchains) + ?: Blockchain.entries.filter { + !it.isTestnet() && it !in excludedBlockchains + } } // endregion diff --git a/data/nft/src/main/kotlin/com/tangem/data/nft/DefaultNFTRepository.kt b/data/nft/src/main/kotlin/com/tangem/data/nft/DefaultNFTRepository.kt index 585b0a640a..7ceb082dd5 100644 --- a/data/nft/src/main/kotlin/com/tangem/data/nft/DefaultNFTRepository.kt +++ b/data/nft/src/main/kotlin/com/tangem/data/nft/DefaultNFTRepository.kt @@ -17,12 +17,10 @@ import com.tangem.datasource.local.nft.converter.NFTSdkCollectionConverter import com.tangem.datasource.local.nft.converter.NFTSdkCollectionIdentifierConverter import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.card.common.extensions.canHandleToken -import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.nft.models.NFTAsset import com.tangem.domain.nft.models.NFTCollection import com.tangem.domain.nft.models.NFTCollections @@ -542,11 +540,10 @@ internal class DefaultNFTRepository @Inject constructor( } private fun Network.canHandleNFTs(userWalletId: UserWalletId): Boolean { - // TODO [REDACTED_TASK_KEY] - val scanResponse = userWalletsStore.getSyncStrict(userWalletId).requireColdWallet().scanResponse + val userWallet = userWalletsStore.getSyncStrict(userWalletId) val blockchain = Blockchain.fromNetworkId(backendId) ?: return false return blockchain.canHandleNFTs() && - scanResponse.card.canHandleToken(blockchain, scanResponse.cardTypesResolver, excludedBlockchains) + userWallet.canHandleToken(blockchain, excludedBlockchains) } } \ No newline at end of file diff --git a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultHotCryptoRepository.kt b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultHotCryptoRepository.kt index 8cda8c8835..cfffffb159 100644 --- a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultHotCryptoRepository.kt +++ b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultHotCryptoRepository.kt @@ -22,7 +22,6 @@ import com.tangem.datasource.local.token.UserTokensResponseStore import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.card.common.extensions.canHandleBlockchain import com.tangem.domain.card.common.extensions.canHandleToken -import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.onramp.model.HotCryptoCurrency @@ -170,23 +169,17 @@ internal class DefaultHotCryptoRepository( // TODO: [REDACTED_JIRA] private fun UserWallet.canHandleHotCrypto(hotToken: HotCryptoResponse.Token): Boolean { - if (this !is UserWallet.Cold) { - return true // TODO [REDACTED_TASK_KEY] - } - val isToken = hotToken.contractAddress != null && hotToken.decimalCount != null val blockchain = hotToken.networkId?.let { Blockchain.fromNetworkId(it) } ?: return false return if (isToken) { - scanResponse.card.canHandleToken( + canHandleToken( blockchain = blockchain, - cardTypesResolver = cardTypesResolver, excludedBlockchains = excludedBlockchains, ) } else { - scanResponse.card.canHandleBlockchain( + canHandleBlockchain( blockchain = blockchain, - cardTypesResolver = cardTypesResolver, excludedBlockchains = excludedBlockchains, ) } diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/DefaultMultiWalletCryptoCurrenciesFetcher.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/DefaultMultiWalletCryptoCurrenciesFetcher.kt index a05b79077f..896fd35e4f 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/DefaultMultiWalletCryptoCurrenciesFetcher.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/DefaultMultiWalletCryptoCurrenciesFetcher.kt @@ -18,7 +18,6 @@ import com.tangem.domain.core.utils.catchOn import com.tangem.domain.demo.models.DemoConfig import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.isMultiCurrency -import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher.Params import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -68,7 +67,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcher( } }, onError = { - handleFetchTokensError(error = it, userWallet = userWallet.requireColdWallet()) // TODO 11142 + handleFetchTokensError(error = it, userWallet = userWallet) }, ) } diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/DefaultMultiWalletCryptoCurrenciesProducer.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/DefaultMultiWalletCryptoCurrenciesProducer.kt index 9d73ca64a0..a959276e85 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/DefaultMultiWalletCryptoCurrenciesProducer.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/DefaultMultiWalletCryptoCurrenciesProducer.kt @@ -4,7 +4,7 @@ import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory import com.tangem.datasource.local.token.UserTokensResponseStore import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.models.wallet.requireColdWallet +import com.tangem.domain.models.wallet.isMultiCurrency import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesProducer import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.assisted.Assisted @@ -35,7 +35,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducer @AssistedInject constr get() = emptySet() override fun produce(): Flow> { - val userWallet = userWalletsStore.getSyncStrict(key = params.userWalletId).requireColdWallet() // TODO [REDACTED_TASK_KEY] + val userWallet = userWalletsStore.getSyncStrict(key = params.userWalletId) if (!userWallet.isMultiCurrency) { error("${this::class.simpleName} supports only multi-currency wallet") diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/derivations/MissedDerivationsFinder.kt b/data/wallets/src/main/java/com/tangem/data/wallets/derivations/MissedDerivationsFinder.kt index 5ea8f38be1..dffa80fd65 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/derivations/MissedDerivationsFinder.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/derivations/MissedDerivationsFinder.kt @@ -53,7 +53,7 @@ internal class MissedDerivationsFinder(private val userWallet: UserWallet) { private fun List.mapToNewDerivations(): List { val config = when (userWallet) { is UserWallet.Cold -> CardConfig.createConfig(userWallet.scanResponse.card) - is UserWallet.Hot -> Wallet2CardConfig // TODO create config [REDACTED_TASK_KEY] + is UserWallet.Hot -> Wallet2CardConfig // TODO [REDACTED_TASK_KEY] [Hot Wallet] Derivation config for hot wallet } return mapNotNull { network -> val blockchain = network.toBlockchain() diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/hot/HotWalletAccessor.kt b/data/wallets/src/main/java/com/tangem/data/wallets/hot/HotWalletAccessor.kt index 9ae11b61c8..c7569b5a3b 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/hot/HotWalletAccessor.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/hot/HotWalletAccessor.kt @@ -46,7 +46,8 @@ class HotWalletAccessor @Inject constructor( auth = auth, block = { blockAuth -> block(blockAuth).also { - // TODO [REDACTED_TASK_KEY] if user has biometry enabled, we set it as the new auth method + // TODO [REDACTED_TASK_KEY] [Hot Wallet] Authorization by access code + // if user has biometry enabled, we set it as the new auth method if (blockAuth is HotAuth.Password /*&& has biometry enabled */) { tangemHotSdk.changeAuth( unlockHotWallet = UnlockHotWallet( diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/common/extensions/CardSdk.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/common/extensions/CardSdk.kt index 32bc4a3ecc..a189ade273 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/common/extensions/CardSdk.kt +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/common/extensions/CardSdk.kt @@ -100,6 +100,20 @@ fun UserWallet.canHandleToken(blockchain: Blockchain, excludedBlockchains: Exclu } } +fun UserWallet.canHandleBlockchain(blockchain: Blockchain, excludedBlockchains: ExcludedBlockchains): Boolean { + return when (this) { + is UserWallet.Cold -> { + scanResponse.card.canHandleBlockchain( + blockchain = blockchain, + excludedBlockchains = excludedBlockchains, + cardTypesResolver = scanResponse.cardTypesResolver, + ) + } + is UserWallet.Hot -> blockchain.isTestnet().not() && + blockchain !in excludedBlockchains + } +} + /** * The same as [CardDTO.supportedTokens] but with supportedTokens input, if previously calculated */ diff --git a/domain/markets/src/main/java/com/tangem/domain/markets/FilterAvailableNetworksForWalletUseCase.kt b/domain/markets/src/main/java/com/tangem/domain/markets/FilterAvailableNetworksForWalletUseCase.kt index 7d01fd1314..f1c80f3ba0 100644 --- a/domain/markets/src/main/java/com/tangem/domain/markets/FilterAvailableNetworksForWalletUseCase.kt +++ b/domain/markets/src/main/java/com/tangem/domain/markets/FilterAvailableNetworksForWalletUseCase.kt @@ -4,8 +4,6 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.domain.card.common.extensions.supportedBlockchains -import com.tangem.domain.card.common.util.cardTypesResolver -import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.wallets.legacy.UserWalletsListManager @@ -26,21 +24,13 @@ class FilterAvailableNetworksForWalletUseCase( it.walletId == userWalletId } ?: return networks.toSet() - return when (userWallet) { - is UserWallet.Cold -> { - val supportedBlockchains = userWallet.scanResponse.card.supportedBlockchains( - cardTypesResolver = userWallet.scanResponse.cardTypesResolver, - excludedBlockchains = excludedBlockchains, - ) + val supportedBlockchains = userWallet.supportedBlockchains( + excludedBlockchains = excludedBlockchains, + ) - networks.filter { - val blockchain = Blockchain.fromNetworkId(it.networkId) - supportedBlockchains.contains(blockchain) - }.toSet() - } - is UserWallet.Hot -> { - networks.toSet() // TODO [REDACTED_TASK_KEY] - } - } + return networks.filter { + val blockchain = Blockchain.fromNetworkId(it.networkId) + supportedBlockchains.contains(blockchain) + }.toSet() } } \ No newline at end of file diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/PrepareForSendUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/PrepareForSendUseCase.kt index 36f6ad081d..a6f1353437 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/PrepareForSendUseCase.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/PrepareForSendUseCase.kt @@ -10,14 +10,14 @@ import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins import com.tangem.domain.card.models.TwinKey import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.models.network.Network +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.transaction.TransactionRepository import com.tangem.domain.transaction.error.SendTransactionError -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.requireColdWallet class PrepareForSendUseCase( private val transactionRepository: TransactionRepository, private val cardSdkConfigRepository: CardSdkConfigRepository, + private val getHotTransactionSigner: (UserWallet.Hot) -> TransactionSigner, ) { suspend operator fun invoke( transactionData: TransactionData, @@ -56,7 +56,13 @@ class PrepareForSendUseCase( } private fun createSigner(userWallet: UserWallet): TransactionSigner { - userWallet.requireColdWallet() // TODO [REDACTED_TASK_KEY] + return when (userWallet) { + is UserWallet.Hot -> getHotTransactionSigner(userWallet) + is UserWallet.Cold -> getColdSigner(userWallet) + } + } + + private fun getColdSigner(userWallet: UserWallet.Cold): TransactionSigner { val card = userWallet.scanResponse.card val isCardNotBackedUp = card.backupStatus?.isActive != true && !card.isTangemTwins diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SignUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SignUseCase.kt index ec98b2d43c..07b302c99c 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SignUseCase.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SignUseCase.kt @@ -3,6 +3,7 @@ package com.tangem.domain.transaction.usecase import arrow.core.Either import arrow.core.left import arrow.core.right +import com.tangem.blockchain.common.TransactionSigner import com.tangem.common.CompletionResult import com.tangem.common.core.TangemError import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins @@ -11,25 +12,21 @@ import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.models.network.Network import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.requireColdWallet class SignUseCase( private val cardSdkConfigRepository: CardSdkConfigRepository, private val walletManagersFacade: WalletManagersFacade, + private val getHotTransactionSigner: (UserWallet.Hot) -> TransactionSigner, ) { suspend operator fun invoke( hash: ByteArray, userWallet: UserWallet, network: Network, ): Either { - userWallet.requireColdWallet() // TODO [REDACTED_TASK_KEY] - val card = userWallet.scanResponse.card - val isCardNotBackedUp = card.backupStatus?.isActive != true && !card.isTangemTwins - - val signer = cardSdkConfigRepository.getCommonSigner( - cardId = card.cardId.takeIf { isCardNotBackedUp }, - twinKey = TwinKey.getOrNull(scanResponse = userWallet.scanResponse), - ) + val signer = when (userWallet) { + is UserWallet.Hot -> getHotTransactionSigner(userWallet) + is UserWallet.Cold -> getColdSigner(userWallet) + } val walletManager = walletManagersFacade.getOrCreateWalletManager(userWallet.walletId, network) ?: error("WalletManager not found") @@ -39,4 +36,14 @@ class SignUseCase( is CompletionResult.Success -> signResult.data.right() } } + + private fun getColdSigner(userWallet: UserWallet.Cold): TransactionSigner { + val card = userWallet.scanResponse.card + val isCardNotBackedUp = card.backupStatus?.isActive != true && !card.isTangemTwins + + return cardSdkConfigRepository.getCommonSigner( + cardId = card.cardId.takeIf { isCardNotBackedUp }, + twinKey = TwinKey.getOrNull(scanResponse = userWallet.scanResponse), + ) + } } \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/builder/HotUserWalletBuilder.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/builder/HotUserWalletBuilder.kt index 8b8bda5d78..4707a09f81 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/builder/HotUserWalletBuilder.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/builder/HotUserWalletBuilder.kt @@ -25,7 +25,7 @@ class HotUserWalletBuilder @AssistedInject constructor( ) { suspend fun build(): UserWallet.Hot = withContext(dispatcherProvider.default) { - val allNetworks = Blockchain.entries // TODO [REDACTED_TASK_KEY] add derivation config + val allNetworks = Blockchain.entries // TODO [REDACTED_TASK_KEY] [Hot Wallet] Derivation config for hot wallet val curves = allNetworks.map { it.getSupportedCurves() }.flatten().toSet() val requests = curves.sortedBy { it.ordinal }.map { curve -> val derivationPaths = allNetworks.filter { curve in it.getSupportedCurves() } diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/extension/UserWalletExtensions.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/extension/UserWalletExtensions.kt index 99348982ad..25d25bd977 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/extension/UserWalletExtensions.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/extension/UserWalletExtensions.kt @@ -13,7 +13,8 @@ fun UserWallet.hasDerivation(blockchain: Blockchain, derivationPath: String): Bo return when (this) { is UserWallet.Cold -> scanResponse.hasDerivation(blockchain, derivationPath) is UserWallet.Hot -> { - val primaryCurve = Wallet2CardConfig.primaryCurve(blockchain) // TODO [REDACTED_TASK_KEY]: handle hot wallet config + // TODO [REDACTED_TASK_KEY] [Hot Wallet] Derivation config for hot wallet + val primaryCurve = Wallet2CardConfig.primaryCurve(blockchain) val list = if (blockchain == Blockchain.Cardano) { listOf( CardanoUtils.extendedDerivationPath(DerivationPath(derivationPath)), diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/model/DetailsModel.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/model/DetailsModel.kt index a5b029fa67..e02206355e 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/model/DetailsModel.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/model/DetailsModel.kt @@ -118,10 +118,14 @@ internal class DetailsModel @Inject constructor( modelScope.launch { val userWallets = getWalletsUseCase.invokeSync() - val scanResponse = - getSelectedWalletSyncUseCase().getOrNull()?.requireColdWallet()?.scanResponse // TODO [REDACTED_TASK_KEY] - ?: error("Selected wallet is null") + val selectedUserWallet = getSelectedWalletSyncUseCase().getOrNull() + ?: error("Selected wallet is null") + if (selectedUserWallet is UserWallet.Hot) { + return@launch // TODO [REDACTED_TASK_KEY] [Hot Wallet] Send feedback + } + + val scanResponse = selectedUserWallet.requireColdWallet().scanResponse val cardInfo = getCardInfoUseCase(scanResponse).getOrNull() ?: return@launch val feedbackType = when { @@ -140,11 +144,13 @@ internal class DetailsModel @Inject constructor( } private fun openUseDesk() { - val scanResponse = - getSelectedWalletSyncUseCase().getOrNull()?.requireColdWallet()?.scanResponse // TODO [REDACTED_TASK_KEY] - ?: error("Selected wallet is null") + val userWallet = getSelectedWalletSyncUseCase().getOrNull() ?: error("Selected wallet is null") - val cardInfo = getCardInfoUseCase(scanResponse).getOrNull() ?: return + if (userWallet is UserWallet.Hot) { + return // TODO [REDACTED_TASK_KEY] [Hot Wallet] UseDesk + } + + val cardInfo = getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return router.push(AppRoute.Usedesk(cardInfo)) } diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/model/OnboardingVisaModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/model/OnboardingVisaModel.kt index 65fc1a9b0f..4de22cecd9 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/model/OnboardingVisaModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/model/OnboardingVisaModel.kt @@ -215,7 +215,8 @@ internal class OnboardingVisaModel @Inject constructor( private fun tryToFindExistingWalletCardId(targetAddress: String): String? { val wallets = getWalletsUseCase.invokeSync().filter { it.isLocked.not() } - return wallets.filterIsInstance().firstOrNull { wallet -> // TODO [REDACTED_TASK_KEY] + // TODO [REDACTED_TASK_KEY] [Hot Wallet] Visa 1.0 flow. Hot wallet as a customer wallet + return wallets.filterIsInstance().firstOrNull { wallet -> wallet.scanResponse.card.wallets.any { val derivedKey = it.derivedKeys[VisaUtilities.visaDefaultDerivationPath] ?: return@any false diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/model/OnrampOperationModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/model/OnrampOperationModel.kt index d889c54e08..890446c68e 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/model/OnrampOperationModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/model/OnrampOperationModel.kt @@ -18,7 +18,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.models.currency.CryptoCurrencyStatus -import com.tangem.domain.models.wallet.requireColdWallet +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.onramp.model.OnrampSource import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.tokens.legacy.TradeCryptoAction @@ -57,7 +57,6 @@ internal class OnrampOperationModel @Inject constructor( private val selectedUserWallet = getWalletsUseCase.invokeSync() .first { it.walletId == params.userWalletId } - .requireColdWallet() init { analyticsEventHandler.send( @@ -154,7 +153,7 @@ internal class OnrampOperationModel @Inject constructor( } private fun showErrorIfDemoModeOrElse(action: () -> Unit) { - if (isDemoCardUseCase(cardId = selectedUserWallet.cardId)) { + if (selectedUserWallet is UserWallet.Cold && isDemoCardUseCase(cardId = selectedUserWallet.cardId)) { val alertUM = AlertDemoModeUM(onConfirmClick = {}) val message = DialogMessage( diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt index d2ce88c2d0..2cdae1912c 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt @@ -15,7 +15,6 @@ import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.tokenlist.TokenList -import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.settings.usercountry.GetUserCountryUseCase import com.tangem.domain.settings.usercountry.models.UserCountry import com.tangem.domain.tokens.GetTokenListUseCase @@ -61,7 +60,6 @@ internal class OnrampTokenListModel @Inject constructor( private val params: OnrampTokenListComponent.Params = paramsContainer.require() private val userWallet by lazy { getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId } - .requireColdWallet() // TODO [REDACTED_TASK_KEY] } init { diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt index 8dac9245d8..2296485619 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt @@ -26,6 +26,7 @@ import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.BlockchainErrorInfo import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.settings.IsSendTapHelpEnabledUseCase import com.tangem.domain.settings.NeverShowTapHelpUseCase @@ -325,8 +326,12 @@ internal class SendConfirmModel @Inject constructor( ), ) + if (userWallet is UserWallet.Hot) { + return // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow + } + val cardInfo = - getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return // TODO [REDACTED_TASK_KEY] + getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return modelScope.launch { sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(cardInfo = cardInfo)) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt index d23c93d631..de07147e93 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt @@ -465,8 +465,12 @@ internal class SendModel @Inject constructor( ), ) + if (userWallet is UserWallet.Hot) { + return // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow + } + val cardInfo = - getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return // TODO [REDACTED_TASK_KEY] + getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return modelScope.launch { sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(cardInfo = cardInfo)) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt index cdb172282b..734bc45601 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt @@ -21,6 +21,7 @@ import com.tangem.domain.feedback.SaveBlockchainErrorUseCase import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.BlockchainErrorInfo import com.tangem.domain.feedback.models.FeedbackEmailType +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.settings.IsSendTapHelpEnabledUseCase import com.tangem.domain.settings.NeverShowTapHelpUseCase import com.tangem.domain.transaction.usecase.CreateNFTTransferTransactionUseCase @@ -231,8 +232,12 @@ internal class NFTSendConfirmModel @Inject constructor( ), ) + if (userWallet is UserWallet.Hot) { + return // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow + } + val cardInfo = - getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return // TODO [REDACTED_TASK_KEY] + getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return modelScope.launch { sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(cardInfo = cardInfo)) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt index 287366db87..0bebb6f107 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt @@ -206,8 +206,12 @@ internal class NFTSendModel @Inject constructor( ), ) + if (userWallet is UserWallet.Hot) { + return // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow + } + val cardInfo = - getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return // TODO [REDACTED_TASK_KEY] + getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return modelScope.launch { sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(cardInfo = cardInfo)) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt index d5efc4972b..31e0e9e58c 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt @@ -859,6 +859,10 @@ internal class StakingModel @Inject constructor( modelScope.launch { val network = cryptoCurrencyStatus.currency.network + if (userWallet is UserWallet.Hot) { + return@launch // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow + } + val cardInfo = getCardInfoUseCase(userWallet.requireColdWallet().scanResponse) .getOrElse { error("CardInfo must be not null") } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/SwapAlertFactory.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/SwapAlertFactory.kt index 7327c593f4..9ea883b4e5 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/SwapAlertFactory.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/SwapAlertFactory.kt @@ -96,8 +96,12 @@ internal class SwapAlertFactory @Inject constructor( ), ) + if (userWallet is UserWallet.Hot) { + return // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow + } + val cardInfo = - getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return // TODO [REDACTED_TASK_KEY] + getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return sendFeedbackEmailUseCase( type = FeedbackEmailType.SwapProblem( diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt index a748df6f52..2e8026f8db 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt @@ -1351,8 +1351,6 @@ internal class SwapModel @Inject constructor( val transaction = dataState.swapDataModel?.transaction val fromCurrencyStatus = dataState.fromCryptoCurrency ?: initialFromStatus val network = fromCurrencyStatus.currency.network - val cardInfo = getCardInfoUseCase(userWallet.requireColdWallet().scanResponse) // TODO [REDACTED_TASK_KEY] - .getOrElse { error("CardInfo must be not null") } saveBlockchainErrorUseCase( error = BlockchainErrorInfo( @@ -1366,6 +1364,13 @@ internal class SwapModel @Inject constructor( ), ) + if (userWallet is UserWallet.Hot) { + return@launch // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow + } + + val cardInfo = getCardInfoUseCase(userWallet.requireColdWallet().scanResponse) + .getOrElse { error("CardInfo must be not null") } + val email = FeedbackEmailType.SwapProblem( cardInfo = cardInfo, providerName = dataState.selectedProvider?.name.orEmpty(), diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt index dcdfa6c203..42ea4e9a86 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt @@ -96,6 +96,7 @@ internal class WalletSettingsModel @Inject constructor( getWalletNotificationsEnabledUseCase(params.userWalletId), ) { maybeWallet, nftEnabled, notificationsEnabled -> val wallet = maybeWallet.getOrNull() ?: return@combine + wallet.requireColdWallet() // TODO [REDACTED_TASK_KEY] [Hot Wallet] Wallet Settings val isRenameWalletAvailable = getShouldSaveUserWalletsSyncUseCase() state.update { value -> value.copy( @@ -126,7 +127,7 @@ internal class WalletSettingsModel @Inject constructor( } private fun buildItems( - userWallet: UserWallet, + userWallet: UserWallet.Cold, dialogNavigation: SlotNavigation, isRenameWalletAvailable: Boolean, isNFTEnabled: Boolean, @@ -137,9 +138,8 @@ internal class WalletSettingsModel @Inject constructor( ): PersistentList = itemsBuilder.buildItems( userWalletId = userWallet.walletId, userWalletName = userWallet.name, - isReferralAvailable = userWallet !is UserWallet.Cold || userWallet.cardTypesResolver.isTangemWallet(), - isLinkMoreCardsAvailable = userWallet is UserWallet.Cold && - userWallet.scanResponse.card.backupStatus == CardDTO.BackupStatus.NoBackup, + isReferralAvailable = userWallet.cardTypesResolver.isTangemWallet(), + isLinkMoreCardsAvailable = userWallet.scanResponse.card.backupStatus == CardDTO.BackupStatus.NoBackup, isManageTokensAvailable = userWallet.isMultiCurrency, isRenameWalletAvailable = isRenameWalletAvailable, renameWallet = { openRenameWalletDialog(userWallet, dialogNavigation) }, @@ -162,7 +162,6 @@ internal class WalletSettingsModel @Inject constructor( messageSender.send(message) }, onLinkMoreCardsClick = { - userWallet.requireColdWallet() onLinkMoreCardsClick(scanResponse = userWallet.scanResponse) }, onReferralClick = { onReferralClick(userWallet) }, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt index b6c6a7f60b..fa98ada4f2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt @@ -243,8 +243,13 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( modelScope.launch(dispatchers.main) { neverToSuggestRateAppUseCase() - val scanResponse = - getSelectedUserWallet()?.requireColdWallet()?.scanResponse ?: return@launch // TODO [REDACTED_TASK_KEY] + val userWallet = getSelectedUserWallet() ?: return@launch + + if (userWallet is UserWallet.Hot) { + return@launch // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow + } + + val scanResponse = userWallet.requireColdWallet().scanResponse val cardInfo = getCardInfoUseCase(scanResponse).getOrNull() ?: return@launch sendFeedbackEmailUseCase(type = FeedbackEmailType.RateCanBeBetter(cardInfo = cardInfo)) @@ -285,7 +290,13 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( } override fun onSupportClick() { - val scanResponse = getSelectedUserWallet()?.requireColdWallet()?.scanResponse ?: return // TODO [REDACTED_TASK_KEY] + val userWallet = getSelectedUserWallet() ?: return + + if (userWallet is UserWallet.Hot) { + return // TODO [REDACTED_TASK_KEY] [Hot Wallet] Email feedback flow + } + + val scanResponse = userWallet.requireColdWallet().scanResponse val cardInfo = getCardInfoUseCase(scanResponse).getOrNull() ?: return modelScope.launch { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UpdateWalletCardsCountTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UpdateWalletCardsCountTransformer.kt index 5099b4e6b9..e717d41de5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UpdateWalletCardsCountTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UpdateWalletCardsCountTransformer.kt @@ -2,7 +2,6 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.domain.card.common.util.getCardsCount import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState @@ -40,8 +39,8 @@ internal class UpdateWalletCardsCountTransformer( return when (this) { is WalletCardState.Content -> copy( additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = userWallet), - imageResId = walletImageResolver.resolve(userWallet = userWallet.requireColdWallet()), // TODO [REDACTED_TASK_KEY] - cardCount = userWallet.requireColdWallet().getCardsCount(), // TODO [REDACTED_TASK_KEY] + imageResId = walletImageResolver.resolve(userWallet = userWallet), + cardCount = (userWallet as? UserWallet.Cold)?.getCardsCount(), ) else -> this } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt index 9307b4c066..a475345feb 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt @@ -45,8 +45,8 @@ internal class WalletLoadingStateFactory( walletCardState = WalletCardState.Loading( id = userWallet.walletId, title = userWallet.name, - additionalInfo = null, // TODO [REDACTED_TASK_KEY] - imageResId = null, // TODO [REDACTED_TASK_KEY] + additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = userWallet), + imageResId = null, dropDownItems = persistentListOf(), ), buttons = createMultiWalletActions(userWallet), From be160483d2af90b98829498bd2615b68520b8dd1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Aug 2025 13:54:22 +0400 Subject: [PATCH 31/53] Updated on 2026-08-14 --- .../di/domain/NotificationsDomainModule.kt | 35 +++- .../tangem/tap/routing/utils/ChildFactory.kt | 1 + .../com/tangem/common/routing/AppRoute.kt | 1 + .../common/ui/notifications/NotificationId.kt | 11 ++ .../local/preferences/PreferencesKeys.kt | 2 + .../DefaultNotificationsRepository.kt | 61 +----- .../DefaultPushNotificationsRepository.kt | 68 +++++++ .../notifications/di/NotificationsModule.kt | 6 + .../DefaultNotificationsRepositoryTest.kt | 175 +++++------------ .../DefaultPushNotificationsRepositoryTest.kt | 178 ++++++++++++++++++ .../notifications/GetApplicationIdUseCase.kt | 12 +- ...etworksAvailableForNotificationsUseCase.kt | 6 +- .../notifications/SendPushTokenUseCase.kt | 6 +- .../SetShouldShowNotificationUseCase.kt | 12 ++ .../ShouldShowNotificationUseCase.kt | 12 ++ .../repository/NotificationsRepository.kt | 44 +++-- .../repository/PushNotificationsRepository.kt | 20 ++ .../GetApplicationIdUseCaseTest.kt | 60 +++--- .../notifications/SendPushTokenUseCaseTest.kt | 16 +- .../component/ChooseManagedTokensComponent.kt | 1 + features/manage-tokens/impl/build.gradle.kts | 1 + .../model/ChooseManagedTokensModel.kt | 22 ++- .../entrypoint/model/SendEntryPointModel.kt | 28 ++- features/swap-v2/impl/build.gradle.kts | 1 + .../v2/impl/amount/model/SwapAmountModel.kt | 8 + 25 files changed, 519 insertions(+), 268 deletions(-) create mode 100644 common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationId.kt create mode 100644 data/notifications/src/main/java/com/tangem/data/notifications/DefaultPushNotificationsRepository.kt create mode 100644 data/notifications/src/test/java/com/tangem/data/notifications/DefaultPushNotificationsRepositoryTest.kt create mode 100644 domain/notifications/src/main/java/com/tangem/domain/notifications/SetShouldShowNotificationUseCase.kt create mode 100644 domain/notifications/src/main/java/com/tangem/domain/notifications/ShouldShowNotificationUseCase.kt create mode 100644 domain/notifications/src/main/java/com/tangem/domain/notifications/repository/PushNotificationsRepository.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/NotificationsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/NotificationsDomainModule.kt index c7b61fd1e0..b35ae23d95 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/NotificationsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/NotificationsDomainModule.kt @@ -3,6 +3,7 @@ package com.tangem.tap.di.domain import com.tangem.core.configtoggle.feature.FeatureTogglesManager import com.tangem.domain.notifications.* import com.tangem.domain.notifications.repository.NotificationsRepository +import com.tangem.domain.notifications.repository.PushNotificationsRepository import com.tangem.domain.notifications.toggles.NotificationsFeatureToggles import com.tangem.tap.domain.notifications.DefaultNotificationsFeatureToggles import com.tangem.utils.notifications.PushNotificationsTokenProvider @@ -18,20 +19,22 @@ internal object NotificationsDomainModule { @Provides @Singleton - fun providesGetApplicationIdUseCase(notificationsRepository: NotificationsRepository): GetApplicationIdUseCase { + fun providesGetApplicationIdUseCase( + pushNotificationsRepository: PushNotificationsRepository, + ): GetApplicationIdUseCase { return GetApplicationIdUseCase( - notificationsRepository = notificationsRepository, + pushNotificationsRepository = pushNotificationsRepository, ) } @Provides @Singleton fun providesSendPushTokenUseCase( - notificationsRepository: NotificationsRepository, + pushNotificationsRepository: PushNotificationsRepository, pushNotificationsTokenProvider: PushNotificationsTokenProvider, ): SendPushTokenUseCase { return SendPushTokenUseCase( - notificationsRepository = notificationsRepository, + pushNotificationsRepository = pushNotificationsRepository, pushNotificationsTokenProvider = pushNotificationsTokenProvider, ) } @@ -56,6 +59,26 @@ internal object NotificationsDomainModule { ) } + @Provides + @Singleton + fun providesShouldShowNotificationUseCase( + notificationsRepository: NotificationsRepository, + ): ShouldShowNotificationUseCase { + return ShouldShowNotificationUseCase( + notificationsRepository = notificationsRepository, + ) + } + + @Provides + @Singleton + fun providesSetShouldShowNotificationUseCase( + notificationsRepository: NotificationsRepository, + ): SetShouldShowNotificationUseCase { + return SetShouldShowNotificationUseCase( + notificationsRepository = notificationsRepository, + ) + } + @Provides @Singleton fun provideNotificationsFeatureToggles(featureTogglesManager: FeatureTogglesManager): NotificationsFeatureToggles { @@ -65,8 +88,8 @@ internal object NotificationsDomainModule { @Provides @Singleton fun provideGetNetworksAvailableForNotifications( - notificationsRepository: NotificationsRepository, + pushNotificationsRepository: PushNotificationsRepository, ): GetNetworksAvailableForNotificationsUseCase { - return GetNetworksAvailableForNotificationsUseCase(notificationsRepository = notificationsRepository) + return GetNetworksAvailableForNotificationsUseCase(pushNotificationsRepository = pushNotificationsRepository) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index 9ecfe8e5c6..c18697013f 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -434,6 +434,7 @@ internal class ChildFactory @Inject constructor( initialCurrency = route.initialCurrency, selectedCurrency = route.selectedCurrency, source = ChooseManagedTokensComponent.Source.valueOf(route.source.name), + showSendViaSwapNotification = route.showSendViaSwapNotification, ), componentFactory = chooseManagedTokensComponentFactory, ) diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index d1afb593f0..def139603e 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -133,6 +133,7 @@ sealed class AppRoute(val path: String) : Route { val initialCurrency: CryptoCurrency, val selectedCurrency: CryptoCurrency?, val source: Source, + val showSendViaSwapNotification: Boolean, ) : AppRoute(path = "/$source/choose_managed_tokens/$userWalletId/${initialCurrency.id.value}") { enum class Source { SendViaSwap, diff --git a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationId.kt b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationId.kt new file mode 100644 index 0000000000..4b770200d0 --- /dev/null +++ b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationId.kt @@ -0,0 +1,11 @@ +package com.tangem.common.ui.notifications + +/** + * NotificationId represents unique identifiers for notifications in the app. + * + * These ids can be used with [ShouldShowNotificationUseCase] and [SetShouldShowNotificationUseCase] + * to check or update the visibility state of notifications. + */ +enum class NotificationId(val key: String) { + SendViaSwapTokenSelectorNotification("SendViaSwapTokenSelectorNotificationKey"), +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt index 874a358083..d39a5c15a8 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt @@ -137,6 +137,8 @@ object PreferencesKeys { val TRON_NETWORK_FEE_NOTIFICATION_SHOW_COUNT_KEY by lazy { intPreferencesKey(name = "tronNetworkFeeNotificationShowCount") } + + fun getShouldShowNotificationKey(key: String) = booleanPreferencesKey("showShowNotificationUM_$key") // endregion // region Promo diff --git a/data/notifications/src/main/java/com/tangem/data/notifications/DefaultNotificationsRepository.kt b/data/notifications/src/main/java/com/tangem/data/notifications/DefaultNotificationsRepository.kt index d1f92e8262..dcb85f6ef2 100644 --- a/data/notifications/src/main/java/com/tangem/data/notifications/DefaultNotificationsRepository.kt +++ b/data/notifications/src/main/java/com/tangem/data/notifications/DefaultNotificationsRepository.kt @@ -1,48 +1,22 @@ package com.tangem.data.notifications -import com.tangem.data.notifications.converters.NotificationsEligibleNetworkConverter -import com.tangem.datasource.api.common.response.getOrThrow -import com.tangem.datasource.api.tangemTech.TangemTechApi -import com.tangem.datasource.api.tangemTech.models.NotificationApplicationCreateBody -import com.tangem.utils.info.AppInfoProvider import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys -import com.tangem.datasource.local.preferences.utils.* -import com.tangem.domain.notifications.models.ApplicationId +import com.tangem.datasource.local.preferences.utils.getSyncOrDefault +import com.tangem.datasource.local.preferences.utils.store import com.tangem.domain.notifications.repository.NotificationsRepository -import com.tangem.domain.notifications.models.NotificationsEligibleNetwork -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.withContext import javax.inject.Inject -internal class DefaultNotificationsRepository @Inject constructor( - private val tangemTechApi: TangemTechApi, - private val appInfoProvider: AppInfoProvider, +class DefaultNotificationsRepository @Inject constructor( private val appPreferencesStore: AppPreferencesStore, - private val dispatchers: CoroutineDispatcherProvider, ) : NotificationsRepository { - override suspend fun createApplicationId(pushToken: String?): ApplicationId = withContext(dispatchers.io) { - tangemTechApi.createApplicationId( - NotificationApplicationCreateBody( - platform = appInfoProvider.platform.lowercase(), - device = appInfoProvider.device, - systemVersion = appInfoProvider.osVersion, - language = appInfoProvider.language, - timezone = appInfoProvider.timezone, - version = appInfoProvider.appVersion, - pushToken = pushToken, - ), - ).getOrThrow().appId.let(::ApplicationId) + override suspend fun shouldShowNotification(key: String): Boolean { + return appPreferencesStore.getSyncOrDefault(PreferencesKeys.getShouldShowNotificationKey(key), true) } - override suspend fun saveApplicationId(appId: ApplicationId) { - appPreferencesStore.store(PreferencesKeys.NOTIFICATIONS_APPLICATION_ID_KEY, appId.value) - } - - override suspend fun getApplicationId(): ApplicationId? { - return appPreferencesStore.getSyncOrNull(PreferencesKeys.NOTIFICATIONS_APPLICATION_ID_KEY) - ?.let(::ApplicationId) + override suspend fun setShouldShowNotifications(key: String, value: Boolean) { + appPreferencesStore.store(PreferencesKeys.getShouldShowNotificationKey(key), value) } override suspend fun incrementTronTokenFeeNotificationShowCounter() { @@ -61,25 +35,4 @@ internal class DefaultNotificationsRepository @Inject constructor( default = 0, ) } - - override suspend fun sendPushToken(appId: ApplicationId, pushToken: String) { - withContext(dispatchers.io) { - tangemTechApi.updatePushTokenForApplicationId( - appId.value, - NotificationApplicationCreateBody( - pushToken = pushToken, - systemVersion = appInfoProvider.osVersion, - language = appInfoProvider.language, - timezone = appInfoProvider.timezone, - version = appInfoProvider.appVersion, - ), - ).getOrThrow() - } - } - - override suspend fun getEligibleNetworks(): List = withContext(dispatchers.io) { - tangemTechApi.getEligibleNetworksForPushNotifications().getOrThrow().mapNotNull { - NotificationsEligibleNetworkConverter.convert(it) - } - } } \ No newline at end of file diff --git a/data/notifications/src/main/java/com/tangem/data/notifications/DefaultPushNotificationsRepository.kt b/data/notifications/src/main/java/com/tangem/data/notifications/DefaultPushNotificationsRepository.kt new file mode 100644 index 0000000000..b881aff0c5 --- /dev/null +++ b/data/notifications/src/main/java/com/tangem/data/notifications/DefaultPushNotificationsRepository.kt @@ -0,0 +1,68 @@ +package com.tangem.data.notifications + +import com.tangem.data.notifications.converters.NotificationsEligibleNetworkConverter +import com.tangem.datasource.api.common.response.getOrThrow +import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.api.tangemTech.models.NotificationApplicationCreateBody +import com.tangem.utils.info.AppInfoProvider +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.utils.* +import com.tangem.domain.notifications.models.ApplicationId +import com.tangem.domain.notifications.repository.PushNotificationsRepository +import com.tangem.domain.notifications.models.NotificationsEligibleNetwork +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.withContext +import javax.inject.Inject + +internal class DefaultPushNotificationsRepository @Inject constructor( + private val tangemTechApi: TangemTechApi, + private val appInfoProvider: AppInfoProvider, + private val appPreferencesStore: AppPreferencesStore, + private val dispatchers: CoroutineDispatcherProvider, +) : PushNotificationsRepository { + + override suspend fun createApplicationId(pushToken: String?): ApplicationId = withContext(dispatchers.io) { + tangemTechApi.createApplicationId( + NotificationApplicationCreateBody( + platform = appInfoProvider.platform.lowercase(), + device = appInfoProvider.device, + systemVersion = appInfoProvider.osVersion, + language = appInfoProvider.language, + timezone = appInfoProvider.timezone, + version = appInfoProvider.appVersion, + pushToken = pushToken, + ), + ).getOrThrow().appId.let(::ApplicationId) + } + + override suspend fun saveApplicationId(appId: ApplicationId) { + appPreferencesStore.store(PreferencesKeys.NOTIFICATIONS_APPLICATION_ID_KEY, appId.value) + } + + override suspend fun getApplicationId(): ApplicationId? { + return appPreferencesStore.getSyncOrNull(PreferencesKeys.NOTIFICATIONS_APPLICATION_ID_KEY) + ?.let(::ApplicationId) + } + + override suspend fun sendPushToken(appId: ApplicationId, pushToken: String) { + withContext(dispatchers.io) { + tangemTechApi.updatePushTokenForApplicationId( + appId.value, + NotificationApplicationCreateBody( + pushToken = pushToken, + systemVersion = appInfoProvider.osVersion, + language = appInfoProvider.language, + timezone = appInfoProvider.timezone, + version = appInfoProvider.appVersion, + ), + ).getOrThrow() + } + } + + override suspend fun getEligibleNetworks(): List = withContext(dispatchers.io) { + tangemTechApi.getEligibleNetworksForPushNotifications().getOrThrow().mapNotNull { + NotificationsEligibleNetworkConverter.convert(it) + } + } +} \ No newline at end of file diff --git a/data/notifications/src/main/java/com/tangem/data/notifications/di/NotificationsModule.kt b/data/notifications/src/main/java/com/tangem/data/notifications/di/NotificationsModule.kt index 555ac66802..18f26e5763 100644 --- a/data/notifications/src/main/java/com/tangem/data/notifications/di/NotificationsModule.kt +++ b/data/notifications/src/main/java/com/tangem/data/notifications/di/NotificationsModule.kt @@ -1,7 +1,9 @@ package com.tangem.data.notifications.di import com.tangem.data.notifications.DefaultNotificationsRepository +import com.tangem.data.notifications.DefaultPushNotificationsRepository import com.tangem.domain.notifications.repository.NotificationsRepository +import com.tangem.domain.notifications.repository.PushNotificationsRepository import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -12,6 +14,10 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) internal interface NotificationsModule { + @Binds + @Singleton + fun bindPushNotificationsRepository(repository: DefaultPushNotificationsRepository): PushNotificationsRepository + @Binds @Singleton fun bindNotificationsRepository(repository: DefaultNotificationsRepository): NotificationsRepository diff --git a/data/notifications/src/test/java/com/tangem/data/notifications/DefaultNotificationsRepositoryTest.kt b/data/notifications/src/test/java/com/tangem/data/notifications/DefaultNotificationsRepositoryTest.kt index 13d4dbb6b1..a92861eca6 100644 --- a/data/notifications/src/test/java/com/tangem/data/notifications/DefaultNotificationsRepositoryTest.kt +++ b/data/notifications/src/test/java/com/tangem/data/notifications/DefaultNotificationsRepositoryTest.kt @@ -1,30 +1,21 @@ package com.tangem.data.notifications -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.stringPreferencesKey -import com.tangem.datasource.api.tangemTech.TangemTechApi -import com.tangem.utils.info.AppInfoProvider +import com.google.common.truth.Truth.assertThat import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys -import com.google.common.truth.Truth.assertThat -import com.squareup.moshi.Moshi -import com.tangem.data.notifications.converters.NotificationsEligibleNetworkConverter -import com.tangem.datasource.api.common.response.ApiResponse -import com.tangem.datasource.api.tangemTech.models.* -import com.tangem.domain.notifications.models.ApplicationId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every import io.mockk.mockk -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Test +import androidx.datastore.preferences.core.Preferences +import com.squareup.moshi.Moshi +import androidx.datastore.core.DataStore +import kotlinx.coroutines.flow.flowOf class DefaultNotificationsRepositoryTest { - private val tangemTechApi: TangemTechApi = mockk() - private val appInfoProvider: AppInfoProvider = mockk() private val preferencesDataStore: DataStore = mockk() private val appPreferencesStore = AppPreferencesStore( moshi = Moshi.Builder().build(), @@ -32,147 +23,77 @@ class DefaultNotificationsRepositoryTest { preferencesDataStore = preferencesDataStore, ) private val repository = DefaultNotificationsRepository( - tangemTechApi = tangemTechApi, - appInfoProvider = appInfoProvider, appPreferencesStore = appPreferencesStore, - dispatchers = TestingCoroutineDispatcherProvider(), ) @Test - fun `GIVEN valid push token WHEN createApplicationId THEN returns application id`() = runTest { + fun `GIVEN shouldShowNotification returns true WHEN called THEN returns true`() = runTest { // GIVEN - val pushToken = "test-push-token" - val expectedAppId = ApplicationId("test-app-id") - val expectedAppIdResponse = NotificationApplicationIdResponse( - appId = expectedAppId.value, - ) - coEvery { appInfoProvider.platform } returns "android" - coEvery { appInfoProvider.device } returns "test-device" - coEvery { appInfoProvider.osVersion } returns "11" - coEvery { appInfoProvider.language } returns "en" - coEvery { appInfoProvider.appVersion } returns "5.21.1" - coEvery { appInfoProvider.timezone } returns "UTC" - coEvery { tangemTechApi.createApplicationId(any()) } returns ApiResponse.Success( - expectedAppIdResponse, - ) + val key = "test-key" + val preferences = mockk(relaxed = true) + every { preferences[PreferencesKeys.getShouldShowNotificationKey(key)] } returns true + coEvery { preferencesDataStore.data } returns flowOf(preferences) // WHEN - val result = repository.createApplicationId(pushToken) + val result = repository.shouldShowNotification(key) // THEN - assertThat(result).isEqualTo(expectedAppId) - coVerify { - tangemTechApi.createApplicationId( - NotificationApplicationCreateBody( - platform = "android", - device = "test-device", - systemVersion = "11", - language = "en", - timezone = "UTC", - version = "5.21.1", - pushToken = pushToken, - ), - ) - } + assertThat(result).isTrue() } @Test - fun `GIVEN application id WHEN saveApplicationId THEN stores it in preferences`() = runTest { + fun `GIVEN shouldShowNotification returns false WHEN called THEN returns false`() = runTest { // GIVEN - val appId = ApplicationId("test-app-id") + val key = "test-key" val preferences = mockk(relaxed = true) - coEvery { preferencesDataStore.updateData(any()) } returns preferences + every { preferences[PreferencesKeys.getShouldShowNotificationKey(key)] } returns false + coEvery { preferencesDataStore.data } returns flowOf(preferences) // WHEN - repository.saveApplicationId(appId) + val result = repository.shouldShowNotification(key) + + // THEN + assertThat(result).isFalse() + } + + @Test + fun `GIVEN setShouldShowNotifications WHEN called THEN stores value in preferences`() = runTest { + // GIVEN + val key = "test-key" + val value = false + coEvery { preferencesDataStore.updateData(any()) } returns mockk(relaxed = true) + + // WHEN + repository.setShouldShowNotifications(key, value) // THEN coVerify { preferencesDataStore.updateData(any()) } } @Test - fun `GIVEN stored application id WHEN getApplicationId THEN returns it`() = runTest { + fun `GIVEN incrementTronTokenFeeNotificationShowCounter WHEN called THEN increments counter`() = runTest { // GIVEN - val expectedAppId = ApplicationId("test-app-id") + coEvery { preferencesDataStore.updateData(any()) } returns mockk(relaxed = true) + + // WHEN + repository.incrementTronTokenFeeNotificationShowCounter() + + // THEN + coVerify { preferencesDataStore.updateData(any()) } + } + + @Test + fun `GIVEN getTronTokenFeeNotificationShowCounter WHEN called THEN returns counter value`() = runTest { + // GIVEN + val expectedCount = 5 val preferences = mockk(relaxed = true) - val key = stringPreferencesKey(PreferencesKeys.NOTIFICATIONS_APPLICATION_ID_KEY.name) - every { preferences[key] } returns expectedAppId.value + every { preferences[PreferencesKeys.TRON_NETWORK_FEE_NOTIFICATION_SHOW_COUNT_KEY] } returns expectedCount coEvery { preferencesDataStore.data } returns flowOf(preferences) // WHEN - val result = repository.getApplicationId() + val result = repository.getTronTokenFeeNotificationShowCounter() // THEN - assertThat(result).isEqualTo(expectedAppId) - } - - @Test - fun `GIVEN application id and push token WHEN sendPushToken THEN updates push token`() = runTest { - // GIVEN - val appId = ApplicationId("test-app-id") - val pushToken = "test-push-token" - coEvery { appInfoProvider.device } returns "test-device" - coEvery { appInfoProvider.osVersion } returns "11" - coEvery { appInfoProvider.language } returns "en" - coEvery { appInfoProvider.appVersion } returns "5.21.1" - coEvery { appInfoProvider.timezone } returns "UTC" - coEvery { - tangemTechApi.updatePushTokenForApplicationId( - appId.value, - NotificationApplicationCreateBody( - pushToken = pushToken, - systemVersion = "11", - language = "en", - timezone = "UTC", - version = "5.21.1", - ), - ) - } returns ApiResponse.Success(Unit) - - // WHEN - repository.sendPushToken(appId, pushToken) - - // THEN - coVerify { - tangemTechApi.updatePushTokenForApplicationId( - appId.value, - NotificationApplicationCreateBody( - pushToken = pushToken, - systemVersion = "11", - language = "en", - timezone = "UTC", - version = "5.21.1", - ), - ) - } - } - - @Test - fun `GIVEN eligible networks WHEN getEligibleNetworks THEN returns converted networks`() = runTest { - // GIVEN - val expectedNetworks = listOf( - CryptoNetworkResponse( - id = 1, - name = "Ethereum", - networkId = "ethereum", - ), - CryptoNetworkResponse( - id = 2, - name = "Bitcoin", - networkId = "bitcoin", - ), - ) - coEvery { tangemTechApi.getEligibleNetworksForPushNotifications() } returns ApiResponse.Success( - expectedNetworks, - ) - - // WHEN - val result = repository.getEligibleNetworks() - - // THEN - assertThat(result).hasSize(2) - assertThat(result[0]).isEqualTo(NotificationsEligibleNetworkConverter.convert(expectedNetworks[0])) - assertThat(result[1]).isEqualTo(NotificationsEligibleNetworkConverter.convert(expectedNetworks[1])) - coVerify { tangemTechApi.getEligibleNetworksForPushNotifications() } + assertThat(result).isEqualTo(expectedCount) } } \ No newline at end of file diff --git a/data/notifications/src/test/java/com/tangem/data/notifications/DefaultPushNotificationsRepositoryTest.kt b/data/notifications/src/test/java/com/tangem/data/notifications/DefaultPushNotificationsRepositoryTest.kt new file mode 100644 index 0000000000..b423cb27ed --- /dev/null +++ b/data/notifications/src/test/java/com/tangem/data/notifications/DefaultPushNotificationsRepositoryTest.kt @@ -0,0 +1,178 @@ +package com.tangem.data.notifications + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.stringPreferencesKey +import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.utils.info.AppInfoProvider +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.google.common.truth.Truth.assertThat +import com.squareup.moshi.Moshi +import com.tangem.data.notifications.converters.NotificationsEligibleNetworkConverter +import com.tangem.datasource.api.common.response.ApiResponse +import com.tangem.datasource.api.tangemTech.models.* +import com.tangem.domain.notifications.models.ApplicationId +import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class DefaultPushNotificationsRepositoryTest { + private val tangemTechApi: TangemTechApi = mockk() + private val appInfoProvider: AppInfoProvider = mockk() + private val preferencesDataStore: DataStore = mockk() + private val appPreferencesStore = AppPreferencesStore( + moshi = Moshi.Builder().build(), + dispatchers = TestingCoroutineDispatcherProvider(), + preferencesDataStore = preferencesDataStore, + ) + private val repository = DefaultPushNotificationsRepository( + tangemTechApi = tangemTechApi, + appInfoProvider = appInfoProvider, + appPreferencesStore = appPreferencesStore, + dispatchers = TestingCoroutineDispatcherProvider(), + ) + + @Test + fun `GIVEN valid push token WHEN createApplicationId THEN returns application id`() = runTest { + // GIVEN + val pushToken = "test-push-token" + val expectedAppId = ApplicationId("test-app-id") + val expectedAppIdResponse = NotificationApplicationIdResponse( + appId = expectedAppId.value, + ) + coEvery { appInfoProvider.platform } returns "android" + coEvery { appInfoProvider.device } returns "test-device" + coEvery { appInfoProvider.osVersion } returns "11" + coEvery { appInfoProvider.language } returns "en" + coEvery { appInfoProvider.appVersion } returns "5.21.1" + coEvery { appInfoProvider.timezone } returns "UTC" + coEvery { tangemTechApi.createApplicationId(any()) } returns ApiResponse.Success( + expectedAppIdResponse, + ) + + // WHEN + val result = repository.createApplicationId(pushToken) + + // THEN + assertThat(result).isEqualTo(expectedAppId) + coVerify { + tangemTechApi.createApplicationId( + NotificationApplicationCreateBody( + platform = "android", + device = "test-device", + systemVersion = "11", + language = "en", + timezone = "UTC", + version = "5.21.1", + pushToken = pushToken, + ), + ) + } + } + + @Test + fun `GIVEN application id WHEN saveApplicationId THEN stores it in preferences`() = runTest { + // GIVEN + val appId = ApplicationId("test-app-id") + val preferences = mockk(relaxed = true) + coEvery { preferencesDataStore.updateData(any()) } returns preferences + + // WHEN + repository.saveApplicationId(appId) + + // THEN + coVerify { preferencesDataStore.updateData(any()) } + } + + @Test + fun `GIVEN stored application id WHEN getApplicationId THEN returns it`() = runTest { + // GIVEN + val expectedAppId = ApplicationId("test-app-id") + val preferences = mockk(relaxed = true) + val key = stringPreferencesKey(PreferencesKeys.NOTIFICATIONS_APPLICATION_ID_KEY.name) + every { preferences[key] } returns expectedAppId.value + coEvery { preferencesDataStore.data } returns flowOf(preferences) + + // WHEN + val result = repository.getApplicationId() + + // THEN + assertThat(result).isEqualTo(expectedAppId) + } + + @Test + fun `GIVEN application id and push token WHEN sendPushToken THEN updates push token`() = runTest { + // GIVEN + val appId = ApplicationId("test-app-id") + val pushToken = "test-push-token" + coEvery { appInfoProvider.device } returns "test-device" + coEvery { appInfoProvider.osVersion } returns "11" + coEvery { appInfoProvider.language } returns "en" + coEvery { appInfoProvider.appVersion } returns "5.21.1" + coEvery { appInfoProvider.timezone } returns "UTC" + coEvery { + tangemTechApi.updatePushTokenForApplicationId( + appId.value, + NotificationApplicationCreateBody( + pushToken = pushToken, + systemVersion = "11", + language = "en", + timezone = "UTC", + version = "5.21.1", + ), + ) + } returns ApiResponse.Success(Unit) + + // WHEN + repository.sendPushToken(appId, pushToken) + + // THEN + coVerify { + tangemTechApi.updatePushTokenForApplicationId( + appId.value, + NotificationApplicationCreateBody( + pushToken = pushToken, + systemVersion = "11", + language = "en", + timezone = "UTC", + version = "5.21.1", + ), + ) + } + } + + @Test + fun `GIVEN eligible networks WHEN getEligibleNetworks THEN returns converted networks`() = runTest { + // GIVEN + val expectedNetworks = listOf( + CryptoNetworkResponse( + id = 1, + name = "Ethereum", + networkId = "ethereum", + ), + CryptoNetworkResponse( + id = 2, + name = "Bitcoin", + networkId = "bitcoin", + ), + ) + coEvery { tangemTechApi.getEligibleNetworksForPushNotifications() } returns ApiResponse.Success( + expectedNetworks, + ) + + // WHEN + val result = repository.getEligibleNetworks() + + // THEN + assertThat(result).hasSize(2) + assertThat(result[0]).isEqualTo(NotificationsEligibleNetworkConverter.convert(expectedNetworks[0])) + assertThat(result[1]).isEqualTo(NotificationsEligibleNetworkConverter.convert(expectedNetworks[1])) + coVerify { tangemTechApi.getEligibleNetworksForPushNotifications() } + } +} \ No newline at end of file diff --git a/domain/notifications/src/main/java/com/tangem/domain/notifications/GetApplicationIdUseCase.kt b/domain/notifications/src/main/java/com/tangem/domain/notifications/GetApplicationIdUseCase.kt index 963d3b99d7..289328d75d 100644 --- a/domain/notifications/src/main/java/com/tangem/domain/notifications/GetApplicationIdUseCase.kt +++ b/domain/notifications/src/main/java/com/tangem/domain/notifications/GetApplicationIdUseCase.kt @@ -2,25 +2,25 @@ package com.tangem.domain.notifications import arrow.core.Either import com.tangem.domain.notifications.models.ApplicationId -import com.tangem.domain.notifications.repository.NotificationsRepository +import com.tangem.domain.notifications.repository.PushNotificationsRepository import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock class GetApplicationIdUseCase( - private val notificationsRepository: NotificationsRepository, + private val pushNotificationsRepository: PushNotificationsRepository, ) { private val mutex = Mutex() suspend operator fun invoke(): Either = Either.catch { - val localApplicationId = notificationsRepository.getApplicationId() + val localApplicationId = pushNotificationsRepository.getApplicationId() if (localApplicationId != null) return@catch localApplicationId mutex.withLock { - val doubleCheckedId = notificationsRepository.getApplicationId() + val doubleCheckedId = pushNotificationsRepository.getApplicationId() if (doubleCheckedId != null) return@withLock doubleCheckedId - val newApplicationId = notificationsRepository.createApplicationId() - notificationsRepository.saveApplicationId(newApplicationId) + val newApplicationId = pushNotificationsRepository.createApplicationId() + pushNotificationsRepository.saveApplicationId(newApplicationId) newApplicationId } } diff --git a/domain/notifications/src/main/java/com/tangem/domain/notifications/GetNetworksAvailableForNotificationsUseCase.kt b/domain/notifications/src/main/java/com/tangem/domain/notifications/GetNetworksAvailableForNotificationsUseCase.kt index edc3de5294..9264da1a16 100644 --- a/domain/notifications/src/main/java/com/tangem/domain/notifications/GetNetworksAvailableForNotificationsUseCase.kt +++ b/domain/notifications/src/main/java/com/tangem/domain/notifications/GetNetworksAvailableForNotificationsUseCase.kt @@ -2,13 +2,13 @@ package com.tangem.domain.notifications import arrow.core.Either import com.tangem.domain.notifications.models.NotificationsEligibleNetwork -import com.tangem.domain.notifications.repository.NotificationsRepository +import com.tangem.domain.notifications.repository.PushNotificationsRepository class GetNetworksAvailableForNotificationsUseCase( - private val notificationsRepository: NotificationsRepository, + private val pushNotificationsRepository: PushNotificationsRepository, ) { suspend operator fun invoke(): Either> = Either.catch { - notificationsRepository.getEligibleNetworks() + pushNotificationsRepository.getEligibleNetworks() } } \ No newline at end of file diff --git a/domain/notifications/src/main/java/com/tangem/domain/notifications/SendPushTokenUseCase.kt b/domain/notifications/src/main/java/com/tangem/domain/notifications/SendPushTokenUseCase.kt index 63247b9a7d..d33888f342 100644 --- a/domain/notifications/src/main/java/com/tangem/domain/notifications/SendPushTokenUseCase.kt +++ b/domain/notifications/src/main/java/com/tangem/domain/notifications/SendPushTokenUseCase.kt @@ -2,16 +2,16 @@ package com.tangem.domain.notifications import arrow.core.Either import com.tangem.domain.notifications.models.ApplicationId -import com.tangem.domain.notifications.repository.NotificationsRepository +import com.tangem.domain.notifications.repository.PushNotificationsRepository import com.tangem.utils.notifications.PushNotificationsTokenProvider class SendPushTokenUseCase( - private val notificationsRepository: NotificationsRepository, + private val pushNotificationsRepository: PushNotificationsRepository, private val pushNotificationsTokenProvider: PushNotificationsTokenProvider, ) { suspend operator fun invoke(applicationId: ApplicationId): Either = Either.catch { val token = pushNotificationsTokenProvider.getToken() - notificationsRepository.sendPushToken(applicationId, token) + pushNotificationsRepository.sendPushToken(applicationId, token) } } \ No newline at end of file diff --git a/domain/notifications/src/main/java/com/tangem/domain/notifications/SetShouldShowNotificationUseCase.kt b/domain/notifications/src/main/java/com/tangem/domain/notifications/SetShouldShowNotificationUseCase.kt new file mode 100644 index 0000000000..44413fe106 --- /dev/null +++ b/domain/notifications/src/main/java/com/tangem/domain/notifications/SetShouldShowNotificationUseCase.kt @@ -0,0 +1,12 @@ +package com.tangem.domain.notifications + +import com.tangem.domain.notifications.repository.NotificationsRepository + +class SetShouldShowNotificationUseCase( + private val notificationsRepository: NotificationsRepository, +) { + + suspend operator fun invoke(key: String, value: Boolean) { + notificationsRepository.setShouldShowNotifications(key, value) + } +} \ No newline at end of file diff --git a/domain/notifications/src/main/java/com/tangem/domain/notifications/ShouldShowNotificationUseCase.kt b/domain/notifications/src/main/java/com/tangem/domain/notifications/ShouldShowNotificationUseCase.kt new file mode 100644 index 0000000000..657ab7819d --- /dev/null +++ b/domain/notifications/src/main/java/com/tangem/domain/notifications/ShouldShowNotificationUseCase.kt @@ -0,0 +1,12 @@ +package com.tangem.domain.notifications + +import com.tangem.domain.notifications.repository.NotificationsRepository + +class ShouldShowNotificationUseCase( + private val notificationsRepository: NotificationsRepository, +) { + + suspend operator fun invoke(key: String): Boolean { + return notificationsRepository.shouldShowNotification(key) + } +} \ No newline at end of file diff --git a/domain/notifications/src/main/java/com/tangem/domain/notifications/repository/NotificationsRepository.kt b/domain/notifications/src/main/java/com/tangem/domain/notifications/repository/NotificationsRepository.kt index a8c46854df..377fc84984 100644 --- a/domain/notifications/src/main/java/com/tangem/domain/notifications/repository/NotificationsRepository.kt +++ b/domain/notifications/src/main/java/com/tangem/domain/notifications/repository/NotificationsRepository.kt @@ -1,24 +1,40 @@ package com.tangem.domain.notifications.repository -import com.tangem.domain.notifications.models.ApplicationId -import com.tangem.domain.notifications.models.NotificationsEligibleNetwork - +/** + * Repository interface for managing local notification logic and state. + * + * This interface provides methods to check and update whether specific notifications should be shown, + * as well as to track the display count for certain notifications (e.g., Tron token fee). + * + * Note: This repository is responsible only for the local logic and state (such as preferences and counters) + * regarding notifications. It does **not** directly show or hide notifications to the user. + * The actual display and hiding of notifications in the UI is handled by [NotificationsUM], + * which uses this repository to determine the appropriate behavior. + */ interface NotificationsRepository { - @Throws - suspend fun createApplicationId(pushToken: String? = null): ApplicationId + /** + * Checks whether a notification with the given [key] should be shown to the user. + * @param key The unique identifier for the notification. + * @return true if the notification should be shown, false otherwise. + */ + suspend fun shouldShowNotification(key: String): Boolean - suspend fun saveApplicationId(appId: ApplicationId) - - suspend fun getApplicationId(): ApplicationId? + /** + * Sets whether a notification with the given [key] should be shown to the user. + * @param key The unique identifier for the notification. + * @param value true if the notification should be shown, false otherwise. + */ + suspend fun setShouldShowNotifications(key: String, value: Boolean) + /** + * Gets the number of times the Tron token fee notification has been shown. + * @return The current show counter for the Tron token fee notification. + */ suspend fun getTronTokenFeeNotificationShowCounter(): Int + /** + * Increments the counter tracking how many times the Tron token fee notification has been shown. + */ suspend fun incrementTronTokenFeeNotificationShowCounter() - - @Throws - suspend fun sendPushToken(appId: ApplicationId, pushToken: String) - - @Throws - suspend fun getEligibleNetworks(): List } \ No newline at end of file diff --git a/domain/notifications/src/main/java/com/tangem/domain/notifications/repository/PushNotificationsRepository.kt b/domain/notifications/src/main/java/com/tangem/domain/notifications/repository/PushNotificationsRepository.kt new file mode 100644 index 0000000000..338a74b0ce --- /dev/null +++ b/domain/notifications/src/main/java/com/tangem/domain/notifications/repository/PushNotificationsRepository.kt @@ -0,0 +1,20 @@ +package com.tangem.domain.notifications.repository + +import com.tangem.domain.notifications.models.ApplicationId +import com.tangem.domain.notifications.models.NotificationsEligibleNetwork + +interface PushNotificationsRepository { + + @Throws + suspend fun createApplicationId(pushToken: String? = null): ApplicationId + + suspend fun saveApplicationId(appId: ApplicationId) + + suspend fun getApplicationId(): ApplicationId? + + @Throws + suspend fun sendPushToken(appId: ApplicationId, pushToken: String) + + @Throws + suspend fun getEligibleNetworks(): List +} \ No newline at end of file diff --git a/domain/notifications/src/test/java/com/tangem/domain/notifications/GetApplicationIdUseCaseTest.kt b/domain/notifications/src/test/java/com/tangem/domain/notifications/GetApplicationIdUseCaseTest.kt index 6b2984554b..e3a191f205 100644 --- a/domain/notifications/src/test/java/com/tangem/domain/notifications/GetApplicationIdUseCaseTest.kt +++ b/domain/notifications/src/test/java/com/tangem/domain/notifications/GetApplicationIdUseCaseTest.kt @@ -3,7 +3,7 @@ package com.tangem.domain.notifications import arrow.core.Either import com.google.common.truth.Truth.assertThat import com.tangem.domain.notifications.models.ApplicationId -import com.tangem.domain.notifications.repository.NotificationsRepository +import com.tangem.domain.notifications.repository.PushNotificationsRepository import io.mockk.coEvery import io.mockk.coVerify import io.mockk.coVerifyOrder @@ -15,14 +15,14 @@ import java.net.SocketTimeoutException class GetApplicationIdUseCaseTest { - private val notificationsRepository: NotificationsRepository = mockk() - private val useCase = GetApplicationIdUseCase(notificationsRepository) + private val pushNotificationsRepository: PushNotificationsRepository = mockk() + private val useCase = GetApplicationIdUseCase(pushNotificationsRepository) @Test fun `GIVEN local application ID exists WHEN invoke THEN return local application ID`() = runTest { // GIVEN val expectedApplicationId = ApplicationId("test-app-id") - coEvery { notificationsRepository.getApplicationId() } returns expectedApplicationId + coEvery { pushNotificationsRepository.getApplicationId() } returns expectedApplicationId // WHEN val result = useCase() @@ -30,10 +30,10 @@ class GetApplicationIdUseCaseTest { // THEN assertThat(result).isInstanceOf(Either.Right::class.java) assertThat((result as Either.Right).value).isEqualTo(expectedApplicationId) - coVerify(exactly = 1) { notificationsRepository.getApplicationId() } + coVerify(exactly = 1) { pushNotificationsRepository.getApplicationId() } coVerify(inverse = true) { - notificationsRepository.createApplicationId() - notificationsRepository.saveApplicationId(any()) + pushNotificationsRepository.createApplicationId() + pushNotificationsRepository.saveApplicationId(any()) } } @@ -41,9 +41,9 @@ class GetApplicationIdUseCaseTest { fun `GIVEN local application ID does not exist WHEN invoke THEN create and save new application ID`() = runTest { // GIVEN val newApplicationId = ApplicationId("new-app-id") - coEvery { notificationsRepository.getApplicationId() } returns null - coEvery { notificationsRepository.createApplicationId() } returns newApplicationId - coEvery { notificationsRepository.saveApplicationId(newApplicationId) } returns Unit + coEvery { pushNotificationsRepository.getApplicationId() } returns null + coEvery { pushNotificationsRepository.createApplicationId() } returns newApplicationId + coEvery { pushNotificationsRepository.saveApplicationId(newApplicationId) } returns Unit // WHEN val result = useCase() @@ -52,10 +52,10 @@ class GetApplicationIdUseCaseTest { assertThat(result).isInstanceOf(Either.Right::class.java) assertThat((result as Either.Right).value).isEqualTo(newApplicationId) coVerifyOrder { - notificationsRepository.getApplicationId() - notificationsRepository.getApplicationId() - notificationsRepository.createApplicationId() - notificationsRepository.saveApplicationId(newApplicationId) + pushNotificationsRepository.getApplicationId() + pushNotificationsRepository.getApplicationId() + pushNotificationsRepository.createApplicationId() + pushNotificationsRepository.saveApplicationId(newApplicationId) } } @@ -63,7 +63,7 @@ class GetApplicationIdUseCaseTest { fun `GIVEN repository throws exception WHEN invoke THEN return Either Left with error`() = runTest { // GIVEN val expectedError = SocketTimeoutException("Test error") - coEvery { notificationsRepository.getApplicationId() } throws expectedError + coEvery { pushNotificationsRepository.getApplicationId() } throws expectedError // WHEN val result = useCase() @@ -71,10 +71,10 @@ class GetApplicationIdUseCaseTest { // THEN assertThat(result).isInstanceOf(Either.Left::class.java) assertThat((result as Either.Left).value).isEqualTo(expectedError) - coVerify(exactly = 1) { notificationsRepository.getApplicationId() } + coVerify(exactly = 1) { pushNotificationsRepository.getApplicationId() } coVerify(inverse = true) { - notificationsRepository.createApplicationId() - notificationsRepository.saveApplicationId(any()) + pushNotificationsRepository.createApplicationId() + pushNotificationsRepository.saveApplicationId(any()) } } @@ -85,14 +85,14 @@ class GetApplicationIdUseCaseTest { val newApplicationId = ApplicationId("new-app-id") var isIdCreated = false - coEvery { notificationsRepository.getApplicationId() } answers { + coEvery { pushNotificationsRepository.getApplicationId() } answers { if (!isIdCreated) null else newApplicationId } - coEvery { notificationsRepository.createApplicationId() } answers { + coEvery { pushNotificationsRepository.createApplicationId() } answers { isIdCreated = true newApplicationId } - coEvery { notificationsRepository.saveApplicationId(newApplicationId) } returns Unit + coEvery { pushNotificationsRepository.saveApplicationId(newApplicationId) } returns Unit // WHEN val results = coroutineScope { @@ -108,9 +108,9 @@ class GetApplicationIdUseCaseTest { assertThat(result).isInstanceOf(Either.Right::class.java) assertThat((result as Either.Right).value).isEqualTo(newApplicationId) } - coVerify(exactly = PARALLEL_COUNT + 1) { notificationsRepository.getApplicationId() } - coVerify(exactly = 1) { notificationsRepository.createApplicationId() } - coVerify(exactly = 1) { notificationsRepository.saveApplicationId(newApplicationId) } + coVerify(exactly = PARALLEL_COUNT + 1) { pushNotificationsRepository.getApplicationId() } + coVerify(exactly = 1) { pushNotificationsRepository.createApplicationId() } + coVerify(exactly = 1) { pushNotificationsRepository.saveApplicationId(newApplicationId) } } @Test @@ -119,14 +119,14 @@ class GetApplicationIdUseCaseTest { val newApplicationId = ApplicationId("new-app-id") var isIdCreated = false - coEvery { notificationsRepository.getApplicationId() } answers { + coEvery { pushNotificationsRepository.getApplicationId() } answers { if (!isIdCreated) null else newApplicationId } - coEvery { notificationsRepository.createApplicationId() } answers { + coEvery { pushNotificationsRepository.createApplicationId() } answers { isIdCreated = true newApplicationId } - coEvery { notificationsRepository.saveApplicationId(newApplicationId) } returns Unit + coEvery { pushNotificationsRepository.saveApplicationId(newApplicationId) } returns Unit // WHEN val results = coroutineScope { @@ -143,9 +143,9 @@ class GetApplicationIdUseCaseTest { assertThat(result).isInstanceOf(Either.Right::class.java) assertThat((result as Either.Right).value).isEqualTo(newApplicationId) } - coVerify(exactly = PARALLEL_COUNT + 1) { notificationsRepository.getApplicationId() } - coVerify(exactly = 1) { notificationsRepository.createApplicationId() } - coVerify(exactly = 1) { notificationsRepository.saveApplicationId(newApplicationId) } + coVerify(exactly = PARALLEL_COUNT + 1) { pushNotificationsRepository.getApplicationId() } + coVerify(exactly = 1) { pushNotificationsRepository.createApplicationId() } + coVerify(exactly = 1) { pushNotificationsRepository.saveApplicationId(newApplicationId) } } companion object { diff --git a/domain/notifications/src/test/java/com/tangem/domain/notifications/SendPushTokenUseCaseTest.kt b/domain/notifications/src/test/java/com/tangem/domain/notifications/SendPushTokenUseCaseTest.kt index 18d716fdf0..fe84d79f16 100644 --- a/domain/notifications/src/test/java/com/tangem/domain/notifications/SendPushTokenUseCaseTest.kt +++ b/domain/notifications/src/test/java/com/tangem/domain/notifications/SendPushTokenUseCaseTest.kt @@ -3,7 +3,7 @@ package com.tangem.domain.notifications import arrow.core.Either import com.google.common.truth.Truth.assertThat import com.tangem.domain.notifications.models.ApplicationId -import com.tangem.domain.notifications.repository.NotificationsRepository +import com.tangem.domain.notifications.repository.PushNotificationsRepository import com.tangem.utils.notifications.PushNotificationsTokenProvider import io.mockk.coEvery import io.mockk.coVerify @@ -14,16 +14,16 @@ import org.junit.Test class SendPushTokenUseCaseTest { - private lateinit var notificationsRepository: NotificationsRepository + private lateinit var pushNotificationsRepository: PushNotificationsRepository private lateinit var pushNotificationsTokenProvider: PushNotificationsTokenProvider private lateinit var sendPushTokenUseCase: SendPushTokenUseCase @Before fun setup() { - notificationsRepository = mockk() + pushNotificationsRepository = mockk() pushNotificationsTokenProvider = mockk() sendPushTokenUseCase = SendPushTokenUseCase( - notificationsRepository = notificationsRepository, + pushNotificationsRepository = pushNotificationsRepository, pushNotificationsTokenProvider = pushNotificationsTokenProvider, ) } @@ -34,14 +34,14 @@ class SendPushTokenUseCaseTest { val applicationId = ApplicationId("test-app-id") val token = "test-token" coEvery { pushNotificationsTokenProvider.getToken() } returns token - coEvery { notificationsRepository.sendPushToken(applicationId, token) } returns Unit + coEvery { pushNotificationsRepository.sendPushToken(applicationId, token) } returns Unit // WHEN val result = sendPushTokenUseCase(applicationId) // THEN assertThat(result).isEqualTo(Either.Right(Unit)) - coVerify(exactly = 1) { notificationsRepository.sendPushToken(applicationId, token) } + coVerify(exactly = 1) { pushNotificationsRepository.sendPushToken(applicationId, token) } } @Test @@ -51,13 +51,13 @@ class SendPushTokenUseCaseTest { val token = "test-token" val expectedError = RuntimeException("Network error") coEvery { pushNotificationsTokenProvider.getToken() } returns token - coEvery { notificationsRepository.sendPushToken(applicationId, token) } throws expectedError + coEvery { pushNotificationsRepository.sendPushToken(applicationId, token) } throws expectedError // WHEN val result = sendPushTokenUseCase(applicationId) // THEN assertThat(result).isEqualTo(Either.Left(expectedError)) - coVerify(exactly = 1) { notificationsRepository.sendPushToken(applicationId, token) } + coVerify(exactly = 1) { pushNotificationsRepository.sendPushToken(applicationId, token) } } } \ No newline at end of file diff --git a/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/component/ChooseManagedTokensComponent.kt b/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/component/ChooseManagedTokensComponent.kt index a12efc7654..d17cd2b862 100644 --- a/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/component/ChooseManagedTokensComponent.kt +++ b/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/component/ChooseManagedTokensComponent.kt @@ -12,6 +12,7 @@ interface ChooseManagedTokensComponent : ComposableContentComponent { val initialCurrency: CryptoCurrency, val selectedCurrency: CryptoCurrency?, val source: Source, + val showSendViaSwapNotification: Boolean, ) enum class Source { diff --git a/features/manage-tokens/impl/build.gradle.kts b/features/manage-tokens/impl/build.gradle.kts index e28cec1763..c6a2a28283 100644 --- a/features/manage-tokens/impl/build.gradle.kts +++ b/features/manage-tokens/impl/build.gradle.kts @@ -33,6 +33,7 @@ dependencies { implementation(projects.domain.wallets) implementation(projects.domain.wallets.models) implementation(projects.domain.swap.models) + implementation(projects.domain.notifications) /* AndroidX */ implementation(deps.androidx.activity.compose) diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/choosetoken/model/ChooseManagedTokensModel.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/choosetoken/model/ChooseManagedTokensModel.kt index 83d65cc3a8..fe98829fd5 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/choosetoken/model/ChooseManagedTokensModel.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/choosetoken/model/ChooseManagedTokensModel.kt @@ -3,6 +3,7 @@ package com.tangem.features.managetokens.choosetoken.model import androidx.annotation.StringRes import com.arkivanov.decompose.router.slot.SlotNavigation import com.arkivanov.decompose.router.slot.activate +import com.tangem.common.ui.notifications.NotificationId import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model @@ -15,6 +16,7 @@ import com.tangem.core.ui.event.triggeredEvent import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.message.SnackbarMessage +import com.tangem.domain.notifications.SetShouldShowNotificationUseCase import com.tangem.features.managetokens.choosetoken.entity.ChooseManageTokensBottomSheetConfig import com.tangem.features.managetokens.choosetoken.entity.ChooseManagedTokenUM import com.tangem.features.managetokens.component.ChooseManagedTokensComponent @@ -43,6 +45,7 @@ internal class ChooseManagedTokensModel @Inject constructor( private val router: Router, override val dispatchers: CoroutineDispatcherProvider, private val uiMessageSender: UiMessageSender, + private val setShouldShowNotificationUseCase: SetShouldShowNotificationUseCase, paramsContainer: ParamsContainer, manageTokensListManagerFactory: ManageTokensListManager.Factory, ) : Model() { @@ -107,18 +110,21 @@ internal class ChooseManagedTokensModel @Inject constructor( } private fun getNotification(): NotificationUM? { - return when (params.source) { - Source.SendViaSwap -> ChooseManagedTokensNotificationUM.SendViaSwap( - onCloseClick = ::removeNotification, - ) + return if (params.source == Source.SendViaSwap && params.showSendViaSwapNotification) { + ChooseManagedTokensNotificationUM.SendViaSwap(onCloseClick = ::removeNotification) + } else { + null } } private fun removeNotification() { - uiState.update { - it.copy( - notificationUM = null, - ) + modelScope.launch { + setShouldShowNotificationUseCase(NotificationId.SendViaSwapTokenSelectorNotification.key, false) + uiState.update { + it.copy( + notificationUM = null, + ) + } } } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/model/SendEntryPointModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/model/SendEntryPointModel.kt index 2f52fcdd3e..3bdbfcaf8d 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/model/SendEntryPointModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/model/SendEntryPointModel.kt @@ -2,9 +2,11 @@ package com.tangem.features.send.v2.entrypoint.model import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter +import com.tangem.common.ui.notifications.NotificationId import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.domain.notifications.ShouldShowNotificationUseCase import com.tangem.features.send.v2.api.SendComponent import com.tangem.features.send.v2.api.SendEntryPointComponent import com.tangem.features.send.v2.subcomponents.amount.SendAmountUpdateTrigger @@ -18,6 +20,7 @@ import jakarta.inject.Inject import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch +@Suppress("LongParameterList") @ModelScoped internal class SendEntryPointModel @Inject constructor( paramsContainer: ParamsContainer, @@ -26,6 +29,7 @@ internal class SendEntryPointModel @Inject constructor( private val swapChooseTokenNetworkListener: SwapChooseTokenNetworkListener, private val sendAmountUpdateTrigger: SendAmountUpdateTrigger, private val swapAmountUpdateTrigger: SwapAmountUpdateTrigger, + private val shouldShowNotificationUseCase: ShouldShowNotificationUseCase, ) : Model(), SendComponent.ModelCallback, SendWithSwapComponent.ModelCallback { private val params: SendEntryPointComponent.Params = paramsContainer.require() @@ -36,15 +40,21 @@ internal class SendEntryPointModel @Inject constructor( private var swapChooseTokenListenerJobHolder = JobHolder() override fun onConvertToAnotherToken(lastAmount: String) { - appRouter.push( - AppRoute.ChooseManagedTokens( - userWalletId = params.userWalletId, - initialCurrency = params.cryptoCurrency, - selectedCurrency = null, - source = AppRoute.ChooseManagedTokens.Source.SendViaSwap, - ), - ) - observeChooseSelectToken(lastAmount) + modelScope.launch { + val showSendViaSwapNotification = shouldShowNotificationUseCase( + NotificationId.SendViaSwapTokenSelectorNotification.key, + ) + appRouter.push( + AppRoute.ChooseManagedTokens( + userWalletId = params.userWalletId, + initialCurrency = params.cryptoCurrency, + selectedCurrency = null, + source = AppRoute.ChooseManagedTokens.Source.SendViaSwap, + showSendViaSwapNotification = showSendViaSwapNotification, + ), + ) + observeChooseSelectToken(lastAmount) + } } override fun onCloseSwap(lastAmount: String) { diff --git a/features/swap-v2/impl/build.gradle.kts b/features/swap-v2/impl/build.gradle.kts index 5cb929fe7e..ab1ccbbe75 100644 --- a/features/swap-v2/impl/build.gradle.kts +++ b/features/swap-v2/impl/build.gradle.kts @@ -64,6 +64,7 @@ dependencies { implementation(projects.domain.settings) implementation(projects.domain.txhistory.models) implementation(projects.domain.txhistory) + implementation(projects.domain.notifications) implementation(projects.domain.feedback.models) implementation(projects.domain.feedback) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt index 149ccd0de1..fa265d5e1c 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt @@ -9,6 +9,7 @@ import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.common.ui.navigationButtons.NavigationButton import com.tangem.common.ui.navigationButtons.NavigationUM +import com.tangem.common.ui.notifications.NotificationId import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer @@ -22,6 +23,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.settings.usercountry.GetUserCountryUseCase import com.tangem.domain.settings.usercountry.models.UserCountry import com.tangem.domain.swap.models.SwapCurrencies +import com.tangem.domain.notifications.ShouldShowNotificationUseCase import com.tangem.domain.swap.models.SwapDirection import com.tangem.domain.swap.models.SwapDirection.Companion.withSwapDirection import com.tangem.domain.swap.models.SwapQuoteModel @@ -81,6 +83,7 @@ internal class SwapAmountModel @Inject constructor( private val swapAmountUpdateListener: SwapAmountUpdateListener, private val swapAmountReduceListener: SwapAmountReduceListener, private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger, + private val shouldShowNotificationUseCase: ShouldShowNotificationUseCase, ) : Model(), SwapAmountClickIntents, SwapChooseProviderComponent.ModelCallback { private val params: SwapAmountComponentParams = paramsContainer.require() @@ -221,6 +224,9 @@ internal class SwapAmountModel @Inject constructor( override fun onSelectTokenClick() { val amountParams = params as? SwapAmountComponentParams.AmountParams ?: return modelScope.launch { + val showSendViaSwapNotification = shouldShowNotificationUseCase( + NotificationId.SendViaSwapTokenSelectorNotification.key, + ) val isEditMode = amountParams.currentRoute.firstOrNull()?.isEditMode == true val selectedCurrency = (uiState.value as? SwapAmountUM.Content)?.secondaryCryptoCurrencyStatus?.currency appRouter.push( @@ -229,6 +235,7 @@ internal class SwapAmountModel @Inject constructor( initialCurrency = primaryCryptoCurrency, selectedCurrency = selectedCurrency.takeIf { isEditMode }, source = AppRoute.ChooseManagedTokens.Source.SendViaSwap, + showSendViaSwapNotification = showSendViaSwapNotification, ), ) } @@ -259,6 +266,7 @@ internal class SwapAmountModel @Inject constructor( private fun confirmSendWithSwapClose() { val amountParams = params as? SwapAmountComponentParams.AmountParams ?: return val amountFieldData = uiState.value.primaryAmount.amountField as? AmountState.Data + val callback = (params as? SwapAmountComponentParams.AmountParams)?.callback ?: return val primaryCryptoCurrencyStatus = (uiState.value as? SwapAmountUM.Content)?.primaryCryptoCurrencyStatus if (primaryCryptoCurrencyStatus != null) { From 158d625e050aa87c22d9a0d1c696afd3962d6ab3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Aug 2025 13:58:50 +0400 Subject: [PATCH 32/53] Updated on 2026-08-14 --- domain/account/.gitignore | 1 + domain/account/build.gradle.kts | 23 ++++ .../domain/account/models/AccountList.kt | 92 +++++++++++++++ .../account/models/AccountStatusList.kt | 21 ++++ .../domain/account/models/AccountListTest.kt | 108 ++++++++++++++++++ .../tangem/domain/models/account/Account.kt | 7 ++ .../tangem/domain/models/account/AccountId.kt | 2 + .../domain/models/account/AccountName.kt | 5 + .../domain/models/account/AccountStatus.kt | 28 +++++ settings.gradle.kts | 1 + 10 files changed, 288 insertions(+) create mode 100644 domain/account/.gitignore create mode 100644 domain/account/build.gradle.kts create mode 100644 domain/account/src/main/java/com/tangem/domain/account/models/AccountList.kt create mode 100644 domain/account/src/main/java/com/tangem/domain/account/models/AccountStatusList.kt create mode 100644 domain/account/src/test/kotlin/com/tangem/domain/account/models/AccountListTest.kt create mode 100644 domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountStatus.kt diff --git a/domain/account/.gitignore b/domain/account/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/domain/account/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/domain/account/build.gradle.kts b/domain/account/build.gradle.kts new file mode 100644 index 0000000000..d21a2a628e --- /dev/null +++ b/domain/account/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + alias(deps.plugins.kotlin.jvm) + alias(deps.plugins.kotlin.serialization) + id("configuration") +} + +tasks.withType().configureEach { + useJUnitPlatform() +} + +dependencies { + + api(projects.domain.models) + api(projects.domain.wallets.models) + + implementation(deps.arrow.core) + implementation(deps.kotlin.serialization) + + testImplementation(deps.test.junit5) + testRuntimeOnly(deps.test.junit5.engine) + testImplementation(deps.test.truth) + testImplementation(deps.test.mockk) +} \ No newline at end of file diff --git a/domain/account/src/main/java/com/tangem/domain/account/models/AccountList.kt b/domain/account/src/main/java/com/tangem/domain/account/models/AccountList.kt new file mode 100644 index 0000000000..fdc9f393b0 --- /dev/null +++ b/domain/account/src/main/java/com/tangem/domain/account/models/AccountList.kt @@ -0,0 +1,92 @@ +package com.tangem.domain.account.models + +import arrow.core.Either +import arrow.core.raise.either +import arrow.core.raise.ensure +import com.tangem.domain.models.account.Account +import com.tangem.domain.models.wallet.UserWallet +import kotlinx.serialization.Serializable + +/** + * Represents a list of accounts associated with a user wallet + * + * @property userWallet the user wallet associated with the account list + * @property accounts a set of accounts belonging to the user wallet + * @property totalAccounts the total number of accounts + * +[REDACTED_AUTHOR] + */ +@Serializable +data class AccountList private constructor( + val userWallet: UserWallet, + val accounts: Set, + val totalAccounts: Int, +) { + + /** Retrieves the main crypto portfolio account from the list of accounts */ + val mainAccount: Account.CryptoPortfolio + get() = accounts.first { it is Account.CryptoPortfolio && it.isMainAccount } as Account.CryptoPortfolio + + /** + * Represents possible errors that can occur when creating an `AccountList` + */ + @Serializable + sealed interface Error { + + val tag: String + get() = this::class.simpleName ?: "AccountListError" + + @Serializable + data object EmptyAccountsList : Error { + override fun toString(): String = "$tag: The accounts list cannot be empty" + } + + @Serializable + data object MainAccountNotFound : Error { + override fun toString(): String { + return "$tag: Account list does not contain a main crypto portfolio account" + } + } + + @Serializable + data object ExceedsMaxMainAccountsCount : Error { + override fun toString(): String { + return "$tag: There should be at most one main crypto portfolio in the account list" + } + } + } + + companion object { + + /** + * Factory method to create an `AccountList` instance. + * Validates the input to ensure the accounts list is not empty and contains exactly one main account. + * + * @param userWallet the user wallet associated with the account list + * @param accounts a set of accounts belonging to the user wallet + * @param totalAccounts the total number of accounts + */ + operator fun invoke( + userWallet: UserWallet, + accounts: Set, + totalAccounts: Int, + ): Either = either { + ensure(accounts.isNotEmpty()) { Error.EmptyAccountsList } + + val mainAccountsCount = accounts.mainAccountsCount() + ensure(mainAccountsCount == 1) { + if (mainAccountsCount == 0) { + Error.MainAccountNotFound + } else { + Error.ExceedsMaxMainAccountsCount + } + } + + AccountList(userWallet = userWallet, accounts = accounts, totalAccounts = totalAccounts) + } + + private fun Set.mainAccountsCount(): Int { + return count { (it as? Account.CryptoPortfolio)?.isMainAccount == true } + } + } +} \ No newline at end of file diff --git a/domain/account/src/main/java/com/tangem/domain/account/models/AccountStatusList.kt b/domain/account/src/main/java/com/tangem/domain/account/models/AccountStatusList.kt new file mode 100644 index 0000000000..acd8f76d35 --- /dev/null +++ b/domain/account/src/main/java/com/tangem/domain/account/models/AccountStatusList.kt @@ -0,0 +1,21 @@ +package com.tangem.domain.account.models + +import com.tangem.domain.models.account.AccountStatus +import com.tangem.domain.models.wallet.UserWallet +import kotlinx.serialization.Serializable + +/** + * Represents a list of account statuses associated with a user wallet + * + * @property userWallet the user wallet to which the account statuses belong + * @property accountStatuses a set of account statuses associated with the user wallet + * @property totalAccounts the total number of accounts + * +[REDACTED_AUTHOR] + */ +@Serializable +data class AccountStatusList( + val userWallet: UserWallet, + val accountStatuses: Set, + val totalAccounts: Int, +) \ No newline at end of file diff --git a/domain/account/src/test/kotlin/com/tangem/domain/account/models/AccountListTest.kt b/domain/account/src/test/kotlin/com/tangem/domain/account/models/AccountListTest.kt new file mode 100644 index 0000000000..2abafd8934 --- /dev/null +++ b/domain/account/src/test/kotlin/com/tangem/domain/account/models/AccountListTest.kt @@ -0,0 +1,108 @@ +package com.tangem.domain.account.models + +import arrow.core.Either +import arrow.core.left +import com.google.common.truth.Truth +import com.tangem.domain.models.account.Account +import com.tangem.domain.models.wallet.UserWallet +import io.mockk.clearMocks +import io.mockk.every +import io.mockk.mockk +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource + +/** +[REDACTED_AUTHOR] + */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class AccountListTest { + + @Test + fun mainAccount() { + // Arrange + val mainAccount = createAccount(isMain = true) + + val accountList = AccountList( + userWallet = mockk(), + accounts = setOf(mainAccount), + totalAccounts = 1, + ) + .getOrNull()!! + + // Act + val actual = accountList.mainAccount + + // Assert + val expected = mainAccount + Truth.assertThat(actual).isEqualTo(expected) + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class Create { + + private val userWallet = mockk() + + @BeforeEach + fun resetMocks() { + clearMocks(userWallet) + } + + @ParameterizedTest + @MethodSource("provideTestModels") + fun invoke(model: CreateTestModel) { + // Act + val actual = AccountList( + userWallet = userWallet, + accounts = model.accounts, + totalAccounts = model.accounts.size, + ) + + // Assert + Truth.assertThat(actual).isEqualTo(model.expected) + } + + private fun provideTestModels() = listOf( + CreateTestModel( + accounts = emptySet(), + expected = AccountList.Error.EmptyAccountsList.left(), + ), + CreateTestModel( + accounts = setOf(createAccount(isMain = false)), + expected = AccountList.Error.MainAccountNotFound.left(), + ), + CreateTestModel( + accounts = setOf( + createAccount(isMain = true), + createAccount(isMain = true), + ), + expected = AccountList.Error.ExceedsMaxMainAccountsCount.left(), + ), + createAccount(isMain = true).let { + CreateTestModel( + accounts = setOf(it), + expected = AccountList( + userWallet = userWallet, + accounts = setOf(it), + totalAccounts = 1, + ), + ) + }, + ) + } + + data class CreateTestModel( + val accounts: Set, + val expected: Either, + ) + + private fun createAccount(isMain: Boolean = false): Account.CryptoPortfolio { + return mockk { + every { isMainAccount } returns isMain + } + } +} \ No newline at end of file diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/account/Account.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/account/Account.kt index 398930b72e..bbea116526 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/account/Account.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/account/Account.kt @@ -8,12 +8,14 @@ import com.tangem.domain.models.TokensSortType import com.tangem.domain.models.account.Account.CryptoPortfolio.Error.AccountNameError import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.wallet.UserWalletId +import kotlinx.serialization.Serializable /** * Represents an account * [REDACTED_AUTHOR] */ +@Serializable sealed interface Account { /** Unique identifier of the account */ @@ -36,6 +38,7 @@ sealed interface Account { * @property isArchived indicates whether the account is archived * @property cryptoCurrencyList list of tokens associated with the account */ + @Serializable data class CryptoPortfolio private constructor( override val accountId: AccountId, override val name: AccountName, @@ -64,6 +67,7 @@ sealed interface Account { * @property sortType sorting type for the tokens * @property groupType grouping type for the tokens */ + @Serializable data class CryptoCurrencyList( val currencies: Set, val sortType: TokensSortType, @@ -73,14 +77,17 @@ sealed interface Account { /** * Represents possible errors when creating a crypto portfolio account */ + @Serializable sealed interface Error { /** Error indicating that the account name is blank */ + @Serializable data class AccountNameError(val cause: AccountName.Error) : Error { override fun toString(): String = cause.toString() } /** Error indicating that the derivation index is negative */ + @Serializable data object NegativeDerivationIndex : Error { override fun toString(): String = "${this::class.simpleName}: Derivation index must be non-negative" } diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountId.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountId.kt index 2f5728579c..725f7143e9 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountId.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountId.kt @@ -1,6 +1,7 @@ package com.tangem.domain.models.account import com.tangem.domain.models.wallet.UserWalletId +import kotlinx.serialization.Serializable /** * Represents a unique identifier for an account @@ -8,6 +9,7 @@ import com.tangem.domain.models.wallet.UserWalletId * @property value a unique string value that distinguishes this account * @property userWalletId the identifier of the user wallet associated with the account */ +@Serializable data class AccountId( val value: String, val userWalletId: UserWalletId, diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountName.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountName.kt index e51a8e5a30..532687ef6c 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountName.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountName.kt @@ -3,6 +3,7 @@ package com.tangem.domain.models.account import arrow.core.Either import arrow.core.raise.either import arrow.core.raise.ensure +import kotlinx.serialization.Serializable /** * Represents an account name @@ -11,6 +12,7 @@ import arrow.core.raise.ensure * [REDACTED_AUTHOR] */ +@Serializable data class AccountName private constructor( val value: String, ) { @@ -18,11 +20,13 @@ data class AccountName private constructor( /** * Represents possible validation errors */ + @Serializable sealed interface Error { /** * Error indicating that the account name is blank */ + @Serializable data object Empty : Error { override fun toString(): String = "${Empty::class.simpleName}: Account name cannot be blank" } @@ -30,6 +34,7 @@ data class AccountName private constructor( /** * Error indicating that the account name exceeds the maximum allowed length */ + @Serializable data object ExceedsMaxLength : Error { override fun toString(): String { return "${ExceedsMaxLength::class.simpleName}: Account name cannot exceed $MAX_LENGTH characters" diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountStatus.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountStatus.kt new file mode 100644 index 0000000000..73bc34e70e --- /dev/null +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/account/AccountStatus.kt @@ -0,0 +1,28 @@ +package com.tangem.domain.models.account + +import com.tangem.domain.models.tokenlist.TokenList +import kotlinx.serialization.Serializable + +/** + * Represents the status of an account + * +[REDACTED_AUTHOR] + */ +@Serializable +sealed interface AccountStatus { + + /** The account associated with this status */ + val account: Account + + /** + * Represents the status of a crypto portfolio account + * + * @property account the crypto portfolio account + * @property tokenList the list of tokens associated with the account + */ + @Serializable + data class CryptoPortfolio( + override val account: Account.CryptoPortfolio, + val tokenList: TokenList, + ) : AccountStatus +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 9eb66f4052..de1aaf92ce 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -276,6 +276,7 @@ include(":features:welcome:impl") include(":domain:models") include(":domain:legacy") +include(":domain:account") include(":domain:card") include(":domain:core") include(":domain:demo") From 3b181b08c7a810627ce581c19644fea9c91484fd Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Aug 2025 19:04:52 +0400 Subject: [PATCH 33/53] Updated on 2026-08-14 --- .../send/v2/feeselector/ui/FeeSelectorModalBottomSheet.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/ui/FeeSelectorModalBottomSheet.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/ui/FeeSelectorModalBottomSheet.kt index 24445e46cf..cbf1e1c4b1 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/ui/FeeSelectorModalBottomSheet.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/ui/FeeSelectorModalBottomSheet.kt @@ -362,7 +362,7 @@ private fun ExpandedCustomFeeItems( showDivider = false, modifier = Modifier .background( - color = TangemTheme.colors.background.action, + color = TangemTheme.colors.background.primary, shape = TangemTheme.shapes.roundedCornersXMedium, ), ) From 5103f81da803c6e7288679bc30094077d574979b Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Aug 2025 17:18:57 +0200 Subject: [PATCH 34/53] Updated on 2026-08-14 --- gradle/dependencies.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 1f665af06c..3aee2f0af5 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -80,8 +80,8 @@ reownWeb3 = "1.1.2" prettyLogger = "2.2.0" okHttp-prettyLogging = "3.1.0" chucker = "4.0.0" -mlKit-barcodeScanning = "17.2.0" -androidXCamera = "1.3.0" +mlKit-barcodeScanning = "17.3.0" +androidXCamera = "1.4.2" listenableFuture = "1.0" swipeRefreshLayout = "1.1.0" web3j = "4.12.3-SNAPSHOT" From 54558f1535cc5969e8d057162d6196cd3112024b Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Aug 2025 15:44:41 +0000 Subject: [PATCH 35/53] Updated on 2026-08-14 --- gradle/tangem_dependencies.toml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index 7250e5eb0c..78d485789e 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,12 +5,14 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "releases-5.26.0-1123" +tangemBlockchainSdk = "develop-1129" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "releases-5.26.0-508" +tangemCardSdk = "develop-509" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem12" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ +tangemHotSdk = "develop-446" +#tangemHotSdk = "0.0.1" # Keep it! - used for local builds ^ @@ -18,6 +20,8 @@ tangemVico = "2.0.0-alpha.25-tangem12" blockchain = { module = "com.tangem:blockchain", version.ref = "tangemBlockchainSdk" } card-android = { module = "com.tangem.tangem-sdk-kotlin:android", version.ref = "tangemCardSdk" } card-core = { module = "com.tangem.tangem-sdk-kotlin:core", version.ref = "tangemCardSdk" } +hot-core = { module = "com.tangem.tangem-hot-sdk-kotlin:core", version.ref = "tangemHotSdk" } +hot-android = { module = "com.tangem.tangem-hot-sdk-kotlin:android", version.ref = "tangemHotSdk" } vico-compose = { group = "com.tangem.vico", name = "compose", version.ref = "tangemVico" } vico-compose-m3 = { group = "com.tangem.vico", name = "compose-m3", version.ref = "tangemVico" } From 1713cfbf76ff6d95ce9e06ae044c7785907b263f Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Aug 2025 16:57:13 +0000 Subject: [PATCH 36/53] Updated on 2026-08-14 --- gradle/tangem_dependencies.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index 7ab02a4628..78d485789e 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,9 +5,9 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "releases-5.27.0-1126" +tangemBlockchainSdk = "develop-1129" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "releases-5.27.0-510" +tangemCardSdk = "develop-509" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem12" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ From a52c9cb10687db4cdcde085e2728904d49e3e7e7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Aug 2025 14:07:40 +0400 Subject: [PATCH 37/53] Updated on 2026-08-14 --- app/build.gradle.kts | 1 + .../tap/di/domain/AccountDomainModule.kt | 40 ++++++++++++++ .../usecase/AddCryptoPortfolioUseCase.kt | 55 +++++++++++++++++++ .../usecase/ArchiveCryptoPortfolioUseCase.kt | 22 ++++++++ .../usecase/RecoverCryptoPortfolioUseCase.kt | 25 +++++++++ .../usecase/UpdateCryptoPortfolioUseCase.kt | 50 +++++++++++++++++ 6 files changed, 193 insertions(+) create mode 100644 app/src/main/java/com/tangem/tap/di/domain/AccountDomainModule.kt create mode 100644 domain/account/src/main/java/com/tangem/domain/account/usecase/AddCryptoPortfolioUseCase.kt create mode 100644 domain/account/src/main/java/com/tangem/domain/account/usecase/ArchiveCryptoPortfolioUseCase.kt create mode 100644 domain/account/src/main/java/com/tangem/domain/account/usecase/RecoverCryptoPortfolioUseCase.kt create mode 100644 domain/account/src/main/java/com/tangem/domain/account/usecase/UpdateCryptoPortfolioUseCase.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 19dd55b0e1..e90dbf8a85 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -80,6 +80,7 @@ configurations.androidTestImplementation { dependencies { implementation(projects.domain.legacy) implementation(projects.libs.blockchainSdk) + implementation(projects.domain.account) implementation(projects.domain.models) implementation(projects.domain.core) implementation(projects.domain.card) diff --git a/app/src/main/java/com/tangem/tap/di/domain/AccountDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/AccountDomainModule.kt new file mode 100644 index 0000000000..22e627efca --- /dev/null +++ b/app/src/main/java/com/tangem/tap/di/domain/AccountDomainModule.kt @@ -0,0 +1,40 @@ +package com.tangem.tap.di.domain + +import com.tangem.domain.account.usecase.AddCryptoPortfolioUseCase +import com.tangem.domain.account.usecase.ArchiveCryptoPortfolioUseCase +import com.tangem.domain.account.usecase.RecoverCryptoPortfolioUseCase +import com.tangem.domain.account.usecase.UpdateCryptoPortfolioUseCase +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object AccountDomainModule { + + @Provides + @Singleton + fun provideAddCryptoPortfolioUseCase(): AddCryptoPortfolioUseCase { + return AddCryptoPortfolioUseCase() + } + + @Provides + @Singleton + fun provideUpdateCryptoPortfolioUseCase(): UpdateCryptoPortfolioUseCase { + return UpdateCryptoPortfolioUseCase() + } + + @Provides + @Singleton + fun provideArchiveCryptoPortfolioUseCase(): ArchiveCryptoPortfolioUseCase { + return ArchiveCryptoPortfolioUseCase() + } + + @Provides + @Singleton + fun provideRecoverCryptoPortfolioUseCase(): RecoverCryptoPortfolioUseCase { + return RecoverCryptoPortfolioUseCase() + } +} \ No newline at end of file diff --git a/domain/account/src/main/java/com/tangem/domain/account/usecase/AddCryptoPortfolioUseCase.kt b/domain/account/src/main/java/com/tangem/domain/account/usecase/AddCryptoPortfolioUseCase.kt new file mode 100644 index 0000000000..fb2ebb4717 --- /dev/null +++ b/domain/account/src/main/java/com/tangem/domain/account/usecase/AddCryptoPortfolioUseCase.kt @@ -0,0 +1,55 @@ +package com.tangem.domain.account.usecase + +import arrow.core.Either +import arrow.core.raise.either +import com.tangem.domain.account.models.AccountList +import com.tangem.domain.account.usecase.AddCryptoPortfolioUseCase.Error.AccountCreation +import com.tangem.domain.models.TokensGroupType +import com.tangem.domain.models.TokensSortType +import com.tangem.domain.models.account.Account +import com.tangem.domain.models.account.AccountId +import com.tangem.domain.models.account.AccountName +import com.tangem.domain.models.account.CryptoPortfolioIcon +import com.tangem.domain.models.wallet.UserWalletId +import java.util.UUID + +/** +[REDACTED_AUTHOR] + */ +class AddCryptoPortfolioUseCase { + + suspend operator fun invoke( + userWalletId: UserWalletId, + accountName: AccountName, + icon: CryptoPortfolioIcon, + derivationIndex: Int, + ): Either = either { + Account.CryptoPortfolio( + accountId = AccountId(userWalletId = userWalletId, value = UUID.randomUUID().toString()), + name = accountName.value, + accountIcon = icon, + derivationIndex = derivationIndex, + isArchived = false, + cryptoCurrencyList = Account.CryptoPortfolio.CryptoCurrencyList( + currencies = emptySet(), + sortType = TokensSortType.NONE, + groupType = TokensGroupType.NONE, + ), + ) + .mapLeft(::AccountCreation) + .bind() + + // TODO: [REDACTED_JIRA] + // Save to local store + // Save to backend (tokens migration) – asynchronously + } + + sealed interface Error { + + data class AccountCreation(val cause: Account.CryptoPortfolio.Error) : Error + + data class AccountListRequirementsNotMet(val cause: AccountList.Error) : Error + + data object DataOperationFailed : Error + } +} \ No newline at end of file diff --git a/domain/account/src/main/java/com/tangem/domain/account/usecase/ArchiveCryptoPortfolioUseCase.kt b/domain/account/src/main/java/com/tangem/domain/account/usecase/ArchiveCryptoPortfolioUseCase.kt new file mode 100644 index 0000000000..8e473b507d --- /dev/null +++ b/domain/account/src/main/java/com/tangem/domain/account/usecase/ArchiveCryptoPortfolioUseCase.kt @@ -0,0 +1,22 @@ +package com.tangem.domain.account.usecase + +import arrow.core.Either +import arrow.core.raise.either +import com.tangem.domain.models.account.AccountId + +/** +[REDACTED_AUTHOR] + */ +class ArchiveCryptoPortfolioUseCase { + + suspend operator fun invoke(accountId: AccountId): Either = either { + // TODO: [REDACTED_JIRA] + // Remove the account from the list of active accounts + // Add the account to the list of archived accounts + // Save to backend + } + + sealed interface Error { + data object DataOperationFailed : Error + } +} \ No newline at end of file diff --git a/domain/account/src/main/java/com/tangem/domain/account/usecase/RecoverCryptoPortfolioUseCase.kt b/domain/account/src/main/java/com/tangem/domain/account/usecase/RecoverCryptoPortfolioUseCase.kt new file mode 100644 index 0000000000..2d27ad3a32 --- /dev/null +++ b/domain/account/src/main/java/com/tangem/domain/account/usecase/RecoverCryptoPortfolioUseCase.kt @@ -0,0 +1,25 @@ +package com.tangem.domain.account.usecase + +import arrow.core.Either +import arrow.core.raise.either +import com.tangem.domain.models.account.Account +import com.tangem.domain.models.account.AccountId + +/** +[REDACTED_AUTHOR] + */ +class RecoverCryptoPortfolioUseCase { + + suspend operator fun invoke(accountId: AccountId): Either = either { + raise(Error.DataOperationFailed) + + // TODO: [REDACTED_JIRA] + // Remove the account from the list of archived accounts + // Add the account to the list of active accounts + // Save to backend + } + + sealed interface Error { + data object DataOperationFailed : Error + } +} \ No newline at end of file diff --git a/domain/account/src/main/java/com/tangem/domain/account/usecase/UpdateCryptoPortfolioUseCase.kt b/domain/account/src/main/java/com/tangem/domain/account/usecase/UpdateCryptoPortfolioUseCase.kt new file mode 100644 index 0000000000..cec8cbc039 --- /dev/null +++ b/domain/account/src/main/java/com/tangem/domain/account/usecase/UpdateCryptoPortfolioUseCase.kt @@ -0,0 +1,50 @@ +package com.tangem.domain.account.usecase + +import arrow.core.Either +import arrow.core.raise.either +import com.tangem.domain.models.TokensGroupType +import com.tangem.domain.models.TokensSortType +import com.tangem.domain.models.account.Account +import com.tangem.domain.models.account.AccountId +import com.tangem.domain.models.account.AccountName +import com.tangem.domain.models.account.CryptoPortfolioIcon +import kotlin.random.Random + +/** +[REDACTED_AUTHOR] + */ +class UpdateCryptoPortfolioUseCase { + + suspend operator fun invoke( + accountId: AccountId, + name: AccountName? = null, + icon: CryptoPortfolioIcon? = null, + ): Either = either { + Account.CryptoPortfolio( + accountId = accountId, + name = name?.value ?: "Account", + accountIcon = icon ?: CryptoPortfolioIcon.ofDefaultCustomAccount(), + derivationIndex = Random.nextInt(from = 0, until = 21), + isArchived = false, + cryptoCurrencyList = Account.CryptoPortfolio.CryptoCurrencyList( + currencies = emptySet(), + sortType = TokensSortType.NONE, + groupType = TokensGroupType.NONE, + ), + ) + .mapLeft { Error.DataOperationFailed } + .bind() + + // TODO: [REDACTED_JIRA] + // Create a domain model AccountName + // Get the current account by [accountId] + // Create a new domain model Account from old data considering new parameters + // Save information in local storage + // Save to backend + } + + sealed interface Error { + + data object DataOperationFailed : Error + } +} \ No newline at end of file From 4cac26e62838ddd71966468ee77d2440b8e5c663 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 31 Jul 2025 16:31:37 +0500 Subject: [PATCH 38/53] Updated on 2026-08-14 --- .../swap/v2/impl/amount/model/SwapAmountModel.kt | 3 +++ .../transformers/SwapAmountSetQuotesTransformer.kt | 2 +- .../transformers/SwapQuoteLoadingStateTransformer.kt | 1 + .../swap/v2/impl/amount/ui/SwapAmountContent.kt | 10 +++++----- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt index fa265d5e1c..efc0561da2 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt @@ -51,6 +51,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.Debouncer import com.tangem.utils.coroutines.PeriodicTask import com.tangem.utils.coroutines.SingleTaskScheduler +import com.tangem.utils.extensions.isZero import com.tangem.utils.extensions.orZero import com.tangem.utils.transformer.update import kotlinx.coroutines.async @@ -481,6 +482,8 @@ internal class SwapAmountModel @Inject constructor( val fromAmountValue = fromAmount?.amountTextField?.cryptoAmount?.value ?: return + if (fromAmountValue.isZero()) return + val swapGroups = state.swapCurrencies.getGroupWithDirection(state.swapDirection) uiState.transformerUpdate(SwapQuoteLoadingStateTransformer) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt index 665a6a37fb..a3f6263d18 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt @@ -26,7 +26,7 @@ internal class SwapAmountSetQuotesTransformer( val sortedQuotes = quotes.sortedWith(SwapQuotesComparator) val bestQuote = findBestQuote(quotes) ?: SwapQuoteUM.Empty - val selectedQuote = if (isSilentReload) { + val selectedQuote = if (isSilentReload && prevState.selectedQuote !is SwapQuoteUM.Loading) { prevState.selectedQuote } else { (bestQuote as? SwapQuoteUM.Content)?.copy(diffPercent = DifferencePercent.Best) ?: bestQuote diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapQuoteLoadingStateTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapQuoteLoadingStateTransformer.kt index a26deaf753..b431b0472a 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapQuoteLoadingStateTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapQuoteLoadingStateTransformer.kt @@ -16,6 +16,7 @@ internal object SwapQuoteLoadingStateTransformer : Transformer { } return prevState.copy( selectedQuote = SwapQuoteUM.Loading, + isPrimaryButtonEnabled = false, ) } } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountContent.kt index 1ac34a19e2..199921a753 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountContent.kt @@ -240,12 +240,12 @@ private fun SwapAmountInfo( SwapAmountInfoMain(amountFieldUM = amountFieldUM) SpacerWMax() AnimatedContent( - amountUM, - ) { wrappedAmountUM -> - if (wrappedAmountUM is SwapAmountUM.Content) { + amountFieldUM, + ) { wrappedFieldAmountUM -> + if (amountUM is SwapAmountUM.Content) { SwapAmountInfoExtra( - amountUM = wrappedAmountUM, - amountFieldUM = amountFieldUM, + amountUM = amountUM, + amountFieldUM = wrappedFieldAmountUM, onMaxAmountClick = onMaxAmountClick, onSelectTokenClick = onSelectTokenClick, ) From 8f69b1dce30d0b80216481c4718d32acdaf81645 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 31 Jul 2025 12:40:48 +0500 Subject: [PATCH 39/53] Updated on 2026-08-14 --- .../tangem/common/ui/footers/SendingText.kt | 4 +- .../java/com/tangem/utils/StringsSigns.kt | 1 + .../send/v2/api}/utils/ConfirmFooterUtils.kt | 8 +- ...endConfirmationNotificationsTransformer.kt | 4 +- ...dConfirmationNotificationsTransformerV2.kt | 4 +- ...endConfirmationNotificationsTransformer.kt | 4 +- .../swap/v2/impl/common/entity/ConfirmUM.kt | 13 ++- .../confirm/SendWithSwapConfirmComponent.kt | 3 + ...dWithSwapConfirmInitialStateTransformer.kt | 1 + ...wapConfirmationNotificationsTransformer.kt | 52 +++++++---- .../confirm/ui/SendWithSwapConfirmContent.kt | 90 +++++++++++++++++-- .../sendviaswap/ui/SendWithSwapContent.kt | 8 +- 12 files changed, 157 insertions(+), 35 deletions(-) rename features/send-v2/{impl/src/main/java/com/tangem/features/send/v2/common => api/src/main/java/com/tangem/features/send/v2/api}/utils/ConfirmFooterUtils.kt (89%) diff --git a/common/ui/src/main/java/com/tangem/common/ui/footers/SendingText.kt b/common/ui/src/main/java/com/tangem/common/ui/footers/SendingText.kt index 3f880fa882..b2c89117fe 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/footers/SendingText.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/footers/SendingText.kt @@ -49,10 +49,10 @@ fun SendingText(footerText: TextReference, modifier: Modifier = Modifier) { text = footerText.resolveAnnotatedReference(), textAlign = TextAlign.Center, style = TangemTheme.typography.caption2, - color = TangemTheme.colors.text.primary1, + color = TangemTheme.colors.text.tertiary, modifier = Modifier .fillMaxWidth() - .padding(12.dp), + .padding(16.dp), ) } } \ No newline at end of file diff --git a/core/utils/src/main/java/com/tangem/utils/StringsSigns.kt b/core/utils/src/main/java/com/tangem/utils/StringsSigns.kt index 534b602d2c..98ba454b64 100644 --- a/core/utils/src/main/java/com/tangem/utils/StringsSigns.kt +++ b/core/utils/src/main/java/com/tangem/utils/StringsSigns.kt @@ -9,6 +9,7 @@ object StringsSigns { const val LOWER_SIGN = "<" const val TILDE_SIGN = "~" const val COMA_SIGN = "," + const val POINT_SIGN = "." const val INFINITY_SIGN = "∞" const val NON_BREAKING_SPACE = '\u00A0' const val PERCENT = "%" diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/utils/ConfirmFooterUtils.kt b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/utils/ConfirmFooterUtils.kt similarity index 89% rename from features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/utils/ConfirmFooterUtils.kt rename to features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/utils/ConfirmFooterUtils.kt index 3c13220638..4b0d95ede3 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/utils/ConfirmFooterUtils.kt +++ b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/utils/ConfirmFooterUtils.kt @@ -1,4 +1,4 @@ -package com.tangem.features.send.v2.common.utils +package com.tangem.features.send.v2.api.utils import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.transaction.Fee @@ -8,10 +8,10 @@ import com.tangem.core.ui.format.bigdecimal.fee import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.features.send.v2.impl.R +import com.tangem.features.send.v2.api.R import com.tangem.utils.StringsSigns.COMA_SIGN -internal fun getTronTokenFeeSendingText(fee: Fee.Tron, fiatFee: String, fiatSending: TextReference): TextReference { +fun getTronTokenFeeSendingText(fee: Fee.Tron, fiatFee: String, fiatSending: TextReference): TextReference { val suffix = when { fee.remainingEnergy == 0L -> { resourceReference( @@ -40,7 +40,7 @@ internal fun getTronTokenFeeSendingText(fee: Fee.Tron, fiatFee: String, fiatSend return combinedReference(prefix, stringReference("$COMA_SIGN "), suffix) } -internal fun formatFooterFiatFee( +fun formatFooterFiatFee( amount: Amount?, isFeeConvertibleToFiat: Boolean, isFeeApproximate: Boolean, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/transformers/SendConfirmationNotificationsTransformer.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/transformers/SendConfirmationNotificationsTransformer.kt index 906f76bb91..8e8987f2f8 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/transformers/SendConfirmationNotificationsTransformer.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/transformers/SendConfirmationNotificationsTransformer.kt @@ -16,8 +16,8 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents import com.tangem.features.send.v2.common.ui.state.ConfirmUM -import com.tangem.features.send.v2.common.utils.formatFooterFiatFee -import com.tangem.features.send.v2.common.utils.getTronTokenFeeSendingText +import com.tangem.features.send.v2.api.utils.formatFooterFiatFee +import com.tangem.features.send.v2.api.utils.getTronTokenFeeSendingText import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.subcomponents.fee.model.checkIfFeeTooHigh import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/transformers/SendConfirmationNotificationsTransformerV2.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/transformers/SendConfirmationNotificationsTransformerV2.kt index 7252942833..a5935435c3 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/transformers/SendConfirmationNotificationsTransformerV2.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/transformers/SendConfirmationNotificationsTransformerV2.kt @@ -16,8 +16,8 @@ import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents import com.tangem.features.send.v2.common.ui.state.ConfirmUM -import com.tangem.features.send.v2.common.utils.formatFooterFiatFee -import com.tangem.features.send.v2.common.utils.getTronTokenFeeSendingText +import com.tangem.features.send.v2.api.utils.formatFooterFiatFee +import com.tangem.features.send.v2.api.utils.getTronTokenFeeSendingText import com.tangem.features.send.v2.impl.R import com.tangem.utils.transformer.Transformer import kotlinx.collections.immutable.toPersistentList diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/transformers/NFTSendConfirmationNotificationsTransformer.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/transformers/NFTSendConfirmationNotificationsTransformer.kt index 561816abe5..fda1601aa9 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/transformers/NFTSendConfirmationNotificationsTransformer.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/transformers/NFTSendConfirmationNotificationsTransformer.kt @@ -12,8 +12,8 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents import com.tangem.features.send.v2.common.ui.state.ConfirmUM -import com.tangem.features.send.v2.common.utils.formatFooterFiatFee -import com.tangem.features.send.v2.common.utils.getTronTokenFeeSendingText +import com.tangem.features.send.v2.api.utils.formatFooterFiatFee +import com.tangem.features.send.v2.api.utils.getTronTokenFeeSendingText import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.subcomponents.fee.model.checkIfFeeTooHigh import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/entity/ConfirmUM.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/entity/ConfirmUM.kt index 701181e121..b105bed8a1 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/entity/ConfirmUM.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/entity/ConfirmUM.kt @@ -18,7 +18,18 @@ internal sealed class ConfirmUM { val showTapHelp: Boolean, val sendingFooter: TextReference, val notifications: ImmutableList, - ) : ConfirmUM() + val tosUM: TosUM?, + ) : ConfirmUM() { + data class TosUM( + val tosLink: LegalUM?, + val policyLink: LegalUM?, + ) + + data class LegalUM( + val title: TextReference, + val link: String, + ) + } data class Success( override val isPrimaryButtonEnabled: Boolean = true, diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt index 47b374269f..d3cb5651c8 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/SendWithSwapConfirmComponent.kt @@ -8,6 +8,7 @@ import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.child import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus @@ -41,6 +42,7 @@ internal class SendWithSwapConfirmComponent @AssistedInject constructor( sendDestinationBlockComponent: SendDestinationBlockComponent.Factory, feeSelectorBlockComponentFactory: FeeSelectorBlockComponent.Factory, sendNotificationsComponentFactory: SendNotificationsComponent.Factory, + private val urlOpener: UrlOpener, ) : ComposableContentComponent, AppComponentContext by appComponentContext { private val model: SendWithSwapConfirmModel = getOrCreateModel(params = params) @@ -151,6 +153,7 @@ internal class SendWithSwapConfirmComponent @AssistedInject constructor( sendNotificationsUM = sendNotificationsUM, swapNotificationsComponent = swapNotificationsComponent, swapNotificationsUM = swapNotificationsUM, + onLinkClick = urlOpener::openUrl, modifier = modifier, ) } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/transformers/SendWithSwapConfirmInitialStateTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/transformers/SendWithSwapConfirmInitialStateTransformer.kt index 4be5c0b56a..64e34d1673 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/transformers/SendWithSwapConfirmInitialStateTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/transformers/SendWithSwapConfirmInitialStateTransformer.kt @@ -15,6 +15,7 @@ internal class SendWithSwapConfirmInitialStateTransformer( showTapHelp = isShowTapHelp, sendingFooter = TextReference.EMPTY, notifications = persistentListOf(), + tosUM = null, ) } } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/transformers/SendWithSwapConfirmationNotificationsTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/transformers/SendWithSwapConfirmationNotificationsTransformer.kt index 014e57650d..3176e70a6c 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/transformers/SendWithSwapConfirmationNotificationsTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/transformers/SendWithSwapConfirmationNotificationsTransformer.kt @@ -5,6 +5,7 @@ import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.ui.extensions.TextReference 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.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format @@ -12,6 +13,8 @@ import com.tangem.domain.swap.models.SwapDirection.Companion.withSwapDirection import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils.checkIfCustomFeeTooHigh import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils.checkIfCustomFeeTooLow +import com.tangem.features.send.v2.api.utils.formatFooterFiatFee +import com.tangem.features.send.v2.api.utils.getTronTokenFeeSendingText import com.tangem.features.swap.v2.impl.R import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM import com.tangem.features.swap.v2.impl.common.entity.ConfirmUM @@ -27,6 +30,7 @@ internal class SendWithSwapConfirmationNotificationsTransformer : Transformer, swapNotificationsComponent: SwapNotificationsComponent, swapNotificationsUM: ImmutableList, + onLinkClick: (String) -> Unit, modifier: Modifier = Modifier, ) { val confirmUM = sendWithSwapUM.confirmUM as? ConfirmUM.Content Column(modifier = modifier) { LazyColumn( - modifier = Modifier.padding(horizontal = 12.dp), + modifier = Modifier + .weight(1f) + .padding(horizontal = 12.dp), ) { item(key = "SendWithSwapBlocks") { Column( @@ -71,7 +78,80 @@ internal fun SendWithSwapConfirmContent( ) } } - SpacerHMax() - SendingText(footerText = confirmUM?.sendingFooter ?: TextReference.EMPTY) + val sendFooter = confirmUM?.sendingFooter ?: TextReference.EMPTY + val legalFooter = getAnnotatedStringForLegals(confirmUM?.tosUM, onClick = onLinkClick) + SendingText( + footerText = if (sendFooter != TextReference.EMPTY || legalFooter != TextReference.EMPTY) { + combinedReference(sendFooter, legalFooter) + } else { + TextReference.EMPTY + }, + ) + } +} + +@Composable +private fun getAnnotatedStringForLegals(tosUM: ConfirmUM.Content.TosUM?, onClick: (String) -> Unit): TextReference { + if (tosUM == null) return TextReference.EMPTY + val tos = tosUM.tosLink + val policy = tosUM.policyLink + return if (tos != null && policy != null) { + val tosTitle = tos.title.resolveReference() + val policyTitle = policy.title.resolveReference() + val fullString = stringResourceSafe(id = R.string.express_legal_two_placeholders, tosTitle, policyTitle) + val tosIndex = fullString.indexOf(tosTitle) + val policyIndex = fullString.indexOf(policyTitle) + + annotatedReference { + append(StringsSigns.POINT_SIGN) + appendSpace() + append(fullString.substring(0, tosIndex)) + withLink( + link = LinkAnnotation.Clickable( + tag = "TOS_TAG", + linkInteractionListener = { onClick(tos.link) }, + ), + block = { + appendColored( + text = fullString.substring(tosIndex, tosIndex + tosTitle.length), + color = TangemTheme.colors.text.accent, + ) + }, + ) + append(fullString.substring(tosIndex + tosTitle.length, policyIndex)) + withLink( + link = LinkAnnotation.Clickable( + tag = "POLICY_TAG", + linkInteractionListener = { onClick(policy.link) }, + ), + block = { + appendColored( + text = fullString.substring(policyIndex, policyIndex + policyTitle.length), + color = TangemTheme.colors.text.accent, + ) + }, + ) + } + } else { + val legal = requireNotNull(tos ?: policy) { "tos or policy must not be null" } + val legalTitle = legal.title.resolveReference() + val fullString = stringResourceSafe(id = R.string.express_legal_one_placeholder, legalTitle) + val legalIndex = fullString.indexOf(legalTitle) + + annotatedReference { + append(fullString.substring(0, legalIndex)) + withLink( + link = LinkAnnotation.Clickable( + tag = "LEGAL_TAG", + linkInteractionListener = { onClick(legal.link) }, + ), + block = { + appendColored( + text = fullString.substring(legalIndex, legalIndex + legalTitle.length), + color = TangemTheme.colors.text.accent, + ) + }, + ) + } } } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/ui/SendWithSwapContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/ui/SendWithSwapContent.kt index 17355d4aa4..e9d03433d4 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/ui/SendWithSwapContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/ui/SendWithSwapContent.kt @@ -59,7 +59,13 @@ internal fun SendWithSwapContent( } // TODO refactor [REDACTED_TASK_KEY] val primaryButton = navigationUM.primaryButton - Row(modifier = Modifier.padding(16.dp)) { + Row( + modifier = Modifier.padding( + start = 16.dp, + end = 16.dp, + bottom = 16.dp, + ), + ) { TangemButton( modifier = Modifier.fillMaxWidth(), text = primaryButton.textReference.resolveReference(), From 5adb9506b21c0bf476651ad6050c3daa4c2c8ad5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 1 Aug 2025 12:07:43 +0500 Subject: [PATCH 40/53] Updated on 2026-08-14 --- .../features/send/v2/send/DefaultSendComponent.kt | 7 ++++++- .../send/v2/send/confirm/model/SendConfirmModel.kt | 6 +++++- .../destination/model/SendDestinationModel.kt | 11 ++++++----- .../impl/sendviaswap/DefaultSendWithSwapComponent.kt | 7 ++++++- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt index 29b3573aa4..9cc15c97bb 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt @@ -12,6 +12,7 @@ import com.arkivanov.decompose.router.stack.pop import com.arkivanov.decompose.value.ObserveLifecycleMode import com.arkivanov.decompose.value.subscribe import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.common.ui.navigationButtons.NavigationUM import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.child @@ -125,7 +126,11 @@ internal class DefaultSendComponent @AssistedInject constructor( val stackState by childStack.subscribeAsState() val state by model.uiState.collectAsStateWithLifecycle() - BackHandler(onBack = ::onChildBack) + BackHandler( + onBack = { + (state.navigationUM as? NavigationUM.Content)?.backIconClick() ?: onChildBack() + }, + ) SendContent( navigationUM = state.navigationUM, stackState = stackState, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt index 2296485619..4f8e762e40 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt @@ -594,7 +594,11 @@ internal class SendConfirmModel @Inject constructor( isValid = confirmUM.isPrimaryButtonEnabled, ), ) - appRouter.pop() + if (state.isRedesignEnabled) { + router.pop() + } else { + appRouter.pop() + } }, primaryButton = primaryButtonUM(), prevButton = null, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt index 6fdf7e3b09..db3ba80d27 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt @@ -145,6 +145,11 @@ internal class SendDestinationModel @Inject constructor( ) } + fun saveResult() { + val params = params as? SendDestinationComponentParams.DestinationParams ?: return + params.callback.onDestinationResult(uiState.value) + } + private fun initSenderAddress() { modelScope.launch { senderAddresses.value = getNetworkAddressesUseCase.invokeSync( @@ -288,11 +293,6 @@ internal class SendDestinationModel @Inject constructor( } } - private fun saveResult() { - val params = params as? SendDestinationComponentParams.DestinationParams ?: return - params.callback.onDestinationResult(uiState.value) - } - @Suppress("LongMethod") private fun configDestinationNavigation() { val params = params as? SendDestinationComponentParams.DestinationParams ?: return @@ -321,6 +321,7 @@ internal class SendDestinationModel @Inject constructor( isValid = state.isPrimaryButtonEnabled, ), ) + saveResult() } params.callback.onBackClick() }, diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt index 986c936162..e331657e56 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt @@ -11,6 +11,7 @@ import com.arkivanov.decompose.router.stack.childStack import com.arkivanov.decompose.router.stack.pop import com.arkivanov.decompose.value.ObserveLifecycleMode import com.arkivanov.decompose.value.subscribe +import com.tangem.common.ui.navigationButtons.NavigationUM import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.model.getOrCreateModel @@ -101,7 +102,11 @@ internal class DefaultSendWithSwapComponent @AssistedInject constructor( val stackState by childStack.subscribeAsState() val state by model.uiState.collectAsStateWithLifecycle() - BackHandler(onBack = ::onChildBack) + BackHandler( + onBack = { + (state.navigationUM as? NavigationUM.Content)?.backIconClick() ?: onChildBack() + }, + ) SendWithSwapContent(navigationUM = state.navigationUM, stackState = stackState) } From 816d8532de35e5860832f2460c5c301a1469ee86 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 1 Aug 2025 16:19:01 +0500 Subject: [PATCH 41/53] Updated on 2026-08-14 --- .../data/express/DefaultExpressRepository.kt | 6 ++- .../data/express/di/ExpressDataModule.kt | 3 ++ .../DefaultSingleQuoteStatusProducer.kt | 7 +-- .../data/swap/DefaultSwapRepositoryV2.kt | 51 ++++++++++--------- .../ui/SwapChooseTokenNetworkContent.kt | 16 ++++-- 5 files changed, 49 insertions(+), 34 deletions(-) diff --git a/data/express/src/main/java/com/tangem/data/express/DefaultExpressRepository.kt b/data/express/src/main/java/com/tangem/data/express/DefaultExpressRepository.kt index 107cf20e9f..7fecd6950c 100644 --- a/data/express/src/main/java/com/tangem/data/express/DefaultExpressRepository.kt +++ b/data/express/src/main/java/com/tangem/data/express/DefaultExpressRepository.kt @@ -10,19 +10,21 @@ import com.tangem.domain.express.ExpressRepository import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.express.models.ExpressProviderType import com.tangem.domain.models.wallet.UserWallet +import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.extensions.filterIf import timber.log.Timber internal class DefaultExpressRepository( private val tangemExpressApi: TangemExpressApi, private val appPreferencesStore: AppPreferencesStore, + private val dispatchers: CoroutineDispatcherProvider, ) : ExpressRepository { override suspend fun getProviders( userWallet: UserWallet, filterProviderTypes: List, - ): List { - return safeApiCall( + ): List = with(dispatchers.io) { + safeApiCall( call = { tangemExpressApi.getProviders( userWalletId = userWallet.walletId.stringValue, diff --git a/data/express/src/main/java/com/tangem/data/express/di/ExpressDataModule.kt b/data/express/src/main/java/com/tangem/data/express/di/ExpressDataModule.kt index 75bfe50bf5..299b631162 100644 --- a/data/express/src/main/java/com/tangem/data/express/di/ExpressDataModule.kt +++ b/data/express/src/main/java/com/tangem/data/express/di/ExpressDataModule.kt @@ -10,6 +10,7 @@ import com.tangem.datasource.di.NetworkMoshi import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.domain.express.ExpressErrorResolver import com.tangem.domain.express.ExpressRepository +import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -34,10 +35,12 @@ internal object ExpressDataModule { fun provideExpressRepository( tangemExpressApi: TangemExpressApi, appPreferencesStore: AppPreferencesStore, + dispatchers: CoroutineDispatcherProvider, ): ExpressRepository { return DefaultExpressRepository( tangemExpressApi = tangemExpressApi, appPreferencesStore = appPreferencesStore, + dispatchers = dispatchers, ) } } \ No newline at end of file diff --git a/data/quotes/src/main/java/com/tangem/data/quotes/single/DefaultSingleQuoteStatusProducer.kt b/data/quotes/src/main/java/com/tangem/data/quotes/single/DefaultSingleQuoteStatusProducer.kt index 692d5cb24a..4adc0ed55a 100644 --- a/data/quotes/src/main/java/com/tangem/data/quotes/single/DefaultSingleQuoteStatusProducer.kt +++ b/data/quotes/src/main/java/com/tangem/data/quotes/single/DefaultSingleQuoteStatusProducer.kt @@ -7,10 +7,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.* /** * Default implementation of [SingleQuoteStatusProducer] @@ -28,7 +25,7 @@ internal class DefaultSingleQuoteStatusProducer @AssistedInject constructor( override fun produce(): Flow { return quotesStatusesStore.get() - .mapNotNull { quotes -> quotes.firstOrNull { it.rawCurrencyId == params.rawCurrencyId } } + .mapNotNull { quotes -> quotes.firstOrNull { it.rawCurrencyId == params.rawCurrencyId } ?: fallback } .distinctUntilChanged() .flowOn(dispatchers.default) } diff --git a/data/swap/src/main/java/com/tangem/data/swap/DefaultSwapRepositoryV2.kt b/data/swap/src/main/java/com/tangem/data/swap/DefaultSwapRepositoryV2.kt index 46346745a5..e2d1ac8603 100644 --- a/data/swap/src/main/java/com/tangem/data/swap/DefaultSwapRepositoryV2.kt +++ b/data/swap/src/main/java/com/tangem/data/swap/DefaultSwapRepositoryV2.kt @@ -24,6 +24,7 @@ import com.tangem.domain.express.models.ExpressRateType import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.NetworkStatus +import com.tangem.domain.models.quote.QuoteStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.quotes.single.SingleQuoteStatusFetcher import com.tangem.domain.quotes.single.SingleQuoteStatusProducer @@ -65,7 +66,7 @@ internal class DefaultSwapRepositoryV2 @Inject constructor( initialCurrency: CryptoCurrency, cryptoCurrencyStatusList: List, filterProviderTypes: List, - ): List = withContext(coroutineDispatcher.io) { + ): List = withContext(coroutineDispatcher.default) { val cryptoCurrencyList = cryptoCurrencyStatusList.map { it.currency } val allPairs = getPairsInternal( @@ -113,7 +114,7 @@ internal class DefaultSwapRepositoryV2 @Inject constructor( initialCurrency: CryptoCurrency, cryptoCurrencyList: List, filterProviderTypes: List, - ): List = withContext(coroutineDispatcher.io) { + ): List = withContext(coroutineDispatcher.default) { val allPairs = getPairsInternal( userWallet = userWallet, initialCurrency = initialCurrency, @@ -330,25 +331,27 @@ internal class DefaultSwapRepositoryV2 @Inject constructor( userWallet: UserWallet, from: List, to: List, - ) = safeApiCall( - call = { - tangemExpressApi.getPairs( - userWalletId = userWallet.walletId.stringValue, - refCode = ExpressUtils.getRefCode( - userWallet = userWallet, - appPreferencesStore = appPreferencesStore, - ), - body = PairsRequestBody( - from = tokenInfoConverter.convertList(from), - to = tokenInfoConverter.convertList(to), - ), - ).getOrThrow() - }, - onError = { - Timber.w(it, "Unable to get pairs") - throw it - }, - ) + ) = withContext(coroutineDispatcher.io) { + safeApiCall( + call = { + tangemExpressApi.getPairs( + userWalletId = userWallet.walletId.stringValue, + refCode = ExpressUtils.getRefCode( + userWallet = userWallet, + appPreferencesStore = appPreferencesStore, + ), + body = PairsRequestBody( + from = tokenInfoConverter.convertList(from), + to = tokenInfoConverter.convertList(to), + ), + ).getOrThrow() + }, + onError = { + Timber.w(it, "Unable to get pairs") + throw it + }, + ) + } /** * Send with swap specific currency status creation @@ -360,9 +363,9 @@ internal class DefaultSwapRepositoryV2 @Inject constructor( val quote = singleQuoteStatusSupplier.getSyncOrNull( params = SingleQuoteStatusProducer.Params(rawCurrencyId = rawCurrencyId), - )?.right() + ) - if (quote == null) { + if (quote == null || quote.value is QuoteStatus.Empty) { singleQuoteStatusFetcher.invoke( params = SingleQuoteStatusFetcher.Params( rawCurrencyId = rawCurrencyId, @@ -373,7 +376,7 @@ internal class DefaultSwapRepositoryV2 @Inject constructor( return currencyStatusProxyCreator.createCurrencyStatus( currency = cryptoCurrency, - maybeQuoteStatus = quote ?: singleQuoteStatusSupplier.getSyncOrNull( + maybeQuoteStatus = quote?.right() ?: singleQuoteStatusSupplier.getSyncOrNull( params = SingleQuoteStatusProducer.Params(rawCurrencyId = rawCurrencyId), ).right(), maybeNetworkStatus = NetworkStatus( diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/choosetoken/fromSupported/ui/SwapChooseTokenNetworkContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/choosetoken/fromSupported/ui/SwapChooseTokenNetworkContent.kt index 03d5b2f8c9..d2c33cddad 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/choosetoken/fromSupported/ui/SwapChooseTokenNetworkContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/choosetoken/fromSupported/ui/SwapChooseTokenNetworkContent.kt @@ -24,12 +24,13 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp -import androidx.compose.ui.util.fastForEach +import androidx.compose.ui.util.fastForEachIndexed import com.tangem.core.ui.components.SpacerWMax import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.message.* import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle +import com.tangem.core.ui.decorations.roundedShapeItemDecoration import com.tangem.core.ui.extensions.* import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview @@ -94,13 +95,22 @@ internal fun SwapChooseTokenNetworkContent(state: SwapChooseTokenNetworkContentU @Composable private fun SwapChooseTokenNetworkContentList(swapNetworks: ImmutableList) { - Column { - swapNetworks.fastForEach { network -> + Column( + modifier = Modifier + .padding(top = 8.dp) + .padding(16.dp), + ) { + swapNetworks.fastForEachIndexed { index, network -> Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier .fillMaxWidth() + .roundedShapeItemDecoration( + currentIndex = index, + lastIndex = swapNetworks.lastIndex, + addDefaultPadding = false, + ) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = ripple(), From 4427ccf4b09d785a4b0024e9fe75c080a356ba8d Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Aug 2025 12:19:28 +0400 Subject: [PATCH 42/53] Updated on 2026-08-14 --- .../send/v2/send/confirm/ui/SendConfirmContent.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/ui/SendConfirmContent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/ui/SendConfirmContent.kt index 21e776b4ad..73b2a5a0fa 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/ui/SendConfirmContent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/ui/SendConfirmContent.kt @@ -4,6 +4,7 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope @@ -13,7 +14,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp import com.tangem.common.ui.notifications.NotificationUM -import com.tangem.core.ui.components.SpacerHMax import com.tangem.core.ui.components.transactions.TransactionDoneTitle import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference @@ -49,9 +49,13 @@ internal fun SendConfirmContent( ) { val confirmUM = sendUM.confirmUM as? ConfirmUM.Content - Column { + Column( + modifier = Modifier.fillMaxSize(), + ) { LazyColumn( - modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16), + modifier = Modifier + .weight(1f) + .padding(horizontal = TangemTheme.dimens.spacing16), ) { blocks( uiState = sendUM, @@ -74,7 +78,6 @@ internal fun SendConfirmContent( ) } } - SpacerHMax() SendingText(footerText = confirmUM?.sendingFooter ?: TextReference.EMPTY) } } From 3a5163061d6030bb30d5fd15ede0bb2e78db663c Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Aug 2025 13:15:49 +0700 Subject: [PATCH 43/53] Updated on 2026-08-14 --- core/res/src/main/java/com/tangem/core/res/Resources.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/res/src/main/java/com/tangem/core/res/Resources.kt b/core/res/src/main/java/com/tangem/core/res/Resources.kt index 46ceaa8b3a..aea235e3ae 100644 --- a/core/res/src/main/java/com/tangem/core/res/Resources.kt +++ b/core/res/src/main/java/com/tangem/core/res/Resources.kt @@ -29,7 +29,7 @@ fun Resources.getStringSafe(@StringRes id: Int, vararg formatArgs: Any): String // If something goes wrong, returns the resource without arguments val string = getString(id) - reportIssue(resources = this, id, *formatArgs) + reportIssue(it, resources = this, id, *formatArgs) string } @@ -61,19 +61,20 @@ fun Resources.getPluralStringSafe(@PluralsRes id: Int, count: Int, vararg format private fun Result.getOrResourceName(resources: Resources, id: Int, vararg formatArgs: Any): String { return getOrElse { - reportIssue(resources, id, formatArgs) + reportIssue(it, resources, id, formatArgs) // If something still goes wrong, returns the resource name resources.getResourceEntryName(id) } } -private fun reportIssue(resources: Resources, id: Int, vararg formatArgs: Any) { +private fun reportIssue(throwable: Throwable, resources: Resources, id: Int, vararg formatArgs: Any) { val exception = IllegalStateException( "An error occurred while parsing the string:\n" + "\tname: R.string.${resources.getResourceEntryName(id)}\n" + "\targs: ${formatArgs.joinToString(prefix = "[", postfix = "]") { it.toString() }}\n" + - "\tlocale: ${SupportedLanguages.getCurrentSupportedLanguageCode()}\n", + "\tlocale: ${SupportedLanguages.getCurrentSupportedLanguageCode()}\n" + + "\terror message: ${throwable.message.orEmpty()}\n", ) Timber.tag("Resources").e(exception) From 60e1f52f94fc6b58c2becb74309a05631fe53908 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Aug 2025 19:21:53 +0700 Subject: [PATCH 44/53] Updated on 2026-08-14 --- .../main/java/com/tangem/core/ui/utils/DecimalFormatterExt.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/DecimalFormatterExt.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/DecimalFormatterExt.kt index a251b0c9ed..ebce7baf36 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/utils/DecimalFormatterExt.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/DecimalFormatterExt.kt @@ -17,7 +17,8 @@ const val DECIMAL_SEPARATOR_LIMIT = 1 @Composable fun rememberDecimalFormat(): DecimalFormat { - val locale = LocalConfiguration.current.locale + val locales = LocalConfiguration.current.locales + val locale = if (locales.isEmpty) Locale.getDefault() else locales[0] val decimalSymbols = remember { DecimalFormatSymbols.getInstance(locale) } return remember { From 203738f16063305f433ffff590725cc09c91090f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Aug 2025 16:24:56 +0300 Subject: [PATCH 45/53] Updated on 2026-08-14 --- .../features/staking/impl/presentation/ui/StakingTosText.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingTosText.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingTosText.kt index b5a6372037..c7e67f5415 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingTosText.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingTosText.kt @@ -12,8 +12,8 @@ import com.tangem.features.staking.impl.R private const val TERMS_OF_USE_KEY = "termsOfUse" private const val PRIVACY_POLICY_KEY = "privacyPolicy" -private const val TERMS_OF_USE_URL = "https://docs.stakek.it/docs/terms-of-use" -private const val PRIVACY_POLICY_URL = "https://docs.stakek.it/docs/privacy-policy" +private const val TERMS_OF_USE_URL = "https://docs.yield.xyz/docs/terms-of-use#/" +private const val PRIVACY_POLICY_URL = "https://docs.yield.xyz/docs/privacy-policy#/" @Composable internal fun StakingTosText(onTextClick: (String) -> Unit) { From a0ba231dbdac654bdeefaf690550dd761c9bb11d Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Aug 2025 13:25:26 +0000 Subject: [PATCH 46/53] Updated on 2026-08-14 --- gradle/tangem_dependencies.toml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index 7250e5eb0c..78d485789e 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,12 +5,14 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "releases-5.26.0-1123" +tangemBlockchainSdk = "develop-1129" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "releases-5.26.0-508" +tangemCardSdk = "develop-509" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem12" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ +tangemHotSdk = "develop-446" +#tangemHotSdk = "0.0.1" # Keep it! - used for local builds ^ @@ -18,6 +20,8 @@ tangemVico = "2.0.0-alpha.25-tangem12" blockchain = { module = "com.tangem:blockchain", version.ref = "tangemBlockchainSdk" } card-android = { module = "com.tangem.tangem-sdk-kotlin:android", version.ref = "tangemCardSdk" } card-core = { module = "com.tangem.tangem-sdk-kotlin:core", version.ref = "tangemCardSdk" } +hot-core = { module = "com.tangem.tangem-hot-sdk-kotlin:core", version.ref = "tangemHotSdk" } +hot-android = { module = "com.tangem.tangem-hot-sdk-kotlin:android", version.ref = "tangemHotSdk" } vico-compose = { group = "com.tangem.vico", name = "compose", version.ref = "tangemVico" } vico-compose-m3 = { group = "com.tangem.vico", name = "compose-m3", version.ref = "tangemVico" } From 539c1a268976453e03c1b5a3b115003c3baa34d7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Aug 2025 21:44:05 +0500 Subject: [PATCH 47/53] Updated on 2026-08-14 --- .../tangem/tap/routing/utils/ChildFactory.kt | 12 ++ .../com/tangem/common/routing/AppRoute.kt | 5 + .../hotwallet/WalletActivationComponent.kt | 14 ++ .../entry/AddExistingWalletModel.kt | 49 ++++-- .../DefaultAddExistingWalletComponent.kt | 52 +++---- .../routing/AddExistingWalletChildFactory.kt | 3 +- .../CreateMobileWalletModel.kt | 2 +- .../check/ManualBackupCheckComponent.kt | 4 +- .../check/di/ManualBackupCheckModule.kt | 20 +++ .../check/entity/ManualBackupCheckUM.kt | 2 + .../check/model/ManualBackupCheckModel.kt | 73 +++++++-- .../phrase/ManualBackupPhraseComponent.kt | 2 + .../phrase/di/ManualBackupPhraseModule.kt | 20 +++ .../phrase/model/ManualBackupPhraseModel.kt | 42 ++++- .../start/di/ManualBackupStartModule.kt | 20 +++ .../stepper/api/HotWalletStepperComponent.kt | 13 +- .../entry/DefaultWalletActivationComponent.kt | 92 +++++++++++ .../entry/WalletActivationModel.kt | 147 ++++++++++++++++++ .../WalletActivationStepperStateManager.kt | 91 +++++++++++ .../entry/di/WalletActivationModule.kt | 42 +++++ .../routing/WalletActivationChildFactory.kt | 85 ++++++++++ .../entry/routing/WalletActivationRoute.kt | 31 ++++ .../entry/ui/WalletActivationContent.kt | 42 +++++ .../api/PushNotificationsParams.kt | 3 + .../impl/model/PushNotificationsModel.kt | 7 +- .../intents/WalletWarningsClickIntents.kt | 3 +- .../domain/GetMultiWalletWarningsFactory.kt | 3 +- 27 files changed, 808 insertions(+), 71 deletions(-) create mode 100644 features/hot-wallet/api/src/main/kotlin/com/tangem/features/hotwallet/WalletActivationComponent.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/di/ManualBackupCheckModule.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/di/ManualBackupPhraseModule.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/start/di/ManualBackupStartModule.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/DefaultWalletActivationComponent.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/WalletActivationModel.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/WalletActivationStepperStateManager.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/di/WalletActivationModule.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/routing/WalletActivationChildFactory.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/routing/WalletActivationRoute.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/ui/WalletActivationContent.kt diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index 436c6fcc3a..0f2952af12 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -14,6 +14,7 @@ import com.tangem.features.details.component.DetailsComponent import com.tangem.features.disclaimer.api.components.DisclaimerComponent import com.tangem.features.hotwallet.AddExistingWalletComponent import com.tangem.features.hotwallet.CreateMobileWalletComponent +import com.tangem.features.hotwallet.WalletActivationComponent import com.tangem.features.managetokens.component.ChooseManagedTokensComponent import com.tangem.features.managetokens.component.ManageTokensComponent import com.tangem.features.managetokens.component.ManageTokensSource @@ -91,6 +92,7 @@ internal class ChildFactory @Inject constructor( private val createWalletSelectionComponentFactory: CreateWalletSelectionComponent.Factory, private val createMobileWalletComponentFactory: CreateMobileWalletComponent.Factory, private val addExistingWalletComponentFactory: AddExistingWalletComponent.Factory, + private val walletActivationComponentFactory: WalletActivationComponent.Factory, private val sendWithSwapComponentFactory: SendWithSwapComponent.Factory, private val sendEntryPointComponentFactory: SendEntryPointComponent.Factory, private val walletConnectFeatureToggles: WalletConnectFeatureToggles, @@ -385,6 +387,7 @@ internal class ChildFactory @Inject constructor( context = context, params = PushNotificationsParams( modelCallbacks = PushNotificationsModelCallbacksStub(), + nextRoute = AppRoute.Home(), ), componentFactory = pushNotificationsComponentFactory, ) @@ -464,6 +467,15 @@ internal class ChildFactory @Inject constructor( componentFactory = addExistingWalletComponentFactory, ) } + is AppRoute.WalletActivation -> { + createComponentChild( + context = context, + params = WalletActivationComponent.Params( + userWalletId = route.userWalletId, + ), + componentFactory = walletActivationComponentFactory, + ) + } is AppRoute.SendEntryPoint -> { createComponentChild( context = context, diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index def139603e..f36983b866 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -301,6 +301,11 @@ sealed class AppRoute(val path: String) : Route { @Serializable object AddExistingWallet : AppRoute(path = "/add_existing_wallet") + @Serializable + data class WalletActivation( + val userWalletId: UserWalletId, + ) : AppRoute(path = "/wallet_activation/${userWalletId.stringValue}") + @Serializable data class SendEntryPoint( val userWalletId: UserWalletId, diff --git a/features/hot-wallet/api/src/main/kotlin/com/tangem/features/hotwallet/WalletActivationComponent.kt b/features/hot-wallet/api/src/main/kotlin/com/tangem/features/hotwallet/WalletActivationComponent.kt new file mode 100644 index 0000000000..8b99c19f36 --- /dev/null +++ b/features/hot-wallet/api/src/main/kotlin/com/tangem/features/hotwallet/WalletActivationComponent.kt @@ -0,0 +1,14 @@ +package com.tangem.features.hotwallet + +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.models.wallet.UserWalletId + +interface WalletActivationComponent : ComposableContentComponent { + + data class Params( + val userWalletId: UserWalletId, + ) + + interface Factory : ComponentFactory +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt index b404d14746..ca924d1eb8 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt @@ -13,8 +13,11 @@ import com.tangem.features.hotwallet.addexistingwallet.start.AddExistingWalletSt import com.tangem.features.hotwallet.manualbackup.completed.ManualBackupCompletedComponent import com.tangem.features.hotwallet.setaccesscode.AccessCodeComponent import com.tangem.features.hotwallet.setupfinished.MobileWalletSetupFinishedComponent +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent +import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import javax.inject.Inject @@ -25,30 +28,30 @@ internal class AddExistingWalletModel @Inject constructor( private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase, ) : Model() { + val hotWalletStepperComponentModelCallback = HotWalletStepperComponentModelCallback() val addExistingWalletStartModelCallbacks = AddExistingWalletStartModelCallbacks() val addExistingWalletImportModelCallbacks = AddExistingWalletImportModelCallbacks() val manualBackupCompletedComponentModelCallbacks = ManualBackupCompletedComponentModelCallbacks() val accessCodeModelCallbacks = AccessCodeModelCallbacks() + val pushNotificationsCallbacks = PushNotificationsCallbacks() val mobileWalletSetupFinishedComponentModelCallbacks = MobileWalletSetupFinishedComponentModelCallbacks() val stackNavigation = StackNavigation() + val startRoute = AddExistingWalletRoute.Start + val currentRoute: MutableStateFlow = MutableStateFlow(startRoute) - fun onChildBack(currentRoute: AddExistingWalletRoute) { - when (currentRoute) { + fun onChildBack() { + when (currentRoute.value) { + is AddExistingWalletRoute.Start -> router.pop() is AddExistingWalletRoute.Import -> stackNavigation.pop() is AddExistingWalletRoute.BackupCompleted -> Unit is AddExistingWalletRoute.SetAccessCode -> Unit is AddExistingWalletRoute.ConfirmAccessCode -> stackNavigation.pop() is AddExistingWalletRoute.PushNotifications -> Unit is AddExistingWalletRoute.SetupFinished -> Unit - is AddExistingWalletRoute.Start -> Unit } } - fun onSkipAccessCode() { - navigateToPushNotificationsOrNext() - } - private fun navigateToPushNotificationsOrNext() { modelScope.launch { val shouldRequestPush = shouldAskPermissionUseCase(PUSH_PERMISSION) @@ -62,6 +65,20 @@ internal class AddExistingWalletModel @Inject constructor( } } + private fun navigateToSetupFinished() { + stackNavigation.replaceAll(AddExistingWalletRoute.SetupFinished) + } + + inner class HotWalletStepperComponentModelCallback : HotWalletStepperComponent.ModelCallback { + override fun onBackClick() { + onChildBack() + } + + override fun onSkipClick() { + navigateToPushNotificationsOrNext() + } + } + inner class AddExistingWalletStartModelCallbacks : AddExistingWalletStartComponent.ModelCallbacks { override fun onBackClick() { router.pop() @@ -74,13 +91,13 @@ internal class AddExistingWalletModel @Inject constructor( inner class AddExistingWalletImportModelCallbacks : AddExistingWalletImportComponent.ModelCallbacks { override fun onWalletImported(userWalletId: UserWalletId) { - stackNavigation.replaceCurrent(AddExistingWalletRoute.BackupCompleted(userWalletId)) + stackNavigation.replaceAll(AddExistingWalletRoute.BackupCompleted(userWalletId)) } } inner class ManualBackupCompletedComponentModelCallbacks : ManualBackupCompletedComponent.ModelCallbacks { override fun onContinueClick(userWalletId: UserWalletId) { - stackNavigation.replaceCurrent(AddExistingWalletRoute.SetAccessCode(userWalletId)) + stackNavigation.replaceAll(AddExistingWalletRoute.SetAccessCode(userWalletId)) } } @@ -94,6 +111,20 @@ internal class AddExistingWalletModel @Inject constructor( } } + inner class PushNotificationsCallbacks : PushNotificationsModelCallbacks { + override fun onAllowSystemPermission() { + navigateToSetupFinished() + } + + override fun onDenySystemPermission() { + navigateToSetupFinished() + } + + override fun onDismiss() { + navigateToSetupFinished() + } + } + inner class MobileWalletSetupFinishedComponentModelCallbacks : MobileWalletSetupFinishedComponent.ModelCallbacks { override fun onContinueClick() { diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/DefaultAddExistingWalletComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/DefaultAddExistingWalletComponent.kt index 53e611282b..e1e673e034 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/DefaultAddExistingWalletComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/DefaultAddExistingWalletComponent.kt @@ -6,19 +6,20 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.decompose.router.stack.childStack +import com.arkivanov.decompose.value.ObserveLifecycleMode +import com.arkivanov.decompose.value.subscribe import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.model.getOrCreateModel -import com.tangem.core.ui.extensions.TextReference import com.tangem.features.hotwallet.AddExistingWalletComponent import com.tangem.features.hotwallet.addexistingwallet.entry.routing.AddExistingWalletChildFactory -import com.tangem.features.hotwallet.addexistingwallet.entry.routing.AddExistingWalletRoute import com.tangem.features.hotwallet.addexistingwallet.entry.ui.AddExistingWalletContent import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent import com.tangem.features.hotwallet.stepper.impl.DefaultHotWalletStepperComponent import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import kotlinx.coroutines.launch internal class DefaultAddExistingWalletComponent @AssistedInject constructor( @Assisted appComponentContext: AppComponentContext, @@ -30,13 +31,11 @@ internal class DefaultAddExistingWalletComponent @AssistedInject constructor( private val model: AddExistingWalletModel = getOrCreateModel(params) - private val startRoute = AddExistingWalletRoute.Start - private val innerStack = childStack( key = "addExistingWalletInnerStack", source = model.stackNavigation, serializer = null, - initialConfiguration = startRoute, + initialConfiguration = model.startRoute, handleBackButton = true, childFactory = { configuration, factoryContext -> addExistingWalletChildFactory.createChild( @@ -50,32 +49,28 @@ internal class DefaultAddExistingWalletComponent @AssistedInject constructor( private val stepperComponent = stepperComponentFactory.create( context = this, params = HotWalletStepperComponent.Params( - initState = HotWalletStepperComponent.StepperUM( - currentStep = 0, - steps = 0, - title = TextReference.EMPTY, - showBackButton = false, - showSkipButton = false, - showFeedbackButton = false, - ), - callback = object : HotWalletStepperComponent.ModelCallback { - override fun onBackClick() { - onChildBack() - } - - override fun onSkipClick() { - model.onSkipAccessCode() - } - }, + initState = HotWalletStepperComponent.StepperUM.initialState(), + callback = model.hotWalletStepperComponentModelCallback, ), ) + init { + innerStack.subscribe( + lifecycle = lifecycle, + mode = ObserveLifecycleMode.CREATE_DESTROY, + ) { stack -> + componentScope.launch { + model.currentRoute.emit(stack.active.configuration) + } + } + } + @Composable override fun Content(modifier: Modifier) { val stackState by innerStack.subscribeAsState() val currentRoute = stackState.active.configuration - BackHandler(onBack = ::onChildBack) + BackHandler(onBack = model::onChildBack) val stepperState = stepperStateManager.getStepperState(currentRoute) stepperState?.let { stepperComponent.updateState(it) } @@ -86,17 +81,6 @@ internal class DefaultAddExistingWalletComponent @AssistedInject constructor( ) } - private fun onChildBack() { - val isEmptyStack = innerStack.value.backStack.isEmpty() - - if (isEmptyStack) { - router.pop() - } else { - val currentRoute = innerStack.value.active.configuration - model.onChildBack(currentRoute) - } - } - @AssistedFactory interface Factory : AddExistingWalletComponent.Factory { override fun create(context: AppComponentContext, params: Unit): DefaultAddExistingWalletComponent diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt index 710b2da90f..8a25cc1176 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt @@ -9,7 +9,6 @@ import com.tangem.features.hotwallet.manualbackup.completed.ManualBackupComplete import com.tangem.features.hotwallet.setaccesscode.AccessCodeComponent import com.tangem.features.hotwallet.setupfinished.MobileWalletSetupFinishedComponent import com.tangem.features.pushnotifications.api.PushNotificationsComponent -import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacksStub import com.tangem.features.pushnotifications.api.PushNotificationsParams import javax.inject.Inject @@ -63,7 +62,7 @@ internal class AddExistingWalletChildFactory @Inject constructor( is AddExistingWalletRoute.PushNotifications -> pushNotificationsComponent.create( context = childContext, params = PushNotificationsParams( - modelCallbacks = PushNotificationsModelCallbacksStub(), + modelCallbacks = model.pushNotificationsCallbacks, ), ) is AddExistingWalletRoute.SetupFinished -> MobileWalletSetupFinishedComponent( diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/CreateMobileWalletModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/CreateMobileWalletModel.kt index a4a7a4f243..d59cf2e39f 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/CreateMobileWalletModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/CreateMobileWalletModel.kt @@ -48,7 +48,7 @@ internal class CreateMobileWalletModel @Inject constructor( saveUserWalletUseCase( hotUserWalletBuilder.build(), ) - router.push(AppRoute.Wallet) + router.replaceAll(AppRoute.Wallet) }.onFailure { Timber.e(it) diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/ManualBackupCheckComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/ManualBackupCheckComponent.kt index dfaf6fe787..7f25916f2d 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/ManualBackupCheckComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/ManualBackupCheckComponent.kt @@ -7,7 +7,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent -import com.tangem.crypto.bip39.Mnemonic +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.features.hotwallet.manualbackup.check.model.ManualBackupCheckModel import com.tangem.features.hotwallet.manualbackup.check.ui.ManualBackupCheckContent import dagger.assisted.Assisted @@ -33,7 +33,7 @@ internal class ManualBackupCheckComponent @AssistedInject constructor( } data class Params( - val generatedWords: Mnemonic, + val userWalletId: UserWalletId, val callbacks: ModelCallbacks, ) } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/di/ManualBackupCheckModule.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/di/ManualBackupCheckModule.kt new file mode 100644 index 0000000000..34c8d3bb8c --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/di/ManualBackupCheckModule.kt @@ -0,0 +1,20 @@ +package com.tangem.features.hotwallet.manualbackup.check.di + +import com.tangem.core.decompose.model.Model +import com.tangem.features.hotwallet.manualbackup.check.model.ManualBackupCheckModel +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +@InstallIn(SingletonComponent::class) +internal interface ManualBackupCheckModule { + + @Binds + @IntoMap + @ClassKey(ManualBackupCheckModel::class) + fun bindManualBackupCheckModel(model: ManualBackupCheckModel): Model +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/entity/ManualBackupCheckUM.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/entity/ManualBackupCheckUM.kt index 19eac1ac68..5d2bc48d20 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/entity/ManualBackupCheckUM.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/entity/ManualBackupCheckUM.kt @@ -2,10 +2,12 @@ package com.tangem.features.hotwallet.manualbackup.check.entity import androidx.compose.ui.text.input.TextFieldValue import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf internal data class ManualBackupCheckUM( val onCompleteButtonClick: () -> Unit, val wordFields: ImmutableList, + val words: ImmutableList = persistentListOf(), val completeButtonEnabled: Boolean, val completeButtonProgress: Boolean, ) { diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/model/ManualBackupCheckModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/model/ManualBackupCheckModel.kt index ffe2a0ad15..6d62bc73c9 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/model/ManualBackupCheckModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/model/ManualBackupCheckModel.kt @@ -2,16 +2,25 @@ package com.tangem.features.hotwallet.manualbackup.check.model import androidx.compose.runtime.Stable import androidx.compose.ui.text.input.TextFieldValue +import arrow.core.getOrElse import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.wallets.usecase.GetUserWalletUseCase +import com.tangem.domain.wallets.usecase.UpdateWalletUseCase import com.tangem.features.hotwallet.manualbackup.check.ManualBackupCheckComponent import com.tangem.features.hotwallet.manualbackup.check.entity.ManualBackupCheckUM +import com.tangem.hot.sdk.TangemHotSdk +import com.tangem.hot.sdk.model.HotAuth +import com.tangem.hot.sdk.model.UnlockHotWallet import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import timber.log.Timber import javax.inject.Inject import kotlin.Boolean import kotlin.Int @@ -20,13 +29,15 @@ import kotlin.Suppress import kotlin.collections.List import kotlin.collections.all import kotlin.collections.map -import kotlin.error @Stable @ModelScoped internal class ManualBackupCheckModel @Inject constructor( paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, + private val getUserWalletUseCase: GetUserWalletUseCase, + private val updateWalletUseCase: UpdateWalletUseCase, + private val tangemHotSdk: TangemHotSdk, ) : Model() { private val params = paramsContainer.require() @@ -35,6 +46,28 @@ internal class ManualBackupCheckModel @Inject constructor( internal val uiState: StateFlow field = MutableStateFlow(getInitialUIState()) + init { + modelScope.launch { + runCatching { + val userWallet = getUserWalletUseCase(params.userWalletId) + .getOrElse { error("User wallet with id ${params.userWalletId} not found") } + if (userWallet is UserWallet.Hot) { + val unlockHotWallet = UnlockHotWallet(userWallet.hotWalletId, HotAuth.NoAuth) + val seedPhrasePrivateInfo = tangemHotSdk.exportMnemonic(unlockHotWallet) + uiState.update { + it.copy( + words = seedPhrasePrivateInfo.mnemonic.mnemonicComponents.filterIndexed { index, _ -> + WORD_FIELD_INDICES.contains(index + 1) + }.toImmutableList(), + ) + } + } + }.onFailure { + Timber.e(it) + } + } + } + @Suppress("MagicNumber") private fun getInitialUIState(): ManualBackupCheckUM { val wordFields = List(WORD_FIELD_INDICES.size) { index -> @@ -57,12 +90,37 @@ internal class ManualBackupCheckModel @Inject constructor( onCompleteButtonClick = { val currentUIState = uiState.value if (currentUIState.completeButtonEnabled) { - callbacks.onCompleteClick() + backupWallet() } }, ) } + private fun backupWallet() { + modelScope.launch { + uiState.update { + it.copy(completeButtonProgress = true) + } + + runCatching { + val userWallet = getUserWalletUseCase(params.userWalletId) + .getOrElse { error("User wallet with id ${params.userWalletId} not found") } + if (userWallet is UserWallet.Hot) { + updateWalletUseCase(userWallet.walletId) { + userWallet.copy(backedUp = true) + } + callbacks.onCompleteClick() + } + }.onFailure { + Timber.e(it) + + uiState.update { + it.copy(completeButtonProgress = false) + } + } + } + } + private fun updateWordField(shownIndex: Int, newText: TextFieldValue) { uiState.update { currentState -> val updatedFields = currentState.wordFields.map { wordField -> @@ -89,14 +147,9 @@ internal class ManualBackupCheckModel @Inject constructor( } private fun checkWordField(word: String, shownIndex: Int): Boolean { - val generatedWords = params.generatedWords - val wordList = generatedWords.mnemonicComponents - - return if (shownIndex <= wordList.size) { - wordList[shownIndex - 1] == word - } else { - false - } + val words = uiState.value.words + val listIndex = WORD_FIELD_INDICES.indexOf(shownIndex) + return words.getOrNull(listIndex)?.let { word == it } == true } companion object { diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/ManualBackupPhraseComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/ManualBackupPhraseComponent.kt index 1dd8b360fc..62d7b79ed4 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/ManualBackupPhraseComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/ManualBackupPhraseComponent.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.features.hotwallet.manualbackup.phrase.model.ManualBackupPhraseModel import com.tangem.features.hotwallet.manualbackup.phrase.ui.ManualBackupPhraseContent import dagger.assisted.Assisted @@ -32,6 +33,7 @@ internal class ManualBackupPhraseComponent @AssistedInject constructor( } data class Params( + val userWalletId: UserWalletId, val callbacks: ModelCallbacks, ) } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/di/ManualBackupPhraseModule.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/di/ManualBackupPhraseModule.kt new file mode 100644 index 0000000000..3b5c8c8018 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/di/ManualBackupPhraseModule.kt @@ -0,0 +1,20 @@ +package com.tangem.features.hotwallet.manualbackup.phrase.di + +import com.tangem.core.decompose.model.Model +import com.tangem.features.hotwallet.manualbackup.phrase.model.ManualBackupPhraseModel +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +@InstallIn(SingletonComponent::class) +internal interface ManualBackupPhraseModule { + + @Binds + @IntoMap + @ClassKey(ManualBackupPhraseModel::class) + fun bindManualBackupPhraseModel(model: ManualBackupPhraseModel): Model +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/model/ManualBackupPhraseModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/model/ManualBackupPhraseModel.kt index 4320a86026..e9625260ad 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/model/ManualBackupPhraseModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/model/ManualBackupPhraseModel.kt @@ -1,14 +1,24 @@ package com.tangem.features.hotwallet.manualbackup.phrase.model import androidx.compose.runtime.Stable +import arrow.core.getOrElse import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.features.hotwallet.manualbackup.phrase.ManualBackupPhraseComponent import com.tangem.features.hotwallet.manualbackup.phrase.entity.ManualBackupPhraseUM +import com.tangem.hot.sdk.TangemHotSdk +import com.tangem.hot.sdk.model.HotAuth +import com.tangem.hot.sdk.model.UnlockHotWallet import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import timber.log.Timber import javax.inject.Inject @Stable @@ -16,17 +26,39 @@ import javax.inject.Inject internal class ManualBackupPhraseModel @Inject constructor( paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, + private val getUserWalletUseCase: GetUserWalletUseCase, + private val tangemHotSdk: TangemHotSdk, ) : Model() { private val params = paramsContainer.require() private val callbacks = params.callbacks internal val uiState: StateFlow - field = MutableStateFlow(getInitialUIState()) - - private fun getInitialUIState(): ManualBackupPhraseUM { - return ManualBackupPhraseUM( + field = MutableStateFlow( + ManualBackupPhraseUM( onContinueClick = callbacks::onContinueClick, - ) + ), + ) + + init { + modelScope.launch { + runCatching { + val userWallet = getUserWalletUseCase(params.userWalletId) + .getOrElse { error("User wallet with id ${params.userWalletId} not found") } + if (userWallet is UserWallet.Hot) { + val unlockHotWallet = UnlockHotWallet(userWallet.hotWalletId, HotAuth.NoAuth) + val seedPhrasePrivateInfo = tangemHotSdk.exportMnemonic(unlockHotWallet) + uiState.update { + it.copy( + words = seedPhrasePrivateInfo.mnemonic.mnemonicComponents.mapIndexed { index, s -> + ManualBackupPhraseUM.MnemonicGridItem(index + 1, s) + }.toImmutableList(), + ) + } + } + }.onFailure { + Timber.e(it) + } + } } } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/start/di/ManualBackupStartModule.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/start/di/ManualBackupStartModule.kt new file mode 100644 index 0000000000..2f506e7b30 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/start/di/ManualBackupStartModule.kt @@ -0,0 +1,20 @@ +package com.tangem.features.hotwallet.manualbackup.start.di + +import com.tangem.core.decompose.model.Model +import com.tangem.features.hotwallet.manualbackup.start.ManualBackupStartModel +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +@InstallIn(SingletonComponent::class) +internal interface ManualBackupStartModule { + + @Binds + @IntoMap + @ClassKey(ManualBackupStartModel::class) + fun bindManualBackupStartModel(model: ManualBackupStartModel): Model +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/api/HotWalletStepperComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/api/HotWalletStepperComponent.kt index c14de91c72..169d037699 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/api/HotWalletStepperComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/api/HotWalletStepperComponent.kt @@ -15,7 +15,18 @@ interface HotWalletStepperComponent : ComposableContentComponent { val showBackButton: Boolean, val showSkipButton: Boolean, val showFeedbackButton: Boolean, - ) + ) { + companion object { + fun initialState() = StepperUM( + currentStep = 0, + steps = 0, + title = TextReference.EMPTY, + showBackButton = false, + showSkipButton = false, + showFeedbackButton = false, + ) + } + } interface ModelCallback { fun onBackClick() diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/DefaultWalletActivationComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/DefaultWalletActivationComponent.kt new file mode 100644 index 0000000000..f24a1ea128 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/DefaultWalletActivationComponent.kt @@ -0,0 +1,92 @@ +package com.tangem.features.hotwallet.walletactivation.entry + +import androidx.activity.compose.BackHandler +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import com.arkivanov.decompose.extensions.compose.subscribeAsState +import com.arkivanov.decompose.router.stack.childStack +import com.arkivanov.decompose.value.ObserveLifecycleMode +import com.arkivanov.decompose.value.subscribe +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.context.childByContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.features.hotwallet.WalletActivationComponent +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent +import com.tangem.features.hotwallet.stepper.impl.DefaultHotWalletStepperComponent +import com.tangem.features.hotwallet.walletactivation.entry.routing.WalletActivationChildFactory +import com.tangem.features.hotwallet.walletactivation.entry.ui.WalletActivationContent +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.launch + +internal class DefaultWalletActivationComponent @AssistedInject constructor( + @Assisted appComponentContext: AppComponentContext, + @Assisted private val params: WalletActivationComponent.Params, + private val stepperStateManager: WalletActivationStepperStateManager, + walletActivationChildFactory: WalletActivationChildFactory, + stepperComponentFactory: DefaultHotWalletStepperComponent.Factory, +) : WalletActivationComponent, AppComponentContext by appComponentContext { + + private val model: WalletActivationModel = getOrCreateModel(params) + + private val innerStack = childStack( + key = "walletActivationInnerStack", + source = model.stackNavigation, + serializer = null, + initialConfiguration = model.startRoute, + handleBackButton = true, + childFactory = { configuration, factoryContext -> + walletActivationChildFactory.createChild( + route = configuration, + childContext = childByContext(factoryContext), + model = model, + ) + }, + ) + + private val stepperComponent = stepperComponentFactory.create( + context = this, + params = HotWalletStepperComponent.Params( + initState = HotWalletStepperComponent.StepperUM.initialState(), + callback = model.hotWalletStepperComponentModelCallback, + ), + ) + + init { + innerStack.subscribe( + lifecycle = lifecycle, + mode = ObserveLifecycleMode.CREATE_DESTROY, + ) { stack -> + componentScope.launch { + model.currentRoute.emit(stack.active.configuration) + } + } + } + + @Composable + override fun Content(modifier: Modifier) { + val stackState by innerStack.subscribeAsState() + val currentRoute = stackState.active.configuration + + BackHandler(onBack = model::onChildBack) + + val stepperState = stepperStateManager.getStepperState(currentRoute) + stepperState?.let { stepperComponent.updateState(it) } + + WalletActivationContent( + stackState = stackState, + stepperComponent = stepperComponent.takeIf { stepperState != null }, + modifier = modifier, + ) + } + + @AssistedFactory + interface Factory : WalletActivationComponent.Factory { + override fun create( + context: AppComponentContext, + params: WalletActivationComponent.Params, + ): DefaultWalletActivationComponent + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/WalletActivationModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/WalletActivationModel.kt new file mode 100644 index 0000000000..3d03a96a04 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/WalletActivationModel.kt @@ -0,0 +1,147 @@ +package com.tangem.features.hotwallet.walletactivation.entry + +import com.arkivanov.decompose.router.stack.StackNavigation +import com.arkivanov.decompose.router.stack.pop +import com.arkivanov.decompose.router.stack.push +import com.arkivanov.decompose.router.stack.replaceAll +import com.tangem.core.decompose.di.ModelScoped +import com.tangem.core.decompose.model.Model +import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.core.decompose.navigation.Router +import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.settings.ShouldAskPermissionUseCase +import com.tangem.features.hotwallet.manualbackup.check.ManualBackupCheckComponent +import com.tangem.features.hotwallet.manualbackup.completed.ManualBackupCompletedComponent +import com.tangem.features.hotwallet.manualbackup.phrase.ManualBackupPhraseComponent +import com.tangem.features.hotwallet.manualbackup.start.ManualBackupStartComponent +import com.tangem.features.hotwallet.setaccesscode.AccessCodeComponent +import com.tangem.features.hotwallet.setupfinished.MobileWalletSetupFinishedComponent +import com.tangem.features.hotwallet.walletactivation.entry.routing.WalletActivationRoute +import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION +import com.tangem.features.hotwallet.WalletActivationComponent +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent +import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@ModelScoped +internal class WalletActivationModel @Inject constructor( + paramsContainer: ParamsContainer, + override val dispatchers: CoroutineDispatcherProvider, + private val router: Router, + private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase, +) : Model() { + + val params = paramsContainer.require() + + val hotWalletStepperComponentModelCallback = HotWalletStepperComponentModelCallback() + val manualBackupStartModelCallbacks = ManualBackupStartModelCallbacks() + val manualBackupPhraseModelCallbacks = ManualBackupPhraseModelCallbacks() + val manualBackupCheckModelCallbacks = ManualBackupCheckModelCallbacks() + val manualBackupCompletedModelCallbacks = ManualBackupCompletedModelCallbacks() + val accessCodeModelCallbacks = AccessCodeModelCallbacks() + val pushNotificationsCallbacks = PushNotificationsCallbacks() + val mobileWalletSetupFinishedModelCallbacks = MobileWalletSetupFinishedModelCallbacks() + + val stackNavigation = StackNavigation() + val startRoute = WalletActivationRoute.ManualBackupStart + val currentRoute: MutableStateFlow = MutableStateFlow(startRoute) + + fun onChildBack() { + when (currentRoute.value) { + is WalletActivationRoute.ManualBackupStart -> router.pop() + is WalletActivationRoute.ManualBackupPhrase -> stackNavigation.pop() + is WalletActivationRoute.ManualBackupCheck -> stackNavigation.pop() + is WalletActivationRoute.ManualBackupCompleted -> Unit + is WalletActivationRoute.SetAccessCode -> Unit + is WalletActivationRoute.ConfirmAccessCode -> Unit + is WalletActivationRoute.PushNotifications -> Unit + is WalletActivationRoute.SetupFinished -> Unit + } + } + + private fun navigateToPushNotificationsOrNext() { + modelScope.launch { + val shouldRequestPush = shouldAskPermissionUseCase(PUSH_PERMISSION) + if (shouldRequestPush) { + // is yet blocked by [REDACTED_TASK_KEY] + // stackNavigation.replaceAll(AddExistingWalletRoute.PushNotifications) + stackNavigation.replaceAll(WalletActivationRoute.SetupFinished) + } else { + stackNavigation.replaceAll(WalletActivationRoute.SetupFinished) + } + } + } + + private fun navigateToSetupFinished() { + stackNavigation.replaceAll(WalletActivationRoute.SetupFinished) + } + + inner class HotWalletStepperComponentModelCallback : HotWalletStepperComponent.ModelCallback { + override fun onBackClick() { + onChildBack() + } + + override fun onSkipClick() { + navigateToPushNotificationsOrNext() + } + } + + inner class ManualBackupStartModelCallbacks : ManualBackupStartComponent.ModelCallbacks { + override fun onContinueClick() { + stackNavigation.push(WalletActivationRoute.ManualBackupPhrase) + } + } + + inner class ManualBackupPhraseModelCallbacks : ManualBackupPhraseComponent.ModelCallbacks { + override fun onContinueClick() { + stackNavigation.push( + WalletActivationRoute.ManualBackupCheck, + ) + } + } + + inner class ManualBackupCheckModelCallbacks : ManualBackupCheckComponent.ModelCallbacks { + override fun onCompleteClick() { + stackNavigation.push(WalletActivationRoute.ManualBackupCompleted) + } + } + + inner class ManualBackupCompletedModelCallbacks : ManualBackupCompletedComponent.ModelCallbacks { + override fun onContinueClick(userWalletId: UserWalletId) { + stackNavigation.push(WalletActivationRoute.SetAccessCode) + } + } + + inner class AccessCodeModelCallbacks : AccessCodeComponent.ModelCallbacks { + override fun onAccessCodeSet(userWalletId: UserWalletId, accessCode: String) { + stackNavigation.push(WalletActivationRoute.ConfirmAccessCode(accessCode)) + } + + override fun onAccessCodeConfirmed(userWalletId: UserWalletId) { + navigateToPushNotificationsOrNext() + } + } + + inner class PushNotificationsCallbacks : PushNotificationsModelCallbacks { + override fun onAllowSystemPermission() { + navigateToSetupFinished() + } + + override fun onDenySystemPermission() { + navigateToSetupFinished() + } + + override fun onDismiss() { + navigateToSetupFinished() + } + } + + inner class MobileWalletSetupFinishedModelCallbacks : MobileWalletSetupFinishedComponent.ModelCallbacks { + override fun onContinueClick() { + router.pop() + } + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/WalletActivationStepperStateManager.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/WalletActivationStepperStateManager.kt new file mode 100644 index 0000000000..5a33b36b87 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/WalletActivationStepperStateManager.kt @@ -0,0 +1,91 @@ +package com.tangem.features.hotwallet.walletactivation.entry + +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.features.hotwallet.impl.R +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent +import com.tangem.features.hotwallet.walletactivation.entry.routing.WalletActivationRoute +import javax.inject.Inject + +internal class WalletActivationStepperStateManager @Inject constructor() { + + fun getStepperState(route: WalletActivationRoute): HotWalletStepperComponent.StepperUM? { + return when (route) { + is WalletActivationRoute.ManualBackupStart -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_BACKUP, + steps = STEPS_COUNT, + title = resourceReference(R.string.common_backup), + showBackButton = true, + showSkipButton = false, + showFeedbackButton = true, + ) + is WalletActivationRoute.ManualBackupPhrase -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_BACKUP_PHRASE, + steps = STEPS_COUNT, + title = resourceReference(R.string.common_backup), + showBackButton = true, + showSkipButton = false, + showFeedbackButton = true, + ) + is WalletActivationRoute.ManualBackupCheck -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_BACKUP_CHECK, + steps = STEPS_COUNT, + title = resourceReference(R.string.common_backup), + showBackButton = true, + showSkipButton = false, + showFeedbackButton = true, + ) + is WalletActivationRoute.ManualBackupCompleted -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_BACKUP_COMPLETED, + steps = STEPS_COUNT, + title = resourceReference(R.string.common_backup), + showBackButton = false, + showSkipButton = false, + showFeedbackButton = false, + ) + is WalletActivationRoute.SetAccessCode -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_ACCESS_CODE, + steps = STEPS_COUNT, + title = resourceReference(R.string.access_code_navtitle), + showBackButton = false, + showSkipButton = true, + showFeedbackButton = false, + ) + is WalletActivationRoute.ConfirmAccessCode -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_ACCESS_CODE, + steps = STEPS_COUNT, + title = resourceReference(R.string.access_code_navtitle), + showBackButton = true, + showSkipButton = false, + showFeedbackButton = false, + ) + is WalletActivationRoute.PushNotifications -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_NOTIFICATIONS, + steps = STEPS_COUNT, + title = resourceReference(R.string.onboarding_title_notifications), + showBackButton = false, + showSkipButton = false, + showFeedbackButton = false, + ) + is WalletActivationRoute.SetupFinished -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_DONE, + steps = STEPS_COUNT, + title = resourceReference(R.string.common_done), + showBackButton = false, + showSkipButton = false, + showFeedbackButton = false, + ) + } + } + + companion object { + private const val STEPS_COUNT = 7 + + private const val STEP_BACKUP = 1 + private const val STEP_BACKUP_PHRASE = 2 + private const val STEP_BACKUP_CHECK = 3 + private const val STEP_BACKUP_COMPLETED = 4 + private const val STEP_ACCESS_CODE = 5 + private const val STEP_NOTIFICATIONS = 6 + private const val STEP_DONE = 7 + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/di/WalletActivationModule.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/di/WalletActivationModule.kt new file mode 100644 index 0000000000..51362cff09 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/di/WalletActivationModule.kt @@ -0,0 +1,42 @@ +package com.tangem.features.hotwallet.walletactivation.entry.di + +import com.tangem.core.decompose.model.Model +import com.tangem.features.hotwallet.WalletActivationComponent +import com.tangem.features.hotwallet.walletactivation.entry.DefaultWalletActivationComponent +import com.tangem.features.hotwallet.walletactivation.entry.WalletActivationModel +import com.tangem.features.hotwallet.walletactivation.entry.WalletActivationStepperStateManager +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal interface WalletActivationModuleBinds { + + @Binds + @Singleton + fun bindWalletActivationComponentFactory( + impl: DefaultWalletActivationComponent.Factory, + ): WalletActivationComponent.Factory + + @Binds + @IntoMap + @ClassKey(WalletActivationModel::class) + fun bindWalletActivationModel(model: WalletActivationModel): Model +} + +@Module +@InstallIn(SingletonComponent::class) +internal object WalletActivationModule { + + @Provides + @Singleton + fun provideWalletActivationStepperStateManager(): WalletActivationStepperStateManager { + return WalletActivationStepperStateManager() + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/routing/WalletActivationChildFactory.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/routing/WalletActivationChildFactory.kt new file mode 100644 index 0000000000..2b93835929 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/routing/WalletActivationChildFactory.kt @@ -0,0 +1,85 @@ +package com.tangem.features.hotwallet.walletactivation.entry.routing + +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.features.hotwallet.manualbackup.check.ManualBackupCheckComponent +import com.tangem.features.hotwallet.manualbackup.completed.ManualBackupCompletedComponent +import com.tangem.features.hotwallet.manualbackup.phrase.ManualBackupPhraseComponent +import com.tangem.features.hotwallet.manualbackup.start.ManualBackupStartComponent +import com.tangem.features.hotwallet.setaccesscode.AccessCodeComponent +import com.tangem.features.hotwallet.setupfinished.MobileWalletSetupFinishedComponent +import com.tangem.features.hotwallet.walletactivation.entry.WalletActivationModel +import com.tangem.features.pushnotifications.api.PushNotificationsComponent +import com.tangem.features.pushnotifications.api.PushNotificationsParams +import javax.inject.Inject + +internal class WalletActivationChildFactory @Inject constructor( + private val pushNotificationsComponent: PushNotificationsComponent.Factory, + private val accessCodeComponentFactory: AccessCodeComponent.Factory, +) { + + fun createChild( + route: WalletActivationRoute, + childContext: AppComponentContext, + model: WalletActivationModel, + ): ComposableContentComponent { + return when (route) { + is WalletActivationRoute.ManualBackupStart -> ManualBackupStartComponent( + context = childContext, + params = ManualBackupStartComponent.Params( + callbacks = model.manualBackupStartModelCallbacks, + ), + ) + is WalletActivationRoute.ManualBackupPhrase -> ManualBackupPhraseComponent( + context = childContext, + params = ManualBackupPhraseComponent.Params( + userWalletId = model.params.userWalletId, + callbacks = model.manualBackupPhraseModelCallbacks, + ), + ) + is WalletActivationRoute.ManualBackupCheck -> ManualBackupCheckComponent( + context = childContext, + params = ManualBackupCheckComponent.Params( + userWalletId = model.params.userWalletId, + callbacks = model.manualBackupCheckModelCallbacks, + ), + ) + is WalletActivationRoute.ManualBackupCompleted -> ManualBackupCompletedComponent( + context = childContext, + params = ManualBackupCompletedComponent.Params( + userWalletId = model.params.userWalletId, + callbacks = model.manualBackupCompletedModelCallbacks, + ), + ) + is WalletActivationRoute.SetAccessCode -> accessCodeComponentFactory.create( + context = childContext, + params = AccessCodeComponent.Params( + isConfirmMode = false, + userWalletId = model.params.userWalletId, + callbacks = model.accessCodeModelCallbacks, + ), + ) + is WalletActivationRoute.ConfirmAccessCode -> accessCodeComponentFactory.create( + context = childContext, + params = AccessCodeComponent.Params( + isConfirmMode = true, + accessCodeToConfirm = route.accessCode, + userWalletId = model.params.userWalletId, + callbacks = model.accessCodeModelCallbacks, + ), + ) + is WalletActivationRoute.PushNotifications -> pushNotificationsComponent.create( + context = childContext, + params = PushNotificationsParams( + modelCallbacks = model.pushNotificationsCallbacks, + ), + ) + is WalletActivationRoute.SetupFinished -> MobileWalletSetupFinishedComponent( + context = childContext, + params = MobileWalletSetupFinishedComponent.Params( + callbacks = model.mobileWalletSetupFinishedModelCallbacks, + ), + ) + } + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/routing/WalletActivationRoute.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/routing/WalletActivationRoute.kt new file mode 100644 index 0000000000..26b6998a2e --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/routing/WalletActivationRoute.kt @@ -0,0 +1,31 @@ +package com.tangem.features.hotwallet.walletactivation.entry.routing + +import com.tangem.core.decompose.navigation.Route +import kotlinx.serialization.Serializable + +internal sealed class WalletActivationRoute : Route { + + @Serializable + object ManualBackupStart : WalletActivationRoute() + + @Serializable + object ManualBackupPhrase : WalletActivationRoute() + + @Serializable + data object ManualBackupCheck : WalletActivationRoute() + + @Serializable + object ManualBackupCompleted : WalletActivationRoute() + + @Serializable + data object SetAccessCode : WalletActivationRoute() + + @Serializable + data class ConfirmAccessCode(val accessCode: String) : WalletActivationRoute() + + @Serializable + object PushNotifications : WalletActivationRoute() + + @Serializable + object SetupFinished : WalletActivationRoute() +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/ui/WalletActivationContent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/ui/WalletActivationContent.kt new file mode 100644 index 0000000000..0c2e2719e9 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/ui/WalletActivationContent.kt @@ -0,0 +1,42 @@ +package com.tangem.features.hotwallet.walletactivation.entry.ui + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.arkivanov.decompose.router.stack.ChildStack +import com.arkivanov.decompose.extensions.compose.stack.Children +import com.arkivanov.decompose.extensions.compose.stack.animation.slide +import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.core.ui.res.TangemTheme +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent +import com.tangem.features.hotwallet.walletactivation.entry.routing.WalletActivationRoute + +@Composable +internal fun WalletActivationContent( + stackState: ChildStack, + stepperComponent: HotWalletStepperComponent?, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .background(color = TangemTheme.colors.background.primary) + .fillMaxSize() + .imePadding() + .systemBarsPadding(), + ) { + stepperComponent?.Content(Modifier) + + Children( + stack = stackState, + animation = stackAnimation(slide()), + modifier = Modifier.fillMaxSize(), + ) { + it.instance.Content(Modifier.fillMaxSize()) + } + } +} \ No newline at end of file diff --git a/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsParams.kt b/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsParams.kt index 27bc4d7131..4e152b9c09 100644 --- a/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsParams.kt +++ b/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsParams.kt @@ -1,6 +1,9 @@ package com.tangem.features.pushnotifications.api +import com.tangem.common.routing.AppRoute + data class PushNotificationsParams( val isBottomSheet: Boolean = false, + val nextRoute: AppRoute? = null, val modelCallbacks: PushNotificationsModelCallbacks, ) \ No newline at end of file diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/model/PushNotificationsModel.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/model/PushNotificationsModel.kt index 735932ce2c..9a6115aa48 100644 --- a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/model/PushNotificationsModel.kt +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/model/PushNotificationsModel.kt @@ -1,7 +1,6 @@ package com.tangem.features.pushnotifications.impl.model import androidx.compose.runtime.Stable -import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsParam @@ -71,7 +70,7 @@ internal class PushNotificationsModel @Inject constructor( neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION) params.modelCallbacks.onDenySystemPermission() if (!params.isBottomSheet) { - appRouter.push(AppRoute.Home()) + params.nextRoute?.let { appRouter.push(it) } } } } @@ -85,7 +84,7 @@ internal class PushNotificationsModel @Inject constructor( neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION) params.modelCallbacks.onAllowSystemPermission() if (!params.isBottomSheet) { - appRouter.push(AppRoute.Home()) + params.nextRoute?.let { appRouter.push(it) } } } } @@ -99,7 +98,7 @@ internal class PushNotificationsModel @Inject constructor( neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION) params.modelCallbacks.onDenySystemPermission() if (!params.isBottomSheet) { - appRouter.push(AppRoute.Home()) + params.nextRoute?.let { appRouter.push(it) } } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt index fa98ada4f2..5e3757b744 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt @@ -387,7 +387,8 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( } override fun onFinishWalletActivationClick() { - // TODO implement wallet activation process + val userWallet = getSelectedUserWallet() ?: return + appRouter.push(AppRoute.WalletActivation(userWallet.walletId)) } private suspend fun fetchCryptoCurrencies(userWalletId: UserWalletId, currencies: List) { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt index 4427d9de7e..248f54a3e6 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt @@ -266,8 +266,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( ) { if (userWallet !is UserWallet.Hot) return - // TODO [REDACTED_TASK_KEY] set an actual value - val shouldShowFinishActivation = false + val shouldShowFinishActivation = !userWallet.backedUp addIf( element = WalletNotification.FinishWalletActivation( From 8ad45b2fe8e4e54088d835e84291f267c99d599a Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 6 Aug 2025 08:21:14 +0000 Subject: [PATCH 48/53] Updated on 2026-08-14 --- gradle/tangem_dependencies.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index 7ab02a4628..78d485789e 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,9 +5,9 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "releases-5.27.0-1126" +tangemBlockchainSdk = "develop-1129" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "releases-5.27.0-510" +tangemCardSdk = "develop-509" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem12" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ From fd3d95097fe1456c510bb0da703109a0a9695eed Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Aug 2025 15:45:35 +0400 Subject: [PATCH 49/53] Updated on 2026-08-14 --- data/account/.gitignore | 1 + data/account/build.gradle.kts | 37 ++++++++++++++ .../data/account/di/AccountDataModule.kt | 25 ++++++++++ .../DefaultAccountsCRUDRepository.kt | 47 ++++++++++++++++++ .../repository/AccountsCRUDRepository.kt | 49 +++++++++++++++++++ settings.gradle.kts | 1 + 6 files changed, 160 insertions(+) create mode 100644 data/account/.gitignore create mode 100644 data/account/build.gradle.kts create mode 100644 data/account/src/main/kotlin/com/tangem/data/account/di/AccountDataModule.kt create mode 100644 data/account/src/main/kotlin/com/tangem/data/account/repository/DefaultAccountsCRUDRepository.kt create mode 100644 domain/account/src/main/java/com/tangem/domain/account/repository/AccountsCRUDRepository.kt diff --git a/data/account/.gitignore b/data/account/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/data/account/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/data/account/build.gradle.kts b/data/account/build.gradle.kts new file mode 100644 index 0000000000..464ba8486e --- /dev/null +++ b/data/account/build.gradle.kts @@ -0,0 +1,37 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.kapt) + id("configuration") +} + +android { + namespace = "com.tangem.data.account" +} + +dependencies { + + // region Project - Core + api(projects.core.utils) + // endregion + + // region Project - Domain + api(projects.domain.account) + api(projects.domain.models) + // endregion + + // Project - Data + implementation(projects.core.datasource) + // endregion + + // region DI + implementation(deps.hilt.core) + kapt(deps.hilt.kapt) + // endregion + + // region Other Dependencies + implementation(deps.arrow.core) + implementation(deps.kotlin.coroutines) + implementation(deps.timber) + // endregion +} \ No newline at end of file diff --git a/data/account/src/main/kotlin/com/tangem/data/account/di/AccountDataModule.kt b/data/account/src/main/kotlin/com/tangem/data/account/di/AccountDataModule.kt new file mode 100644 index 0000000000..e05c402f68 --- /dev/null +++ b/data/account/src/main/kotlin/com/tangem/data/account/di/AccountDataModule.kt @@ -0,0 +1,25 @@ +package com.tangem.data.account.di + +import com.tangem.data.account.repository.DefaultAccountsCRUDRepository +import com.tangem.datasource.local.datastore.RuntimeSharedStore +import com.tangem.datasource.local.userwallet.UserWalletsStore +import com.tangem.domain.account.repository.AccountsCRUDRepository +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object AccountDataModule { + + @Provides + @Singleton + fun provideAccountsCRUDRepository(userWalletsStore: UserWalletsStore): AccountsCRUDRepository { + return DefaultAccountsCRUDRepository( + runtimeStore = RuntimeSharedStore(), + userWalletsStore = userWalletsStore, + ) + } +} \ No newline at end of file diff --git a/data/account/src/main/kotlin/com/tangem/data/account/repository/DefaultAccountsCRUDRepository.kt b/data/account/src/main/kotlin/com/tangem/data/account/repository/DefaultAccountsCRUDRepository.kt new file mode 100644 index 0000000000..76d86bd0fe --- /dev/null +++ b/data/account/src/main/kotlin/com/tangem/data/account/repository/DefaultAccountsCRUDRepository.kt @@ -0,0 +1,47 @@ +package com.tangem.data.account.repository + +import arrow.core.Option +import arrow.core.Option.Companion.catch +import arrow.core.none +import com.tangem.datasource.local.datastore.RuntimeSharedStore +import com.tangem.datasource.local.userwallet.UserWalletsStore +import com.tangem.domain.account.models.AccountList +import com.tangem.domain.account.repository.AccountsCRUDRepository +import com.tangem.domain.models.account.Account +import com.tangem.domain.models.account.AccountId +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.utils.extensions.addOrReplace + +/** +[REDACTED_AUTHOR] + */ +// TODO: [REDACTED_JIRA] +internal class DefaultAccountsCRUDRepository( + private val runtimeStore: RuntimeSharedStore>, + private val userWalletsStore: UserWalletsStore, +) : AccountsCRUDRepository { + + override suspend fun getAccounts(userWalletId: UserWalletId): Option = catch { + runtimeStore.getSyncOrNull() + ?.firstOrNull { it.userWallet.walletId == userWalletId } + ?: return none() + } + + override suspend fun getAccount(accountId: AccountId): Option = catch { + runtimeStore.getSyncOrNull().orEmpty() + .flatMap { it.accounts } + .firstOrNull { it.accountId == accountId } as? Account.CryptoPortfolio + ?: return none() + } + + override suspend fun saveAccounts(accountList: AccountList) { + runtimeStore.update(emptyList()) { + it.addOrReplace(accountList) { it.userWallet.walletId == accountList.userWallet.walletId } + } + } + + override fun getUserWallet(userWalletId: UserWalletId): UserWallet { + return userWalletsStore.getSyncStrict(userWalletId) + } +} \ No newline at end of file diff --git a/domain/account/src/main/java/com/tangem/domain/account/repository/AccountsCRUDRepository.kt b/domain/account/src/main/java/com/tangem/domain/account/repository/AccountsCRUDRepository.kt new file mode 100644 index 0000000000..70b8543734 --- /dev/null +++ b/domain/account/src/main/java/com/tangem/domain/account/repository/AccountsCRUDRepository.kt @@ -0,0 +1,49 @@ +package com.tangem.domain.account.repository + +import arrow.core.Option +import com.tangem.domain.account.models.AccountList +import com.tangem.domain.models.account.Account +import com.tangem.domain.models.account.AccountId +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId + +/** + * Repository interface for performing CRUD operations on accounts + * +[REDACTED_AUTHOR] + */ +interface AccountsCRUDRepository { + + /** + * Retrieves a list of accounts associated with a specific user wallet + * + * @param userWalletId the unique identifier of the user wallet + * @return an [Option] containing the [AccountList] if found, or `Option.None` if not + */ + suspend fun getAccounts(userWalletId: UserWalletId): Option + + /** + * Retrieves a specific account by its unique identifier + * + * @param accountId the unique identifier of the account + * @return an [Option] containing the [Account.CryptoPortfolio] if found, or `Option.None` if not + */ + suspend fun getAccount(accountId: AccountId): Option + + /** + * Saves a list of accounts to the repository + * + * @param accountList the list of accounts to be saved. + */ + @Throws + suspend fun saveAccounts(accountList: AccountList) + + /** + * Retrieves a user wallet by its unique identifier + * + * @param userWalletId the unique identifier of the user wallet + * @return the [UserWallet] associated with the given identifier + */ + @Throws + fun getUserWallet(userWalletId: UserWalletId): UserWallet +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index de1aaf92ce..bab69533de 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -334,6 +334,7 @@ include(":domain:wallet-manager:models") // endregion Domain modules // region Data modules +include(":data:account") include(":data:app-currency") include(":data:app-theme") include(":data:balance-hiding") From f6fad0e6d18011e7f5b573f8978381b29094cbd5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 6 Aug 2025 17:09:04 +0700 Subject: [PATCH 50/53] Updated on 2026-08-14 --- app/build.gradle.kts | 2 + core/res/src/main/res/values-de/strings.xml | 2 + core/res/src/main/res/values-es/strings.xml | 2 + core/res/src/main/res/values-fr/strings.xml | 2 + core/res/src/main/res/values-it/strings.xml | 1 + core/res/src/main/res/values-ja/strings.xml | 21 ++ core/res/src/main/res/values-ru/strings.xml | 2 + .../src/main/res/values-uk-rUA/strings.xml | 2 + .../src/main/res/values-zh-rTW/strings.xml | 1 + core/res/src/main/res/values/strings.xml | 19 + .../ui/components/fields/AmountTextField.kt | 27 +- .../ui/components/fields/AutoSizeTextField.kt | 204 +++++++++++ .../ui/components/fields/SimpleTextField.kt | 11 +- features/account/api/.gitignore | 1 + features/account/api/build.gradle.kts | 16 + .../account/AccountCreateEditComponent.kt | 14 + features/account/impl/.gitignore | 1 + features/account/impl/build.gradle.kts | 59 +++ .../createedit/AccountCreateEditModel.kt | 54 +++ .../DefaultAccountCreateEditComponent.kt | 38 ++ .../createedit/di/AccountCreateEditModule.kt | 27 ++ .../createedit/entity/AccountCreateEditUM.kt | 42 +++ .../createedit/ui/AccountCreateEditContent.kt | 344 ++++++++++++++++++ settings.gradle.kts | 3 + 24 files changed, 870 insertions(+), 25 deletions(-) create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/fields/AutoSizeTextField.kt create mode 100644 features/account/api/.gitignore create mode 100644 features/account/api/build.gradle.kts create mode 100644 features/account/api/src/main/java/com/tangem/features/account/AccountCreateEditComponent.kt create mode 100644 features/account/impl/.gitignore create mode 100644 features/account/impl/build.gradle.kts create mode 100644 features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt create mode 100644 features/account/impl/src/main/java/com/tangem/features/account/createedit/DefaultAccountCreateEditComponent.kt create mode 100644 features/account/impl/src/main/java/com/tangem/features/account/createedit/di/AccountCreateEditModule.kt create mode 100644 features/account/impl/src/main/java/com/tangem/features/account/createedit/entity/AccountCreateEditUM.kt create mode 100644 features/account/impl/src/main/java/com/tangem/features/account/createedit/ui/AccountCreateEditContent.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e90dbf8a85..0f40005c3c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -235,6 +235,8 @@ dependencies { implementation(projects.features.createWalletSelection.impl) implementation(projects.features.home.api) implementation(projects.features.home.impl) + implementation(projects.features.account.api) + implementation(projects.features.account.impl) /** AndroidX libraries */ implementation(deps.androidx.core.ktx) diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index ead5525ab6..47e0920719 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -120,6 +120,7 @@ Token hinzufügen Alle Erlauben + Betrag Analysen Anwenden Genehmigung @@ -165,6 +166,7 @@ Deaktiviert Trennen Erledigt + Bearbeiten Aktivieren Aktiviert Fehler diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml index 1ce6e28079..39ee2f7881 100644 --- a/core/res/src/main/res/values-es/strings.xml +++ b/core/res/src/main/res/values-es/strings.xml @@ -109,6 +109,7 @@ Agregar token Todos Autorizar + Montante Analítica Aplicar Aprobación @@ -153,6 +154,7 @@ Desactivado Desconectar Listo + Editar Activar Activado Error diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index b28e3174c2..3973a3df94 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -99,6 +99,7 @@ Ajouter un jeton Tous Permettre + Montant Analytique Appliquer Approbation @@ -140,6 +141,7 @@ Désactivé Se déconnecter Exécuté + Modifier Activer Activé Erreur diff --git a/core/res/src/main/res/values-it/strings.xml b/core/res/src/main/res/values-it/strings.xml index 7132029cfe..5caec91961 100644 --- a/core/res/src/main/res/values-it/strings.xml +++ b/core/res/src/main/res/values-it/strings.xml @@ -5,6 +5,7 @@ Questa carta non è progettata per funzionare con Tangem L\'importo inviato e il cambio non può essere inferiore a 1 ADA Accetta + Importo Saldo: %s Impossibile creare la transazione Non hai fornito l\'accesso alla tua videocamera, modifica le tue impostazioni sulla privacy diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index af6731c66e..93c2b842f3 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -12,6 +12,18 @@ ウォレットのロックを解除するには、 %s桁のアクセスコードを設定します。 アクセスコードの作成 アクセスコード + アカウントを追加 + 保存 + アカウント名 + アカウント + 新しいアカウント + アカウントを追加 + アカウントを編集 + 編集を続ける + 破棄 + 新しいアカウントを破棄してもよろしいですか? + 編集内容を破棄してもよろしいですか? + 保存されていない変更 トークンが見つかりませんか?メインページのマーケットセクションに移動し、ポートフォリオに追加して買付できるようにします。 トークンが見つかりませんか?メインページのマーケットセクションに移動し、ポートフォリオに追加して売却できるようにします。 売却 @@ -127,6 +139,7 @@ トークンを追加 すべて 許可する + 金額 アナリティクス 適用する 承認 @@ -170,6 +183,7 @@ 無効 切断 完了 + 編集 有効にする 有効 エラー @@ -1110,6 +1124,7 @@ 手数料見積りエラーです。サポートにフィードバックをお送りください。 スワップする 選択したトークンをこの量を交換すると、価格に大きな影響が生じ、結果が減少します。 + 価格への影響が甚大です 残高不足 許可を与える スワップ @@ -1285,6 +1300,7 @@ このウォレットを保護するためのシークレットコードです。ログインと署名に使用されます。 アクセスコードの設定 / 変更 ウォレットの受信取引とTangemの更新について通知を受け取る。 + 現在、Huaweiデバイスではプッシュ通知が機能しない可能性があります。現在、解決策の検討に取り組んでおり、今後のアップデートで修正をリリースする予定です。ご理解のほどよろしくお願いいたします。 取引通知 ウォレット設定 Tangem @@ -1426,15 +1442,19 @@ すべてのdAppを接続解除する ウォレットの変更の予測 取引をシミュレーションできませんでした。注意して続行してください。 + %sでは見積もりはサポートされていません + %sによる提案 悪意のある取引 このウォレットのポートフォリオに%sネットワークを追加します ウォレットに必要なネットワークはありません 新しい接続 ウォレットを別のdAppに接続する セッションなし + ウォレットの変更は検出されませんでした このドメインは複数のセキュリティプロバイダーから安全でないとの警告を受けています。あなたの資産を守るため、直ちにアクセスを中止してください。 既知のセキュリティリスク リクエスト元 + とにかく送金 署名タイプ 宛先 取引リクエスト @@ -1443,6 +1463,7 @@ 各ペアリング試行で、新しくユニークなURIが使用されていることを確認します URIはすでに使用されています ウォレットコネクト + 不審な取引 破棄 バックアップが中断されました。再開しますか? はい、再開します diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 5a430645a7..474f997c12 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -96,6 +96,7 @@ Добавить токен Все Разрешить + Сумма Аналитика Применить Одобрение @@ -140,6 +141,7 @@ Отключено Отключить Готово + Изменить Включить Включено Ошибка diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml index 9b3e3dfe52..5a09461ddc 100644 --- a/core/res/src/main/res/values-uk-rUA/strings.xml +++ b/core/res/src/main/res/values-uk-rUA/strings.xml @@ -96,6 +96,7 @@ Додати токен Усе Дозволити + Сума Аналітика Застосовувати Затвердження @@ -139,6 +140,7 @@ Вимкнуто Від\'єднати Готово + Редагувати Увімкнути Увімкнено Помилка diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml index d5ccd296e0..b33588b325 100644 --- a/core/res/src/main/res/values-zh-rTW/strings.xml +++ b/core/res/src/main/res/values-zh-rTW/strings.xml @@ -40,6 +40,7 @@ 卡片設置 發送金額和找零不能少於1個ADA 接受 + 數量 允許 注意 餘額: %s diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 704c48f319..70f17192aa 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -12,7 +12,18 @@ Set a %s-digit Access Code to unlock your wallet. Create Access Code Access code + Add account + Save Account name + Account + New account + Add account + Edit account + Keep Editing + Discard + Are you sure you want to discard new account? + Are you sure you want to discard edits? + Unsaved Changes Can’t find your token? Go to the Market section on the main page and add it to your portfolio for purchase Can’t find your token? Go to the Market section on the main page and add it to your portfolio for selling. Sell @@ -132,6 +143,7 @@ Add token All Allow + Amount Analytics Apply Approval @@ -177,6 +189,7 @@ Disabled Disconnect Done + Edit Enable Enabled Error @@ -432,6 +445,8 @@ Finish Now Finish Wallet Activation To complete setup, secure app access with Access Code. + If you do, you\'ll need to start over. + Are you sure you want to exit the activation process? Recover an existing wallet stored in your Google Drive backup Google Drive Backup Go to backup @@ -1129,6 +1144,7 @@ Fee estimation error. Please send feedback to support. You swap Swapping this amount of selected tokens will cause a significant price impact and reduce your outcome. + High price impact Insufficient funds Give Permission Swap @@ -1495,6 +1511,8 @@ Disconect All dApps Estimated wallet changes The transaction couldn\'t be simulated. Please proceed with caution. + Estimation is not supported for %s + Suggested by %s Malicious transaction Add the %s network to your portfolio for this wallet The wallet has no required networks @@ -1505,6 +1523,7 @@ This domain is flagged as unsafe by multiple security providers. Leave immediately to protect your assets Known security risk Request from + Send anyway Signature Type To Transaction request diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt index be5c0a9f2c..e4d65b95dd 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt @@ -12,12 +12,7 @@ import androidx.compose.ui.Alignment.Companion.TopCenter import androidx.compose.ui.Alignment.Companion.TopStart import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.ParagraphIntrinsics import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.createFontFamilyResolver import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextAlign @@ -77,24 +72,10 @@ fun AmountTextField( ) { val decimalFormat = rememberDecimalFormat() BoxWithConstraints(modifier = modifier) { - var fontSize = textStyle.fontSize - if (isAutoResize) { - val calculateIntrinsics = @Composable { - val transformedText = visualTransformation.filter(AnnotatedString(value)).text.text - ParagraphIntrinsics( - text = transformedText, - style = textStyle.copy(fontSize = fontSize), - density = LocalDensity.current, - fontFamilyResolver = createFontFamilyResolver(LocalContext.current), - ) - } - var intrinsics = calculateIntrinsics() - with(LocalDensity.current) { - while (intrinsics.maxIntrinsicWidth > maxWidth.toPx()) { - fontSize *= reduceFactor - intrinsics = calculateIntrinsics() - } - } + val fontSize = if (isAutoResize) { + resizeFont(visualTransformation, value, textStyle, reduceFactor) + } else { + textStyle.fontSize } val textColor = if (value.isBlank()) TangemTheme.colors.text.disabled else color SimpleTextField( diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AutoSizeTextField.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AutoSizeTextField.kt new file mode 100644 index 0000000000..23a4cf2348 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AutoSizeTextField.kt @@ -0,0 +1,204 @@ +package com.tangem.core.ui.components.fields + +import android.annotation.SuppressLint +import androidx.annotation.FloatRange +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.BoxWithConstraintsScope +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.ParagraphIntrinsics +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.createFontFamilyResolver +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.text.style.TextDirection +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.TextUnit +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview + +/** + * Simple text field for auto size input. + * Can display aligned placeholder. + * + * @param value initial text + * @param onValueChange callback + * @param isAutoResize is text font auto resize + * @param reduceFactor font resize factor + * @param textStyle text and placeholder styles + * @param textFieldModifier modifier for [SimpleTextField] + * @param boxModifier modifier for [BoxWithConstraints] + * @see [SimpleTextField] for other text field params + */ +@SuppressLint("UnusedBoxWithConstraintsScope") +@Composable +fun AutoSizeTextField( + value: String, + onValueChange: (String) -> Unit, + + // region AutoSize + isAutoResize: Boolean = true, + @FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false) + reduceFactor: Double = 0.9, + + // region TextField + textFieldModifier: Modifier = Modifier, + boxModifier: Modifier = Modifier, + placeholder: TextReference? = null, + singleLine: Boolean = isAutoResize, + centered: Boolean = false, + visualTransformation: VisualTransformation = VisualTransformation.None, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + color: Color = TangemTheme.colors.text.primary1, + textStyle: TextStyle = TangemTheme.typography.body2.copy(color = color), + placeholderColor: Color = TangemTheme.colors.text.disabled, + readOnly: Boolean = false, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + isValuePasted: Boolean = false, + onValuePastedTriggerDismiss: () -> Unit = {}, + decorationBox: (@Composable (innerTextField: @Composable () -> Unit) -> Unit)? = null, +) { + BoxWithConstraints(modifier = boxModifier) { + val fontSize = if (isAutoResize) { + resizeFont(visualTransformation, value, textStyle, reduceFactor) + } else { + textStyle.fontSize + } + val textColor = if (value.isBlank()) TangemTheme.colors.text.disabled else color + SimpleTextField( + value = value, + onValueChange = onValueChange, + textStyle = textStyle.copy( + fontSize = fontSize, + textDirection = TextDirection.ContentOrLtr, + ), + isValuePasted = isValuePasted, + onValuePastedTriggerDismiss = onValuePastedTriggerDismiss, + color = textColor, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + placeholder = placeholder, + placeholderColor = placeholderColor, + singleLine = singleLine, + interactionSource = interactionSource, + readOnly = readOnly, + centered = centered, + visualTransformation = visualTransformation, + decorationBox = decorationBox, + modifier = textFieldModifier, + ) + } +} + +@Composable +internal fun BoxWithConstraintsScope.resizeFont( + visualTransformation: VisualTransformation, + value: String, + textStyle: TextStyle, + reduceFactor: Double, +): TextUnit { + var result = textStyle.fontSize + val calculateIntrinsics = @Composable { + val transformedText = visualTransformation.filter(AnnotatedString(value)).text.text + ParagraphIntrinsics( + text = transformedText, + style = textStyle.copy(fontSize = result), + density = LocalDensity.current, + fontFamilyResolver = createFontFamilyResolver(LocalContext.current), + ) + } + var intrinsics = calculateIntrinsics() + with(LocalDensity.current) { + while (intrinsics.maxIntrinsicWidth > maxWidth.toPx()) { + result *= reduceFactor + intrinsics = calculateIntrinsics() + } + } + return result +} + +// region preview +@Preview(widthDp = 360, showBackground = true) +@Composable +private fun AmountTextFieldPreview( + @PreviewParameter(AutoSizeTextFieldPreviewProvider::class) data: AutoSizeTextFieldPreviewData, +) { + var text by remember { mutableStateOf(data.value) } + TangemThemePreview { + AutoSizeTextField( + textFieldModifier = Modifier.fillMaxWidth(), + value = text, + onValueChange = { text = it }, + centered = data.centered, + isAutoResize = data.isAutoResize, + placeholder = data.placeholder, + ) + } +} + +private class AutoSizeTextFieldPreviewProvider : PreviewParameterProvider { + override val values = sequenceOf( + AutoSizeTextFieldPreviewData( + value = "AutoSizeTextField", + placeholder = stringReference("placeholder"), + isAutoResize = true, + centered = false, + ), + AutoSizeTextFieldPreviewData( + value = "AutoSizeTextFieldAutoSizeTextFieldAutoSizeTextFieldAutoSizeTextField", + placeholder = stringReference("placeholder"), + isAutoResize = true, + centered = false, + ), + AutoSizeTextFieldPreviewData( + value = "AutoSizeTextFieldAutoSizeTextFieldAutoSizeTextFieldAutoSizeTextField", + placeholder = stringReference("Placeholder"), + isAutoResize = true, + centered = false, + ), + AutoSizeTextFieldPreviewData( + value = "", + placeholder = stringReference("Placeholder"), + isAutoResize = true, + centered = false, + ), + AutoSizeTextFieldPreviewData( + value = "AutoSizeTextField", + placeholder = stringReference("Placeholder"), + isAutoResize = false, + centered = true, + ), + AutoSizeTextFieldPreviewData( + value = "AutoSizeTextFieldAutoSizeTextFieldAutoSizeTextFieldAutoSizeTextField", + placeholder = stringReference("Placeholder"), + isAutoResize = false, + centered = true, + ), + AutoSizeTextFieldPreviewData( + value = "", + placeholder = stringReference("Placeholder"), + isAutoResize = false, + centered = true, + ), + ) +} + +private data class AutoSizeTextFieldPreviewData( + val value: String, + val placeholder: TextReference, + val isAutoResize: Boolean, + val centered: Boolean, +) +// endregion \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt index c37307f136..1616968df4 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Text import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester @@ -17,6 +18,7 @@ import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.text.style.TextAlign import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme @@ -39,6 +41,7 @@ fun SimpleTextField( textStyle: TextStyle = TangemTheme.typography.body2.copy(color = color), placeholderColor: Color = TangemTheme.colors.text.disabled, readOnly: Boolean = false, + centered: Boolean = false, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, isValuePasted: Boolean = false, onValuePastedTriggerDismiss: () -> Unit = {}, @@ -80,6 +83,8 @@ fun SimpleTextField( onValuePastedTriggerDismiss() } } + var textStyle = textStyle.copy(color = color) + if (centered) textStyle = textStyle.copy(textAlign = TextAlign.Center) BasicTextField( value = textFieldValue, @@ -91,7 +96,7 @@ fun SimpleTextField( if (stringChangedSinceLastInvocation) onValueChange(newTextFieldValueState.text) }, - textStyle = textStyle.copy(color = color), + textStyle = textStyle, cursorBrush = SolidColor(TangemTheme.colors.text.primary1), singleLine = singleLine, readOnly = readOnly, @@ -105,6 +110,7 @@ fun SimpleTextField( value = value, textStyle = textStyle, textValue = textValue, + centered = centered, color = placeholderColor, ) }, @@ -118,10 +124,11 @@ private fun SimpleTextPlaceholder( placeholder: TextReference?, value: String, textStyle: TextStyle, + centered: Boolean, textValue: @Composable () -> Unit, color: Color = TangemTheme.colors.text.disabled, ) { - Box { + Box(contentAlignment = if (centered) Alignment.Center else Alignment.TopStart) { if (value.isBlank() && placeholder != null) { AnimatedContent( targetState = placeholder, diff --git a/features/account/api/.gitignore b/features/account/api/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/features/account/api/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/features/account/api/build.gradle.kts b/features/account/api/build.gradle.kts new file mode 100644 index 0000000000..349aee77c0 --- /dev/null +++ b/features/account/api/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + id("configuration") +} + +android { + namespace = "com.tangem.features.account.api" +} + +dependencies { + + /* Project - Core */ + implementation(projects.core.decompose) + implementation(projects.core.ui) +} \ No newline at end of file diff --git a/features/account/api/src/main/java/com/tangem/features/account/AccountCreateEditComponent.kt b/features/account/api/src/main/java/com/tangem/features/account/AccountCreateEditComponent.kt new file mode 100644 index 0000000000..1f6de5f0b2 --- /dev/null +++ b/features/account/api/src/main/java/com/tangem/features/account/AccountCreateEditComponent.kt @@ -0,0 +1,14 @@ +package com.tangem.features.account + +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.decompose.ComposableContentComponent + +interface AccountCreateEditComponent : ComposableContentComponent { + interface Factory : ComponentFactory + + sealed interface Params { + + data object Create : Params + data object Edit : Params + } +} \ No newline at end of file diff --git a/features/account/impl/.gitignore b/features/account/impl/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/features/account/impl/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/features/account/impl/build.gradle.kts b/features/account/impl/build.gradle.kts new file mode 100644 index 0000000000..55714f3021 --- /dev/null +++ b/features/account/impl/build.gradle.kts @@ -0,0 +1,59 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.kapt) + alias(deps.plugins.kotlin.serialization) + alias(deps.plugins.hilt.android) + id("configuration") +} + +android { + namespace = "com.tangem.features.account.impl" +} + +dependencies { + /** Api */ + implementation(projects.features.account.api) + + /** Core modules */ + implementation(projects.core.analytics) + implementation(projects.core.analytics.models) + implementation(projects.core.utils) + implementation(projects.core.ui) + implementation(projects.core.res) + implementation(projects.core.decompose) + implementation(projects.core.navigation) + implementation(projects.core.datasource) + + /** Domain */ + implementation(projects.domain.models) + + /** Common */ + implementation(projects.common.ui) + implementation(projects.common.routing) + + /** AndroidX libraries */ + implementation(deps.androidx.core.ktx) + implementation(deps.lifecycle.runtime.ktx) + + /** Compose libraries */ + implementation(deps.compose.material3) + implementation(deps.compose.animation) + implementation(deps.compose.foundation) + implementation(deps.compose.ui) + implementation(deps.compose.ui.tooling) + implementation(deps.compose.coil) + implementation(deps.decompose.ext.compose) + implementation(deps.androidx.activity.compose) + + /** Other libraries */ + implementation(deps.arrow.core) + implementation(deps.kotlin.immutable.collections) + implementation(deps.kotlin.serialization) + implementation(deps.timber) + implementation(deps.firebase.crashlytics) + + /** DI */ + implementation(deps.hilt.android) + kapt(deps.hilt.kapt) +} \ No newline at end of file diff --git a/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt b/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt new file mode 100644 index 0000000000..872b7af23f --- /dev/null +++ b/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt @@ -0,0 +1,54 @@ +package com.tangem.features.account.createedit + +import com.tangem.core.decompose.di.ModelScoped +import com.tangem.core.decompose.model.Model +import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.core.decompose.navigation.Router +import com.tangem.core.decompose.ui.UiMessageSender +import com.tangem.core.res.R +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.message.DialogMessage +import com.tangem.core.ui.message.EventMessageAction +import com.tangem.features.account.AccountCreateEditComponent +import com.tangem.features.account.createedit.entity.AccountCreateEditUM +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import javax.inject.Inject + +@ModelScoped +internal class AccountCreateEditModel @Inject constructor( + paramsContainer: ParamsContainer, + private val messageSender: UiMessageSender, + private val router: Router, + override val dispatchers: CoroutineDispatcherProvider, +) : Model() { + + private val params = paramsContainer.require() + + val uiState: StateFlow + field = MutableStateFlow(TODO()) + + init { + params + } + + fun unsaveChangeDialog() { + val firstAction = EventMessageAction( + title = resourceReference(R.string.account_unsaved_dialog_action_first), + onClick = {}, + ) + val secondAction = EventMessageAction( + title = resourceReference(R.string.account_unsaved_dialog_action_second), + onClick = { router.pop() }, + ) + messageSender.send( + DialogMessage( + title = resourceReference(R.string.account_unsaved_dialog_title), + message = resourceReference(R.string.account_unsaved_dialog_message_create), + firstActionBuilder = { firstAction }, + secondActionBuilder = { secondAction }, + ), + ) + } +} \ No newline at end of file diff --git a/features/account/impl/src/main/java/com/tangem/features/account/createedit/DefaultAccountCreateEditComponent.kt b/features/account/impl/src/main/java/com/tangem/features/account/createedit/DefaultAccountCreateEditComponent.kt new file mode 100644 index 0000000000..06725717ad --- /dev/null +++ b/features/account/impl/src/main/java/com/tangem/features/account/createedit/DefaultAccountCreateEditComponent.kt @@ -0,0 +1,38 @@ +package com.tangem.features.account.createedit + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.features.account.AccountCreateEditComponent +import com.tangem.features.account.createedit.ui.AccountCreateEditContent +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +internal class DefaultAccountCreateEditComponent @AssistedInject constructor( + @Assisted appComponentContext: AppComponentContext, + @Assisted params: AccountCreateEditComponent.Params, +) : AppComponentContext by appComponentContext, AccountCreateEditComponent { + + private val model: AccountCreateEditModel = getOrCreateModel(params) + + @Composable + override fun Content(modifier: Modifier) { + val state by model.uiState.collectAsStateWithLifecycle() + AccountCreateEditContent( + modifier = modifier, + state = state, + ) + } + + @AssistedFactory + interface Factory : AccountCreateEditComponent.Factory { + override fun create( + context: AppComponentContext, + params: AccountCreateEditComponent.Params, + ): DefaultAccountCreateEditComponent + } +} \ No newline at end of file diff --git a/features/account/impl/src/main/java/com/tangem/features/account/createedit/di/AccountCreateEditModule.kt b/features/account/impl/src/main/java/com/tangem/features/account/createedit/di/AccountCreateEditModule.kt new file mode 100644 index 0000000000..ab899b3f5c --- /dev/null +++ b/features/account/impl/src/main/java/com/tangem/features/account/createedit/di/AccountCreateEditModule.kt @@ -0,0 +1,27 @@ +package com.tangem.features.account.createedit.di + +import com.tangem.core.decompose.model.Model +import com.tangem.features.account.AccountCreateEditComponent +import com.tangem.features.account.createedit.AccountCreateEditModel +import com.tangem.features.account.createedit.DefaultAccountCreateEditComponent +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +@InstallIn(SingletonComponent::class) +internal interface AccountCreateEditModule { + + @Binds + fun bindAccountCreateEditComponentFactory( + impl: DefaultAccountCreateEditComponent.Factory, + ): AccountCreateEditComponent.Factory + + @Binds + @IntoMap + @ClassKey(AccountCreateEditModel::class) + fun bindAccountCreateEditModel(model: AccountCreateEditModel): Model +} \ No newline at end of file diff --git a/features/account/impl/src/main/java/com/tangem/features/account/createedit/entity/AccountCreateEditUM.kt b/features/account/impl/src/main/java/com/tangem/features/account/createedit/entity/AccountCreateEditUM.kt new file mode 100644 index 0000000000..4ef4877b97 --- /dev/null +++ b/features/account/impl/src/main/java/com/tangem/features/account/createedit/entity/AccountCreateEditUM.kt @@ -0,0 +1,42 @@ +package com.tangem.features.account.createedit.entity + +import com.tangem.core.ui.extensions.TextReference +import com.tangem.domain.models.account.CryptoPortfolioIcon +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf + +data class AccountCreateEditUM( + val title: TextReference = TextReference.EMPTY, + val account: Account = Account(), + val colorsState: Colors, + val iconsState: Icons, + val buttonState: Button = Button(), + val onCloseClick: () -> Unit = {}, +) { + + data class Account( + val name: TextReference = TextReference.EMPTY, + val portfolioIcon: CryptoPortfolioIcon = CryptoPortfolioIcon.ofDefaultCustomAccount(), + val derivationInfo: TextReference = TextReference.EMPTY, + val inputPlaceholder: TextReference = TextReference.EMPTY, + val onNameChange: (String) -> Unit = {}, + ) + + data class Colors( + val selected: CryptoPortfolioIcon.Color, + val list: ImmutableList = persistentListOf(), + val onColorSelect: (CryptoPortfolioIcon.Color) -> Unit = {}, + ) + + data class Icons( + val selected: CryptoPortfolioIcon.Icon, + val list: ImmutableList = persistentListOf(), + val onIconSelect: (CryptoPortfolioIcon.Icon) -> Unit = {}, + ) + + data class Button( + val isButtonEnabled: Boolean = false, + val onConfirmClick: () -> Unit = {}, + val text: TextReference = TextReference.EMPTY, + ) +} \ No newline at end of file diff --git a/features/account/impl/src/main/java/com/tangem/features/account/createedit/ui/AccountCreateEditContent.kt b/features/account/impl/src/main/java/com/tangem/features/account/createedit/ui/AccountCreateEditContent.kt new file mode 100644 index 0000000000..dd927df0f0 --- /dev/null +++ b/features/account/impl/src/main/java/com/tangem/features/account/createedit/ui/AccountCreateEditContent.kt @@ -0,0 +1,344 @@ +package com.tangem.features.account.createedit.ui + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.itemsIndexed +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import androidx.compose.ui.unit.dp +import com.tangem.common.ui.R +import com.tangem.common.ui.account.getResId +import com.tangem.common.ui.account.getUiColor +import com.tangem.core.ui.components.PrimaryButton +import com.tangem.core.ui.components.SpacerH +import com.tangem.core.ui.components.SpacerH24 +import com.tangem.core.ui.components.SpacerH8 +import com.tangem.core.ui.components.appbar.AppBarWithBackButton +import com.tangem.core.ui.components.fields.AutoSizeTextField +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.domain.models.account.CryptoPortfolioIcon +import com.tangem.features.account.createedit.entity.AccountCreateEditUM +import com.tangem.features.account.createedit.entity.AccountCreateEditUM.Account +import kotlinx.collections.immutable.toImmutableList + +@Suppress("LongMethod", "MagicNumber") +@Composable +internal fun AccountCreateEditContent(state: AccountCreateEditUM, modifier: Modifier = Modifier) { + Column( + modifier = modifier + .background(color = TangemTheme.colors.background.tertiary) + .fillMaxSize() + .imePadding() + .systemBarsPadding(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + AppBarWithBackButton( + text = state.title.resolveReference(), + onBackClick = state.onCloseClick, + iconRes = R.drawable.ic_close_24, + modifier = Modifier.height(TangemTheme.dimens.size56), + ) + + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + .weight(1f), + + ) { + AccountSummary(state.account, state.account.onNameChange) + SpacerH24() + AccountColor(state.colorsState) + SpacerH24() + AccountIcon(state.iconsState) + SpacerH8() + Text( + modifier = Modifier.padding(horizontal = 8.dp), + text = state.account.derivationInfo.resolveReference(), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) + } + PrimaryButton( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + enabled = state.buttonState.isButtonEnabled, + text = state.buttonState.text.resolveReference(), + onClick = state.buttonState.onConfirmClick, + ) + } +} + +@Composable +private fun AccountSummary(account: AccountCreateEditUM.Account, onNameChange: (String) -> Unit) { + Column( + modifier = Modifier + .clip(RoundedCornerShape(16.dp)) + .fillMaxWidth() + .background(TangemTheme.colors.background.action), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Spacer(modifier = Modifier.height(24.dp)) + + AccountIcon(account) + Spacer(modifier = Modifier.height(24.dp)) + + Text( + text = stringResourceSafe(R.string.account_form_name), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) + Spacer(modifier = Modifier.height(2.dp)) + + AutoSizeTextField( + centered = true, + textStyle = TangemTheme.typography.head, + placeholder = account.inputPlaceholder, + value = account.name.resolveReference(), + singleLine = true, + onValueChange = onNameChange, + ) + SpacerH(20.dp) + } +} + +@Composable +private fun AccountIcon(account: AccountCreateEditUM.Account) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .size(88.dp) + .clip(RoundedCornerShape(TangemTheme.dimens.radius24)) + .background(account.portfolioIcon.color.getUiColor()), + ) { + val icon = account.portfolioIcon.value + when { + icon == CryptoPortfolioIcon.Icon.Letter -> Text( + text = account.name.resolveReference().first().uppercase(), + style = TangemTheme.typography.head, + color = TangemTheme.colors.text.constantWhite, + ) + else -> Icon( + modifier = Modifier.size(44.dp), + tint = TangemTheme.colors.text.constantWhite, + imageVector = ImageVector.vectorResource(id = icon.getResId()), + contentDescription = null, + ) + } + } +} + +@Suppress("LongMethod", "MagicNumber") +@Composable +private fun AccountColor(colorsState: AccountCreateEditUM.Colors) { + Box( + Modifier + .clip(RoundedCornerShape(16.dp)) + .fillMaxWidth() + .background(TangemTheme.colors.background.action), + ) { + val columns = GridCells.Fixed(6) + val contentPadding = PaddingValues(horizontal = 8.dp, vertical = 12.dp) + LazyVerticalGrid( + columns = columns, + contentPadding = contentPadding, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + itemsIndexed(colorsState.list) { index, color -> + val isSelected = color == colorsState.selected + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .wrapContentSize() + .clickable(onClick = { colorsState.onColorSelect(color) }) + .size(48.dp), + ) { + if (isSelected) { + Box( + modifier = Modifier + .size(47.dp) + .border(2.dp, color.getUiColor(), shape = CircleShape), + ) + Box( + modifier = Modifier + .size(36.dp) + .background(color = color.getUiColor(), shape = CircleShape), + ) + } else { + Box( + modifier = Modifier + .size(40.dp) + .background(color = color.getUiColor(), shape = CircleShape), + ) + } + } + } + } + } +} + +@Suppress("LongMethod", "MagicNumber") +@Composable +private fun AccountIcon(iconsState: AccountCreateEditUM.Icons) { + Box( + Modifier + .clip(RoundedCornerShape(16.dp)) + .fillMaxWidth() + .background(TangemTheme.colors.background.action) + .padding(8.dp), + ) { + val columns = GridCells.Fixed(6) + LazyVerticalGrid( + columns = columns, + ) { + itemsIndexed(iconsState.list) { index, icon -> + val isSelected = icon == iconsState.selected + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .wrapContentSize() + .clickable(onClick = { iconsState.onIconSelect(icon) }) + .size(52.dp), + ) { + if (isSelected) { + val borderColor: Color + val iconTint: Color + val backgroundTint: Color + if (index == 0) { + borderColor = TangemTheme.colors.icon.accent + iconTint = TangemTheme.colors.icon.accent + backgroundTint = TangemTheme.colors.icon.accent.copy(alpha = 0.1f) + } else { + borderColor = TangemTheme.colors.icon.informative + iconTint = TangemTheme.colors.icon.secondary + backgroundTint = TangemTheme.colors.field.focused + } + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .size(44.dp) + .border(2.dp, borderColor, shape = CircleShape), + ) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .size(36.dp) + .background(color = backgroundTint, shape = CircleShape), + ) { + Icon( + imageVector = ImageVector.vectorResource(id = icon.getResId()), + contentDescription = null, + tint = iconTint, + ) + } + } + } else { + val iconTint: Color + val backgroundTint: Color + if (index == 0) { + iconTint = TangemTheme.colors.icon.accent + backgroundTint = TangemTheme.colors.icon.accent.copy(alpha = 0.1f) + } else { + iconTint = TangemTheme.colors.text.tertiary + backgroundTint = TangemTheme.colors.field.focused + } + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .size(40.dp) + .background(color = backgroundTint, shape = CircleShape), + ) { + Icon( + imageVector = ImageVector.vectorResource(id = icon.getResId()), + contentDescription = null, + tint = iconTint, + ) + } + } + } + } + } + } +} + +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun WcConnectionsContentPreview(@PreviewParameter(PreviewStateProvider::class) params: AccountCreateEditUM) { + TangemThemePreview { + AccountCreateEditContent(state = params) + } +} + +private class PreviewStateProvider : CollectionPreviewParameterProvider( + buildList { + val colors = CryptoPortfolioIcon.Color.entries.toImmutableList() + val icons = CryptoPortfolioIcon.Icon.entries.toImmutableList() + var portfolioIcon = CryptoPortfolioIcon.ofDefaultCustomAccount() + val first = AccountCreateEditUM( + title = stringReference("Add account"), + account = Account( + portfolioIcon = portfolioIcon, + derivationInfo = stringReference("Account #03 — used for address derivation."), + ), + colorsState = AccountCreateEditUM.Colors( + selected = portfolioIcon.color, + list = colors.toImmutableList(), + ), + iconsState = AccountCreateEditUM.Icons( + selected = portfolioIcon.value, + list = icons.toImmutableList(), + ), + buttonState = AccountCreateEditUM.Button( + text = stringReference("Add account"), + ), + ) + add(first) + + portfolioIcon = CryptoPortfolioIcon.ofCustomAccount( + value = CryptoPortfolioIcon.Icon.Letter, + color = CryptoPortfolioIcon.Color.entries.random(), + ) + val second = AccountCreateEditUM( + title = stringReference("Edit account"), + account = Account( + portfolioIcon = portfolioIcon, + name = stringReference("Main account"), + derivationInfo = stringReference("Account #03 — used for address derivation."), + ), + colorsState = AccountCreateEditUM.Colors( + selected = portfolioIcon.color, + list = colors.toImmutableList(), + ), + iconsState = AccountCreateEditUM.Icons( + selected = portfolioIcon.value, + list = icons.toImmutableList(), + ), + buttonState = AccountCreateEditUM.Button( + text = stringReference("Save"), + ), + ) + add(second) + }, +) \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index bab69533de..2e4a4b6f20 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -269,6 +269,9 @@ include(":features:create-wallet-selection:impl") include(":features:welcome:api") include(":features:welcome:impl") + +include(":features:account:api") +include(":features:account:impl") // endregion Feature modules // region Domain modules From 5a42d4a84d6485b1e052f38107b96f432f6224f4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 6 Aug 2025 13:45:57 +0500 Subject: [PATCH 51/53] Updated on 2026-08-14 --- .../core/ui/components/fields/SearchBar.kt | 9 ++ .../component/ChooseManagedTokensComponent.kt | 6 + .../DefaultChooseManagedTokensComponent.kt | 2 +- .../model/ChooseManagedTokensModel.kt | 4 +- features/send-v2/impl/build.gradle.kts | 1 + .../DefaultSendEntryPointComponent.kt | 105 +++++++++++++++--- .../send/v2/entrypoint/SendEntryRoute.kt | 11 ++ .../entrypoint/model/SendEntryPointModel.kt | 70 +++++------- 8 files changed, 148 insertions(+), 60 deletions(-) create mode 100644 features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/SendEntryRoute.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SearchBar.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SearchBar.kt index 4366b157be..2c45500ae0 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SearchBar.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SearchBar.kt @@ -11,10 +11,13 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.* import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.Stable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusManager +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor @@ -47,6 +50,7 @@ fun SearchBar( ) { val keyboardController = LocalSoftwareKeyboardController.current val focusManager = LocalFocusManager.current + val focusRequester = remember { FocusRequester() } val interactionSource = remember { MutableInteractionSource() } BasicTextField( @@ -60,6 +64,7 @@ fun SearchBar( state.onActiveChange(false) } } + .focusRequester(focusRequester) .testTag(SelectCountryBottomSheetTestTags.SEARCH_BAR), enabled = enabled, value = state.query, @@ -92,6 +97,10 @@ fun SearchBar( ) }, ) + + LaunchedEffect(Unit) { + focusRequester.requestFocus() + } } @Suppress("LongParameterList") diff --git a/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/component/ChooseManagedTokensComponent.kt b/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/component/ChooseManagedTokensComponent.kt index d17cd2b862..c5d28df18c 100644 --- a/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/component/ChooseManagedTokensComponent.kt +++ b/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/component/ChooseManagedTokensComponent.kt @@ -13,11 +13,17 @@ interface ChooseManagedTokensComponent : ComposableContentComponent { val selectedCurrency: CryptoCurrency?, val source: Source, val showSendViaSwapNotification: Boolean, + val callback: ModelCallback? = null, ) enum class Source { SendViaSwap, } + interface ModelCallback { + fun onResult() + fun onBack() + } + interface Factory : ComponentFactory } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/choosetoken/DefaultChooseManagedTokensComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/choosetoken/DefaultChooseManagedTokensComponent.kt index 8e71b560bf..99206f1591 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/choosetoken/DefaultChooseManagedTokensComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/choosetoken/DefaultChooseManagedTokensComponent.kt @@ -70,7 +70,7 @@ internal class DefaultChooseManagedTokensComponent @AssistedInject constructor( cryptoCurrency = cryptoCurrency, shouldResetNavigation = params.selectedCurrency != null, ) - router.pop() + params.callback?.onResult() ?: router.pop() } }, ), diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/choosetoken/model/ChooseManagedTokensModel.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/choosetoken/model/ChooseManagedTokensModel.kt index fe98829fd5..53b4245820 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/choosetoken/model/ChooseManagedTokensModel.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/choosetoken/model/ChooseManagedTokensModel.kt @@ -89,13 +89,13 @@ internal class ChooseManagedTokensModel @Inject constructor( return ChooseManagedTokenUM( notificationUM = getNotification(), readContent = ManageTokensUM.ReadContent( - popBack = router::pop, + popBack = { params.callback?.onBack() ?: router.pop() }, isInitialBatchLoading = true, isNextBatchLoading = false, items = getLoadingItems(), topBar = ManageTokensTopBarUM.ReadContent( title = resourceReference(R.string.common_choose_token), - onBackButtonClick = router::pop, + onBackButtonClick = { params.callback?.onBack() ?: router.pop() }, ), search = SearchBarUM( placeholderText = resourceReference(R.string.common_search), diff --git a/features/send-v2/impl/build.gradle.kts b/features/send-v2/impl/build.gradle.kts index ef4d10fe2d..9d3830f696 100644 --- a/features/send-v2/impl/build.gradle.kts +++ b/features/send-v2/impl/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(projects.features.txhistory.api) implementation(projects.features.nft.api) implementation(projects.features.swapV2.api) + implementation(projects.features.manageTokens.api) /** Libs */ implementation(projects.libs.crypto) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/DefaultSendEntryPointComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/DefaultSendEntryPointComponent.kt index 4d66bc37aa..0985a678fb 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/DefaultSendEntryPointComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/DefaultSendEntryPointComponent.kt @@ -1,18 +1,29 @@ package com.tangem.features.send.v2.entrypoint -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.extensions.compose.stack.Children +import com.arkivanov.decompose.extensions.compose.stack.animation.fade +import com.arkivanov.decompose.extensions.compose.stack.animation.plus +import com.arkivanov.decompose.extensions.compose.stack.animation.slide +import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation +import com.arkivanov.decompose.extensions.compose.subscribeAsState +import com.arkivanov.decompose.router.stack.StackNavigation +import com.arkivanov.decompose.router.stack.childStack +import com.arkivanov.decompose.router.stack.pop import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.child +import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.decompose.navigation.inner.InnerRouter +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.features.managetokens.component.ChooseManagedTokensComponent import com.tangem.features.send.v2.api.SendComponent import com.tangem.features.send.v2.api.SendEntryPointComponent -import com.tangem.features.send.v2.entrypoint.model.SendEntryPoint import com.tangem.features.send.v2.entrypoint.model.SendEntryPointModel import com.tangem.features.swap.v2.api.SendWithSwapComponent import dagger.assisted.Assisted @@ -24,9 +35,17 @@ internal class DefaultSendEntryPointComponent @AssistedInject constructor( @Assisted private val params: SendEntryPointComponent.Params, sendWithSwapComponentFactory: SendWithSwapComponent.Factory, sendComponentFactory: SendComponent.Factory, + private val chooseManagedTokensComponentFactory: ChooseManagedTokensComponent.Factory, ) : SendEntryPointComponent, AppComponentContext by appComponentContext { - private val model: SendEntryPointModel = getOrCreateModel(params = params) + private val stackNavigation = StackNavigation() + + private val innerRouter = InnerRouter( + stackNavigation = stackNavigation, + popCallback = { onChildBack() }, + ) + + private val model: SendEntryPointModel = getOrCreateModel(params = params, router = innerRouter) private val sendWithSwapComponent = sendWithSwapComponentFactory.create( context = child("sendEntrySendWithSwap"), @@ -46,18 +65,76 @@ internal class DefaultSendEntryPointComponent @AssistedInject constructor( ), ) + private val childStack = childStack( + key = "sendEntryStack", + source = stackNavigation, + serializer = null, + initialConfiguration = SendEntryRoute.Send, + handleBackButton = true, + childFactory = { configuration, factoryContext -> + getChildComponent( + configuration = configuration, + factoryContext = childByContext( + componentContext = factoryContext, + router = innerRouter, + ), + ) + }, + ) + @Composable override fun Content(modifier: Modifier) { - val sendEntryState by model.sendEntryPointState.collectAsStateWithLifecycle() + val childStackValue by childStack.subscribeAsState() - sendComponent.Content(modifier) + Children( + stack = childStackValue, + animation = stackAnimation { child -> + when (child.configuration) { + SendEntryRoute.Send, + SendEntryRoute.SendWithSwap, + -> fade() + is SendEntryRoute.ChooseToken -> slide(orientation = Orientation.Vertical) + fade() + } + }, + ) { child -> + child.instance.Content(modifier.fillMaxSize()) + } + } - AnimatedVisibility( - visible = sendEntryState == SendEntryPoint.SendWithSwap, - enter = fadeIn(), - exit = fadeOut(), - ) { - sendWithSwapComponent.Content(modifier) + private fun getChildComponent( + configuration: SendEntryRoute, + factoryContext: AppComponentContext, + ): ComposableContentComponent = when (configuration) { + is SendEntryRoute.ChooseToken -> getManagedTokensComponent( + componentContext = factoryContext, + showSendViaSwapNotification = configuration.showSendViaSwapNotification, + ) + SendEntryRoute.Send -> sendComponent + SendEntryRoute.SendWithSwap -> sendWithSwapComponent + } + + private fun getManagedTokensComponent( + componentContext: ComponentContext, + showSendViaSwapNotification: Boolean, + ): ChooseManagedTokensComponent { + return chooseManagedTokensComponentFactory.create( + context = childByContext(componentContext), + params = ChooseManagedTokensComponent.Params( + userWalletId = params.userWalletId, + initialCurrency = params.cryptoCurrency, + source = ChooseManagedTokensComponent.Source.SendViaSwap, + selectedCurrency = null, + showSendViaSwapNotification = showSendViaSwapNotification, + callback = model, + ), + ) + } + + private fun onChildBack() { + if (childStack.value.backStack.isEmpty()) { + router.pop() + } else { + stackNavigation.pop() } } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/SendEntryRoute.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/SendEntryRoute.kt new file mode 100644 index 0000000000..470cfb6215 --- /dev/null +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/SendEntryRoute.kt @@ -0,0 +1,11 @@ +package com.tangem.features.send.v2.entrypoint + +import com.tangem.core.decompose.navigation.Route + +internal sealed class SendEntryRoute : Route { + data object Send : SendEntryRoute() + data object SendWithSwap : SendEntryRoute() + data class ChooseToken( + val showSendViaSwapNotification: Boolean, + ) : SendEntryRoute() +} \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/model/SendEntryPointModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/model/SendEntryPointModel.kt index 3bdbfcaf8d..984330ba7d 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/model/SendEntryPointModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/model/SendEntryPointModel.kt @@ -1,90 +1,74 @@ package com.tangem.features.send.v2.entrypoint.model -import com.tangem.common.routing.AppRoute -import com.tangem.common.routing.AppRouter import com.tangem.common.ui.notifications.NotificationId import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model -import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.core.decompose.navigation.Router import com.tangem.domain.notifications.ShouldShowNotificationUseCase +import com.tangem.features.managetokens.component.ChooseManagedTokensComponent import com.tangem.features.send.v2.api.SendComponent -import com.tangem.features.send.v2.api.SendEntryPointComponent +import com.tangem.features.send.v2.entrypoint.SendEntryRoute import com.tangem.features.send.v2.subcomponents.amount.SendAmountUpdateTrigger import com.tangem.features.swap.v2.api.SendWithSwapComponent -import com.tangem.features.swap.v2.api.choosetoken.SwapChooseTokenNetworkListener import com.tangem.features.swap.v2.api.subcomponents.SwapAmountUpdateTrigger import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import com.tangem.utils.coroutines.JobHolder -import com.tangem.utils.coroutines.saveIn import jakarta.inject.Inject -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.delay import kotlinx.coroutines.launch @Suppress("LongParameterList") @ModelScoped internal class SendEntryPointModel @Inject constructor( - paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, - val appRouter: AppRouter, - private val swapChooseTokenNetworkListener: SwapChooseTokenNetworkListener, + val router: Router, private val sendAmountUpdateTrigger: SendAmountUpdateTrigger, private val swapAmountUpdateTrigger: SwapAmountUpdateTrigger, private val shouldShowNotificationUseCase: ShouldShowNotificationUseCase, -) : Model(), SendComponent.ModelCallback, SendWithSwapComponent.ModelCallback { +) : Model(), + SendComponent.ModelCallback, + SendWithSwapComponent.ModelCallback, + ChooseManagedTokensComponent.ModelCallback { - private val params: SendEntryPointComponent.Params = paramsContainer.require() - - val sendEntryPointState: StateFlow - field = MutableStateFlow(SendEntryPoint.SendVanilla) - - private var swapChooseTokenListenerJobHolder = JobHolder() + private var lastSavedAmount = "" override fun onConvertToAnotherToken(lastAmount: String) { + lastSavedAmount = lastAmount modelScope.launch { val showSendViaSwapNotification = shouldShowNotificationUseCase( NotificationId.SendViaSwapTokenSelectorNotification.key, ) - appRouter.push( - AppRoute.ChooseManagedTokens( - userWalletId = params.userWalletId, - initialCurrency = params.cryptoCurrency, - selectedCurrency = null, - source = AppRoute.ChooseManagedTokens.Source.SendViaSwap, + router.push( + SendEntryRoute.ChooseToken( showSendViaSwapNotification = showSendViaSwapNotification, ), ) - observeChooseSelectToken(lastAmount) } } override fun onCloseSwap(lastAmount: String) { + lastSavedAmount = lastAmount modelScope.launch { if (lastAmount.isNotBlank()) { sendAmountUpdateTrigger.triggerUpdateAmount(lastAmount) } - triggerScreenUpdate(SendEntryPoint.SendVanilla) + router.replaceAll(SendEntryRoute.Send) } } - private fun observeChooseSelectToken(lastAmount: String) { - swapChooseTokenNetworkListener.swapChooseTokenNetworkResultFlow - .onEach { currency -> - if (lastAmount.isNotBlank()) { - swapAmountUpdateTrigger.triggerUpdateAmount(lastAmount) - } - triggerScreenUpdate(SendEntryPoint.SendWithSwap) + @Suppress("MagicNumber") + override fun onResult() { + modelScope.launch { + if (lastSavedAmount.isNotBlank()) { + swapAmountUpdateTrigger.triggerUpdateAmount(lastSavedAmount) } - .launchIn(modelScope) - .saveIn(swapChooseTokenListenerJobHolder) + // Workaround in order to execute correct exit animation on SendEntryRoute.ChooseToken + router.pop() + delay(10L) + router.replaceAll(SendEntryRoute.SendWithSwap) + } } - private fun triggerScreenUpdate(entry: SendEntryPoint) { - swapChooseTokenListenerJobHolder.cancel() - sendEntryPointState.update { entry } + override fun onBack() { + router.pop() } -} - -enum class SendEntryPoint { - SendVanilla, - SendWithSwap, } \ No newline at end of file From e6820c0898f2d67f4e9946ee0d59e4874cd9db6b Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 6 Aug 2025 15:45:04 +0500 Subject: [PATCH 52/53] Updated on 2026-08-14 --- features/send-v2/api/build.gradle.kts | 1 + .../analytics/CommonSendAnalyticEvents.kt | 22 +++++++++++++++---- .../CommonSendAmountAnalyticEvents.kt} | 10 ++++----- .../analytics/CommonSendFeeAnalyticEvents.kt} | 10 ++++----- .../send/v2/send/DefaultSendComponent.kt | 2 +- .../v2/send/analytics/SendAnalyticEvents.kt | 2 +- .../v2/send/confirm/model/SendConfirmModel.kt | 4 ++-- ...endConfirmationNotificationsTransformer.kt | 4 ++-- ...dConfirmationNotificationsTransformerV2.kt | 4 ++-- .../features/send/v2/send/model/SendModel.kt | 4 ++-- .../success/model/SendConfirmSuccessModel.kt | 9 +++++--- .../v2/sendnft/DefaultNFTSendComponent.kt | 2 +- .../analytics/NFTSendAnalyticEvents.kt | 2 +- .../confirm/model/NFTSendConfirmModel.kt | 6 ++--- ...endConfirmationNotificationsTransformer.kt | 4 ++-- .../amount/model/SendAmountModel.kt | 8 +++---- .../destination/model/SendDestinationModel.kt | 4 ++-- .../subcomponents/fee/model/SendFeeModel.kt | 15 +++++++------ 18 files changed, 66 insertions(+), 47 deletions(-) rename features/send-v2/{impl/src/main/java/com/tangem/features/send/v2/common => api/src/main/java/com/tangem/features/send/v2/api}/analytics/CommonSendAnalyticEvents.kt (87%) rename features/send-v2/{impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/analytics/SendAmountAnalyticEvents.kt => api/src/main/java/com/tangem/features/send/v2/api/subcomponents/amount/analytics/CommonSendAmountAnalyticEvents.kt} (70%) rename features/send-v2/{impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/analytics/SendFeeAnalyticEvents.kt => api/src/main/java/com/tangem/features/send/v2/api/subcomponents/feeSelector/analytics/CommonSendFeeAnalyticEvents.kt} (81%) diff --git a/features/send-v2/api/build.gradle.kts b/features/send-v2/api/build.gradle.kts index c1a6e00fc9..454fcee782 100644 --- a/features/send-v2/api/build.gradle.kts +++ b/features/send-v2/api/build.gradle.kts @@ -16,6 +16,7 @@ dependencies { /** Core */ implementation(projects.core.decompose) implementation(projects.core.ui) + implementation(projects.core.analytics.models) /** Common */ implementation(projects.common.ui) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/analytics/CommonSendAnalyticEvents.kt b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/analytics/CommonSendAnalyticEvents.kt similarity index 87% rename from features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/analytics/CommonSendAnalyticEvents.kt rename to features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/analytics/CommonSendAnalyticEvents.kt index acaf68a42f..7b355b9c83 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/analytics/CommonSendAnalyticEvents.kt +++ b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/analytics/CommonSendAnalyticEvents.kt @@ -1,4 +1,4 @@ -package com.tangem.features.send.v2.common.analytics +package com.tangem.features.send.v2.api.analytics import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam.Key.BLOCKCHAIN @@ -6,9 +6,9 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.SOURCE import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM /** - * Send screen analytics + * Send analytics */ -internal sealed class CommonSendAnalyticEvents( +sealed class CommonSendAnalyticEvents( category: String, event: String, params: Map = mapOf(), @@ -114,12 +114,26 @@ internal sealed class CommonSendAnalyticEvents( ), ) + /** Token chosen to convert with sending */ + data class TokenChosen( + val categoryName: String, + val token: String, + val blockchain: String, + ) : CommonSendAnalyticEvents( + category = categoryName, + event = "Token chosen", + params = mapOf( + TOKEN_PARAM to token, + BLOCKCHAIN to blockchain, + ), + ) + companion object { const val SEND_CATEGORY = "Token / Send" const val NFT_SEND_CATEGORY = "NFT" } - internal enum class SendScreenSource { + enum class SendScreenSource { Address, Amount, Fee, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/analytics/SendAmountAnalyticEvents.kt b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/amount/analytics/CommonSendAmountAnalyticEvents.kt similarity index 70% rename from features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/analytics/SendAmountAnalyticEvents.kt rename to features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/amount/analytics/CommonSendAmountAnalyticEvents.kt index 97d65a88e9..55d91be4b5 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/analytics/SendAmountAnalyticEvents.kt +++ b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/amount/analytics/CommonSendAmountAnalyticEvents.kt @@ -1,9 +1,9 @@ -package com.tangem.features.send.v2.subcomponents.amount.analytics +package com.tangem.features.send.v2.api.subcomponents.amount.analytics import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam.Key.TYPE -internal sealed class SendAmountAnalyticEvents( +sealed class CommonSendAmountAnalyticEvents( category: String, event: String, params: Map = mapOf(), @@ -13,7 +13,7 @@ internal sealed class SendAmountAnalyticEvents( data class SelectedCurrency( val categoryName: String, val type: SelectedCurrencyType, - ) : SendAmountAnalyticEvents( + ) : CommonSendAmountAnalyticEvents( category = categoryName, event = "Selected Currency", params = mapOf(TYPE to type.value), @@ -22,9 +22,9 @@ internal sealed class SendAmountAnalyticEvents( /** Max amount button clicked */ data class MaxAmountButtonClicked( val categoryName: String, - ) : SendAmountAnalyticEvents(category = categoryName, event = "Max Amount Taped") + ) : CommonSendAmountAnalyticEvents(category = categoryName, event = "Max Amount Taped") - internal enum class SelectedCurrencyType(val value: String) { + enum class SelectedCurrencyType(val value: String) { Token("Token"), AppCurrency("App Currency"), } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/analytics/SendFeeAnalyticEvents.kt b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/feeSelector/analytics/CommonSendFeeAnalyticEvents.kt similarity index 81% rename from features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/analytics/SendFeeAnalyticEvents.kt rename to features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/feeSelector/analytics/CommonSendFeeAnalyticEvents.kt index c65789aaa4..579c005570 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/analytics/SendFeeAnalyticEvents.kt +++ b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/feeSelector/analytics/CommonSendFeeAnalyticEvents.kt @@ -1,9 +1,9 @@ -package com.tangem.features.send.v2.subcomponents.fee.analytics +package com.tangem.features.send.v2.api.subcomponents.feeSelector.analytics import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam -internal sealed class SendFeeAnalyticEvents( +sealed class CommonSendFeeAnalyticEvents( category: String, event: String, params: Map = mapOf(), @@ -15,7 +15,7 @@ internal sealed class SendFeeAnalyticEvents( data class SelectedFee( override val categoryName: String, val feeType: AnalyticsParam.FeeType, - ) : SendFeeAnalyticEvents( + ) : CommonSendFeeAnalyticEvents( category = categoryName, event = "Fee Selected", params = mapOf("Fee Type" to feeType.value), @@ -24,7 +24,7 @@ internal sealed class SendFeeAnalyticEvents( /** Custom fee selected */ data class CustomFeeButtonClicked( override val categoryName: String, - ) : SendFeeAnalyticEvents( + ) : CommonSendFeeAnalyticEvents( category = categoryName, event = "Custom Fee Clicked", ) @@ -32,7 +32,7 @@ internal sealed class SendFeeAnalyticEvents( /** Custom fee edited */ data class GasPriceInserter( override val categoryName: String, - ) : SendFeeAnalyticEvents( + ) : CommonSendFeeAnalyticEvents( category = categoryName, event = "Gas Price Inserted", ) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt index 9cc15c97bb..013178fc55 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt @@ -24,10 +24,10 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.send.v2.api.FeeSelectorBlockComponent import com.tangem.features.send.v2.api.SendComponent +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM import com.tangem.features.send.v2.common.CommonSendRoute -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents import com.tangem.features.send.v2.common.ui.SendContent import com.tangem.features.send.v2.common.ui.state.ConfirmUM import com.tangem.features.send.v2.impl.R diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticEvents.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticEvents.kt index 30a9234904..120a1808f8 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticEvents.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticEvents.kt @@ -7,7 +7,7 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.FEE_TYPE import com.tangem.core.analytics.models.AnalyticsParam.Key.NONCE import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM import com.tangem.core.ui.extensions.capitalize -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents /** * Send screen analytics diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt index 4f8e762e40..343cd6d29e 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt @@ -38,6 +38,8 @@ import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase import com.tangem.domain.utils.convertToSdkAmount import com.tangem.features.send.v2.api.SendNotificationsComponent import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents.SendScreenSource import com.tangem.features.send.v2.api.callbacks.FeeSelectorModelCallback import com.tangem.features.send.v2.api.entity.FeeNonce import com.tangem.features.send.v2.api.params.FeeSelectorParams.FeeStateConfiguration @@ -48,8 +50,6 @@ import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificat import com.tangem.features.send.v2.common.CommonSendRoute import com.tangem.features.send.v2.common.SendBalanceUpdater import com.tangem.features.send.v2.common.SendConfirmAlertFactory -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents.SendScreenSource import com.tangem.features.send.v2.common.ui.state.ConfirmUM import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.send.analytics.SendAnalyticHelper diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/transformers/SendConfirmationNotificationsTransformer.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/transformers/SendConfirmationNotificationsTransformer.kt index 8e8987f2f8..98b863e56e 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/transformers/SendConfirmationNotificationsTransformer.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/transformers/SendConfirmationNotificationsTransformer.kt @@ -14,10 +14,10 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents -import com.tangem.features.send.v2.common.ui.state.ConfirmUM +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents import com.tangem.features.send.v2.api.utils.formatFooterFiatFee import com.tangem.features.send.v2.api.utils.getTronTokenFeeSendingText +import com.tangem.features.send.v2.common.ui.state.ConfirmUM import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.subcomponents.fee.model.checkIfFeeTooHigh import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/transformers/SendConfirmationNotificationsTransformerV2.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/transformers/SendConfirmationNotificationsTransformerV2.kt index a5935435c3..22e99488fc 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/transformers/SendConfirmationNotificationsTransformerV2.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/transformers/SendConfirmationNotificationsTransformerV2.kt @@ -12,12 +12,12 @@ import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents -import com.tangem.features.send.v2.common.ui.state.ConfirmUM import com.tangem.features.send.v2.api.utils.formatFooterFiatFee import com.tangem.features.send.v2.api.utils.getTronTokenFeeSendingText +import com.tangem.features.send.v2.common.ui.state.ConfirmUM import com.tangem.features.send.v2.impl.R import com.tangem.utils.transformer.Transformer import kotlinx.collections.immutable.toPersistentList diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt index de07147e93..7af880d59a 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt @@ -41,6 +41,8 @@ import com.tangem.domain.wallets.models.GetUserWalletError import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.features.send.v2.api.SendComponent import com.tangem.features.send.v2.api.SendFeatureToggles +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents.SendScreenSource import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.send.v2.api.entity.PredefinedValues import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponent @@ -48,8 +50,6 @@ import com.tangem.features.send.v2.api.subcomponents.destination.entity.Destinat import com.tangem.features.send.v2.common.CommonSendRoute import com.tangem.features.send.v2.common.CommonSendRoute.* import com.tangem.features.send.v2.common.SendConfirmAlertFactory -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents.SendScreenSource import com.tangem.features.send.v2.common.ui.state.ConfirmUM import com.tangem.features.send.v2.send.confirm.SendConfirmComponent import com.tangem.features.send.v2.send.success.SendConfirmSuccessComponent diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/success/model/SendConfirmSuccessModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/success/model/SendConfirmSuccessModel.kt index 663d111b4f..80bdafb04f 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/success/model/SendConfirmSuccessModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/success/model/SendConfirmSuccessModel.kt @@ -12,14 +12,17 @@ import com.tangem.core.navigation.share.ShareManager import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents.SendScreenSource import com.tangem.features.send.v2.common.CommonSendRoute -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents.SendScreenSource import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.send.success.SendConfirmSuccessComponent import com.tangem.features.send.v2.send.ui.state.SendUM import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import javax.inject.Inject @Stable diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/DefaultNFTSendComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/DefaultNFTSendComponent.kt index 60fe6511a2..c4ecdee6e2 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/DefaultNFTSendComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/DefaultNFTSendComponent.kt @@ -20,10 +20,10 @@ import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.extensions.resourceReference import com.tangem.features.nft.component.NFTDetailsBlockComponent import com.tangem.features.send.v2.api.NFTSendComponent +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM import com.tangem.features.send.v2.common.CommonSendRoute -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents import com.tangem.features.send.v2.common.ui.SendContent import com.tangem.features.send.v2.common.ui.state.ConfirmUM import com.tangem.features.send.v2.impl.R diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/analytics/NFTSendAnalyticEvents.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/analytics/NFTSendAnalyticEvents.kt index de2ae7cbb4..f542b5f217 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/analytics/NFTSendAnalyticEvents.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/analytics/NFTSendAnalyticEvents.kt @@ -7,7 +7,7 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.FEE_TYPE import com.tangem.core.analytics.models.AnalyticsParam.Key.NONCE import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM import com.tangem.core.ui.extensions.capitalize -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents /** * Send screen analytics diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt index 734bc45601..ae95518260 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt @@ -22,23 +22,23 @@ import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.BlockchainErrorInfo import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.settings.IsSendTapHelpEnabledUseCase import com.tangem.domain.settings.NeverShowTapHelpUseCase import com.tangem.domain.transaction.usecase.CreateNFTTransferTransactionUseCase import com.tangem.domain.transaction.usecase.SendTransactionUseCase import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase -import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.features.nft.entity.NFTSendSuccessTrigger import com.tangem.features.send.v2.api.SendNotificationsComponent import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents.SendScreenSource import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateListener import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateTrigger import com.tangem.features.send.v2.common.CommonSendRoute import com.tangem.features.send.v2.common.SendBalanceUpdater import com.tangem.features.send.v2.common.SendConfirmAlertFactory -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents.SendScreenSource import com.tangem.features.send.v2.common.ui.state.ConfirmUM import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.sendnft.analytics.NFTSendAnalyticHelper diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/transformers/NFTSendConfirmationNotificationsTransformer.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/transformers/NFTSendConfirmationNotificationsTransformer.kt index fda1601aa9..9232bc21da 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/transformers/NFTSendConfirmationNotificationsTransformer.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/transformers/NFTSendConfirmationNotificationsTransformer.kt @@ -10,10 +10,10 @@ import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents -import com.tangem.features.send.v2.common.ui.state.ConfirmUM +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents import com.tangem.features.send.v2.api.utils.formatFooterFiatFee import com.tangem.features.send.v2.api.utils.getTronTokenFeeSendingText +import com.tangem.features.send.v2.common.ui.state.ConfirmUM import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.subcomponents.fee.model.checkIfFeeTooHigh import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt index 28b1e1cc38..72f75f5424 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt @@ -30,14 +30,14 @@ import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.features.send.v2.api.SendFeatureToggles import com.tangem.features.send.v2.api.entity.PredefinedValues +import com.tangem.features.send.v2.api.subcomponents.amount.analytics.CommonSendAmountAnalyticEvents +import com.tangem.features.send.v2.api.subcomponents.amount.analytics.CommonSendAmountAnalyticEvents.SelectedCurrencyType import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger import com.tangem.features.send.v2.common.CommonSendRoute import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceListener import com.tangem.features.send.v2.subcomponents.amount.SendAmountUpdateListener -import com.tangem.features.send.v2.subcomponents.amount.analytics.SendAmountAnalyticEvents -import com.tangem.features.send.v2.subcomponents.amount.analytics.SendAmountAnalyticEvents.SelectedCurrencyType import com.tangem.features.send.v2.subcomponents.fee.SendFeeData import com.tangem.features.send.v2.subcomponents.fee.SendFeeReloadTrigger import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -218,7 +218,7 @@ internal class SendAmountModel @Inject constructor( ), ) analyticsEventHandler.send( - SendAmountAnalyticEvents.MaxAmountButtonClicked(categoryName = analyticsCategoryName), + CommonSendAmountAnalyticEvents.MaxAmountButtonClicked(categoryName = analyticsCategoryName), ) } @@ -229,7 +229,7 @@ internal class SendAmountModel @Inject constructor( override fun onAmountNext() { (uiState.value as? AmountState.Data)?.amountTextField?.isFiatValue?.let { isFiatSelected -> analyticsEventHandler.send( - SendAmountAnalyticEvents.SelectedCurrency( + CommonSendAmountAnalyticEvents.SelectedCurrency( categoryName = analyticsCategoryName, type = if (isFiatSelected) { SelectedCurrencyType.AppCurrency diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt index db3ba80d27..b457df2b9a 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt @@ -26,12 +26,12 @@ import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase import com.tangem.domain.txhistory.usecase.GetFixedTxHistoryItemsUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.features.send.v2.api.SendFeatureToggles +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents.SendScreenSource import com.tangem.features.send.v2.api.entity.PredefinedValues import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams.DestinationBlockParams import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents.SendScreenSource import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.subcomponents.destination.analytics.EnterAddressSource import com.tangem.features.send.v2.subcomponents.destination.analytics.SendDestinationAnalyticEvents diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/SendFeeModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/SendFeeModel.kt index e1f722720f..5cad0c8511 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/SendFeeModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/SendFeeModel.kt @@ -11,14 +11,13 @@ import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents.NonceInserted +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents +import com.tangem.features.send.v2.api.subcomponents.feeSelector.analytics.CommonSendFeeAnalyticEvents import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.subcomponents.fee.SendFeeCheckReloadListener import com.tangem.features.send.v2.subcomponents.fee.SendFeeCheckReloadTrigger import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponentParams import com.tangem.features.send.v2.subcomponents.fee.SendFeeReloadListener -import com.tangem.features.send.v2.subcomponents.fee.analytics.SendFeeAnalyticEvents -import com.tangem.features.send.v2.subcomponents.fee.analytics.SendFeeAnalyticEvents.GasPriceInserter import com.tangem.features.send.v2.subcomponents.fee.model.transformers.* import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType @@ -112,7 +111,7 @@ internal class SendFeeModel @Inject constructor( updateFeeNotifications() if (feeType == FeeType.Custom) { analyticsEventHandler.send( - SendFeeAnalyticEvents.CustomFeeButtonClicked(categoryName = analyticsCategoryName), + CommonSendFeeAnalyticEvents.CustomFeeButtonClicked(categoryName = analyticsCategoryName), ) } } @@ -155,10 +154,12 @@ internal class SendFeeModel @Inject constructor( val isCustomFeeEdited = feeSelectorUM.selectedFee?.amount?.value != feeSelectorUM.fees.normal.amount.value if (feeSelectorUM.selectedType == FeeType.Custom && isCustomFeeEdited) { - analyticsEventHandler.send(GasPriceInserter(categoryName = analyticsCategoryName)) + analyticsEventHandler.send( + CommonSendFeeAnalyticEvents.GasPriceInserter(categoryName = analyticsCategoryName), + ) } analyticsEventHandler.send( - SendFeeAnalyticEvents.SelectedFee( + CommonSendFeeAnalyticEvents.SelectedFee( categoryName = analyticsCategoryName, feeType = feeSelectorUM.selectedType.toAnalyticType(feeSelectorUM), ), @@ -166,7 +167,7 @@ internal class SendFeeModel @Inject constructor( if (feeSelectorUM.nonce != null) { analyticsEventHandler.send( - NonceInserted( + CommonSendAnalyticEvents.NonceInserted( categoryName = analyticsCategoryName, token = cryptoCurrencyStatus.currency.symbol, blockchain = cryptoCurrencyStatus.currency.network.name, From 3a641b3edc225a5efa278351905736cf11bd13b2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 6 Aug 2025 14:21:00 +0300 Subject: [PATCH 53/53] Updated on 2026-08-14 --- .../kotlin/com/tangem/common/BaseTestCase.kt | 11 ++++++ .../tangem/common/constants/TestConstants.kt | 5 +++ .../common/extensions/BaseTestCaseExt.kt | 14 ++++--- .../kotlin/com/tangem/tests/BuyTokenTest.kt | 37 ++++++++++++------- .../kotlin/com/tangem/tests/HideTokenTest.kt | 3 +- .../com/tangem/tests/OrganizeTokensTest.kt | 37 +++++++++++++++---- .../kotlin/com/tangem/tests/StoriesTest.kt | 5 +-- .../com/tangem/tests/TermsOfServiceTest.kt | 6 +-- app/src/main/assets/tangem-app-config | 2 +- tangem-android-tools | 2 +- 10 files changed, 86 insertions(+), 36 deletions(-) create mode 100644 app/src/androidTest/kotlin/com/tangem/common/constants/TestConstants.kt diff --git a/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt b/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt index d42aeddab4..57c7c6cd46 100644 --- a/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt +++ b/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt @@ -15,8 +15,11 @@ import com.kaspersky.kaspresso.testcases.api.testcase.TestCase import com.tangem.common.allure.FailedStepScreenshotInterceptor import com.tangem.common.rules.ApiEnvironmentRule import com.tangem.datasource.api.common.config.managers.ApiConfigsManager +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.tap.MainActivity import dagger.hilt.android.testing.HiltAndroidRule +import kotlinx.coroutines.runBlocking import org.junit.Rule import org.junit.rules.RuleChain import org.junit.rules.TestRule @@ -40,6 +43,9 @@ abstract class BaseTestCase : TestCase( @Inject lateinit var apiConfigsManager: ApiConfigsManager + @Inject + lateinit var appPreferencesStore: AppPreferencesStore + private val hiltRule = HiltAndroidRule(this) private val apiEnvironmentRule = ApiEnvironmentRule() private val permissionRule = GrantPermissionRule.grant( @@ -73,6 +79,11 @@ abstract class BaseTestCase : TestCase( additionalAfterSection: () -> Unit = {}, ) = before { hiltRule.inject() + runBlocking { + appPreferencesStore.editData { mutablePreferences -> mutablePreferences.set( + key = PreferencesKeys.NOTIFICATIONS_USER_ALLOW_SEND_ADDRESSES_KEY, value = false + ) } + } apiEnvironmentRule.setup(apiConfigsManager) ActivityScenario.launch(MainActivity::class.java) Intents.init() diff --git a/app/src/androidTest/kotlin/com/tangem/common/constants/TestConstants.kt b/app/src/androidTest/kotlin/com/tangem/common/constants/TestConstants.kt new file mode 100644 index 0000000000..29774a64fd --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/common/constants/TestConstants.kt @@ -0,0 +1,5 @@ +package com.tangem.common.constants + +object TestConstants { + const val TOTAL_BALANCE = "$3,299.18" +} \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/common/extensions/BaseTestCaseExt.kt b/app/src/androidTest/kotlin/com/tangem/common/extensions/BaseTestCaseExt.kt index bff5addff5..72637225d9 100644 --- a/app/src/androidTest/kotlin/com/tangem/common/extensions/BaseTestCaseExt.kt +++ b/app/src/androidTest/kotlin/com/tangem/common/extensions/BaseTestCaseExt.kt @@ -2,14 +2,16 @@ package com.tangem.common.extensions import com.tangem.common.BaseTestCase -fun BaseTestCase.swipeToCloseApp() { - +fun BaseTestCase.swipeUp( + startHeightRatio: Float = 0.8f, + endHeightRatio: Float = 0.03f, + steps: Int = 15 +) { device.uiDevice.swipe( device.uiDevice.displayWidth / 2, - device.uiDevice.displayHeight / 2, + (device.uiDevice.displayHeight * startHeightRatio).toInt(), device.uiDevice.displayWidth / 2, - device.uiDevice.displayHeight / 30, - 15 + (device.uiDevice.displayHeight * endHeightRatio).toInt(), + steps ) - } \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/tests/BuyTokenTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/BuyTokenTest.kt index 1c05fa58dc..fb942f0550 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/BuyTokenTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/BuyTokenTest.kt @@ -1,6 +1,7 @@ package com.tangem.tests import com.tangem.common.BaseTestCase +import com.tangem.common.constants.TestConstants.TOTAL_BALANCE import com.tangem.common.extensions.clickWithAssertion import com.tangem.common.utils.resetWireMockScenarios import com.tangem.common.utils.setWireMockScenarioState @@ -24,7 +25,7 @@ class BuyTokenTest : BaseTestCase() { } ).run { val tokenTitle = "Bitcoin" - val balance = "$184.85" + val balance = TOTAL_BALANCE resetWireMockScenarios() @@ -67,13 +68,15 @@ class BuyTokenTest : BaseTestCase() { fun validateCurrencySelectorTest() { setupHooks().run { val tokenTitle = "Polygon" - val balance = "$184.85" + val balance = TOTAL_BALANCE val popularFiatsTitle = "Popular Fiats" val otherCurrenciesTitle = "Other currencies" val australianDollar = "AUD" val fiatAmount = "1" val tokenAmount = "POL 488.24938338" + resetWireMockScenarios() + step("Open 'Main Screen'") { scenario(OpenMainScreenScenario(composeTestRule)) } @@ -155,11 +158,13 @@ class BuyTokenTest : BaseTestCase() { fun validateBuyTokenScreenTest() { setupHooks().run { val tokenTitle = "Polygon" - val balance = "$184.85" + val balance = TOTAL_BALANCE val euro = "EUR" val fiatAmount = "1" val tokenAmount = "POL 488.24938338" + resetWireMockScenarios() + step("Open 'Main Screen'") { scenario(OpenMainScreenScenario(composeTestRule)) } @@ -226,10 +231,12 @@ class BuyTokenTest : BaseTestCase() { fun validateResidenceSettingsScreenTest() { setupHooks().run { val tokenTitle = "Polygon" - val balance = "$184.85" + val balance = TOTAL_BALANCE val country = "Albania" val unavailableCountry = "Lebanon" + resetWireMockScenarios() + step("Open 'Main Screen'") { scenario(OpenMainScreenScenario(composeTestRule)) } @@ -305,7 +312,7 @@ class BuyTokenTest : BaseTestCase() { fun validateProvidersScreenTest() { setupHooks().run { val tokenTitle = "Polygon" - val balance = "$184.85" + val balance = TOTAL_BALANCE val paymentMethod = "Card" val fiatAmount = "1" val providerNameMercuryo = "Mercuryo" @@ -314,6 +321,8 @@ class BuyTokenTest : BaseTestCase() { val bestRate = "Best rate" val rate = "-0.00%" + resetWireMockScenarios() + step("Open 'Main Screen'") { scenario(OpenMainScreenScenario(composeTestRule)) } @@ -347,13 +356,6 @@ class BuyTokenTest : BaseTestCase() { step("Open 'Select Provider' bottom sheet") { onBuyTokenDetailsScreen { providerTitle.performClick() } } - step("Assert available provider name is displayed") { - onSelectProviderBottomSheet { - flakySafely(timeoutMs = 20_000) { - availableProviderItem.assertIsDisplayed() - } - } - } step("Assert unavailable provider name is displayed") { onSelectProviderBottomSheet { flakySafely(timeoutMs = 20_000) { @@ -361,6 +363,13 @@ class BuyTokenTest : BaseTestCase() { } } } + step("Assert available provider name is displayed") { + onSelectProviderBottomSheet { + flakySafely(timeoutMs = 20_000) { + availableProviderItem.assertIsDisplayed() + } + } + } step("Click on 'Expand payment methods' button") { onSelectProviderBottomSheet { paymentMethodExpandButton.clickWithAssertion() } } @@ -407,13 +416,15 @@ class BuyTokenTest : BaseTestCase() { fun validatePaymentMethodScreenTest() { setupHooks().run { val tokenTitle = "Polygon" - val balance = "$184.85" + val balance = TOTAL_BALANCE val card = "Card" val googlePay = "Google Pay" val invoiceRevolutPay = "Invoice Revolut Pay" val sepa = "Sepa" val fiatAmount = "1" + resetWireMockScenarios() + step("Open 'Main Screen'") { scenario(OpenMainScreenScenario(composeTestRule)) } diff --git a/app/src/androidTest/kotlin/com/tangem/tests/HideTokenTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/HideTokenTest.kt index 6bbf318ec7..706bd497d5 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/HideTokenTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/HideTokenTest.kt @@ -1,6 +1,7 @@ package com.tangem.tests import com.tangem.common.BaseTestCase +import com.tangem.common.constants.TestConstants.TOTAL_BALANCE import com.tangem.common.extensions.clickWithAssertion import com.tangem.scenarios.OpenMainScreenScenario import com.tangem.screens.* @@ -17,7 +18,7 @@ class HideTokenTest : BaseTestCase() { @Test fun hideWalletTokenByHideButtonTest() { val tokenTitle = "Polygon" - val balance = "$184.85" + val balance = TOTAL_BALANCE setupHooks().run { step("Open 'Main Screen'") { scenario(OpenMainScreenScenario(composeTestRule)) diff --git a/app/src/androidTest/kotlin/com/tangem/tests/OrganizeTokensTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/OrganizeTokensTest.kt index 6d89ee640d..44cab70be3 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/OrganizeTokensTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/OrganizeTokensTest.kt @@ -2,7 +2,9 @@ package com.tangem.tests import androidx.compose.ui.test.onAllNodesWithText import com.tangem.common.BaseTestCase +import com.tangem.common.constants.TestConstants.TOTAL_BALANCE import com.tangem.common.extensions.clickWithAssertion +import com.tangem.common.extensions.swipeUp import com.tangem.scenarios.OpenMainScreenScenario import com.tangem.screens.onMainScreen import com.tangem.screens.onOrganizeTokensScreen @@ -27,6 +29,9 @@ class OrganizeTokensTest : BaseTestCase() { step("Click on 'Synchronize addresses' button" ) { onMainScreen { synchronizeAddressesButton.clickWithAssertion() } } + step("Swipe to 'Organize tokens' button") { + swipeUp() + } step("Click 'Organize tokens' button") { onMainScreen { organizeTokensButton().clickWithAssertion() } } @@ -48,6 +53,9 @@ class OrganizeTokensTest : BaseTestCase() { step("Assert tokens were grouped on 'Main screen'") { onMainScreen { tokenNetworkGroupTitle(tokenNetwork).assertIsDisplayed() } } + step("Swipe to 'Organize tokens' button") { + swipeUp() + } step("Click 'Organize tokens' button") { onMainScreen { organizeTokensButton().clickWithAssertion() } } @@ -79,7 +87,7 @@ class OrganizeTokensTest : BaseTestCase() { setupHooks().run { val ethereumTitle = "Ethereum" val bitcoinTitle = "Bitcoin" - val balance = "$184.85" + val balance = TOTAL_BALANCE step("Open 'Main Screen'") { scenario(OpenMainScreenScenario(composeTestRule)) } @@ -95,6 +103,9 @@ class OrganizeTokensTest : BaseTestCase() { tokenWithTitleAndPosition(ethereumTitle, 1).assertIsDisplayed() } } + step("Swipe to 'Organize tokens' button") { + swipeUp() + } step("Click 'Organize tokens' button") { onMainScreen { organizeTokensButton().clickWithAssertion() } } @@ -127,6 +138,9 @@ class OrganizeTokensTest : BaseTestCase() { tokenWithTitleAndPosition(ethereumTitle, 1).assertIsDisplayed() } } + step("Swipe to 'Organize tokens' button") { + swipeUp() + } step("Click 'Organize tokens' button") { onMainScreen { organizeTokensButton().clickWithAssertion() } } @@ -158,7 +172,8 @@ class OrganizeTokensTest : BaseTestCase() { val ethereumTitle = "Ethereum" val bitcoinTitle = "Bitcoin" val polygonTitle = "Polygon" - val balance = "$184.85" + val polExMaticTitle = "POL (ex-MATIC)" + val balance = TOTAL_BALANCE step("Open 'Main Screen'") { scenario(OpenMainScreenScenario(composeTestRule)) } @@ -175,6 +190,9 @@ class OrganizeTokensTest : BaseTestCase() { tokenWithTitleAndPosition(polygonTitle, 2).assertIsDisplayed() } } + step("Swipe to 'Organize tokens' button") { + swipeUp() + } step("Click 'Organize tokens' button") { onMainScreen { organizeTokensButton().clickWithAssertion() } } @@ -183,6 +201,7 @@ class OrganizeTokensTest : BaseTestCase() { tokenWithTitleAndPosition(bitcoinTitle, 1).assertIsDisplayed() tokenWithTitleAndPosition(ethereumTitle, 2).assertIsDisplayed() tokenWithTitleAndPosition(polygonTitle, 3).assertIsDisplayed() + tokenWithTitleAndPosition(polExMaticTitle, 4).assertIsDisplayed() } } step("Click 'By Balance' button") { @@ -192,9 +211,10 @@ class OrganizeTokensTest : BaseTestCase() { } step("Check positions of tokens by balance on 'Organize tokens' screen") { onOrganizeTokensScreen { - tokenWithTitleAndPosition(polygonTitle, 1).assertIsDisplayed() - tokenWithTitleAndPosition(ethereumTitle, 2).assertIsDisplayed() - tokenWithTitleAndPosition(bitcoinTitle, 3).assertIsDisplayed() + tokenWithTitleAndPosition(ethereumTitle, 1).assertIsDisplayed() + tokenWithTitleAndPosition(polExMaticTitle, 2).assertIsDisplayed() + tokenWithTitleAndPosition(polygonTitle, 3).assertIsDisplayed() + tokenWithTitleAndPosition(bitcoinTitle, 4).assertIsDisplayed() } } step("Click 'Apply' button") { @@ -202,9 +222,10 @@ class OrganizeTokensTest : BaseTestCase() { } step("Check positions of tokens by balance on 'Organize tokens' screen") { onMainScreen { - tokenWithTitleAndPosition(polygonTitle, 0).assertIsDisplayed() - tokenWithTitleAndPosition(ethereumTitle, 1).assertIsDisplayed() - tokenWithTitleAndPosition(bitcoinTitle, 2).assertIsDisplayed() + tokenWithTitleAndPosition(ethereumTitle, 0).assertIsDisplayed() + tokenWithTitleAndPosition(polExMaticTitle, 1).assertIsDisplayed() + tokenWithTitleAndPosition(polygonTitle, 2).assertIsDisplayed() + tokenWithTitleAndPosition(bitcoinTitle, 3).assertIsDisplayed() } } } diff --git a/app/src/androidTest/kotlin/com/tangem/tests/StoriesTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/StoriesTest.kt index 63a4706cb0..d87e1be98c 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/StoriesTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/StoriesTest.kt @@ -7,15 +7,14 @@ import com.tangem.screens.onDisclaimerScreen import com.tangem.screens.onStoriesScreen import dagger.hilt.android.testing.HiltAndroidTest import io.github.kakaocup.kakao.intent.KIntent -import org.junit.Test @HiltAndroidTest class StoriesTest : BaseTestCase() { - @Test + // @Test fun clickOnOrderButtonTest() = setupHooks().run { - val buyWalletUrl = "https://buy.tangem.com/?utm_source=tangem-app&utm_medium=app" + val buyWalletUrl = "https://buy.tangem.com/" onDisclaimerScreen { step("Click on 'Accept' button") { acceptButton.clickWithAssertion() diff --git a/app/src/androidTest/kotlin/com/tangem/tests/TermsOfServiceTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/TermsOfServiceTest.kt index 0e596d4528..7f729f1c3c 100644 --- a/app/src/androidTest/kotlin/com/tangem/tests/TermsOfServiceTest.kt +++ b/app/src/androidTest/kotlin/com/tangem/tests/TermsOfServiceTest.kt @@ -3,7 +3,7 @@ package com.tangem.tests import androidx.test.InstrumentationRegistry.getTargetContext import com.tangem.common.BaseTestCase import com.tangem.common.extensions.clickWithAssertion -import com.tangem.common.extensions.swipeToCloseApp +import com.tangem.common.extensions.swipeUp import com.tangem.screens.onDisclaimerScreen import com.tangem.screens.onStoriesScreen import dagger.hilt.android.testing.HiltAndroidTest @@ -67,7 +67,7 @@ class TermsOfServiceTest : BaseTestCase() { device.uiDevice.pressRecentApps() } step("Stop app by swipe") { - swipeToCloseApp() + swipeUp(startHeightRatio = 0.5f) } step("Launch app") { device.apps.launch(packageName) @@ -90,7 +90,7 @@ class TermsOfServiceTest : BaseTestCase() { device.uiDevice.pressRecentApps() } step("Stop app by swipe") { - swipeToCloseApp() + swipeUp(startHeightRatio = 0.5f) } step("Launch app") { device.apps.launch(packageName) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 3ac868e93f..e87768263c 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 3ac868e93f88498258867d457f8b8c4577b40f98 +Subproject commit e87768263c0a79018958b0872940c604180a623a diff --git a/tangem-android-tools b/tangem-android-tools index bc4cd43085..428b83bb37 160000 --- a/tangem-android-tools +++ b/tangem-android-tools @@ -1 +1 @@ -Subproject commit bc4cd430853ca794614b8d5163c9b28b9ca26112 +Subproject commit 428b83bb378b615209e23afa05c88c454d06a9f1