Updated on 2026-08-14
This commit is contained in:
parent
186f8f77c4
commit
e8301388db
8 changed files with 89 additions and 63 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ internal data class CardSettingsScreenState(
|
|||
val cardDetails: List<CardInfo>? = null,
|
||||
val onScanCardClick: () -> Unit,
|
||||
val onElementClick: (CardInfo) -> Unit,
|
||||
val onBackClick: () -> Unit,
|
||||
)
|
||||
|
||||
internal sealed class CardInfo(
|
||||
|
|
|
|||
|
|
@ -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<ScanResponse?>(value = null)
|
||||
|
||||
val screenState: MutableStateFlow<CardSettingsScreenState> = 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Bundle>(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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ScanResponse?>(value = null)
|
||||
val scannedScanResponse: StateFlow<ScanResponse?> = _scannedScanResponse
|
||||
|
||||
fun initialize(scanResponse: ScanResponse) {
|
||||
_scannedScanResponse.value = scanResponse
|
||||
}
|
||||
|
||||
fun update(transform: (ScanResponse) -> ScanResponse) {
|
||||
_scannedScanResponse.update {
|
||||
requireNotNull(it)
|
||||
transform(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
_scannedScanResponse.value = null
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue