Updated on 2026-08-14
This commit is contained in:
parent
48180a9b38
commit
76164ac486
12 changed files with 249 additions and 50 deletions
|
|
@ -348,11 +348,10 @@ class DeepLinkFactoryTest {
|
|||
fun `getParams filters malicious parameters`() = runTest {
|
||||
every { mockedUri.scheme } returns "tangem"
|
||||
every { mockedUri.host } returns "onramp"
|
||||
every { mockedUri.query } returns "safe=ok&malicious=%3Cscript%3E"e=O%27Brien"
|
||||
every { mockedUri.queryParameterNames } returns setOf("safe", "malicious", "quote")
|
||||
every { mockedUri.query } returns "safe=ok&malicious=%3Cscript%3E"
|
||||
every { mockedUri.queryParameterNames } returns setOf("safe", "malicious")
|
||||
every { mockedUri.getQueryParameter("safe") } returns "ok"
|
||||
every { mockedUri.getQueryParameter("malicious") } returns "<script>"
|
||||
every { mockedUri.getQueryParameter("quote") } returns "O'Brien"
|
||||
|
||||
deepLinkFactory.checkRoutingReadiness(AppRoute.Wallet)
|
||||
deepLinkFactory.handleDeeplink(mockedUri, testScope, isFromOnNewIntent)
|
||||
|
|
@ -389,13 +388,6 @@ class DeepLinkFactoryTest {
|
|||
advanceUntilIdle()
|
||||
verify { onrampDeepLinkFactory.create(eq(testScope), eq(emptyMap())) }
|
||||
|
||||
every { mockedUri.query } returns "unsafe=O'Brien"
|
||||
every { mockedUri.queryParameterNames } returns setOf("unsafe")
|
||||
every { mockedUri.getQueryParameter("unsafe") } returns "O'Brien"
|
||||
deepLinkFactory.handleDeeplink(mockedUri, testScope, isFromOnNewIntent)
|
||||
advanceUntilIdle()
|
||||
verify { onrampDeepLinkFactory.create(eq(testScope), eq(emptyMap())) }
|
||||
|
||||
every { mockedUri.query } returns "unsafe=test;"
|
||||
every { mockedUri.queryParameterNames } returns setOf("unsafe")
|
||||
every { mockedUri.getQueryParameter("unsafe") } returns "test;"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.utils.extensions
|
||||
|
||||
private const val DEEPLINK_VALIDATION_REGEX = "['\";<>()+\\\\]"
|
||||
private const val DEEPLINK_VALIDATION_REGEX = "[\";<>()+\\\\]"
|
||||
|
||||
/**
|
||||
* Check for malicious symbol in uri part
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ dependencies {
|
|||
|
||||
/** Feature Apis */
|
||||
implementation(projects.features.tokendetails.api)
|
||||
implementation(projects.features.wallet.api)
|
||||
implementation(projects.features.staking.api)
|
||||
implementation(projects.features.markets.api)
|
||||
implementation(projects.features.onramp.api)
|
||||
|
|
|
|||
|
|
@ -1,20 +1,29 @@
|
|||
package com.tangem.feature.tokendetails.deeplink
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.deeplink.DeeplinkConst.DERIVATION_PATH_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.NETWORK_ID_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.TOKEN_ID_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.TRANSACTION_ID_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.TYPE_KEY
|
||||
import com.tangem.core.deeplink.DeeplinkConst.WALLET_ID_KEY
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.notifications.models.NotificationType
|
||||
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.wallet.WalletBalanceFetcher
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
import com.tangem.domain.wallets.models.isLocked
|
||||
import com.tangem.domain.wallets.models.isMultiCurrency
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.SelectWalletUseCase
|
||||
import com.tangem.features.pushnotifications.api.analytics.PushNotificationAnalyticEvents
|
||||
import com.tangem.features.tokendetails.deeplink.TokenDetailsDeepLinkHandler
|
||||
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionTrigger
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
|
@ -28,10 +37,17 @@ internal class DefaultTokenDetailsDeepLinkHandler @AssistedInject constructor(
|
|||
@Assisted private val queryParams: Map<String, String>,
|
||||
@Assisted private val isFromOnNewIntent: Boolean,
|
||||
private val appRouter: AppRouter,
|
||||
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
|
||||
private val selectWalletUseCase: SelectWalletUseCase,
|
||||
private val getCryptoCurrenciesUseCase: GetCryptoCurrenciesUseCase,
|
||||
private val getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase,
|
||||
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
|
||||
private val tokenDetailsDeepLinkActionTrigger: TokenDetailsDeepLinkActionTrigger,
|
||||
private val walletDeepLinkActionTrigger: WalletDeepLinkActionTrigger,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val tokensFeatureToggles: TokensFeatureToggles,
|
||||
private val walletBalanceFetcher: WalletBalanceFetcher,
|
||||
private val fetchCardTokenListUseCase: FetchCardTokenListUseCase,
|
||||
) : TokenDetailsDeepLinkHandler {
|
||||
|
||||
init {
|
||||
|
|
@ -42,27 +58,23 @@ internal class DefaultTokenDetailsDeepLinkHandler @AssistedInject constructor(
|
|||
val networkId = queryParams[NETWORK_ID_KEY]
|
||||
val tokenId = queryParams[TOKEN_ID_KEY]
|
||||
val type = NotificationType.getType(queryParams[TYPE_KEY])
|
||||
|
||||
// It is okay here, we are navigating from outside, and there is no other way to getting UserWallet
|
||||
val selectedUserWalletId = getSelectedWalletSyncUseCase().getOrNull()?.walletId
|
||||
val walletId = queryParams[WALLET_ID_KEY]?.let(::UserWalletId) ?: selectedUserWalletId
|
||||
|
||||
// If selected user wallet is different than from deeplink - ignore deeplink
|
||||
// If selected user wallet is null - ignore deeplink
|
||||
if (walletId != selectedUserWalletId || selectedUserWalletId == null) {
|
||||
Timber.e("Error on getting user wallet")
|
||||
return
|
||||
}
|
||||
val transactionId = queryParams[TRANSACTION_ID_KEY]
|
||||
val walletId = queryParams[WALLET_ID_KEY]
|
||||
|
||||
scope.launch {
|
||||
val cryptoCurrency = getCryptoCurrenciesUseCase(userWalletId = selectedUserWalletId).getOrElse {
|
||||
Timber.e("Error on getting crypto currency list")
|
||||
val userWalletId = walletId?.let(::UserWalletId)
|
||||
val userWallet = userWalletId?.let { getUserWalletUseCase(userWalletId) }?.getOrNull()
|
||||
// If wallet to select is null or locked, ignore deeplink
|
||||
if (userWallet == null || userWallet.isLocked) {
|
||||
Timber.e("Error on getting user wallet")
|
||||
return@launch
|
||||
}.firstOrNull {
|
||||
val isNetwork = it.network.backendId.equals(networkId, ignoreCase = true)
|
||||
val isCurrency = it.id.rawCurrencyId?.value?.equals(tokenId, ignoreCase = true) == true
|
||||
isNetwork && isCurrency
|
||||
}
|
||||
if (selectWalletUseCase(userWalletId).getOrNull() == null) {
|
||||
Timber.e("Error on selecting user wallet")
|
||||
return@launch
|
||||
}
|
||||
|
||||
val cryptoCurrency = getCryptoCurrency(userWallet = userWallet, networkId = networkId, tokenId = tokenId)
|
||||
|
||||
if (cryptoCurrency == null) {
|
||||
Timber.e(
|
||||
|
|
@ -77,23 +89,78 @@ internal class DefaultTokenDetailsDeepLinkHandler @AssistedInject constructor(
|
|||
|
||||
analyticsEventHandler.send(PushNotificationAnalyticEvents.NotificationOpened(type.name))
|
||||
|
||||
appRouter.push(
|
||||
AppRoute.CurrencyDetails(
|
||||
userWalletId = selectedUserWalletId,
|
||||
currency = cryptoCurrency,
|
||||
),
|
||||
)
|
||||
|
||||
if (isFromOnNewIntent) {
|
||||
fetchCurrencyStatusUseCase.invoke(
|
||||
userWalletId = selectedUserWalletId,
|
||||
id = cryptoCurrency.id,
|
||||
refresh = true,
|
||||
if (userWallet.isMultiCurrency) {
|
||||
appRouter.push(
|
||||
route = AppRoute.CurrencyDetails(
|
||||
userWalletId = userWallet.walletId,
|
||||
currency = cryptoCurrency,
|
||||
),
|
||||
onComplete = { walletDeepLinkActionTrigger.selectWallet(userWallet.walletId) },
|
||||
)
|
||||
} else {
|
||||
walletDeepLinkActionTrigger.selectWallet(userWallet.walletId)
|
||||
}
|
||||
|
||||
if (transactionId != null) {
|
||||
when (type) {
|
||||
NotificationType.SwapStatus,
|
||||
NotificationType.OnrampStatus,
|
||||
-> tokenDetailsDeepLinkActionTrigger.trigger(transactionId)
|
||||
NotificationType.Promo,
|
||||
NotificationType.IncomeTransactions,
|
||||
NotificationType.Unknown,
|
||||
-> Unit
|
||||
}
|
||||
}
|
||||
if (isFromOnNewIntent) fetchCurrency(userWallet, cryptoCurrency)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchCurrency(userWallet: UserWallet, cryptoCurrency: CryptoCurrency) {
|
||||
val isMultiCurrency = userWallet.isMultiCurrency
|
||||
// single-currency wallet with token (NODL)
|
||||
val isSingleWalletWithToken = userWallet is UserWallet.Cold &&
|
||||
userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()
|
||||
when {
|
||||
isMultiCurrency -> fetchCurrencyStatusUseCase.invoke(
|
||||
userWalletId = userWallet.walletId,
|
||||
id = cryptoCurrency.id,
|
||||
refresh = true,
|
||||
)
|
||||
!isMultiCurrency && tokensFeatureToggles.isWalletBalanceFetcherEnabled ->
|
||||
walletBalanceFetcher(params = WalletBalanceFetcher.Params(userWalletId = userWallet.walletId))
|
||||
// remove below after delete tokensFeatureToggles.isWalletBalanceFetcherEnabled
|
||||
!isMultiCurrency && userWallet is UserWallet.Cold && isSingleWalletWithToken ->
|
||||
fetchCardTokenListUseCase.invoke(
|
||||
userWalletId = userWallet.walletId,
|
||||
refresh = true,
|
||||
)
|
||||
!isMultiCurrency -> fetchCurrencyStatusUseCase.invoke(
|
||||
userWalletId = userWallet.walletId,
|
||||
refresh = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getCryptoCurrency(userWallet: UserWallet, networkId: String?, tokenId: String?) =
|
||||
if (userWallet.isMultiCurrency) {
|
||||
val derivationPath = queryParams[DERIVATION_PATH_KEY]
|
||||
|
||||
getCryptoCurrenciesUseCase(userWalletId = userWallet.walletId)
|
||||
.getOrNull()
|
||||
?.firstOrNull {
|
||||
val isNetwork = it.network.backendId.equals(networkId, ignoreCase = true)
|
||||
val isCurrency = it.id.rawCurrencyId?.value?.equals(tokenId, ignoreCase = true) == true
|
||||
|
||||
val isDefaultDerivation = it.network.derivationPath is Network.DerivationPath.Card
|
||||
val isCustomDerivation = derivationPath?.equals(it.network.derivationPath.value) == true
|
||||
val isCorrectDerivation = isDefaultDerivation || isCustomDerivation
|
||||
isNetwork && isCurrency && isCorrectDerivation
|
||||
}
|
||||
} else {
|
||||
getCryptoCurrencyUseCase(userWalletId = userWallet.walletId).getOrNull()
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : TokenDetailsDeepLinkHandler.Factory {
|
||||
override fun create(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tangem.feature.tokendetails.deeplink
|
||||
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
interface TokenDetailsDeepLinkActionTrigger {
|
||||
suspend fun trigger(txId: String)
|
||||
}
|
||||
|
||||
interface TokenDetailsDeepLinkActionListener {
|
||||
val tokenDetailsActionFlow: SharedFlow<String>
|
||||
}
|
||||
|
||||
@Singleton
|
||||
internal class DefaultTokenDetailsDeepLinkActionTrigger @Inject constructor() :
|
||||
TokenDetailsDeepLinkActionTrigger,
|
||||
TokenDetailsDeepLinkActionListener {
|
||||
|
||||
override val tokenDetailsActionFlow: SharedFlow<String>
|
||||
field = MutableSharedFlow<String>()
|
||||
|
||||
override suspend fun trigger(txId: String) {
|
||||
tokenDetailsActionFlow.emit(txId)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
package com.tangem.feature.tokendetails.deeplink.di
|
||||
|
||||
import com.tangem.feature.tokendetails.deeplink.DefaultTokenDetailsDeepLinkActionTrigger
|
||||
import com.tangem.feature.tokendetails.deeplink.DefaultTokenDetailsDeepLinkHandler
|
||||
import com.tangem.feature.tokendetails.deeplink.TokenDetailsDeepLinkActionListener
|
||||
import com.tangem.feature.tokendetails.deeplink.TokenDetailsDeepLinkActionTrigger
|
||||
import com.tangem.features.tokendetails.deeplink.TokenDetailsDeepLinkHandler
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
|
|
@ -17,4 +20,16 @@ internal interface TokenDetailsDeepLinkModule {
|
|||
fun bindWalletDeepLinkHandlerFactory(
|
||||
impl: DefaultTokenDetailsDeepLinkHandler.Factory,
|
||||
): TokenDetailsDeepLinkHandler.Factory
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindTokenDetailsDeepLinkActionTrigger(
|
||||
impl: DefaultTokenDetailsDeepLinkActionTrigger,
|
||||
): TokenDetailsDeepLinkActionTrigger
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindTokenDetailsDeepLinkActionListener(
|
||||
impl: DefaultTokenDetailsDeepLinkActionTrigger,
|
||||
): TokenDetailsDeepLinkActionListener
|
||||
}
|
||||
|
|
@ -70,6 +70,7 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
import com.tangem.domain.wallets.models.requireColdWallet
|
||||
import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.feature.tokendetails.deeplink.TokenDetailsDeepLinkActionListener
|
||||
import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.TokenDetailsCurrencyStatusAnalyticsSender
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.TokenDetailsNotificationsAnalyticsSender
|
||||
|
|
@ -134,6 +135,7 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase,
|
||||
private val appRouter: AppRouter,
|
||||
private val router: InnerTokenDetailsRouter,
|
||||
private val tokenDetailsDeepLinkActionListener: TokenDetailsDeepLinkActionListener,
|
||||
) : Model(), TokenDetailsClickIntents {
|
||||
|
||||
private val params = paramsContainer.require<TokenDetailsComponent.Params>()
|
||||
|
|
@ -153,6 +155,9 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
private var cryptoCurrencyStatus: CryptoCurrencyStatus? = null
|
||||
private var expressTxStatusTaskScheduler = SingleTaskScheduler<PersistentList<ExpressTransactionStateUM>>()
|
||||
|
||||
/** Transaction id to check for status */
|
||||
private val waitForFirstExpressStatusEmmit = MutableStateFlow(false)
|
||||
|
||||
private val stateFactory = TokenDetailsStateFactory(
|
||||
currentStateProvider = Provider { uiState.value },
|
||||
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
|
||||
|
|
@ -199,6 +204,7 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
initButtons()
|
||||
updateContent()
|
||||
handleBalanceHiding()
|
||||
checkForActionUpdates()
|
||||
}
|
||||
|
||||
fun onBuyCurrencyDeepLink() {
|
||||
|
|
@ -335,6 +341,7 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
expressStatusFactory
|
||||
.getExpressStatuses()
|
||||
.distinctUntilChanged()
|
||||
.onEach { waitForFirstExpressStatusEmmit.value = true }
|
||||
.onEach { expressTxs ->
|
||||
internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs(
|
||||
expressTxs,
|
||||
|
|
@ -801,7 +808,8 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onExpressTransactionClick(txId: String) {
|
||||
val expressTxState = internalUiState.value.expressTxsToDisplay.first { it.info.txId == txId }
|
||||
val expressTxState = internalUiState.value.expressTxsToDisplay.firstOrNull { it.info.txId == txId }
|
||||
?: return
|
||||
internalUiState.value = expressStatusFactory.getStateWithExpressStatusBottomSheet(expressTxState)
|
||||
}
|
||||
|
||||
|
|
@ -1042,6 +1050,15 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun checkForActionUpdates() {
|
||||
combine(
|
||||
tokenDetailsDeepLinkActionListener.tokenDetailsActionFlow,
|
||||
waitForFirstExpressStatusEmmit.filter { it },
|
||||
) { transactionId, _ -> transactionId }
|
||||
.onEach(::onExpressTransactionClick)
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val EXPRESS_STATUS_UPDATE_DELAY = 10_000L
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.features.wallet.deeplink
|
||||
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface WalletDeepLinkActionTrigger {
|
||||
fun selectWallet(userWalletId: UserWalletId)
|
||||
}
|
||||
|
||||
interface WalletDeepLinkActionListener {
|
||||
val selectWalletFlow: Flow<UserWalletId>
|
||||
}
|
||||
|
|
@ -45,9 +45,11 @@ import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvid
|
|||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
|
||||
import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull
|
||||
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionListener
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.*
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
|
@ -86,6 +88,7 @@ internal class WalletModel @Inject constructor(
|
|||
private val appRouter: AppRouter,
|
||||
private val routingFeatureToggle: RoutingFeatureToggle,
|
||||
private val observeAndClearNFTCacheIfNeedUseCase: ObserveAndClearNFTCacheIfNeedUseCase,
|
||||
private val walletDeepLinkActionListener: WalletDeepLinkActionListener,
|
||||
val screenLifecycleProvider: ScreenLifecycleProvider,
|
||||
val innerWalletRouter: InnerWalletRouter,
|
||||
) : Model() {
|
||||
|
|
@ -167,7 +170,16 @@ internal class WalletModel @Inject constructor(
|
|||
return innerWalletRouter.isWalletLastScreen() && shouldShowSaveWalletScreenUseCase() && canUseBiometryUseCase()
|
||||
}
|
||||
|
||||
private fun subscribeToUserWalletsUpdates() {
|
||||
private fun subscribeToUserWalletsUpdates() = channelFlow<Unit> {
|
||||
val firstWalletsUseCaseEmit = MutableStateFlow(false)
|
||||
suspend fun waitFirstWalletsUseCaseEmit() = firstWalletsUseCaseEmit.filter { it }.first()
|
||||
|
||||
// deepLinkActionFlow must wait for fist getWalletsUseCase() emit to correct handle Action.InitializeWallets
|
||||
walletDeepLinkActionListener.selectWalletFlow
|
||||
.onEach { waitFirstWalletsUseCaseEmit() }
|
||||
.onEach(::selectWalletById)
|
||||
.launchIn(this)
|
||||
|
||||
getWalletsUseCase()
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
|
|
@ -178,10 +190,13 @@ internal class WalletModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
.onEach(::updateWallets)
|
||||
.flowOn(dispatchers.default)
|
||||
.launchIn(modelScope)
|
||||
.saveIn(walletsUpdateJobHolder)
|
||||
.onEach { firstWalletsUseCaseEmit.update { true } }
|
||||
.launchIn(this)
|
||||
awaitClose()
|
||||
}
|
||||
.flowOn(dispatchers.default)
|
||||
.launchIn(modelScope)
|
||||
.saveIn(walletsUpdateJobHolder)
|
||||
|
||||
private fun subscribeOnBalanceHiding() {
|
||||
getBalanceHidingSettingsUseCase()
|
||||
|
|
@ -240,6 +255,19 @@ internal class WalletModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun selectWalletById(selectedWalletId: UserWalletId) {
|
||||
val currentWalletId = stateHolder.getSelectedWalletId()
|
||||
|
||||
if (currentWalletId == selectedWalletId) return
|
||||
|
||||
val currentIndex = stateHolder.getWalletIndexByWalletId(userWalletId = currentWalletId) ?: return
|
||||
val newIndex = stateHolder.getWalletIndexByWalletId(userWalletId = selectedWalletId) ?: return
|
||||
|
||||
scrollToWallet(prevIndex = currentIndex, newIndex = newIndex) {
|
||||
stateHolder.update { it.copy(selectedWalletIndex = newIndex) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun addReferralDeepLink(userWallet: UserWallet) {
|
||||
deepLinksRegistry.register(
|
||||
ReferralDeepLink(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.feature.wallet.deeplink
|
||||
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionListener
|
||||
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionTrigger
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
internal class DefaultWalletDeepLinkActionTrigger @Inject constructor() :
|
||||
WalletDeepLinkActionTrigger,
|
||||
WalletDeepLinkActionListener {
|
||||
|
||||
private val _selectWalletFlow = Channel<UserWalletId>()
|
||||
override val selectWalletFlow: Flow<UserWalletId>
|
||||
get() = _selectWalletFlow.receiveAsFlow()
|
||||
|
||||
override fun selectWallet(userWalletId: UserWalletId) {
|
||||
_selectWalletFlow.trySend(userWalletId)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
package com.tangem.feature.wallet.deeplink.di
|
||||
|
||||
import com.tangem.feature.wallet.deeplink.DefaultWalletDeepLinkActionTrigger
|
||||
import com.tangem.feature.wallet.deeplink.DefaultWalletDeepLinkHandler
|
||||
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionListener
|
||||
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionTrigger
|
||||
import com.tangem.features.wallet.deeplink.WalletDeepLinkHandler
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
|
|
@ -15,4 +18,12 @@ internal interface WalletDeepLinkModule {
|
|||
@Binds
|
||||
@Singleton
|
||||
fun bindWalletDeepLinkHandlerFactory(impl: DefaultWalletDeepLinkHandler.Factory): WalletDeepLinkHandler.Factory
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindWalletDeepLinkActionTrigger(impl: DefaultWalletDeepLinkActionTrigger): WalletDeepLinkActionTrigger
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindWalletDeepLinkActionListener(impl: DefaultWalletDeepLinkActionTrigger): WalletDeepLinkActionListener
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTopBarCon
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.OpenBottomSheetTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.WalletScreenStateTransformer
|
||||
import com.tangem.utils.extensions.indexOfFirstOrNull
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -68,6 +69,10 @@ internal class WalletStateController @Inject constructor() {
|
|||
return with(value) { wallets[selectedWalletIndex].walletCardState.id }
|
||||
}
|
||||
|
||||
fun getWalletIndexByWalletId(userWalletId: UserWalletId): Int? {
|
||||
return with(value) { wallets.indexOfFirstOrNull { it.walletCardState.id == userWalletId } }
|
||||
}
|
||||
|
||||
fun showBottomSheet(
|
||||
content: TangemBottomSheetConfigContent,
|
||||
userWalletId: UserWalletId = getSelectedWalletId(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue