Updated on 2026-08-14
This commit is contained in:
parent
7a202ea6dc
commit
a0e31b3412
18 changed files with 448 additions and 53 deletions
|
|
@ -6,6 +6,8 @@ import com.tangem.tap.common.redux.AppDialog
|
|||
import com.tangem.tap.common.redux.global.GlobalState
|
||||
import com.tangem.tap.common.ui.SimpleAlertDialog
|
||||
import com.tangem.tap.common.ui.SimpleCancelableAlertDialog
|
||||
import com.tangem.tap.features.details.redux.DetailsDialog
|
||||
import com.tangem.tap.features.details.redux.PrivacySetting
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.*
|
||||
import com.tangem.tap.features.onboarding.AddressInfoBottomSheetDialog
|
||||
|
|
@ -131,6 +133,18 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
primaryButtonRes = state.dialog.primaryButtonRes,
|
||||
primaryButtonAction = state.dialog.onOk
|
||||
)
|
||||
is DetailsDialog.ConfirmDisablingSaving -> {
|
||||
val messageRes = when (state.dialog.setting) {
|
||||
PrivacySetting.SAVE_CARDS -> R.string.security_and_privacy_save_card_disable_warning
|
||||
PrivacySetting.SAVE_ACCESS_CODE -> R.string.security_and_privacy_save_password_disable_warning
|
||||
}
|
||||
SimpleCancelableAlertDialog.create(
|
||||
titleRes = R.string.common_warning,
|
||||
messageRes = messageRes,
|
||||
context = context,
|
||||
primaryButtonAction = state.dialog.onOk
|
||||
)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
dialog?.show()
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import com.tangem.tap.common.redux.navigation.FragmentShareTransition
|
|||
import com.tangem.tap.features.details.ui.DetailsConfirmFragment
|
||||
import com.tangem.tap.features.details.ui.DetailsFragment
|
||||
import com.tangem.tap.features.details.ui.DetailsSecurityFragment
|
||||
import com.tangem.tap.features.details.ui.securityprivacy.ChangeAccessCodeFragment
|
||||
import com.tangem.tap.features.details.ui.securityprivacy.SecurityAndPrivacyFragment
|
||||
import com.tangem.tap.features.details.ui.walletconnect.QrScanFragment
|
||||
import com.tangem.tap.features.details.ui.walletconnect.WalletConnectSessionsFragment
|
||||
import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment
|
||||
|
|
@ -90,6 +92,8 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
|
|||
AppScreen.Details -> DetailsFragment()
|
||||
AppScreen.DetailsConfirm -> DetailsConfirmFragment()
|
||||
AppScreen.DetailsSecurity -> DetailsSecurityFragment()
|
||||
AppScreen.SecurityAndPrivacy -> SecurityAndPrivacyFragment()
|
||||
AppScreen.ChangeAccessCode -> ChangeAccessCodeFragment()
|
||||
AppScreen.Disclaimer -> DisclaimerFragment()
|
||||
AppScreen.AddTokens -> AddTokensFragment()
|
||||
AppScreen.AddCustomToken -> AddCustomTokenFragment()
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ enum class AppScreen {
|
|||
OnboardingNote, OnboardingWallet, OnboardingTwins, OnboardingOther,
|
||||
Wallet, WalletDetails,
|
||||
Send,
|
||||
Details, DetailsConfirm, DetailsSecurity,
|
||||
Details, DetailsConfirm, DetailsSecurity, SecurityAndPrivacy, ChangeAccessCode,
|
||||
AddTokens, AddCustomToken,
|
||||
WalletConnectSessions,
|
||||
QrScan
|
||||
|
|
|
|||
|
|
@ -59,5 +59,13 @@ sealed class DetailsAction : Action {
|
|||
}
|
||||
}
|
||||
|
||||
sealed class ManagePrivacy : DetailsAction() {
|
||||
data class SwitchPrivacySetting(val allow: Boolean, val setting: PrivacySetting) :
|
||||
ManagePrivacy()
|
||||
|
||||
data class ConfirmSwitchingSetting(val allow: Boolean, val setting: PrivacySetting) :
|
||||
ManagePrivacy()
|
||||
}
|
||||
|
||||
data class ChangeAppCurrency(val fiatCurrency: FiatCurrency) : DetailsAction()
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import com.tangem.common.core.TangemSdkError
|
|||
import com.tangem.operations.pins.CheckUserCodesResponse
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.AnalyticsParam
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.dispatchNotification
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
|
|
@ -28,24 +29,28 @@ import org.rekotlin.Middleware
|
|||
class DetailsMiddleware {
|
||||
private val eraseWalletMiddleware = EraseWalletMiddleware()
|
||||
private val manageSecurityMiddleware = ManageSecurityMiddleware()
|
||||
private val managePrivacyMiddleware = ManagePrivacyMiddleware()
|
||||
val detailsMiddleware: Middleware<AppState> = { _, _ ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
when (action) {
|
||||
is DetailsAction.ResetToFactory -> eraseWalletMiddleware.handle(action)
|
||||
is DetailsAction.ManageSecurity -> manageSecurityMiddleware.handle(action)
|
||||
is DetailsAction.ManagePrivacy -> managePrivacyMiddleware.handle(action)
|
||||
is DetailsAction.ShowDisclaimer -> {
|
||||
store.dispatch(DisclaimerAction.ShowAcceptedDisclaimer)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer))
|
||||
}
|
||||
is DetailsAction.ReCreateTwinsWallet -> {
|
||||
val wallet = store.state.walletState.walletManagers.map { it.wallet }.firstOrNull()
|
||||
val wallet =
|
||||
store.state.walletState.walletManagers.map { it.wallet }.firstOrNull()
|
||||
if (wallet == null) {
|
||||
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins))
|
||||
} else {
|
||||
if (wallet.hasSendableAmountsOrPendingTransactions()) {
|
||||
val walletIsNotEmpty = store.state.globalState.resources.strings.walletIsNotEmpty
|
||||
val walletIsNotEmpty =
|
||||
store.state.globalState.resources.strings.walletIsNotEmpty
|
||||
store.dispatchNotification(walletIsNotEmpty)
|
||||
} else {
|
||||
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet))
|
||||
|
|
@ -56,7 +61,12 @@ class DetailsMiddleware {
|
|||
is DetailsAction.CreateBackup -> {
|
||||
store.state.detailsState.scanResponse?.let {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingWallet))
|
||||
store.dispatch(GlobalAction.Onboarding.Start(it, fromHomeScreen = false))
|
||||
store.dispatch(
|
||||
GlobalAction.Onboarding.Start(
|
||||
it,
|
||||
fromHomeScreen = false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -121,13 +131,22 @@ class DetailsMiddleware {
|
|||
val simulatedResponse = CheckUserCodesResponse(
|
||||
action.card.isAccessCodeSet, action.card.isPasscodeSet ?: false
|
||||
)
|
||||
store.dispatch(DetailsAction.ManageSecurity.SetCurrentOption(simulatedResponse))
|
||||
store.dispatch(
|
||||
DetailsAction.ManageSecurity.SetCurrentOption(
|
||||
simulatedResponse
|
||||
)
|
||||
)
|
||||
store.dispatch(DetailsAction.ManageSecurity.OpenSecurity)
|
||||
} else {
|
||||
scope.launch {
|
||||
when (val response = tangemSdkManager.checkUserCodes(action.card.cardId)) {
|
||||
when (val response =
|
||||
tangemSdkManager.checkUserCodes(action.card.cardId)) {
|
||||
is CompletionResult.Success -> {
|
||||
store.dispatchOnMain(DetailsAction.ManageSecurity.SetCurrentOption(response.data))
|
||||
store.dispatchOnMain(
|
||||
DetailsAction.ManageSecurity.SetCurrentOption(
|
||||
response.data
|
||||
)
|
||||
)
|
||||
store.dispatchOnMain(DetailsAction.ManageSecurity.OpenSecurity)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
|
|
@ -152,7 +171,8 @@ class DetailsMiddleware {
|
|||
}
|
||||
is DetailsAction.ManageSecurity.SaveChanges -> {
|
||||
val cardId = store.state.detailsState.scanResponse?.card?.cardId
|
||||
val selectedOption = store.state.detailsState.securityScreenState?.selectedOption
|
||||
val selectedOption =
|
||||
store.state.detailsState.securityScreenState?.selectedOption
|
||||
scope.launch {
|
||||
val result = when (selectedOption) {
|
||||
SecurityOption.LongTap -> tangemSdkManager.setLongTap(cardId)
|
||||
|
|
@ -196,4 +216,26 @@ class DetailsMiddleware {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ManagePrivacyMiddleware {
|
||||
fun handle(action: DetailsAction.ManagePrivacy) {
|
||||
when (action) {
|
||||
is DetailsAction.ManagePrivacy.ConfirmSwitchingSetting -> {
|
||||
// TODO()
|
||||
}
|
||||
is DetailsAction.ManagePrivacy.SwitchPrivacySetting -> {
|
||||
if (action.allow) {
|
||||
store.dispatch(
|
||||
DetailsAction.ManagePrivacy.ConfirmSwitchingSetting(
|
||||
action.allow,
|
||||
action.setting
|
||||
)
|
||||
)
|
||||
} else {
|
||||
store.dispatchDialogShow(DetailsDialog.ConfirmDisablingSaving(action.setting))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import com.tangem.tap.domain.extensions.isWalletDataSupported
|
|||
import com.tangem.tap.domain.extensions.signedHashesCount
|
||||
import com.tangem.tap.features.wallet.models.hasSendableAmountsOrPendingTransactions
|
||||
import com.tangem.tap.store
|
||||
import java.util.EnumSet
|
||||
import org.rekotlin.Action
|
||||
import java.util.*
|
||||
|
||||
class DetailsReducer {
|
||||
companion object {
|
||||
|
|
@ -32,6 +32,9 @@ private fun internalReduce(action: Action, state: AppState): DetailsState {
|
|||
is DetailsAction.ManageSecurity -> {
|
||||
handleSecurityAction(action, detailsState)
|
||||
}
|
||||
is DetailsAction.ManagePrivacy -> {
|
||||
handlePrivacyAction(action, detailsState)
|
||||
}
|
||||
is DetailsAction.ChangeAppCurrency ->
|
||||
detailsState.copy(appCurrency = action.fiatCurrency)
|
||||
|
||||
|
|
@ -154,6 +157,20 @@ private fun handleSecurityAction(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handlePrivacyAction(
|
||||
action: DetailsAction.ManagePrivacy, state: DetailsState,
|
||||
): DetailsState {
|
||||
return when (action) {
|
||||
is DetailsAction.ManagePrivacy.ConfirmSwitchingSetting -> {
|
||||
when (action.setting) {
|
||||
PrivacySetting.SAVE_CARDS -> state.copy(saveCards = action.allow)
|
||||
PrivacySetting.SAVE_ACCESS_CODE -> state.copy(savePasswords = action.allow)
|
||||
}
|
||||
}
|
||||
is DetailsAction.ManagePrivacy.SwitchPrivacySetting -> state
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareAllowedSecurityOptions(
|
||||
card: Card?, currentSecurityOption: SecurityOption?,
|
||||
): EnumSet<SecurityOption> {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.blockchain.common.Wallet
|
|||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.entities.Button
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.StateType
|
||||
|
|
@ -20,7 +21,9 @@ data class DetailsState(
|
|||
val securityScreenState: SecurityScreenState? = null,
|
||||
val cardTermsOfUseUrl: Uri? = null,
|
||||
val createBackupAllowed: Boolean = false,
|
||||
val appCurrency: FiatCurrency = FiatCurrency.Default
|
||||
val appCurrency: FiatCurrency = FiatCurrency.Default,
|
||||
val saveCards: Boolean = true,
|
||||
val savePasswords: Boolean = true
|
||||
) : StateType {
|
||||
|
||||
// if you do not delegate - the application crashes on startup,
|
||||
|
|
@ -48,4 +51,16 @@ data class SecurityScreenState(
|
|||
val buttonProceed: Button = Button(true),
|
||||
)
|
||||
|
||||
enum class SecurityOption { LongTap, PassCode, AccessCode }
|
||||
enum class SecurityOption { LongTap, PassCode, AccessCode }
|
||||
|
||||
|
||||
sealed interface DetailsDialog : StateDialog {
|
||||
data class ConfirmDisablingSaving(val setting: PrivacySetting) : DetailsDialog {
|
||||
val onOk: () -> Unit =
|
||||
{ store.dispatch(DetailsAction.ManagePrivacy.ConfirmSwitchingSetting(false, setting)) }
|
||||
}
|
||||
}
|
||||
|
||||
enum class PrivacySetting {
|
||||
SAVE_CARDS, SAVE_ACCESS_CODE
|
||||
}
|
||||
|
|
@ -15,7 +15,6 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
|
|||
import com.tangem.tap.domain.extensions.isMultiwalletAllowed
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
import com.tangem.tap.features.details.redux.SecurityOption
|
||||
import com.tangem.tap.features.feedback.FeedbackEmail
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -132,17 +131,10 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
|
|||
store.dispatch(NavigationAction.NavigateTo(AppScreen.WalletConnectSessions))
|
||||
}
|
||||
|
||||
llManageSecurity.setOnClickListener {
|
||||
store.dispatch(DetailsAction.ManageSecurity.CheckCurrentSecurityOption(state.scanResponse!!.card))
|
||||
tvSecurityAndPrivacy.setOnClickListener {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.SecurityAndPrivacy))
|
||||
}
|
||||
|
||||
val currentSecurity = when (state.securityScreenState?.currentOption) {
|
||||
SecurityOption.LongTap -> R.string.details_manage_security_long_tap
|
||||
SecurityOption.PassCode -> R.string.details_manage_security_passcode
|
||||
SecurityOption.AccessCode -> R.string.details_manage_security_access_code
|
||||
null -> null
|
||||
}
|
||||
currentSecurity?.let { tvSecurity.text = getString(it) }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.tap.features.details.ui.securityprivacy
|
||||
|
||||
import com.tangem.tap.features.BaseStoreFragment
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
class ChangeAccessCodeFragment : BaseStoreFragment(R.layout.view_compose_fragment),
|
||||
StoreSubscriber<DetailsState> {
|
||||
|
||||
|
||||
override fun newState(state: DetailsState) {
|
||||
|
||||
}
|
||||
|
||||
override fun subscribeToStore() {
|
||||
store.subscribe(this) { state ->
|
||||
state.skipRepeats { oldState, newState ->
|
||||
oldState.detailsState == newState.detailsState
|
||||
}.select { it.detailsState }
|
||||
}
|
||||
storeSubscribersList.add(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
package com.tangem.tap.features.details.ui.securityprivacy
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.BaseStoreFragment
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
import com.tangem.tap.features.details.redux.PrivacySetting
|
||||
import com.tangem.tap.features.details.redux.SecurityOption
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.FragmentDetailsSecurityPrivacyBinding
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
class SecurityAndPrivacyFragment : BaseStoreFragment(R.layout.fragment_details_security_privacy),
|
||||
StoreSubscriber<DetailsState> {
|
||||
|
||||
private val binding: FragmentDetailsSecurityPrivacyBinding
|
||||
by viewBinding(FragmentDetailsSecurityPrivacyBinding::bind)
|
||||
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
with(binding) {
|
||||
tvAccessCode.setOnClickListener {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.ChangeAccessCode))
|
||||
}
|
||||
|
||||
switchSaveCards.setOnCheckedChangeListener { _, isChecked ->
|
||||
store.dispatch(
|
||||
DetailsAction.ManagePrivacy.SwitchPrivacySetting(
|
||||
isChecked,
|
||||
PrivacySetting.SAVE_CARDS
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
switchSavePasswords.setOnCheckedChangeListener { _, isChecked ->
|
||||
store.dispatch(
|
||||
DetailsAction.ManagePrivacy.SwitchPrivacySetting(
|
||||
isChecked,
|
||||
PrivacySetting.SAVE_ACCESS_CODE
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun newState(state: DetailsState) {
|
||||
with(binding) {
|
||||
llAccessManagement.setOnClickListener {
|
||||
store.dispatch(
|
||||
DetailsAction.ManageSecurity.CheckCurrentSecurityOption(
|
||||
state.scanResponse!!.card
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val currentSecurity = when (state.securityScreenState?.currentOption) {
|
||||
SecurityOption.LongTap -> R.string.details_manage_security_long_tap
|
||||
SecurityOption.PassCode -> R.string.details_manage_security_passcode
|
||||
SecurityOption.AccessCode -> R.string.details_manage_security_access_code
|
||||
null -> null
|
||||
}
|
||||
currentSecurity?.let { tvAccessManagement.text = getString(it) }
|
||||
|
||||
switchSaveCards.isChecked = state.saveCards
|
||||
switchSavePasswords.isChecked = state.savePasswords
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun subscribeToStore() {
|
||||
store.subscribe(this) { state ->
|
||||
state.skipRepeats { oldState, newState ->
|
||||
oldState.detailsState == newState.detailsState
|
||||
}.select { it.detailsState }
|
||||
}
|
||||
storeSubscribersList.add(this)
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue