Updated on 2026-08-14
This commit is contained in:
commit
dc49208ccc
35 changed files with 277 additions and 235 deletions
|
|
@ -12,7 +12,6 @@ import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsReducer
|
|||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletReducer
|
||||
import com.tangem.tap.features.saveWallet.redux.SaveWalletReducer
|
||||
import com.tangem.tap.features.send.redux.reducers.SendScreenReducer
|
||||
import com.tangem.tap.features.signin.redux.SignInReducer
|
||||
import com.tangem.tap.features.tokens.legacy.redux.TokensReducer
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeReducer
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
|
|
@ -38,7 +37,6 @@ fun appReducer(action: Action, state: AppState?, appStateHolder: AppStateHolder)
|
|||
walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState),
|
||||
welcomeState = WelcomeReducer.reduce(action, state),
|
||||
saveWalletState = SaveWalletReducer.reduce(action, state),
|
||||
signInState = SignInReducer.reduce(action, state),
|
||||
daggerGraphState = DaggerGraphReducer.reduce(action, state),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@ import com.tangem.tap.features.saveWallet.redux.SaveWalletMiddleware
|
|||
import com.tangem.tap.features.saveWallet.redux.SaveWalletState
|
||||
import com.tangem.tap.features.send.redux.middlewares.SendMiddleware
|
||||
import com.tangem.tap.features.send.redux.states.SendState
|
||||
import com.tangem.tap.features.signin.redux.SignInMiddleware
|
||||
import com.tangem.tap.features.signin.redux.SignInState
|
||||
import com.tangem.tap.features.tokens.legacy.redux.TokensState
|
||||
import com.tangem.tap.features.wallet.redux.middlewares.TradeCryptoMiddleware
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeMiddleware
|
||||
|
|
@ -56,7 +54,6 @@ data class AppState(
|
|||
val walletConnectState: WalletConnectState = WalletConnectState(),
|
||||
val welcomeState: WelcomeState = WelcomeState(),
|
||||
val saveWalletState: SaveWalletState = SaveWalletState(),
|
||||
val signInState: SignInState = SignInState(),
|
||||
val daggerGraphState: DaggerGraphState = DaggerGraphState(),
|
||||
) : StateType {
|
||||
|
||||
|
|
@ -93,7 +90,6 @@ data class AppState(
|
|||
SaveWalletMiddleware().middleware,
|
||||
LockUserWalletsTimerMiddleware().middleware,
|
||||
AccessCodeRequestPolicyMiddleware().middleware,
|
||||
SignInMiddleware.middleware,
|
||||
DaggerGraphMiddleware.daggerGraphMiddleware,
|
||||
LegacyMiddleware.legacyMiddleware,
|
||||
TradeCryptoMiddleware.middleware,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import android.content.Context
|
|||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.IntroductionProcess
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.tap.common.extensions.dispatchDialogHide
|
||||
import com.tangem.tap.common.feedback.ScanFailsEmail
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -20,7 +20,7 @@ object ScanFailsDialog {
|
|||
setTitle(context.getString(R.string.common_warning))
|
||||
setMessage(R.string.alert_troubleshooting_scan_card_title)
|
||||
setPositiveButton(R.string.alert_button_request_support) { _, _ ->
|
||||
Analytics.send(IntroductionProcess.ButtonRequestSupport())
|
||||
Analytics.send(Basic.ButtonSupport())
|
||||
store.dispatch(GlobalAction.SendEmail(ScanFailsEmail()))
|
||||
}
|
||||
setNeutralButton(R.string.common_cancel) { _, _ -> }
|
||||
|
|
|
|||
|
|
@ -122,34 +122,38 @@ internal class GeneralUserWalletsListManager(
|
|||
appPreferencesStore.get(key = PreferencesKeys.SAVE_USER_WALLETS_KEY, default = false)
|
||||
.distinctUntilChanged()
|
||||
.onEach { shouldSaveUserWallets ->
|
||||
val manager = if (shouldSaveUserWallets) {
|
||||
biometricUserWalletsListManager.copyFrom(runtimeUserWalletsListManager)
|
||||
val possibleManager = if (shouldSaveUserWallets) {
|
||||
biometricUserWalletsListManager
|
||||
} else {
|
||||
runtimeUserWalletsListManager.copyFrom(biometricUserWalletsListManager)
|
||||
runtimeUserWalletsListManager
|
||||
}
|
||||
|
||||
Timber.d("Switch to ${manager::class.java.simpleName}")
|
||||
if (possibleManager == implementation.value) {
|
||||
Timber.e("Switch to same manager ${possibleManager::class.simpleName}")
|
||||
}
|
||||
|
||||
implementation.value = manager
|
||||
Timber.d("Switch to ${possibleManager::class.simpleName}")
|
||||
|
||||
clearOldManager(manager)
|
||||
val previousManager = implementation.value
|
||||
implementation.value = copySelectedUserWallet(
|
||||
sourceManager = previousManager,
|
||||
destinationManager = possibleManager,
|
||||
)
|
||||
|
||||
previousManager.clear()
|
||||
}
|
||||
.flowOn(dispatchers.io)
|
||||
.launchIn(applicationScope)
|
||||
}
|
||||
|
||||
/** Copy data from [old] manager */
|
||||
private suspend fun UserWalletsListManager.copyFrom(old: UserWalletsListManager): UserWalletsListManager {
|
||||
old.selectedUserWalletSync?.let { this.save(it) }
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
private suspend fun clearOldManager(current: UserWalletsListManager) {
|
||||
if (current == biometricUserWalletsListManager) {
|
||||
runtimeUserWalletsListManager.clear()
|
||||
} else {
|
||||
biometricUserWalletsListManager.clear()
|
||||
private suspend fun copySelectedUserWallet(
|
||||
sourceManager: UserWalletsListManager,
|
||||
destinationManager: UserWalletsListManager,
|
||||
): UserWalletsListManager {
|
||||
sourceManager.selectedUserWalletSync?.let { selectedWallet ->
|
||||
destinationManager.save(selectedWallet, canOverride = true)
|
||||
}
|
||||
|
||||
return destinationManager
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import com.tangem.tap.features.details.redux.walletconnect.WalletForSession
|
|||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
import com.trustwallet.walletconnect.models.session.WCSession
|
||||
import timber.log.Timber
|
||||
import java.io.FileNotFoundException
|
||||
import java.nio.charset.Charset
|
||||
|
||||
class WalletConnectRepository(val context: Application) {
|
||||
|
|
@ -35,6 +36,8 @@ class WalletConnectRepository(val context: Application) {
|
|||
val json = context.readFileText(FILE_NAME_PREFIX_SESSIONS)
|
||||
.hexToUtf8()
|
||||
walletConnectAdapter.fromJson(json)!!.map { it.toSession() }
|
||||
} catch (e: FileNotFoundException) {
|
||||
emptyList()
|
||||
} catch (exception: Exception) {
|
||||
Timber.w(exception)
|
||||
emptyList()
|
||||
|
|
|
|||
|
|
@ -14,12 +14,9 @@ import com.walletconnect.android.CoreClient
|
|||
import com.walletconnect.android.relay.ConnectionType
|
||||
import com.walletconnect.web3.wallet.client.Wallet
|
||||
import com.walletconnect.web3.wallet.client.Web3Wallet
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -192,8 +189,8 @@ class WalletConnectRepositoryImpl @Inject constructor(
|
|||
if (sessionDelete is Wallet.Model.SessionDelete.Success) {
|
||||
scope.launch {
|
||||
_events.emit(WalletConnectEvents.SessionDeleted(sessionDelete.topic))
|
||||
updateSessionsInternal().join()
|
||||
}
|
||||
updateSessions()
|
||||
}
|
||||
Timber.d("onSessionDelete: $sessionDelete")
|
||||
}
|
||||
|
|
@ -209,21 +206,21 @@ class WalletConnectRepositoryImpl @Inject constructor(
|
|||
accounts = userNamespaces?.flatMap { it.value } ?: emptyList(),
|
||||
),
|
||||
)
|
||||
updateSessionsInternal().join()
|
||||
}
|
||||
updateSessions()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSessionUpdateResponse(sessionUpdateResponse: Wallet.Model.SessionUpdateResponse) {
|
||||
// Triggered when wallet receives the session update response from Dapp
|
||||
Timber.d("onSessionUpdateResponse: $sessionUpdateResponse")
|
||||
updateSessions()
|
||||
updateSessionsInternal()
|
||||
}
|
||||
|
||||
override fun onConnectionStateChange(state: Wallet.Model.ConnectionState) {
|
||||
// Triggered whenever the connection state is changed
|
||||
Timber.d("onConnectionStateChange: $state")
|
||||
if (state.isAvailable) updateSessions()
|
||||
if (state.isAvailable) updateSessionsInternal()
|
||||
}
|
||||
|
||||
override fun onError(error: Wallet.Model.Error) {
|
||||
|
|
@ -416,7 +413,7 @@ class WalletConnectRepositoryImpl @Inject constructor(
|
|||
dAppUrl = session?.url ?: "",
|
||||
),
|
||||
)
|
||||
updateSessions()
|
||||
updateSessionsInternal()
|
||||
Timber.d("Disconnected successfully: $it")
|
||||
},
|
||||
onError = {
|
||||
|
|
@ -440,20 +437,22 @@ class WalletConnectRepositoryImpl @Inject constructor(
|
|||
}
|
||||
|
||||
override fun updateSessions() {
|
||||
scope.launch {
|
||||
val availableSessions = Web3Wallet.getListOfActiveSessions()
|
||||
.map {
|
||||
WalletConnectSession(
|
||||
topic = it.topic,
|
||||
icon = it.metaData?.icons?.firstOrNull(),
|
||||
name = it.metaData?.name,
|
||||
url = it.metaData?.url,
|
||||
)
|
||||
}
|
||||
Timber.d("Available sessions: $availableSessions")
|
||||
currentSessions = availableSessions
|
||||
_activeSessions.emit(availableSessions)
|
||||
}
|
||||
updateSessionsInternal()
|
||||
}
|
||||
|
||||
private fun updateSessionsInternal(): Job = scope.launch {
|
||||
val availableSessions = Web3Wallet.getListOfActiveSessions()
|
||||
.map {
|
||||
WalletConnectSession(
|
||||
topic = it.topic,
|
||||
icon = it.metaData?.icons?.firstOrNull(),
|
||||
name = it.metaData?.name,
|
||||
url = it.metaData?.url,
|
||||
)
|
||||
}
|
||||
Timber.d("Available sessions: $availableSessions")
|
||||
currentSessions = availableSessions
|
||||
_activeSessions.emit(availableSessions)
|
||||
}
|
||||
|
||||
private fun findMissingNetworks(
|
||||
|
|
|
|||
|
|
@ -37,10 +37,12 @@ class WalletConnectInteractor(
|
|||
suspend fun startListening(userWalletId: String, cardId: String?) {
|
||||
this.userWalletId = userWalletId
|
||||
this.cardId = cardId
|
||||
walletConnectRepository.updateSessions()
|
||||
|
||||
coroutineScope {
|
||||
launch { subscribeToEvents() }
|
||||
launch { subscribeToSessions() }
|
||||
|
||||
walletConnectRepository.updateSessions()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -88,11 +90,12 @@ class WalletConnectInteractor(
|
|||
is WalletConnectEvents.SessionApprovalSuccess -> {
|
||||
sessionsRepository.saveSession(
|
||||
userWallet = userWalletId,
|
||||
session = Session.fromAccounts(
|
||||
session = Session(
|
||||
accounts = wcEvent.accounts,
|
||||
topic = wcEvent.topic,
|
||||
),
|
||||
)
|
||||
walletConnectRepository.updateSessions()
|
||||
handler.onSessionEstablished()
|
||||
}
|
||||
is WalletConnectEvents.SessionDeleted -> {
|
||||
|
|
|
|||
|
|
@ -3,13 +3,4 @@ package com.tangem.tap.domain.walletconnect2.domain.models
|
|||
data class Session(
|
||||
val topic: String,
|
||||
val accounts: List<Account>,
|
||||
) {
|
||||
companion object {
|
||||
fun fromAccounts(accounts: List<Account>, topic: String): Session {
|
||||
return Session(
|
||||
topic = topic,
|
||||
accounts = accounts,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
@ -20,7 +20,7 @@ import com.tangem.domain.models.scan.ScanResponse
|
|||
import com.tangem.domain.userwallets.UserWalletBuilder
|
||||
import com.tangem.domain.userwallets.UserWalletIdBuilder
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.legacy.isLockedSync
|
||||
import com.tangem.domain.wallets.legacy.asLockable
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Settings
|
||||
|
|
@ -39,6 +39,7 @@ import com.tangem.utils.coroutines.saveIn
|
|||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.rekotlin.Action
|
||||
|
|
@ -81,6 +82,7 @@ class DetailsMiddleware {
|
|||
}
|
||||
|
||||
class EraseWalletMiddleware {
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
fun handle(action: DetailsAction.ResetToFactory) {
|
||||
when (action) {
|
||||
is DetailsAction.ResetToFactory.Start -> {
|
||||
|
|
@ -119,8 +121,12 @@ class DetailsMiddleware {
|
|||
|
||||
doBeforeErase()
|
||||
tangemSdkManager.resetToFactorySettings(card.cardId, true)
|
||||
.flatMap { userWalletsListManager.delete(listOfNotNull(userWalletId)) }
|
||||
.flatMap { tangemSdkManager.deleteSavedUserCodes(setOf(card.cardId)) }
|
||||
.flatMap {
|
||||
userWalletsListManager.delete(listOfNotNull(userWalletId))
|
||||
}
|
||||
.flatMap {
|
||||
tangemSdkManager.deleteSavedUserCodes(setOf(card.cardId))
|
||||
}
|
||||
.doOnSuccess {
|
||||
Analytics.send(Settings.CardSettings.FactoryResetFinished())
|
||||
|
||||
|
|
@ -129,7 +135,9 @@ class DetailsMiddleware {
|
|||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet))
|
||||
store.onUserWalletSelected(selectedUserWallet)
|
||||
} else {
|
||||
if (userWalletsListManager.isLockedSync) {
|
||||
val isLocked = runCatching { userWalletsListManager.asLockable()?.isLockedSync }
|
||||
.fold(onSuccess = { true }, onFailure = { false })
|
||||
if (isLocked && userWalletsListManager.hasUserWallets) {
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Welcome))
|
||||
} else {
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
|
|
@ -217,7 +225,7 @@ class DetailsMiddleware {
|
|||
}
|
||||
}
|
||||
is DetailsAction.AppSettings.CheckBiometricsStatus -> {
|
||||
observeBiometricsStatusChanges(state, action.lifecycleScope)
|
||||
observeBiometricsStatusChanges(action.lifecycleScope)
|
||||
}
|
||||
is DetailsAction.AppSettings.EnrollBiometrics -> {
|
||||
enrollBiometrics()
|
||||
|
|
@ -239,20 +247,27 @@ class DetailsMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun observeBiometricsStatusChanges(state: DetailsState, lifecycleScope: LifecycleCoroutineScope) {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
private fun observeBiometricsStatusChanges(lifecycleScope: LifecycleCoroutineScope) {
|
||||
val needEnrollBiometricsFlow = flow {
|
||||
do {
|
||||
val needEnrollBiometrics = runCatching(tangemSdkManager::needEnrollBiometrics).getOrNull()
|
||||
|
||||
if (needEnrollBiometrics != null &&
|
||||
needEnrollBiometrics != state.appSettingsState.needEnrollBiometrics
|
||||
) {
|
||||
store.dispatchWithMain(DetailsAction.AppSettings.BiometricsStatusChanged(needEnrollBiometrics))
|
||||
if (needEnrollBiometrics != null) {
|
||||
emit(needEnrollBiometrics)
|
||||
}
|
||||
|
||||
delay(timeMillis = 500)
|
||||
delay(timeMillis = 200)
|
||||
} while (true)
|
||||
}.saveIn(checkBiometricsStatusJobHolder)
|
||||
}
|
||||
|
||||
needEnrollBiometricsFlow
|
||||
.distinctUntilChanged()
|
||||
.onEach { needEnrollBiometrics ->
|
||||
store.dispatchWithMain(DetailsAction.AppSettings.BiometricsStatusChanged(needEnrollBiometrics))
|
||||
}
|
||||
.flowOn(Dispatchers.IO)
|
||||
.launchIn(lifecycleScope)
|
||||
.saveIn(checkBiometricsStatusJobHolder)
|
||||
}
|
||||
|
||||
private fun enrollBiometrics() {
|
||||
|
|
|
|||
|
|
@ -59,8 +59,7 @@ sealed class WalletConnectAction : Action {
|
|||
) : WalletConnectAction()
|
||||
|
||||
data class FailureEstablishingSession(val session: WCSession?, val error: TapError? = null) : WalletConnectAction()
|
||||
data class SetSessionsRestored(val sessions: List<WalletConnectSession>) :
|
||||
WalletConnectAction()
|
||||
data class SetSessionsRestored(val sessions: List<WalletConnectSession>) : WalletConnectAction()
|
||||
|
||||
data class DisconnectSession(val topic: String, val session: WCSession?) : WalletConnectAction()
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,9 @@ object WalletConnectReducer {
|
|||
is WalletConnectAction.SetNewSessionData -> {
|
||||
state.copy(newSessionData = action.newSession)
|
||||
}
|
||||
is WalletConnectAction.SetSessionsRestored ->
|
||||
WalletConnectState(sessions = action.sessions)
|
||||
is WalletConnectAction.SetSessionsRestored -> state.copy(
|
||||
sessions = action.sessions,
|
||||
)
|
||||
is WalletConnectAction.RemoveSession -> {
|
||||
val sessions =
|
||||
state.sessions.filterNot { it.session.toUri() == action.session.toUri() }
|
||||
|
|
|
|||
|
|
@ -9,15 +9,16 @@ import com.tangem.core.analytics.models.AnalyticsEvent
|
|||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.userwallets.UserWalletBuilder
|
||||
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.*
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.home.redux.HomeMiddleware.NEW_BUY_WALLET_URL
|
||||
import com.tangem.tap.features.signin.redux.SignInAction
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.scope
|
||||
|
|
@ -112,14 +113,32 @@ private fun proceedWithScanResponse(scanResponse: ScanResponse) = scope.launch {
|
|||
Timber.e(error, "Unable to save user wallet")
|
||||
}
|
||||
.doOnSuccess {
|
||||
scope.launch { store.onUserWalletSelected(userWallet) }
|
||||
sendSignedInCardAnalyticsEvent(scanResponse)
|
||||
scope.launch { store.onUserWalletSelected(userWallet = userWallet) }
|
||||
}
|
||||
.doOnResult {
|
||||
store.dispatchOnMain(SignInAction.SetSignInType(Basic.SignedIn.SignInType.Card))
|
||||
navigateTo(AppScreen.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 = userWalletsListManager.walletsCount.toString(),
|
||||
hasBackup = scanResponse.card.backupStatus?.isActive,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun navigateTo(appScreen: AppScreen) {
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(appScreen))
|
||||
delay(HIDE_PROGRESS_DELAY)
|
||||
|
|
|
|||
|
|
@ -80,16 +80,20 @@ object OnboardingHelper {
|
|||
when {
|
||||
// When should save user wallets, then save card without navigate to save wallet screen
|
||||
store.inject(DaggerGraphState::walletsRepository).shouldSaveUserWalletsSync() -> {
|
||||
proceedWithScanResponse(scanResponse, backupCardsIds, hasBackupError)
|
||||
|
||||
store.dispatchOnMain(
|
||||
store.dispatchWithMain(
|
||||
SaveWalletAction.ProvideBackupInfo(
|
||||
scanResponse = scanResponse,
|
||||
accessCode = accessCode,
|
||||
backupCardsIds = backupCardsIds?.toSet(),
|
||||
),
|
||||
)
|
||||
store.dispatchOnMain(SaveWalletAction.Save)
|
||||
|
||||
val toggles = store.inject(DaggerGraphState::userWalletsListManagerFeatureToggles)
|
||||
if (toggles.isGeneralManagerEnabled) {
|
||||
store.dispatchWithMain(SaveWalletAction.SaveWalletAfterBackup(hasBackupError))
|
||||
} else {
|
||||
store.dispatchWithMain(SaveWalletAction.Save)
|
||||
}
|
||||
}
|
||||
// When should not save user wallets but device has biometry and save wallet screen has not been shown,
|
||||
// then open save wallet screen
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import com.tangem.domain.common.extensions.withMainContext
|
|||
import com.tangem.domain.common.util.twinsIsTwinned
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.userwallets.UserWalletIdBuilder
|
||||
import com.tangem.domain.wallets.legacy.isLockedSync
|
||||
import com.tangem.domain.wallets.legacy.asLockable
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Onboarding
|
||||
import com.tangem.tap.common.entities.ProgressState
|
||||
|
|
@ -356,7 +356,9 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
|
||||
private fun getPopBackScreen(): AppScreen {
|
||||
return if (userWalletsListManager.hasUserWallets) {
|
||||
if (userWalletsListManager.isLockedSync) {
|
||||
val isLocked = runCatching { userWalletsListManager.asLockable()?.isLockedSync }
|
||||
.fold(onSuccess = { true }, onFailure = { false })
|
||||
if (isLocked) {
|
||||
AppScreen.Welcome
|
||||
} else {
|
||||
AppScreen.Wallet
|
||||
|
|
|
|||
|
|
@ -27,4 +27,6 @@ internal sealed interface SaveWalletAction : Action {
|
|||
}
|
||||
|
||||
data object SaveWalletWasShown : SaveWalletAction
|
||||
|
||||
data class SaveWalletAfterBackup(val hasBackupError: Boolean) : SaveWalletAction
|
||||
}
|
||||
|
|
@ -1,17 +1,15 @@
|
|||
package com.tangem.tap.features.saveWallet.redux
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.*
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.common.flatMap
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.userwallets.UserWalletBuilder
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.legacy.isLockable
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.MainScreen
|
||||
|
|
@ -19,15 +17,22 @@ import com.tangem.tap.common.analytics.events.Onboarding
|
|||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.dispatchWithMain
|
||||
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.domain.userWalletList.di.provideBiometricImplementation
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Middleware
|
||||
import org.rekotlin.Store
|
||||
import timber.log.Timber
|
||||
|
||||
internal class SaveWalletMiddleware {
|
||||
|
||||
private val saveWalletJobHolder = JobHolder()
|
||||
|
||||
val middleware: Middleware<AppState> = { _, stateProvider ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
|
|
@ -47,6 +52,7 @@ internal class SaveWalletMiddleware {
|
|||
is SaveWalletAction.EnrollBiometrics.Enroll -> enrollBiometrics()
|
||||
is SaveWalletAction.SaveWalletWasShown -> saveWalletWasShown()
|
||||
is SaveWalletAction.Dismiss -> dismiss(state)
|
||||
is SaveWalletAction.SaveWalletAfterBackup -> saveWalletAfterBackup(state, action.hasBackupError)
|
||||
is SaveWalletAction.Save.Success,
|
||||
is SaveWalletAction.ProvideBackupInfo,
|
||||
is SaveWalletAction.CloseError,
|
||||
|
|
@ -57,6 +63,31 @@ internal class SaveWalletMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun saveWalletAfterBackup(state: SaveWalletState, hasBackupError: Boolean) {
|
||||
scope.launch {
|
||||
val backupInfo = state.backupInfo ?: error("Backup info is null")
|
||||
|
||||
val userWallet = UserWalletBuilder(backupInfo.scanResponse)
|
||||
.backupCardsIds(state.backupInfo.backupCardsIds)
|
||||
.hasBackupError(hasBackupError)
|
||||
.build()
|
||||
.guard {
|
||||
Timber.e("User wallet not created")
|
||||
return@launch
|
||||
}
|
||||
|
||||
userWalletsListManager.save(userWallet, canOverride = true)
|
||||
.flatMap {
|
||||
saveAccessCodeIfNeeded(accessCode = backupInfo.accessCode, cardsInWallet = userWallet.cardsInWallet)
|
||||
}
|
||||
.doOnFailure { error ->
|
||||
Timber.e(error, "Unable to save user wallet")
|
||||
}
|
||||
.doOnSuccess { mainScope.launch { store.onUserWalletSelected(userWallet) } }
|
||||
.doOnResult { store.navigateToWallet() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun enrollBiometrics() {
|
||||
store.dispatchOnMain(NavigationAction.OpenBiometricsSettings)
|
||||
}
|
||||
|
|
@ -99,9 +130,7 @@ internal class SaveWalletMiddleware {
|
|||
?: return@launch
|
||||
|
||||
val featureToggles = store.inject(DaggerGraphState::userWalletsListManagerFeatureToggles)
|
||||
if (featureToggles.isGeneralManagerEnabled) {
|
||||
store.inject(DaggerGraphState::walletsRepository).saveShouldSaveUserWallets(item = true)
|
||||
} else {
|
||||
if (!featureToggles.isGeneralManagerEnabled) {
|
||||
provideLockableUserWalletsListManagerIfNot()
|
||||
}
|
||||
|
||||
|
|
@ -134,16 +163,9 @@ internal class SaveWalletMiddleware {
|
|||
}
|
||||
|
||||
store.dispatchOnMain(SaveWalletAction.Save.Success)
|
||||
|
||||
store.dispatchOnMain(
|
||||
if (store.state.navigationState.backStack.contains(AppScreen.Wallet)) {
|
||||
NavigationAction.PopBackTo(AppScreen.Wallet)
|
||||
} else {
|
||||
NavigationAction.NavigateTo(AppScreen.Wallet)
|
||||
},
|
||||
)
|
||||
store.navigateToWallet()
|
||||
}
|
||||
}
|
||||
}.saveIn(saveWalletJobHolder)
|
||||
}
|
||||
|
||||
private fun allowToUseBiometrics(state: SaveWalletState) {
|
||||
|
|
@ -152,10 +174,6 @@ internal class SaveWalletMiddleware {
|
|||
return
|
||||
}
|
||||
|
||||
val scanResponse = state.backupInfo?.scanResponse
|
||||
?: store.state.globalState.scanResponse
|
||||
?: return
|
||||
|
||||
if (state.backupInfo != null) {
|
||||
// TODO: Remove after onboarding refactoring
|
||||
Analytics.send(Onboarding.EnableBiometrics(AnalyticsParam.OnOffState.On))
|
||||
|
|
@ -164,31 +182,30 @@ internal class SaveWalletMiddleware {
|
|||
}
|
||||
|
||||
scope.launch {
|
||||
val userWallet = userWalletsListManager.selectedUserWalletSync
|
||||
?: UserWalletBuilder(scanResponse)
|
||||
.backupCardsIds(state.backupInfo?.backupCardsIds)
|
||||
.build()
|
||||
?: return@launch
|
||||
/*
|
||||
|
||||
store.inject(DaggerGraphState::walletsRepository).saveShouldSaveUserWallets(item = true)
|
||||
* because it will be automatically saved on UserWalletsListManager switch
|
||||
*/
|
||||
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
|
||||
val error = IllegalStateException("No selected user wallet")
|
||||
Timber.e(error, "Unable to save user wallet")
|
||||
store.dispatchWithMain(SaveWalletAction.Save.Error(TangemSdkError.ExceptionError(error)))
|
||||
return@launch
|
||||
}
|
||||
|
||||
saveAccessCodeIfNeeded(accessCode = state.backupInfo?.accessCode, cardsInWallet = userWallet.cardsInWallet)
|
||||
.flatMap {
|
||||
userWalletsListManager.save(userWallet, canOverride = true)
|
||||
}
|
||||
.doOnFailure { error ->
|
||||
store.dispatchWithMain(SaveWalletAction.Save.Error(error))
|
||||
}
|
||||
.doOnSuccess {
|
||||
preferencesStorage.shouldSaveAccessCodes = true
|
||||
store.inject(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
|
||||
isBiometricsRequestPolicy = userWallet.hasAccessCode,
|
||||
)
|
||||
handleSuccessAllowing(selectedUserWallet)
|
||||
}.saveIn(saveWalletJobHolder)
|
||||
}
|
||||
|
||||
store.dispatchOnMain(SaveWalletAction.Save.Success)
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet))
|
||||
}
|
||||
}
|
||||
private suspend fun handleSuccessAllowing(userWallet: UserWallet) {
|
||||
store.inject(DaggerGraphState::walletsRepository).saveShouldSaveUserWallets(item = true)
|
||||
preferencesStorage.shouldSaveAccessCodes = true
|
||||
store.inject(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
|
||||
isBiometricsRequestPolicy = userWallet.hasAccessCode,
|
||||
)
|
||||
|
||||
store.dispatchWithMain(SaveWalletAction.Save.Success)
|
||||
store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Wallet))
|
||||
}
|
||||
|
||||
private suspend fun provideLockableUserWalletsListManagerIfNot() {
|
||||
|
|
@ -223,15 +240,25 @@ internal class SaveWalletMiddleware {
|
|||
cardsInWallet: Set<String>,
|
||||
): CompletionResult<Unit> {
|
||||
return when {
|
||||
accessCode != null -> {
|
||||
accessCode.isNullOrBlank() -> {
|
||||
CompletionResult.Success(Unit)
|
||||
}
|
||||
else -> {
|
||||
tangemSdkManager.saveAccessCode(
|
||||
accessCode = accessCode,
|
||||
cardsIds = cardsInWallet,
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
CompletionResult.Success(Unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun Store<AppState>.navigateToWallet() {
|
||||
dispatchWithMain(
|
||||
if (store.state.navigationState.backStack.contains(AppScreen.Wallet)) {
|
||||
NavigationAction.PopBackTo(AppScreen.Wallet)
|
||||
} else {
|
||||
NavigationAction.NavigateTo(AppScreen.Wallet)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -47,7 +47,9 @@ internal object SaveWalletReducer {
|
|||
needEnrollBiometrics = false,
|
||||
isSaveInProgress = false,
|
||||
)
|
||||
is SaveWalletAction.SaveWalletWasShown -> state
|
||||
is SaveWalletAction.SaveWalletWasShown,
|
||||
is SaveWalletAction.SaveWalletAfterBackup,
|
||||
-> state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package com.tangem.tap.features.signin.redux
|
||||
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import org.rekotlin.Action
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
sealed interface SignInAction : Action {
|
||||
|
||||
data class SetSignInType(val type: Basic.SignedIn.SignInType) : SignInAction
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package com.tangem.tap.features.signin.redux
|
||||
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
object SignInMiddleware {
|
||||
val middleware: Middleware<AppState> = { _, _ ->
|
||||
{ next ->
|
||||
{ action -> next(action) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package com.tangem.tap.features.signin.redux
|
||||
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import org.rekotlin.Action
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
object SignInReducer {
|
||||
fun reduce(action: Action, state: AppState): SignInState {
|
||||
if (action !is SignInAction) return state.signInState
|
||||
|
||||
return when (action) {
|
||||
is SignInAction.SetSignInType -> state.signInState.copy(type = action.type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package com.tangem.tap.features.signin.redux
|
||||
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class SignInState(val type: Basic.SignedIn.SignInType? = null)
|
||||
|
|
@ -25,7 +25,6 @@ import com.tangem.tap.common.extensions.onUserWalletSelected
|
|||
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.features.signin.redux.SignInAction
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -98,7 +97,6 @@ internal class WelcomeMiddleware {
|
|||
signInType = Basic.SignedIn.SignInType.Biometric,
|
||||
)
|
||||
|
||||
store.dispatchWithMain(SignInAction.SetSignInType(Basic.SignedIn.SignInType.Biometric))
|
||||
store.dispatchWithMain(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
store.dispatchWithMain(WelcomeAction.ProceedWithBiometrics.Success)
|
||||
store.onUserWalletSelected(userWallet = selectedUserWallet)
|
||||
|
|
@ -128,7 +126,6 @@ internal class WelcomeMiddleware {
|
|||
.doOnSuccess {
|
||||
sendSignedInAnalyticsEvent(scanResponse = scanResponse, signInType = Basic.SignedIn.SignInType.Card)
|
||||
|
||||
store.dispatchWithMain(SignInAction.SetSignInType(Basic.SignedIn.SignInType.Card))
|
||||
store.dispatchWithMain(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
store.dispatchWithMain(WelcomeAction.ProceedWithCard.Success)
|
||||
store.onUserWalletSelected(userWallet = userWallet)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.tap.network.auth
|
||||
|
||||
import com.tangem.lib.auth.AppVersionProvider
|
||||
import com.tangem.wallet.BuildConfig
|
||||
|
||||
internal class DefaultAppVersionProvider : AppVersionProvider {
|
||||
|
||||
override fun getAppVersion(): String {
|
||||
return BuildConfig.VERSION_NAME
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,10 @@ package com.tangem.tap.network.auth.di
|
|||
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.lib.auth.AppVersionProvider
|
||||
import com.tangem.lib.auth.AuthProvider
|
||||
import com.tangem.lib.auth.ExpressAuthProvider
|
||||
import com.tangem.tap.network.auth.DefaultAppVersionProvider
|
||||
import com.tangem.tap.network.auth.DefaultAuthProvider
|
||||
import com.tangem.tap.network.auth.DefaultExpressAuthProvider
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
|
|
@ -34,4 +36,10 @@ class AuthModule {
|
|||
configManager = configManager,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppVersionProvider(): AppVersionProvider {
|
||||
return DefaultAppVersionProvider()
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import com.tangem.datasource.api.tangemTech.TangemTechApi
|
|||
import com.tangem.datasource.utils.RequestHeader.*
|
||||
import com.tangem.datasource.utils.addHeaders
|
||||
import com.tangem.datasource.utils.addLoggers
|
||||
import com.tangem.lib.auth.AppVersionProvider
|
||||
import com.tangem.lib.auth.ExpressAuthProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -30,6 +31,7 @@ class NetworkModule {
|
|||
@NetworkMoshi moshi: Moshi,
|
||||
@ApplicationContext context: Context,
|
||||
expressAuthProvider: ExpressAuthProvider,
|
||||
appVersionProvider: AppVersionProvider,
|
||||
): TangemExpressApi {
|
||||
val url = if (BuildConfig.ENVIRONMENT == "dev") {
|
||||
DEV_EXPRESS_BASE_URL
|
||||
|
|
@ -43,6 +45,7 @@ class NetworkModule {
|
|||
.client(
|
||||
OkHttpClient.Builder()
|
||||
.addHeaders(Express(expressAuthProvider))
|
||||
.addHeaders(AppVersionPlatformHeaders(appVersionProvider))
|
||||
.addLoggers(context)
|
||||
.build(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.datasource.utils
|
||||
|
||||
import com.tangem.lib.auth.AppVersionProvider
|
||||
import com.tangem.lib.auth.AuthBearerProvider
|
||||
import com.tangem.lib.auth.AuthProvider
|
||||
import com.tangem.lib.auth.ExpressAuthProvider
|
||||
|
|
@ -30,4 +31,9 @@ sealed class RequestHeader(vararg pairs: Pair<String, () -> String>) {
|
|||
class AuthBearerHeader(authBearerProvider: AuthBearerProvider) : RequestHeader(
|
||||
"Authorization" to { "Bearer " + authBearerProvider.getApiKey() },
|
||||
)
|
||||
|
||||
class AppVersionPlatformHeaders(appVersionProvider: AppVersionProvider) : RequestHeader(
|
||||
"version" to { appVersionProvider.getAppVersion() },
|
||||
"platform" to { "android" },
|
||||
)
|
||||
}
|
||||
|
|
@ -34,7 +34,9 @@ import com.tangem.core.ui.utils.BigDecimalFormatter
|
|||
*/
|
||||
@Composable
|
||||
fun MarketPriceBlock(state: MarketPriceBlockState, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
var rootWidth by remember { mutableIntStateOf(value = 0) }
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.primary,
|
||||
|
|
@ -42,27 +44,13 @@ fun MarketPriceBlock(state: MarketPriceBlockState, modifier: Modifier = Modifier
|
|||
)
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = TangemTheme.dimens.size72)
|
||||
.padding(all = TangemTheme.dimens.spacing12),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
.padding(all = TangemTheme.dimens.spacing12)
|
||||
.onSizeChanged { rootWidth = it.width },
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
var rootWidth by remember { mutableIntStateOf(value = 0) }
|
||||
|
||||
Column(
|
||||
modifier = Modifier.onSizeChanged { rootWidth = it.width },
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
Title(currencyName = state.currencySymbol)
|
||||
Content(state = state, rootWidth = rootWidth)
|
||||
}
|
||||
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size20),
|
||||
painter = painterResource(id = R.drawable.ic_chevron_right_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
Title(currencyName = state.currencySymbol)
|
||||
Content(state = state, rootWidth = rootWidth)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -478,11 +478,11 @@ internal class StateBuilder(
|
|||
fromToken: CryptoCurrency,
|
||||
warnings: MutableList<SwapWarning>,
|
||||
) {
|
||||
val feeEnoughState = quoteModel.preparedSwapConfigState.feeState
|
||||
if (feeEnoughState is SwapFeeState.NotEnough &&
|
||||
quoteModel.preparedSwapConfigState.isBalanceEnough &&
|
||||
quoteModel.permissionState !is PermissionDataState.PermissionLoading
|
||||
) {
|
||||
val feeEnoughState = quoteModel.preparedSwapConfigState.feeState as? SwapFeeState.NotEnough ?: return
|
||||
val needShowCoverWarning = quoteModel.preparedSwapConfigState.isBalanceEnough &&
|
||||
quoteModel.permissionState !is PermissionDataState.PermissionLoading &&
|
||||
feeEnoughState.feeCurrency != fromToken
|
||||
if (needShowCoverWarning) {
|
||||
warnings.add(
|
||||
SwapWarning.UnableToCoverFeeWarning(
|
||||
createUnableToCoverFeeNotificationConfig(
|
||||
|
|
@ -1144,7 +1144,6 @@ internal class StateBuilder(
|
|||
fun showSelectProviderBottomSheet(
|
||||
uiState: SwapStateHolder,
|
||||
selectedProviderId: String,
|
||||
bestRatedProviderId: String,
|
||||
pricesLowerBest: Map<String, Float>,
|
||||
providersStates: Map<SwapProvider, SwapState>,
|
||||
unavailableProviders: List<SwapProvider>,
|
||||
|
|
@ -1152,7 +1151,7 @@ internal class StateBuilder(
|
|||
): SwapStateHolder {
|
||||
val availableProvidersStates = providersStates.entries
|
||||
.mapNotNull {
|
||||
it.convertToProviderBottomSheetState(pricesLowerBest, bestRatedProviderId, actions.onProviderSelect)
|
||||
it.convertToProviderBottomSheetState(pricesLowerBest, actions.onProviderSelect)
|
||||
}
|
||||
.sortedWith(ProviderPercentDiffComparator)
|
||||
val unavailableProviderStates = unavailableProviders.map {
|
||||
|
|
@ -1300,7 +1299,6 @@ internal class StateBuilder(
|
|||
|
||||
private fun Map.Entry<SwapProvider, SwapState>.convertToProviderBottomSheetState(
|
||||
pricesLowerBest: Map<String, Float>,
|
||||
bestRatedProviderId: String,
|
||||
onProviderSelect: (String) -> Unit,
|
||||
): ProviderState? {
|
||||
val provider = this.key
|
||||
|
|
@ -1308,7 +1306,6 @@ internal class StateBuilder(
|
|||
is SwapState.EmptyAmountState -> null
|
||||
is SwapState.QuotesLoadedState -> {
|
||||
provider.convertToContentSelectableProviderState(
|
||||
isBestRate = bestRatedProviderId == provider.providerId,
|
||||
state = state,
|
||||
onProviderClick = onProviderSelect,
|
||||
pricesLowerBest = pricesLowerBest,
|
||||
|
|
@ -1447,7 +1444,6 @@ internal class StateBuilder(
|
|||
}
|
||||
|
||||
private fun SwapProvider.convertToContentSelectableProviderState(
|
||||
isBestRate: Boolean,
|
||||
state: SwapState.QuotesLoadedState,
|
||||
selectionType: ProviderState.SelectionType,
|
||||
pricesLowerBest: Map<String, Float>,
|
||||
|
|
@ -1457,8 +1453,6 @@ internal class StateBuilder(
|
|||
val rateString = toTokenInfo.tokenAmount.getFormattedCryptoAmount(toTokenInfo.cryptoCurrencyStatus.currency)
|
||||
val additionalBadge = if (state.permissionState is PermissionDataState.PermissionReadyForRequest) {
|
||||
ProviderState.AdditionalBadge.PermissionRequired
|
||||
} else if (isBestRate) {
|
||||
ProviderState.AdditionalBadge.BestTrade
|
||||
} else {
|
||||
ProviderState.AdditionalBadge.Empty
|
||||
}
|
||||
|
|
|
|||
|
|
@ -940,7 +940,6 @@ internal class SwapViewModel @Inject constructor(
|
|||
selectedProviderId = providerId,
|
||||
pricesLowerBest = pricesLowerBest,
|
||||
unavailableProviders = unavailableProviders,
|
||||
bestRatedProviderId = findBestQuoteProvider(states)?.providerId ?: providerId,
|
||||
providersStates = dataState.lastLoadedSwapStates,
|
||||
) { uiState = stateBuilder.dismissBottomSheet(uiState) }
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,16 +1,24 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
/**
|
||||
* Reinitialize wallet transformer
|
||||
*
|
||||
* @property prevWalletId reinitialized wallet id
|
||||
* @property newUserWallet new user wallet
|
||||
* @property clickIntents click intents
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class ReinitializeWalletTransformer(
|
||||
private val userWallet: UserWallet,
|
||||
private val prevWalletId: UserWalletId,
|
||||
private val newUserWallet: UserWallet,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) : WalletScreenStateTransformer {
|
||||
|
||||
|
|
@ -18,9 +26,10 @@ internal class ReinitializeWalletTransformer(
|
|||
|
||||
override fun transform(prevState: WalletScreenState): WalletScreenState {
|
||||
return prevState.copy(
|
||||
wallets = persistentListOf(
|
||||
walletLoadingStateFactory.create(userWallet),
|
||||
),
|
||||
wallets = prevState.wallets
|
||||
.filterNot { it.walletCardState.id == prevWalletId }
|
||||
.plus(element = walletLoadingStateFactory.create(newUserWallet))
|
||||
.toImmutableList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -223,7 +223,11 @@ internal class WalletViewModel @Inject constructor(
|
|||
)
|
||||
|
||||
stateHolder.update(
|
||||
ReinitializeWalletTransformer(userWallet = action.selectedWallet, clickIntents = clickIntents),
|
||||
ReinitializeWalletTransformer(
|
||||
prevWalletId = action.prevWalletId,
|
||||
newUserWallet = action.selectedWallet,
|
||||
clickIntents = clickIntents,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -214,7 +214,10 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
}
|
||||
|
||||
/**
|
||||
* Reinitialize selected wallet. Example, scanning a new card if wallets saving is turned off
|
||||
* Reinitialize selected wallet.
|
||||
* Uses when:
|
||||
* 1. user scanned a new card but wallets saving is turned off;
|
||||
* 2. user reset Twins.
|
||||
*
|
||||
* @property prevWalletId previous selected wallet id
|
||||
* @property selectedWallet selected wallet
|
||||
|
|
|
|||
|
|
@ -80,11 +80,10 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
override fun onAddBackupCardClick() {
|
||||
analyticsEventHandler.send(MainScreen.NoticeBackupYourWalletTapped)
|
||||
|
||||
prepareOnboardingProcess()
|
||||
router.openOnboardingScreen()
|
||||
prepareAndStartOnboardingProcess()
|
||||
}
|
||||
|
||||
private fun prepareOnboardingProcess() {
|
||||
private fun prepareAndStartOnboardingProcess() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
getSelectedUserWallet()?.let {
|
||||
reduxStateHolder.dispatch(
|
||||
|
|
@ -94,6 +93,8 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
),
|
||||
)
|
||||
}
|
||||
// navigation action shouldn't be out of coroutine to avoid race
|
||||
router.openOnboardingScreen()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -85,9 +85,9 @@ leakcanary = "2.13"
|
|||
# endregion Other libraries
|
||||
|
||||
# region Tangem
|
||||
tangemBlockchainSdk = "develop-546"
|
||||
tangemBlockchainSdk = "develop-547"
|
||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||
tangemCardSdk = "develop-337"
|
||||
tangemCardSdk = "develop-339"
|
||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
# endregion Tangem
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.lib.auth
|
||||
|
||||
interface AppVersionProvider {
|
||||
|
||||
fun getAppVersion(): String
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue