From e8301388dbeba0290d394c7349a834df9423a7aa Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 1 Aug 2024 13:33:39 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../ui/cardsettings/CardSettingsFragment.kt | 11 +---- .../ui/cardsettings/CardSettingsScreen.kt | 17 ++++---- .../cardsettings/CardSettingsScreenState.kt | 1 + .../ui/cardsettings/CardSettingsViewModel.kt | 33 ++++++++------ .../AccessCodeRecoveryViewModel.kt | 43 ++++++++----------- .../domain/CardSettingsInteractor.kt | 35 +++++++++++++++ .../ui/resetcard/ResetCardViewModel.kt | 4 ++ .../com/tangem/common/routing/AppRoute.kt | 8 +--- 8 files changed, 89 insertions(+), 63 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/domain/CardSettingsInteractor.kt diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsFragment.kt index 6054581056..328815f377 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsFragment.kt @@ -5,11 +5,8 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.fragment.app.viewModels import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.tangem.common.routing.AppRouter import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.screen.ComposeFragment -import com.tangem.tap.common.extensions.dispatchNavigationAction -import com.tangem.tap.store import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -25,12 +22,6 @@ internal class CardSettingsFragment : ComposeFragment() { override fun ScreenContent(modifier: Modifier) { val state by viewModel.screenState.collectAsStateWithLifecycle() - CardSettingsScreen( - modifier = modifier, - state = state, - onBackClick = { - store.dispatchNavigationAction(AppRouter::pop) - }, - ) + CardSettingsScreen(modifier = modifier, state = state) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreen.kt index b0e2785b32..57f1103703 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreen.kt @@ -1,10 +1,13 @@ package com.tangem.tap.features.details.ui.cardsettings import android.content.res.Configuration -import androidx.compose.foundation.* +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -13,18 +16,14 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview import com.tangem.tap.features.details.ui.common.DetailsMainButton import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold import com.tangem.wallet.R @Composable -internal fun CardSettingsScreen( - state: CardSettingsScreenState, - onBackClick: () -> Unit, - modifier: Modifier = Modifier, -) { +internal fun CardSettingsScreen(state: CardSettingsScreenState, modifier: Modifier = Modifier) { val needReadCard = state.cardDetails == null SettingsScreensScaffold( @@ -37,7 +36,7 @@ internal fun CardSettingsScreen( } }, titleRes = R.string.card_settings_title, - onBackClick = onBackClick, + onBackClick = state.onBackClick, ) } @@ -180,7 +179,7 @@ private fun CardSettings(state: CardSettingsScreenState) { // region Preview @Composable private fun CardSettingsScreenStateSample() { - CardSettingsScreen(state = CardSettingsScreenState(onScanCardClick = {}, onElementClick = {}), {}) + CardSettingsScreen(state = CardSettingsScreenState(onBackClick = {}, onScanCardClick = {}, onElementClick = {})) } @Preview(showBackground = true, widthDp = 360) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreenState.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreenState.kt index 46425706b4..bbde2c8579 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreenState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreenState.kt @@ -12,6 +12,7 @@ internal data class CardSettingsScreenState( val cardDetails: List? = null, val onScanCardClick: () -> Unit, val onElementClick: (CardInfo) -> Unit, + val onBackClick: () -> Unit, ) internal sealed class CardInfo( diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt index 47425804ff..e3fe0469f9 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.viewModelScope import com.tangem.common.CompletionResult import com.tangem.common.doOnSuccess import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter import com.tangem.common.routing.bundle.unbundle import com.tangem.core.analytics.Analytics import com.tangem.domain.card.ScanCardProcessor @@ -23,14 +24,14 @@ import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.domain.extensions.signedHashesCount import com.tangem.tap.domain.sdk.TangemSdkManager +import com.tangem.tap.features.details.ui.cardsettings.domain.CardSettingsInteractor import com.tangem.tap.features.details.ui.common.utils.* import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction import com.tangem.tap.store import com.tangem.wallet.R import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.update +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -39,6 +40,7 @@ import javax.inject.Inject internal class CardSettingsViewModel @Inject constructor( private val scanCardProcessor: ScanCardProcessor, private val tangemSdkManager: TangemSdkManager, + private val cardSettingsInteractor: CardSettingsInteractor, savedStateHandle: SavedStateHandle, ) : ViewModel() { @@ -46,24 +48,28 @@ internal class CardSettingsViewModel @Inject constructor( ?.unbundle(UserWalletId.serializer()) ?: error("User wallet ID is required for CardSettingsViewModel") - private val scannedScanResponse = MutableStateFlow(value = null) - val screenState: MutableStateFlow = MutableStateFlow(getInitialState()) + init { + cardSettingsInteractor.scannedScanResponse + .filterNotNull() + .onEach(::updateCardDetails) + .launchIn(viewModelScope) + } + private fun getInitialState() = CardSettingsScreenState( cardDetails = null, onElementClick = ::handleClickingItem, onScanCardClick = ::scanCard, + onBackClick = ::onBackClick, ) private fun scanCard() = viewModelScope.launch { scanCardProcessor.scan(allowsRequestAccessCodeFromRepository = true) .doOnSuccess { scanResponse -> - scannedScanResponse.value = scanResponse - val scannedUserWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build() if (userWalletId == scannedUserWalletId || scannedUserWalletId == null) { - updateCardDetails(scanResponse) + cardSettingsInteractor.initialize(scanResponse) } else { store.dispatchDialogShow( AppDialog.SimpleOkDialogRes( @@ -141,16 +147,14 @@ internal class CardSettingsViewModel @Inject constructor( } } is CardInfo.AccessCodeRecovery -> { - store.dispatchNavigationAction { - push(route = AppRoute.AccessCodeRecovery(userWalletId)) - } + store.dispatchNavigationAction { push(AppRoute.AccessCodeRecovery) } } else -> {} } } private fun resetWalletToFactorySettings() { - val scanResponse = requireNotNull(scannedScanResponse.value) { + val scanResponse = requireNotNull(cardSettingsInteractor.scannedScanResponse.value) { "Impossible to reset card if ScanResponse is null" } @@ -181,7 +185,7 @@ internal class CardSettingsViewModel @Inject constructor( } private fun changeAccessCode() = viewModelScope.launch { - val scanResponse = requireNotNull(scannedScanResponse.value) { "Scan response is null" } + val scanResponse = requireNotNull(cardSettingsInteractor.scannedScanResponse.value) { "Scan response is null" } when (val result = tangemSdkManager.setAccessCode(scanResponse.card.cardId)) { is CompletionResult.Success -> Analytics.send(Settings.CardSettings.UserCodeChanged()) @@ -196,4 +200,9 @@ internal class CardSettingsViewModel @Inject constructor( val isNotAllowed = hasPermanentWallet || cardTypesResolver.isStart2Coin() return !isNotAllowed } + + private fun onBackClick() { + cardSettingsInteractor.clear() + store.dispatchNavigationAction(AppRouter::pop) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/AccessCodeRecoveryViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/AccessCodeRecoveryViewModel.kt index 8404af2beb..d65fb74a54 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/AccessCodeRecoveryViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/AccessCodeRecoveryViewModel.kt @@ -1,23 +1,16 @@ package com.tangem.tap.features.details.ui.cardsettings.coderecovery -import android.os.Bundle -import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import arrow.core.getOrElse import com.tangem.common.doOnSuccess -import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter -import com.tangem.common.routing.bundle.unbundle import com.tangem.core.analytics.Analytics import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Settings import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.domain.sdk.TangemSdkManager +import com.tangem.tap.features.details.ui.cardsettings.domain.CardSettingsInteractor import com.tangem.tap.features.details.ui.common.utils.isAccessCodeRecoveryEnabled import com.tangem.tap.store import dagger.hilt.android.lifecycle.HiltViewModel @@ -28,25 +21,21 @@ import javax.inject.Inject @HiltViewModel internal class AccessCodeRecoveryViewModel @Inject constructor( - private val getUserWalletUseCase: GetUserWalletUseCase, private val tangemSdkManager: TangemSdkManager, - savedStateHandle: SavedStateHandle, + private val cardSettingsInteractor: CardSettingsInteractor, ) : ViewModel() { - private val userWalletId = savedStateHandle.get(AppRoute.AccessCodeRecovery.USER_WALLET_ID_KEY) - ?.unbundle(UserWalletId.serializer()) - ?: error("UserWalletId is required for AccessCodeRecoveryViewModel") + private val scannedScanResponse = cardSettingsInteractor.scannedScanResponse.value + ?: error("Scan response is null") val screenState = MutableStateFlow( value = getInitialState(), ) private fun getInitialState(): AccessCodeRecoveryScreenState { - val userWallet = getUserWallet() - val isEnabled = isAccessCodeRecoveryEnabled( - typeResolver = userWallet.scanResponse.cardTypesResolver, - card = userWallet.scanResponse.card, + typeResolver = scannedScanResponse.cardTypesResolver, + card = scannedScanResponse.card, ) return AccessCodeRecoveryScreenState( @@ -59,11 +48,10 @@ internal class AccessCodeRecoveryViewModel @Inject constructor( } private fun saveChanges() = viewModelScope.launch { - val userWallet = getUserWallet() val isEnabled = screenState.value.enabledSelection tangemSdkManager - .setAccessCodeRecoveryEnabled(userWallet.cardId, isEnabled) + .setAccessCodeRecoveryEnabled(scannedScanResponse.card.cardId, isEnabled) .doOnSuccess { Analytics.send( Settings.CardSettings.AccessCodeRecoveryChanged( @@ -71,6 +59,16 @@ internal class AccessCodeRecoveryViewModel @Inject constructor( ), ) + cardSettingsInteractor.update { scanResponse -> + scanResponse.copy( + card = scanResponse.card.copy( + userSettings = scanResponse.card.userSettings?.copy( + isUserCodeRecoveryAllowed = isEnabled, + ), + ), + ) + } + store.dispatchNavigationAction(AppRouter::pop) } } @@ -78,14 +76,9 @@ internal class AccessCodeRecoveryViewModel @Inject constructor( private fun selectOption(isEnabled: Boolean) { screenState.update { it.copy( + enabledSelection = isEnabled, isSaveChangesEnabled = isEnabled != it.enabledOnCard, ) } } - - private fun getUserWallet(): UserWallet { - return getUserWalletUseCase(userWalletId).getOrElse { - error("Unable to get user wallet $userWalletId: $it") - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/domain/CardSettingsInteractor.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/domain/CardSettingsInteractor.kt new file mode 100644 index 0000000000..73bbf10991 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/domain/CardSettingsInteractor.kt @@ -0,0 +1,35 @@ +package com.tangem.tap.features.details.ui.cardsettings.domain + +import com.tangem.domain.models.scan.ScanResponse +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Interactor for sharing logic and data between all card settings screens + * +[REDACTED_AUTHOR] + */ +@Singleton +internal class CardSettingsInteractor @Inject constructor() { + + private val _scannedScanResponse = MutableStateFlow(value = null) + val scannedScanResponse: StateFlow = _scannedScanResponse + + fun initialize(scanResponse: ScanResponse) { + _scannedScanResponse.value = scanResponse + } + + fun update(transform: (ScanResponse) -> ScanResponse) { + _scannedScanResponse.update { + requireNotNull(it) + transform(it) + } + } + + fun clear() { + _scannedScanResponse.value = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt index 41d0535147..fefbdf122e 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt @@ -23,6 +23,7 @@ import com.tangem.tap.common.analytics.events.Settings import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.common.extensions.onUserWalletSelected import com.tangem.tap.features.details.redux.ResetCardDialog +import com.tangem.tap.features.details.ui.cardsettings.domain.CardSettingsInteractor import com.tangem.tap.features.details.ui.common.utils.getResetToFactoryDescription import com.tangem.tap.store import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE @@ -44,6 +45,7 @@ internal class ResetCardViewModel @Inject constructor( private val deleteWalletUseCase: DeleteWalletUseCase, private val userWalletsListManager: UserWalletsListManager, private val analyticsEventHandler: AnalyticsEventHandler, + private val cardSettingsInteractor: CardSettingsInteractor, savedStateHandle: SavedStateHandle, ) : ViewModel() { @@ -255,6 +257,8 @@ internal class ResetCardViewModel @Inject constructor( } private fun finishFullReset() { + cardSettingsInteractor.clear() + val newSelectedWallet = userWalletsListManager.selectedUserWalletSync if (newSelectedWallet != null) { diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index 8bb8395a7d..20d08dcf6a 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -177,15 +177,9 @@ sealed class AppRoute(val path: String) : Route { } @Serializable - data class AccessCodeRecovery( - val userWalletId: UserWalletId, - ) : AppRoute(path = "/access_code_recovery/${userWalletId.stringValue}"), RouteBundleParams { + data object AccessCodeRecovery : AppRoute(path = "/access_code_recovery"), RouteBundleParams { override fun getBundle(): Bundle = bundle(serializer()) - - companion object { - const val USER_WALLET_ID_KEY = "userWalletId" - } } @Serializable