Updated on 2026-08-14

This commit is contained in:
Tangem 2024-09-03 12:36:09 +03:00
commit d617c9c30d
491 changed files with 15019 additions and 5050 deletions

View file

@ -10,7 +10,7 @@ import com.tangem.core.navigation.url.UrlOpener
import com.tangem.datasource.asset.loader.AssetLoader
import com.tangem.datasource.config.ConfigManager
import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.datasource.local.token.UserTokensStore
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
import com.tangem.domain.apptheme.GetAppThemeModeUseCase
import com.tangem.domain.apptheme.repository.AppThemeModeRepository
@ -76,7 +76,7 @@ interface ApplicationEntryPoint {
fun getBalanceHidingRepository(): BalanceHidingRepository
fun getUserTokensStore(): UserTokensStore
fun getAppPreferencesStore(): AppPreferencesStore
fun getGetAppThemeModeUseCase(): GetAppThemeModeUseCase

View file

@ -22,7 +22,7 @@ import com.tangem.datasource.config.ConfigManager
import com.tangem.datasource.config.FeaturesLocalLoader
import com.tangem.datasource.config.models.Config
import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.datasource.local.token.UserTokensStore
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
import com.tangem.domain.apptheme.GetAppThemeModeUseCase
import com.tangem.domain.apptheme.repository.AppThemeModeRepository
@ -126,8 +126,8 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
private val balanceHidingRepository: BalanceHidingRepository
get() = entryPoint.getBalanceHidingRepository()
private val userTokensStore: UserTokensStore
get() = entryPoint.getUserTokensStore()
private val appPreferencesStore: AppPreferencesStore
get() = entryPoint.getAppPreferencesStore()
val getAppThemeModeUseCase: GetAppThemeModeUseCase
get() = entryPoint.getGetAppThemeModeUseCase()
@ -228,7 +228,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
}
derivationsFinder = DerivationsFinder(
newTokensStore = userTokensStore,
appPreferencesStore = appPreferencesStore,
dispatchers = AppCoroutineDispatcherProvider(),
)
appStateHolder.mainStore = store

View file

@ -0,0 +1,24 @@
package com.tangem.tap.common.analytics.handlers.firebase
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.ktx.Firebase
import com.tangem.core.analytics.AppInstanceIdProvider
import kotlinx.coroutines.suspendCancellableCoroutine
import timber.log.Timber
import kotlin.coroutines.resume
internal class FirebaseAppInstanceIdProvider : AppInstanceIdProvider {
override suspend fun getAppInstanceId(): String? = suspendCancellableCoroutine { continuation ->
Firebase.analytics.appInstanceId
.addOnSuccessListener { continuation.resume(it) }
.addOnFailureListener {
Timber.w("Fail to get appInstanceId")
continuation.resume(null)
}
}
override fun getAppInstanceIdSync(): String? {
return Firebase.analytics.appInstanceId.result
}
}

View file

@ -1,9 +1,11 @@
package com.tangem.tap.di.analytics
import com.tangem.core.analytics.AppInstanceIdProvider
import com.tangem.core.analytics.utils.AnalyticsContextProxy
import com.tangem.domain.analytics.ChangeCardAnalyticsContextUseCase
import com.tangem.tap.common.analytics.DefaultAnalyticsContextProxy
import com.tangem.tap.common.analytics.DefaultChangeCardAnalyticsContextUseCase
import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAppInstanceIdProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -23,4 +25,8 @@ internal object AnalyticsModule {
@Provides
@Singleton
fun provideAnalyticsContextProxy(): AnalyticsContextProxy = DefaultAnalyticsContextProxy()
@Provides
@Singleton
fun provideAppInstanceIdProvider(): AppInstanceIdProvider = FirebaseAppInstanceIdProvider()
}

View file

@ -92,4 +92,12 @@ internal object CardDomainModule {
fun provideNetworkHasDerivationUseCase(): NetworkHasDerivationUseCase {
return NetworkHasDerivationUseCase()
}
@Provides
@Singleton
fun provideIsRequiredDerivePublicKeysUseCase(
derivationsRepository: DerivationsRepository,
): HasMissedDerivationsUseCase {
return HasMissedDerivationsUseCase(derivationsRepository)
}
}

View file

@ -1,6 +1,8 @@
package com.tangem.tap.di.domain
import com.tangem.domain.managetokens.GetManagedTokensUseCase
import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.managetokens.*
import com.tangem.domain.managetokens.repository.CustomTokensRepository
import com.tangem.domain.managetokens.repository.ManageTokensRepository
import dagger.Module
import dagger.Provides
@ -17,4 +19,66 @@ internal object ManageTokensDomainModule {
fun provideGetManageTokensUseCase(manageTokensRepository: ManageTokensRepository): GetManagedTokensUseCase {
return GetManagedTokensUseCase(manageTokensRepository)
}
@Provides
@Singleton
fun provideValidateTokenFormatUseCase(customTokensRepository: CustomTokensRepository): ValidateTokenFormUseCase {
return ValidateTokenFormUseCase(customTokensRepository)
}
@Provides
@Singleton
fun provideCreateCurrencyUseCase(customTokensRepository: CustomTokensRepository): CreateCurrencyUseCase {
return CreateCurrencyUseCase(customTokensRepository)
}
@Provides
@Singleton
fun provideFindTokenUseCase(customTokensRepository: CustomTokensRepository): FindTokenUseCase {
return FindTokenUseCase(customTokensRepository)
}
@Provides
@Singleton
fun provideCheckIsCurrencyNotAddedUseCase(
customTokensRepository: CustomTokensRepository,
): CheckIsCurrencyNotAddedUseCase {
return CheckIsCurrencyNotAddedUseCase(customTokensRepository)
}
@Provides
@Singleton
fun provideRemoveCustomManagedCryptoCurrencyUseCase(
customTokensRepository: CustomTokensRepository,
): RemoveCustomManagedCryptoCurrencyUseCase {
return RemoveCustomManagedCryptoCurrencyUseCase(customTokensRepository)
}
@Provides
@Singleton
fun provideSaveManagedTokensUseCase(
manageTokensRepository: ManageTokensRepository,
derivationsRepository: DerivationsRepository,
): SaveManagedTokensUseCase {
return SaveManagedTokensUseCase(
manageTokensRepository = manageTokensRepository,
derivationsRepository = derivationsRepository,
)
}
@Provides
@Singleton
fun provideGetSupportedNetworksUseCase(
customTokensRepository: CustomTokensRepository,
): GetSupportedNetworksUseCase {
return GetSupportedNetworksUseCase(customTokensRepository)
}
@Provides
@Singleton
fun provideValidateDerivationPathUseCase(
customTokensRepository: CustomTokensRepository,
): ValidateDerivationPathUseCase {
return ValidateDerivationPathUseCase(customTokensRepository)
}
}

View file

@ -1,10 +1,8 @@
package com.tangem.tap.di.domain
import com.tangem.domain.markets.GetMarketsTokenListFlowUseCase
import com.tangem.domain.markets.GetTokenMarketInfoUseCase
import com.tangem.domain.markets.GetTokenPriceChartUseCase
import com.tangem.domain.markets.GetTokenQuotesUseCase
import com.tangem.domain.markets.*
import com.tangem.domain.markets.repositories.MarketsTokenRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -37,7 +35,13 @@ object MarketsDomainModule {
@Provides
@Singleton
fun provideGetTokenQuotesUseCase(marketsTokenRepository: MarketsTokenRepository): GetTokenQuotesUseCase {
return GetTokenQuotesUseCase(marketsTokenRepository = marketsTokenRepository)
fun provideTokenFullQuotesUseCase(marketsTokenRepository: MarketsTokenRepository): GetTokenFullQuotesUseCase {
return GetTokenFullQuotesUseCase(marketsTokenRepository = marketsTokenRepository)
}
@Provides
@Singleton
fun provideGetTokenQuotesUseCase(quotesRepository: QuotesRepository): GetCurrencyQuotesUseCase {
return GetCurrencyQuotesUseCase(quotesRepository = quotesRepository)
}
}

View file

@ -145,18 +145,6 @@ internal object StakingDomainModule {
)
}
@Provides
@Singleton
fun provideIsStakeMoreAvailableUseCase(
stakingRepository: StakingRepository,
stakingErrorResolver: StakingErrorResolver,
): IsStakeMoreAvailableUseCase {
return IsStakeMoreAvailableUseCase(
stakingRepository = stakingRepository,
stakingErrorResolver = stakingErrorResolver,
)
}
@Provides
@Singleton
fun provideIsApproveNeededUseCase(

View file

@ -104,6 +104,22 @@ internal object TokensDomainModule {
)
}
@Provides
@Singleton
fun provideGetAllWalletsCryptoCurrencyStatusesUseCase(
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
): GetAllWalletsCryptoCurrencyStatusesUseCase {
return GetAllWalletsCryptoCurrencyStatusesUseCase(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
)
}
@Provides
@Singleton
fun provideGetCurrencyWarningsUseCase(
@ -158,8 +174,9 @@ internal object TokensDomainModule {
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
): FetchCurrencyStatusUseCase {
return FetchCurrencyStatusUseCase(currenciesRepository, networksRepository, quotesRepository)
return FetchCurrencyStatusUseCase(currenciesRepository, networksRepository, quotesRepository, stakingRepository)
}
@Provides
@ -419,4 +436,10 @@ internal object TokensDomainModule {
fun provideCheckHasLinkedTokensUseCase(currenciesRepository: CurrenciesRepository): CheckHasLinkedTokensUseCase {
return CheckHasLinkedTokensUseCase(currenciesRepository)
}
@Provides
@Singleton
fun provideGetCurrencyCheckUseCase(currencyChecksRepository: CurrencyChecksRepository): GetCurrencyCheckUseCase {
return GetCurrencyCheckUseCase(currencyChecksRepository)
}
}

View file

@ -42,6 +42,21 @@ internal object TransactionDomainModule {
)
}
@Provides
@Singleton
fun provideSendMultipleTransactionUseCase(
cardSdkConfigRepository: CardSdkConfigRepository,
transactionRepository: TransactionRepository,
walletManagersFacade: WalletManagersFacade,
): SendMultipleTransactionUseCase {
return SendMultipleTransactionUseCase(
demoConfig = DemoConfig(),
cardSdkConfigRepository = cardSdkConfigRepository,
transactionRepository = transactionRepository,
walletManagersFacade = walletManagersFacade,
)
}
@Provides
@Singleton
fun provideAssociateAssetUseCase(

View file

@ -1,5 +1,7 @@
package com.tangem.tap.domain.card
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
@ -8,10 +10,13 @@ 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.currency.getNetwork
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.operations.derivation.ExtendedPublicKeysMap
@ -30,6 +35,10 @@ internal class DefaultDerivationsRepository(
) : DerivationsRepository {
override suspend fun derivePublicKeys(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
derivePublicKeysByNetworks(userWalletId = userWalletId, networks = currencies.map(CryptoCurrency::network))
}
override suspend fun derivePublicKeysByNetworks(userWalletId: UserWalletId, networks: List<Network>) {
val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found")
if (!userWallet.scanResponse.card.settings.isHDWalletAllowed) {
@ -38,7 +47,7 @@ internal class DefaultDerivationsRepository(
}
val derivations = MissedDerivationsFinder(scanResponse = userWallet.scanResponse)
.find(currencies)
.findByNetworks(networks)
.ifEmpty {
Timber.d("Nothing to derive")
return
@ -47,6 +56,26 @@ internal class DefaultDerivationsRepository(
derivePublicKeys(userWalletId = userWalletId, derivations = derivations)
}
override suspend fun hasMissedDerivations(
userWalletId: UserWalletId,
networksWithDerivationPath: Map<Network.ID, String?>,
): Boolean {
val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found")
val derivations = MissedDerivationsFinder(scanResponse = userWallet.scanResponse)
.findByNetworks(
networksWithDerivationPath.mapNotNull { (networkId, extraDerivationPath) ->
getNetwork(
blockchain = Blockchain.fromNetworkId(networkId.value) ?: return@mapNotNull null,
extraDerivationPath = extraDerivationPath,
derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider,
)
},
)
return derivations.isNotEmpty()
}
override suspend fun derivePublicKeys(userWalletId: UserWalletId, derivations: Derivations): DerivedKeys {
tangemSdkManager.derivePublicKeys(cardId = null, derivations = derivations)
.doOnSuccess { response ->

View file

@ -11,6 +11,7 @@ import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.models.scan.KeyWalletPublicKey
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.operations.derivation.ExtendedPublicKeysMap
private typealias DerivationData = Pair<ByteArrayKey, List<DerivationPath>>
@ -26,8 +27,12 @@ internal class MissedDerivationsFinder(private val scanResponse: ScanResponse) {
/** Find missed derivations for given currencies [currencies] */
fun find(currencies: List<CryptoCurrency>): Derivations {
return currencies.map { it.network }.let(::findByNetworks)
}
fun findByNetworks(networks: List<Network>): Derivations {
return buildMap<ByteArrayKey, MutableList<DerivationPath>> {
currencies
networks
.mapToNewDerivations()
.forEach { data ->
val current = this[data.first]
@ -41,25 +46,25 @@ internal class MissedDerivationsFinder(private val scanResponse: ScanResponse) {
}
}
private fun List<CryptoCurrency>.mapToNewDerivations(): List<DerivationData> {
private fun List<Network>.mapToNewDerivations(): List<DerivationData> {
val config = CardConfig.createConfig(scanResponse.card)
return mapNotNull { currency ->
val blockchain = Blockchain.fromId(id = currency.network.id.value)
return mapNotNull { network ->
val blockchain = Blockchain.fromId(id = network.id.value)
val curve = config.primaryCurve(blockchain) ?: return@mapNotNull null
findNewDerivations(curve = curve, scanResponse = scanResponse, currency = currency)
findNewDerivations(curve = curve, scanResponse = scanResponse, network = network)
}
}
private fun findNewDerivations(
curve: EllipticCurve,
scanResponse: ScanResponse,
currency: CryptoCurrency,
network: Network,
): DerivationData? {
val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null
val publicKey = wallet.publicKey.toMapKey()
val derivationCandidates = currency
val derivationCandidates = network
.getDerivationCandidates(curve)
.ifEmpty { return null }
.filterAlreadyDerivedKeys(publicKey)
@ -68,13 +73,13 @@ internal class MissedDerivationsFinder(private val scanResponse: ScanResponse) {
return publicKey to derivationCandidates
}
private fun CryptoCurrency.getDerivationCandidates(curve: EllipticCurve): List<DerivationPath> {
val blockchain = Blockchain.fromId(id = network.id.value)
private fun Network.getDerivationCandidates(curve: EllipticCurve): List<DerivationPath> {
val blockchain = Blockchain.fromId(id = this.id.value)
return buildList {
add(blockchain.getDerivationPath(curve = curve))
add(blockchain.getCustomDerivationPath(curve = curve, currency = this@getDerivationCandidates))
add(blockchain.getCardanoDerivationPathIfNeeded(currency = this@getDerivationCandidates))
add(blockchain.getCustomDerivationPath(curve = curve, network = this@getDerivationCandidates))
add(blockchain.getCardanoDerivationPathIfNeeded(network = this@getDerivationCandidates))
}
.filterNotNull()
.distinct()
@ -88,17 +93,17 @@ internal class MissedDerivationsFinder(private val scanResponse: ScanResponse) {
}
}
private fun Blockchain.getCustomDerivationPath(curve: EllipticCurve, currency: CryptoCurrency): DerivationPath? {
private fun Blockchain.getCustomDerivationPath(curve: EllipticCurve, network: Network): DerivationPath? {
return if (getSupportedCurves().contains(curve)) {
currency.network.derivationPath.value?.let(::DerivationPath)
network.derivationPath.value?.let(::DerivationPath)
} else {
null
}
}
private fun Blockchain.getCardanoDerivationPathIfNeeded(currency: CryptoCurrency): DerivationPath? {
return if (currency is CryptoCurrency.Coin && this == Blockchain.Cardano) {
currency.network.derivationPath.value?.let {
private fun Blockchain.getCardanoDerivationPathIfNeeded(network: Network): DerivationPath? {
return if (this == Blockchain.Cardano) {
network.derivationPath.value?.let {
CardanoUtils.extendedDerivationPath(derivationPath = DerivationPath(it))
}
} else {

View file

@ -111,6 +111,10 @@ object WalletMockContent : MockContent {
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
),
DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey(
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
),
),
extendedPublicKey = ExtendedPublicKey(
publicKey = byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5),
@ -189,6 +193,13 @@ object WalletMockContent : MockContent {
parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0,
),
DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey(
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0,
),
),
),
),

View file

@ -5,7 +5,10 @@ import com.tangem.blockchain.common.Blockchain
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.UserTokensStore
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
import com.tangem.domain.common.DerivationStyleProvider
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
import com.tangem.domain.models.scan.CardDTO
@ -22,7 +25,7 @@ internal data class BlockchainToDerive(
// FIXME: May be move to DI, currently unnecessary
internal class DerivationsFinder(
private val newTokensStore: UserTokensStore,
private val appPreferencesStore: AppPreferencesStore,
private val dispatchers: CoroutineDispatcherProvider,
) {
@ -64,7 +67,9 @@ internal class DerivationsFinder(
}
private suspend fun getBlockchains(userWalletId: UserWalletId): MutableSet<BlockchainToDerive> {
val responseTokens = newTokensStore.getSyncOrNull(userWalletId)
val responseTokens = appPreferencesStore.getObjectSyncOrNull<UserTokensResponse>(
key = PreferencesKeys.getUserTokensKey(userWalletId.stringValue),
)
?.tokens
?: return hashSetOf()

View file

@ -7,7 +7,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import com.tangem.core.ui.components.BasicDialog
import com.tangem.core.ui.components.DialogButton
import com.tangem.core.ui.components.DialogButtonUM
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.tap.features.details.ui.appsettings.AppSettingsDialogsFactory
@ -20,12 +20,12 @@ internal fun SettingsAlertDialog(dialog: Dialog.Alert) {
title = dialog.title.resolveReference(),
message = dialog.description.resolveReference(),
isDismissable = false,
confirmButton = DialogButton(
confirmButton = DialogButtonUM(
title = dialog.confirmText.resolveReference(),
warning = true,
onClick = dialog.onConfirm,
),
dismissButton = DialogButton(
dismissButton = DialogButtonUM(
title = stringResource(id = R.string.common_cancel),
onClick = dialog.onDismiss,
),

View file

@ -6,7 +6,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import com.tangem.core.ui.components.DialogButton
import com.tangem.core.ui.components.DialogButtonUM
import com.tangem.core.ui.components.SelectorDialog
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemThemePreview
@ -21,7 +21,7 @@ internal fun SettingsSelectorDialog(dialog: Dialog.Selector) {
title = dialog.title.resolveReference(),
selectedItemIndex = dialog.selectedItemIndex,
items = dialog.items.map { it.resolveReference() }.toImmutableList(),
confirmButton = DialogButton(
confirmButton = DialogButtonUM(
title = stringResource(R.string.common_cancel),
onClick = dialog.onDismiss,
),

View file

@ -13,6 +13,7 @@ import com.tangem.core.analytics.Analytics
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.util.getBackupCardsCount
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
@ -173,13 +174,7 @@ internal class CardSettingsViewModel @Inject constructor(
userWalletId = userWalletId,
cardId = card.cardId,
isActiveBackupStatus = card.backupStatus?.isActive == true,
backupCardsCount = when (val status = card.backupStatus) {
is CardDTO.BackupStatus.Active -> status.cardCount
is CardDTO.BackupStatus.CardLinked,
CardDTO.BackupStatus.NoBackup,
null,
-> 0
},
backupCardsCount = scanResponse.getBackupCardsCount() ?: 0,
),
)
}

View file

@ -190,11 +190,11 @@ private fun CommonResetDialog(dialog: ResetCardScreenState.Dialog) {
BasicDialog(
title = stringResource(dialog.titleResId),
message = stringResource(dialog.messageResId),
dismissButton = DialogButton(
dismissButton = DialogButtonUM(
title = stringResource(id = R.string.common_cancel),
onClick = dialog.onDismiss,
),
confirmButton = DialogButton(
confirmButton = DialogButtonUM(
title = stringResource(id = R.string.card_settings_action_sheet_reset),
warning = true,
onClick = dialog.onConfirmClick,
@ -208,7 +208,7 @@ private fun CompletedResetDialog(dialog: ResetCardDialog) {
BasicDialog(
title = stringResource(id = dialog.titleResId),
message = stringResource(id = dialog.messageResId),
confirmButton = DialogButton(
confirmButton = DialogButtonUM(
title = stringResource(id = R.string.common_ok),
onClick = dialog.onConfirmClick,
),

View file

@ -98,7 +98,7 @@ internal class HomeFragment : ComposeFragment(), StoreSubscriber<HomeState> {
viewModel.onSearchClick()
} else {
Analytics.send(IntroductionProcess.ButtonTokensList())
store.dispatchNavigationAction { push(AppRoute.ManageTokens(readOnlyContent = true)) }
store.dispatchNavigationAction { push(AppRoute.ManageTokens()) }
store.dispatch(TokensAction.SetArgs.ReadAccess)
}
},

View file

@ -61,7 +61,7 @@ internal class HomeViewModel @Inject constructor(
analyticsEventHandler.send(IntroductionProcess.ButtonTokensList())
store.dispatch(TokensAction.SetArgs.ReadAccess)
store.dispatchNavigationAction { push(AppRoute.ManageTokens(readOnlyContent = true)) }
store.dispatchNavigationAction { push(AppRoute.ManageTokens()) }
}
private fun scanCard() {

View file

@ -9,9 +9,9 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R
import com.tangem.core.ui.components.BasicDialog
import com.tangem.core.ui.components.DialogButton
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.components.DialogButtonUM
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.tap.features.saveWallet.ui.models.EnrollBiometricsDialog
@Composable
@ -19,11 +19,11 @@ fun EnrollBiometricsDialogContent(dialog: EnrollBiometricsDialog) {
BasicDialog(
title = stringResource(R.string.save_user_wallet_agreement_enroll_biometrics_title),
message = stringResource(R.string.save_user_wallet_agreement_enroll_biometrics_description),
confirmButton = DialogButton(
confirmButton = DialogButtonUM(
title = stringResource(R.string.common_enable),
onClick = dialog.onEnroll,
),
dismissButton = DialogButton(
dismissButton = DialogButtonUM(
onClick = dialog.onCancel,
),
onDismissDialog = dialog.onCancel,

View file

@ -5,7 +5,6 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.Analytics
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.tokens.legacy.TradeCryptoAction
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.NetworkAddress
@ -48,12 +47,6 @@ object TradeCryptoMiddleware {
is TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId)
is TradeCryptoAction.Buy -> proceedBuyAction(state, action)
is TradeCryptoAction.Sell -> proceedSellAction(action)
is TradeCryptoAction.Swap -> openSwap(currency = action.cryptoCurrency)
is TradeCryptoAction.Stake -> openStaking(
userWalletId = action.userWalletId,
cryptoCurrencyId = action.cryptoCurrencyId,
yield = action.yield,
)
is TradeCryptoAction.SendToken -> handleNewSendToken(action = action)
is TradeCryptoAction.SendCoin -> handleNewSendCoin(action = action)
}
@ -141,22 +134,6 @@ object TradeCryptoMiddleware {
)?.let { store.dispatchOpenUrl(it) }
}
private fun openSwap(currency: CryptoCurrency) {
store.dispatchNavigationAction { push(AppRoute.Swap(currency = currency)) }
}
private fun openStaking(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID, yield: Yield) {
store.dispatchNavigationAction {
push(
AppRoute.Staking(
userWalletId = userWalletId,
cryptoCurrencyId = cryptoCurrencyId,
yield = yield,
),
)
}
}
private fun handleNewSendToken(action: TradeCryptoAction.SendToken) {
handleNewSend(
userWalletId = action.userWallet.walletId,

View file

@ -7,7 +7,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.BasicDialog
import com.tangem.core.ui.components.DialogButton
import com.tangem.core.ui.components.DialogButtonUM
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.tap.features.welcome.ui.model.WarningModel
import com.tangem.wallet.R
@ -27,7 +27,7 @@ internal fun WarningDialog(warning: WarningModel?) {
},
),
onDismissDialog = warning.onDismiss,
confirmButton = DialogButton(
confirmButton = DialogButtonUM(
title = stringResource(id = R.string.common_ok),
onClick = warning.onDismiss,
),
@ -38,7 +38,7 @@ internal fun WarningDialog(warning: WarningModel?) {
title = stringResource(id = R.string.common_attention),
message = stringResource(id = R.string.key_invalidated_warning_description),
onDismissDialog = warning.onDismiss,
confirmButton = DialogButton(
confirmButton = DialogButtonUM(
title = stringResource(id = R.string.common_ok),
onClick = warning.onDismiss,
),
@ -50,7 +50,7 @@ internal fun WarningDialog(warning: WarningModel?) {
message = stringResource(id = R.string.biometric_unavailable_warning),
onDismissDialog = warning.onDismiss,
isDismissable = false,
confirmButton = DialogButton(
confirmButton = DialogButtonUM(
title = stringResource(id = R.string.common_ok),
onClick = warning.onDismiss,
),

View file

@ -11,6 +11,7 @@ import com.tangem.features.details.component.DetailsComponent
import com.tangem.features.disclaimer.api.components.DisclaimerComponent
import com.tangem.features.managetokens.ManageTokensToggles
import com.tangem.features.managetokens.component.ManageTokensComponent
import com.tangem.features.markets.details.MarketsTokenDetailsComponent
import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles
import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter
import com.tangem.features.send.api.navigation.SendRouter
@ -50,6 +51,7 @@ internal class ChildFactory @Inject constructor(
private val walletSettingsComponentFactory: WalletSettingsComponent.Factory,
private val disclaimerComponentFactory: DisclaimerComponent.Factory,
private val manageTokensComponentFactory: ManageTokensComponent.Factory,
private val marketsTokenDetailsComponentFactory: MarketsTokenDetailsComponent.Factory,
private val sendRouter: SendRouter,
private val tokenDetailsRouter: TokenDetailsRouter,
private val walletRouter: WalletRouter,
@ -126,13 +128,7 @@ internal class ChildFactory @Inject constructor(
if (manageTokensToggles.isFeatureEnabled) {
route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = ManageTokensComponent.Params(
mode = if (route.readOnlyContent) {
ManageTokensComponent.Mode.READ_ONLY
} else {
ManageTokensComponent.Mode.MANAGE
},
),
params = ManageTokensComponent.Params(route.userWalletId),
componentFactory = manageTokensComponentFactory,
)
} else {
@ -191,6 +187,16 @@ internal class ChildFactory @Inject constructor(
componentFactory = walletSettingsComponentFactory,
)
}
is AppRoute.MarketsTokenDetails -> {
route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = MarketsTokenDetailsComponent.Params(
token = route.token,
appCurrency = route.appCurrency,
),
componentFactory = marketsTokenDetailsComponentFactory,
)
}
}
}