Updated on 2026-08-14
This commit is contained in:
parent
e72bf276da
commit
41dea25009
17 changed files with 348 additions and 23 deletions
8
app/src/main/java/com/tangem/tap/ActivityResultCaller.kt
Normal file
8
app/src/main/java/com/tangem/tap/ActivityResultCaller.kt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.tap
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
|
||||
interface ActivityResultCaller {
|
||||
val activityResultLauncher: ActivityResultLauncher<Intent>?
|
||||
}
|
||||
|
|
@ -2,11 +2,18 @@ package com.tangem.tap
|
|||
|
||||
import android.app.Activity
|
||||
import android.app.Application.ActivityLifecycleCallbacks
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import java.util.WeakHashMap
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import java.util.*
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class ForegroundActivityObserver {
|
||||
class ForegroundActivityObserver : ActivityResultCaller {
|
||||
override var activityResultLauncher: ActivityResultLauncher<Intent>? = null
|
||||
private set
|
||||
|
||||
private val activities = WeakHashMap<KClass<out Activity>, Activity>()
|
||||
|
||||
val foregroundActivity: Activity?
|
||||
|
|
@ -15,25 +22,38 @@ class ForegroundActivityObserver {
|
|||
.firstOrNull()
|
||||
?.value
|
||||
|
||||
val callbacks get() = Callbacks()
|
||||
internal val callbacks: ActivityLifecycleCallbacks
|
||||
get() = Callbacks()
|
||||
|
||||
internal inner class Callbacks : ActivityLifecycleCallbacks {
|
||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||
activityResultLauncher = (activity as? AppCompatActivity)?.registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult(),
|
||||
) {
|
||||
/* no-op */
|
||||
}
|
||||
}
|
||||
|
||||
inner class Callbacks : ActivityLifecycleCallbacks {
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
activities[activity::class] = activity
|
||||
}
|
||||
|
||||
override fun onActivityDestroyed(activity: Activity) {
|
||||
activities.remove(activity::class)
|
||||
if (activities.isEmpty()) {
|
||||
activityResultLauncher = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||
}
|
||||
override fun onActivityStarted(activity: Activity) {
|
||||
}
|
||||
|
||||
override fun onActivityPaused(activity: Activity) {
|
||||
}
|
||||
|
||||
override fun onActivityStopped(activity: Activity) {
|
||||
}
|
||||
|
||||
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ import javax.inject.Inject
|
|||
lateinit var store: Store<AppState>
|
||||
|
||||
lateinit var foregroundActivityObserver: ForegroundActivityObserver
|
||||
lateinit var activityResultCaller: ActivityResultCaller
|
||||
lateinit var preferencesStorage: PreferencesStorage
|
||||
lateinit var walletConnectRepository: WalletConnectRepository
|
||||
lateinit var shopService: TangemShopService
|
||||
|
|
@ -82,6 +83,7 @@ class TapApplication : Application(), ImageLoaderFactory {
|
|||
}
|
||||
|
||||
foregroundActivityObserver = ForegroundActivityObserver()
|
||||
activityResultCaller = foregroundActivityObserver
|
||||
registerActivityLifecycleCallbacks(foregroundActivityObserver.callbacks)
|
||||
|
||||
initMoshiConverter()
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteRedu
|
|||
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsReducer
|
||||
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.shop.redux.ShopReducer
|
||||
import com.tangem.tap.features.tokens.redux.TokensReducer
|
||||
|
|
@ -38,6 +39,7 @@ fun appReducer(action: Action, state: AppState?, appStateHolder: AppStateHolder)
|
|||
walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState),
|
||||
shopState = ShopReducer.reduce(action, state.shopState),
|
||||
welcomeState = WelcomeReducer.reduce(action, state),
|
||||
saveWalletState = SaveWalletReducer.reduce(action, state),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import com.tangem.tap.features.onboarding.products.wallet.redux.BackupMiddleware
|
|||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayMiddleware
|
||||
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
|
||||
|
|
@ -86,6 +87,7 @@ data class AppState(
|
|||
BackupMiddleware().backupMiddleware,
|
||||
ShopMiddleware().shopMiddleware,
|
||||
WelcomeMiddleware().middleware,
|
||||
SaveWalletMiddleware().middleware,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ sealed class NavigationAction : Action {
|
|||
|
||||
data class OpenDocument(val url: Uri) : NavigationAction()
|
||||
|
||||
object OpenBiometricsSettings : NavigationAction()
|
||||
|
||||
data class Share(val data: String) : NavigationAction()
|
||||
|
||||
data class ActivityCreated(val activity: WeakReference<AppCompatActivity>) : NavigationAction()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
package com.tangem.tap.common.redux.navigation
|
||||
|
||||
import android.content.Intent
|
||||
import android.hardware.biometrics.BiometricManager
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import com.tangem.tap.activityResultCaller
|
||||
import com.tangem.tap.common.CustomTabsManager
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.openFragment
|
||||
|
|
@ -48,6 +52,28 @@ val navigationMiddleware: Middleware<AppState> = { _, state ->
|
|||
intent.data = action.url
|
||||
navState?.activity?.get()?.startActivity(intent)
|
||||
}
|
||||
is NavigationAction.OpenBiometricsSettings -> {
|
||||
val settingsAction = when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
|
||||
Settings.ACTION_BIOMETRIC_ENROLL
|
||||
}
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -> {
|
||||
Settings.ACTION_FINGERPRINT_ENROLL
|
||||
}
|
||||
else -> {
|
||||
Settings.ACTION_SETTINGS
|
||||
}
|
||||
}
|
||||
val intent = Intent(settingsAction).apply {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
putExtra(
|
||||
Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED,
|
||||
BiometricManager.Authenticators.BIOMETRIC_STRONG,
|
||||
)
|
||||
}
|
||||
}
|
||||
activityResultCaller.activityResultLauncher?.launch(intent)
|
||||
}
|
||||
is NavigationAction.Share -> {
|
||||
navState?.activity?.get()?.shareText(action.data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,19 +5,12 @@ import androidx.annotation.StringRes
|
|||
import com.tangem.Message
|
||||
import com.tangem.TangemSdk
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.CardFilter
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.SuccessResponse
|
||||
import com.tangem.common.UserCodeType
|
||||
import com.tangem.common.*
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.common.core.CardIdDisplayFormat
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.Config
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.core.UserCodeRequestPolicy
|
||||
import com.tangem.common.core.*
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.common.map
|
||||
import com.tangem.common.usersCode.UserCodeRepository
|
||||
import com.tangem.domain.common.CardDTO
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.operations.CommandResponse
|
||||
|
|
@ -43,6 +36,11 @@ import kotlin.coroutines.resume
|
|||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Context) {
|
||||
val canUseBiometry: Boolean
|
||||
get() = tangemSdk.biometricManager.canAuthenticate || canEnrollBiometrics
|
||||
|
||||
val canEnrollBiometrics: Boolean
|
||||
get() = tangemSdk.biometricManager.canEnrollBiometrics
|
||||
|
||||
suspend fun scanProduct(
|
||||
userTokensRepository: UserTokensRepository,
|
||||
|
|
@ -104,6 +102,16 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
|
|||
.map { CardDTO(it) }
|
||||
}
|
||||
|
||||
suspend fun saveAccessCode(accessCode: String, cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
return createUserCodeRepository().save(
|
||||
cardIds = cardsIds,
|
||||
userCode = UserCode(
|
||||
type = UserCodeType.AccessCode,
|
||||
stringValue = accessCode,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun setPasscode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeCommand.changePasscode(null),
|
||||
|
|
@ -136,9 +144,14 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun scanCard(): CompletionResult<CardDTO> {
|
||||
suspend fun scanCard(
|
||||
cardId: String? = null,
|
||||
useBiometricsForAccessCode: Boolean = false,
|
||||
): CompletionResult<CardDTO> {
|
||||
setAccessCodeRequestPolicy(useBiometricsForAccessCode)
|
||||
return runTaskAsyncReturnOnMain(
|
||||
runnable = ScanTask(),
|
||||
cardId = cardId,
|
||||
initialMessage = Message(context.getString(R.string.initial_message_tap_header)),
|
||||
)
|
||||
.map { CardDTO(it) }
|
||||
|
|
@ -190,6 +203,13 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
|
|||
}
|
||||
}
|
||||
|
||||
private fun createUserCodeRepository() = with(tangemSdk) {
|
||||
UserCodeRepository(
|
||||
biometricManager = biometricManager,
|
||||
secureStorage = secureStorage,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val config = Config(
|
||||
linkedTerminal = true,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ data class UserWallet(
|
|||
val cardId: String
|
||||
get() = scanResponse.card.cardId
|
||||
|
||||
val hasAccessCode: Boolean
|
||||
get() = scanResponse.card.isAccessCodeSet
|
||||
|
||||
companion object {
|
||||
suspend operator fun invoke(
|
||||
scanResponse: ScanResponse,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -33,6 +33,24 @@ class PreferencesStorage(applicationContext: Application) {
|
|||
get() = preferences.getLong(CHAT_FIRST_LAUNCH_KEY, 0).takeIf { it != 0L }
|
||||
set(value) = preferences.edit { putLong(CHAT_FIRST_LAUNCH_KEY, value ?: 0) }
|
||||
|
||||
var shouldShowSaveWallet: Boolean
|
||||
get() = preferences.getBoolean(SAVE_WALLET_DIALOG_SHOWN_KEY, true)
|
||||
set(value) = preferences.edit {
|
||||
putBoolean(SAVE_WALLET_DIALOG_SHOWN_KEY, value)
|
||||
}
|
||||
|
||||
var shouldSaveUserWallets: Boolean
|
||||
get() = preferences.getBoolean(SAVE_USER_WALLETS_KEY, false)
|
||||
set(value) = preferences.edit {
|
||||
putBoolean(SAVE_USER_WALLETS_KEY, value)
|
||||
}
|
||||
|
||||
var shouldSaveAccessCodes: Boolean
|
||||
get() = preferences.getBoolean(SAVE_ACCESS_CODES_KEY, false)
|
||||
set(value) = preferences.edit {
|
||||
putBoolean(SAVE_ACCESS_CODES_KEY, value)
|
||||
}
|
||||
|
||||
fun getCountOfLaunches(): Int = preferences.getInt(APP_LAUNCH_COUNT_KEY, 1)
|
||||
|
||||
fun saveTwinsOnboardingShown() {
|
||||
|
|
@ -53,6 +71,9 @@ class PreferencesStorage(applicationContext: Application) {
|
|||
private const val TWINS_ONBOARDING_SHOWN_KEY = "twinsOnboardingShown"
|
||||
private const val APP_LAUNCH_COUNT_KEY = "launchCount"
|
||||
private const val CHAT_FIRST_LAUNCH_KEY = "chatFirstLaunchKey"
|
||||
private const val SAVE_WALLET_DIALOG_SHOWN_KEY = "saveUserWalletShown"
|
||||
private const val SAVE_USER_WALLETS_KEY = "saveUserWallets"
|
||||
private const val SAVE_ACCESS_CODES_KEY = "saveAccessCodes"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue