Updated on 2026-08-14
This commit is contained in:
parent
e72bf276da
commit
41dea25009
17 changed files with 348 additions and 23 deletions
|
|
@ -1,9 +1,16 @@
|
|||
package com.tangem.tap.features.saveWallet.redux
|
||||
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import org.rekotlin.Action
|
||||
|
||||
internal sealed interface SaveWalletAction : Action {
|
||||
data class ProvideAdditionalInfo(
|
||||
val scanResponse: ScanResponse,
|
||||
val accessCode: String?,
|
||||
val backupCardsIds: Set<String>?,
|
||||
) : SaveWalletAction
|
||||
|
||||
object Save : SaveWalletAction {
|
||||
object Success : SaveWalletAction
|
||||
data class Error(val error: TangemError) : SaveWalletAction
|
||||
|
|
@ -12,6 +19,10 @@ internal sealed interface SaveWalletAction : Action {
|
|||
object Dismiss : SaveWalletAction
|
||||
|
||||
object CloseError : SaveWalletAction
|
||||
object EnrollBiometrics : SaveWalletAction {
|
||||
object Enroll : SaveWalletAction
|
||||
object Cancel : SaveWalletAction
|
||||
}
|
||||
|
||||
object SaveWalletWasShown : SaveWalletAction
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
package com.tangem.tap.features.saveWallet.redux
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.common.flatMap
|
||||
import com.tangem.common.map
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.model.UserWallet
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
internal class SaveWalletMiddleware {
|
||||
val middleware: Middleware<AppState> = { _, stateProvider ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
val state = stateProvider()
|
||||
if (action is SaveWalletAction && state != null) {
|
||||
handleAction(action, state.saveWalletState)
|
||||
}
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAction(action: SaveWalletAction, state: SaveWalletState) {
|
||||
when (action) {
|
||||
is SaveWalletAction.Save -> saveWalletIfBiometricsEnrolled(state)
|
||||
is SaveWalletAction.Save.Success -> popBack()
|
||||
is SaveWalletAction.EnrollBiometrics.Enroll -> enrollBiometrics()
|
||||
is SaveWalletAction.SaveWalletWasShown -> saveWalletWasShown()
|
||||
is SaveWalletAction.ProvideAdditionalInfo,
|
||||
is SaveWalletAction.Dismiss,
|
||||
is SaveWalletAction.CloseError,
|
||||
is SaveWalletAction.Save.Error,
|
||||
is SaveWalletAction.EnrollBiometrics,
|
||||
is SaveWalletAction.EnrollBiometrics.Cancel,
|
||||
-> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun enrollBiometrics() {
|
||||
store.dispatchOnMain(NavigationAction.OpenBiometricsSettings)
|
||||
}
|
||||
|
||||
private fun saveWalletIfBiometricsEnrolled(state: SaveWalletState) {
|
||||
if (tangemSdkManager.canEnrollBiometrics) {
|
||||
store.dispatchOnMain(SaveWalletAction.EnrollBiometrics)
|
||||
} else {
|
||||
saveWallet(state)
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveWallet(state: SaveWalletState) {
|
||||
val scanResponse = state.additionalInfo?.scanResponse
|
||||
?: store.state.globalState.scanResponse
|
||||
?: return
|
||||
|
||||
scope.launch {
|
||||
val userWallet = UserWallet(
|
||||
scanResponse = scanResponse,
|
||||
backupCardsIds = state.additionalInfo?.backupCardsIds,
|
||||
)
|
||||
|
||||
userWalletsListManager.save(userWallet)
|
||||
.flatMap {
|
||||
trySaveAccessCode(
|
||||
userWallet = userWallet,
|
||||
additionalInfo = state.additionalInfo,
|
||||
)
|
||||
}
|
||||
.doOnFailure { error ->
|
||||
store.dispatchOnMain(SaveWalletAction.Save.Error(error))
|
||||
}
|
||||
.doOnSuccess {
|
||||
preferencesStorage.shouldSaveUserWallets = true
|
||||
preferencesStorage.shouldSaveAccessCodes = true
|
||||
store.dispatchOnMain(SaveWalletAction.Save.Success)
|
||||
store.onUserWalletSelected(userWallet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun popBack() {
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo())
|
||||
}
|
||||
|
||||
private fun saveWalletWasShown() {
|
||||
preferencesStorage.shouldShowSaveWallet = false
|
||||
}
|
||||
|
||||
private suspend fun trySaveAccessCode(
|
||||
userWallet: UserWallet,
|
||||
additionalInfo: SaveWalletState.WalletAdditionalInfo?,
|
||||
): CompletionResult<Unit> {
|
||||
return when {
|
||||
additionalInfo?.accessCode != null -> {
|
||||
tangemSdkManager.saveAccessCode(
|
||||
accessCode = additionalInfo.accessCode,
|
||||
cardsIds = userWallet.cardsInWallet,
|
||||
)
|
||||
}
|
||||
userWallet.hasAccessCode -> {
|
||||
tangemSdkManager
|
||||
.scanCard(
|
||||
cardId = userWallet.cardId,
|
||||
useBiometricsForAccessCode = true,
|
||||
)
|
||||
.map { /* no-op */ }
|
||||
}
|
||||
else -> {
|
||||
CompletionResult.Success(Unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package com.tangem.tap.features.saveWallet.redux
|
||||
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import org.rekotlin.Action
|
||||
|
||||
internal object SaveWalletReducer {
|
||||
fun reduce(action: Action, state: AppState): SaveWalletState {
|
||||
return if (action is SaveWalletAction) {
|
||||
internalReduce(action, state.saveWalletState)
|
||||
} else state.saveWalletState
|
||||
}
|
||||
|
||||
private fun internalReduce(action: SaveWalletAction, state: SaveWalletState): SaveWalletState {
|
||||
return when (action) {
|
||||
is SaveWalletAction.ProvideAdditionalInfo -> state.copy(
|
||||
additionalInfo = SaveWalletState.WalletAdditionalInfo(
|
||||
scanResponse = action.scanResponse,
|
||||
accessCode = action.accessCode,
|
||||
backupCardsIds = action.backupCardsIds,
|
||||
),
|
||||
)
|
||||
is SaveWalletAction.Save -> state.copy(
|
||||
isSaveInProgress = true,
|
||||
)
|
||||
is SaveWalletAction.Save.Error -> state.copy(
|
||||
error = action.error,
|
||||
isSaveInProgress = false,
|
||||
)
|
||||
is SaveWalletAction.Save.Success -> state.copy(
|
||||
additionalInfo = null,
|
||||
isSaveInProgress = false,
|
||||
)
|
||||
is SaveWalletAction.Dismiss -> state.copy(
|
||||
additionalInfo = null,
|
||||
)
|
||||
is SaveWalletAction.CloseError -> state.copy(
|
||||
error = null,
|
||||
)
|
||||
is SaveWalletAction.EnrollBiometrics -> state.copy(
|
||||
needEnrollBiometrics = true,
|
||||
)
|
||||
is SaveWalletAction.EnrollBiometrics.Enroll,
|
||||
is SaveWalletAction.EnrollBiometrics.Cancel,
|
||||
-> state.copy(
|
||||
needEnrollBiometrics = false,
|
||||
isSaveInProgress = false,
|
||||
)
|
||||
is SaveWalletAction.SaveWalletWasShown -> state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,18 @@
|
|||
package com.tangem.tap.features.saveWallet.redux
|
||||
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import org.rekotlin.StateType
|
||||
|
||||
class SaveWalletState : StateType
|
||||
data class SaveWalletState(
|
||||
val additionalInfo: WalletAdditionalInfo? = null,
|
||||
val isSaveInProgress: Boolean = false,
|
||||
val needEnrollBiometrics: Boolean = false,
|
||||
val error: TangemError? = null,
|
||||
) : StateType {
|
||||
data class WalletAdditionalInfo(
|
||||
val scanResponse: ScanResponse,
|
||||
val accessCode: String?,
|
||||
val backupCardsIds: Set<String>?,
|
||||
)
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import com.tangem.core.ui.components.dialogs.EnrollBiometricsDialogContent
|
|||
import com.tangem.core.ui.fragments.ComposeBottomSheetFragment
|
||||
import com.tangem.core.ui.models.EnrollBiometricsDialog
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.features.details.ui.cardsettings.resolveReference
|
||||
import com.tangem.tap.features.saveWallet.ui.components.SaveWalletScreenContent
|
||||
|
||||
internal class SaveWalletBottomSheetFragment : ComposeBottomSheetFragment<SaveWalletScreenState>() {
|
||||
|
|
@ -43,7 +44,7 @@ internal class SaveWalletBottomSheetFragment : ComposeBottomSheetFragment<SaveWa
|
|||
state: SaveWalletScreenState,
|
||||
) {
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val errorMessage by rememberUpdatedState(newValue = state.error?.customMessage)
|
||||
val errorMessage by rememberUpdatedState(newValue = state.error?.resolveReference())
|
||||
val enrollBiometricsDialog by rememberUpdatedState(newValue = state.enrollBiometricsDialog)
|
||||
|
||||
Box(modifier = modifier) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
package com.tangem.tap.features.saveWallet.ui
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.core.ui.models.EnrollBiometricsDialog
|
||||
import com.tangem.tap.features.details.ui.cardsettings.TextReference
|
||||
|
||||
@Immutable
|
||||
internal data class SaveWalletScreenState(
|
||||
val showProgress: Boolean = false,
|
||||
val enrollBiometricsDialog: EnrollBiometricsDialog? = null,
|
||||
val error: TangemError? = null,
|
||||
val error: TextReference? = null,
|
||||
)
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.tap.features.saveWallet.ui
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.tangem.core.ui.models.EnrollBiometricsDialog
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.features.details.ui.cardsettings.TextReference
|
||||
import com.tangem.tap.features.saveWallet.redux.SaveWalletAction
|
||||
import com.tangem.tap.features.saveWallet.redux.SaveWalletState
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -33,8 +35,16 @@ internal class SaveWalletViewModel : ViewModel(), StoreSubscriber<SaveWalletStat
|
|||
|
||||
override fun newState(state: SaveWalletState) {
|
||||
stateInternal.update { prevState ->
|
||||
// TODO: Update screen state
|
||||
prevState
|
||||
prevState.copy(
|
||||
showProgress = state.isSaveInProgress,
|
||||
enrollBiometricsDialog = if (state.needEnrollBiometrics) createEnrollBiometricsDialog() else null,
|
||||
error = state.error
|
||||
?.takeUnless { it.silent }
|
||||
?.let { error ->
|
||||
error.messageResId?.let { TextReference.Res(it) }
|
||||
?: TextReference.Str(error.customMessage)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -48,4 +58,13 @@ internal class SaveWalletViewModel : ViewModel(), StoreSubscriber<SaveWalletStat
|
|||
.select { it.saveWalletState }
|
||||
}
|
||||
}
|
||||
|
||||
private fun createEnrollBiometricsDialog() = EnrollBiometricsDialog(
|
||||
onEnroll = {
|
||||
store.dispatchOnMain(SaveWalletAction.EnrollBiometrics.Enroll)
|
||||
},
|
||||
onCancel = {
|
||||
store.dispatchOnMain(SaveWalletAction.EnrollBiometrics.Cancel)
|
||||
},
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue