diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index cd51cff94e..7adedbed8f 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -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), ) } diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index 7cab6dbfbd..59b899ee8b 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -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, diff --git a/app/src/main/java/com/tangem/tap/common/ui/ScanFailsDialog.kt b/app/src/main/java/com/tangem/tap/common/ui/ScanFailsDialog.kt index 0034c0a08a..70ad852f71 100644 --- a/app/src/main/java/com/tangem/tap/common/ui/ScanFailsDialog.kt +++ b/app/src/main/java/com/tangem/tap/common/ui/ScanFailsDialog.kt @@ -4,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) { _, _ -> } diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/GeneralUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/GeneralUserWalletsListManager.kt index e0e160a524..1e01c26cd2 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/GeneralUserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/GeneralUserWalletsListManager.kt @@ -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 } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt index e38e7fcaf1..de77928810 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt @@ -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() diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/WalletConnectRepositoryImpl.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/WalletConnectRepositoryImpl.kt index 4f37c03839..71f910ce82 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/WalletConnectRepositoryImpl.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/WalletConnectRepositoryImpl.kt @@ -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( diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt index c3af721e25..884d3ac5d7 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt @@ -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 -> { diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/Session.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/Session.kt index 4637a99acb..896b6dc97a 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/Session.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/Session.kt @@ -3,13 +3,4 @@ package com.tangem.tap.domain.walletconnect2.domain.models data class Session( val topic: String, val accounts: List, -) { - companion object { - fun fromAccounts(accounts: List, topic: String): Session { - return Session( - topic = topic, - accounts = accounts, - ) - } - } -} \ No newline at end of file +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index f429e3423f..90aa10f2cd 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -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() { diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt index 8e1bb3f2e1..47afd1b484 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt @@ -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) : - WalletConnectAction() + data class SetSessionsRestored(val sessions: List) : WalletConnectAction() data class DisconnectSession(val topic: String, val session: WCSession?) : WalletConnectAction() diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt index 44130862c0..6840f26c63 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt @@ -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() } diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index 8b83c455cf..18ec4fb565 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -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) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt index f6b11bbe7b..4030b48f6b 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt @@ -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 diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt index 90f14188f3..c2fc60baaa 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt @@ -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 diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletAction.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletAction.kt index e5625ad1d2..ff18f45362 100644 --- a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletAction.kt @@ -27,4 +27,6 @@ internal sealed interface SaveWalletAction : Action { } data object SaveWalletWasShown : SaveWalletAction + + data class SaveWalletAfterBackup(val hasBackupError: Boolean) : SaveWalletAction } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt index 344a0909de..67272db72d 100644 --- a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt @@ -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 = { _, 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, ): CompletionResult { return when { - accessCode != null -> { + accessCode.isNullOrBlank() -> { + CompletionResult.Success(Unit) + } + else -> { tangemSdkManager.saveAccessCode( accessCode = accessCode, cardsIds = cardsInWallet, ) } - else -> { - CompletionResult.Success(Unit) - } } } + + private suspend fun Store.navigateToWallet() { + dispatchWithMain( + if (store.state.navigationState.backStack.contains(AppScreen.Wallet)) { + NavigationAction.PopBackTo(AppScreen.Wallet) + } else { + NavigationAction.NavigateTo(AppScreen.Wallet) + }, + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletReducer.kt index b1c7269494..88b5697eca 100644 --- a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletReducer.kt @@ -47,7 +47,9 @@ internal object SaveWalletReducer { needEnrollBiometrics = false, isSaveInProgress = false, ) - is SaveWalletAction.SaveWalletWasShown -> state + is SaveWalletAction.SaveWalletWasShown, + is SaveWalletAction.SaveWalletAfterBackup, + -> state } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/signin/redux/SignInAction.kt b/app/src/main/java/com/tangem/tap/features/signin/redux/SignInAction.kt deleted file mode 100644 index 7978ed3f13..0000000000 --- a/app/src/main/java/com/tangem/tap/features/signin/redux/SignInAction.kt +++ /dev/null @@ -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 -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/signin/redux/SignInMiddleware.kt b/app/src/main/java/com/tangem/tap/features/signin/redux/SignInMiddleware.kt deleted file mode 100644 index 1f44dff0f7..0000000000 --- a/app/src/main/java/com/tangem/tap/features/signin/redux/SignInMiddleware.kt +++ /dev/null @@ -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 = { _, _ -> - { next -> - { action -> next(action) } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/signin/redux/SignInReducer.kt b/app/src/main/java/com/tangem/tap/features/signin/redux/SignInReducer.kt deleted file mode 100644 index 3d5910774d..0000000000 --- a/app/src/main/java/com/tangem/tap/features/signin/redux/SignInReducer.kt +++ /dev/null @@ -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) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/signin/redux/SignInState.kt b/app/src/main/java/com/tangem/tap/features/signin/redux/SignInState.kt deleted file mode 100644 index 27858a8ad7..0000000000 --- a/app/src/main/java/com/tangem/tap/features/signin/redux/SignInState.kt +++ /dev/null @@ -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) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index ca7d00d461..e2ff46f387 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -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) diff --git a/app/src/main/java/com/tangem/tap/network/auth/DefaultAppVersionProvider.kt b/app/src/main/java/com/tangem/tap/network/auth/DefaultAppVersionProvider.kt new file mode 100644 index 0000000000..7987d7ce6e --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/auth/DefaultAppVersionProvider.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt b/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt index fa9aa920a1..0185472a6e 100644 --- a/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt +++ b/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt @@ -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() + } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt index eb61db86b2..e52008bbc5 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt @@ -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(), ) diff --git a/core/datasource/src/main/java/com/tangem/datasource/utils/RequestHeader.kt b/core/datasource/src/main/java/com/tangem/datasource/utils/RequestHeader.kt index d84b00ac2e..bd725ea0b6 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/utils/RequestHeader.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/utils/RequestHeader.kt @@ -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>) { class AuthBearerHeader(authBearerProvider: AuthBearerProvider) : RequestHeader( "Authorization" to { "Bearer " + authBearerProvider.getApiKey() }, ) + + class AppVersionPlatformHeaders(appVersionProvider: AppVersionProvider) : RequestHeader( + "version" to { appVersionProvider.getAppVersion() }, + "platform" to { "android" }, + ) } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlock.kt b/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlock.kt index efbf7b9f2a..873d6dbcb2 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlock.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlock.kt @@ -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) } } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index 70bfd13e22..eaf4ddfa6b 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -478,11 +478,11 @@ internal class StateBuilder( fromToken: CryptoCurrency, warnings: MutableList, ) { - 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, providersStates: Map, unavailableProviders: List, @@ -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.convertToProviderBottomSheetState( pricesLowerBest: Map, - 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, @@ -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 } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index b297169695..d9598de96f 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -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) } }, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ReinitializeWalletTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ReinitializeWalletTransformer.kt index 7caf5d79e0..8b18dcf457 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ReinitializeWalletTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ReinitializeWalletTransformer.kt @@ -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(), ) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 65ca30c4ff..63db20714d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -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, + ), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletsUpdateActionResolver.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletsUpdateActionResolver.kt index 012aee200d..94b310fd4a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletsUpdateActionResolver.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletsUpdateActionResolver.kt @@ -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 diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt index f16f13dda2..1ff5dc6191 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt @@ -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() } } diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 5fe435d39f..a8f85f8c06 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -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 diff --git a/libs/auth/src/main/java/com/tangem/lib/auth/AppVersionProvider.kt b/libs/auth/src/main/java/com/tangem/lib/auth/AppVersionProvider.kt new file mode 100644 index 0000000000..3b8e606e9c --- /dev/null +++ b/libs/auth/src/main/java/com/tangem/lib/auth/AppVersionProvider.kt @@ -0,0 +1,6 @@ +package com.tangem.lib.auth + +interface AppVersionProvider { + + fun getAppVersion(): String +} \ No newline at end of file