Updated on 2026-08-14
This commit is contained in:
parent
23fd02dc06
commit
8b30a5251f
85 changed files with 2195 additions and 1534 deletions
|
|
@ -9,7 +9,11 @@ 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.details.ui.walletconnect.dialogs.ApproveWcSessionDialog
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.BnbTransactionDialog
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.ClipboardOrScanQrDialog
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialog
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionDialog
|
||||
import com.tangem.tap.features.onboarding.AddressInfoBottomSheetDialog
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.features.onboarding.products.twins.ui.dialog.CreateWalletInterruptDialog
|
||||
|
|
@ -19,7 +23,12 @@ import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.BackupInPro
|
|||
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.ConfirmDiscardingBackupDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.UnfinishedBackupFoundDialog
|
||||
import com.tangem.tap.features.wallet.redux.models.WalletDialog
|
||||
import com.tangem.tap.features.wallet.ui.dialogs.*
|
||||
import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendBottomSheetDialog
|
||||
import com.tangem.tap.features.wallet.ui.dialogs.ChooseTradeActionBottomSheetDialog
|
||||
import com.tangem.tap.features.wallet.ui.dialogs.RussianCardholdersWarningBottomSheetDialog
|
||||
import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog
|
||||
import com.tangem.tap.features.wallet.ui.dialogs.SignedHashesWarningDialog
|
||||
import com.tangem.tap.features.wallet.ui.dialogs.SimpleOkDialog
|
||||
import com.tangem.tap.features.wallet.ui.wallet.CurrencySelectionDialog
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -143,8 +152,8 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
RussianCardholdersWarningBottomSheetDialog(context)
|
||||
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
|
||||
PrivacySetting.SAVE_CARDS -> R.string.app_settings_off_saved_wallet_alert_message
|
||||
PrivacySetting.SAVE_ACCESS_CODE -> R.string.app_settings_off_saved_access_code_alert_message
|
||||
}
|
||||
SimpleCancelableAlertDialog.create(
|
||||
titleRes = R.string.common_warning,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
package com.tangem.tap.common.compose
|
||||
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.Font
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.wallet.R
|
||||
|
||||
object TangemTypography {
|
||||
|
||||
val body1 = TextStyle(
|
||||
fontFamily = FontFamily(Font(R.font.roboto)),
|
||||
fontSize = 16.0.sp,
|
||||
letterSpacing = 0.5.sp,
|
||||
lineHeight = 24.0.sp,
|
||||
)
|
||||
val body2 = TextStyle(
|
||||
fontFamily = FontFamily(Font(R.font.roboto)),
|
||||
fontSize = 14.0.sp,
|
||||
letterSpacing = 0.25.sp,
|
||||
lineHeight = 20.0.sp,
|
||||
)
|
||||
val button = TextStyle(
|
||||
fontFamily = FontFamily(Font(R.font.roboto_medium)),
|
||||
fontSize = 14.0.sp,
|
||||
letterSpacing = 0.10000000149011612.sp,
|
||||
lineHeight = 20.0.sp,
|
||||
)
|
||||
val caption = TextStyle(
|
||||
fontFamily = FontFamily(Font(R.font.roboto)),
|
||||
fontSize = 12.0.sp,
|
||||
letterSpacing = 0.4000000059604645.sp,
|
||||
lineHeight = 16.0.sp,
|
||||
)
|
||||
val headline1 = TextStyle(
|
||||
fontFamily = FontFamily(Font(R.font.roboto)),
|
||||
fontSize = 34.0.sp,
|
||||
letterSpacing = 0.0.sp,
|
||||
lineHeight = 44.0.sp,
|
||||
)
|
||||
val headline2 = TextStyle(
|
||||
fontFamily = FontFamily(Font(R.font.roboto_medium)),
|
||||
fontSize = 24.0.sp,
|
||||
letterSpacing = 0.18000000715255737.sp,
|
||||
lineHeight = 32.0.sp,
|
||||
)
|
||||
val headline3 = TextStyle(
|
||||
fontFamily = FontFamily(Font(R.font.roboto_medium)),
|
||||
fontSize = 20.0.sp,
|
||||
letterSpacing = 0.15000000596046448.sp,
|
||||
lineHeight = 24.0.sp,
|
||||
)
|
||||
val overline = TextStyle(
|
||||
fontFamily = FontFamily(Font(R.font.roboto_medium)),
|
||||
fontSize = 10.0.sp,
|
||||
letterSpacing = 1.5.sp,
|
||||
lineHeight = 16.0.sp,
|
||||
)
|
||||
val subtitle1 = TextStyle(
|
||||
fontFamily = FontFamily(Font(R.font.roboto_medium)),
|
||||
fontSize = 16.0.sp,
|
||||
letterSpacing = 0.15000000596046448.sp,
|
||||
lineHeight = 24.0.sp,
|
||||
)
|
||||
val subtitle2 = TextStyle(
|
||||
fontFamily = FontFamily(Font(R.font.roboto_medium)),
|
||||
fontSize = 14.0.sp,
|
||||
letterSpacing = 0.10000000149011612.sp,
|
||||
lineHeight = 24.0.sp,
|
||||
)
|
||||
}
|
||||
|
|
@ -7,11 +7,10 @@ import androidx.fragment.app.FragmentManager
|
|||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
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.cardsettings.CardSettingsFragment
|
||||
import com.tangem.tap.features.details.ui.details.DetailsFragment
|
||||
import com.tangem.tap.features.details.ui.resetcard.ResetCardFragment
|
||||
import com.tangem.tap.features.details.ui.securitymode.SecurityModeFragment
|
||||
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
|
||||
|
|
@ -94,10 +93,10 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
|
|||
AppScreen.Wallet -> WalletFragment()
|
||||
AppScreen.Send -> SendFragment()
|
||||
AppScreen.Details -> DetailsFragment()
|
||||
AppScreen.DetailsConfirm -> DetailsConfirmFragment()
|
||||
AppScreen.DetailsSecurity -> DetailsSecurityFragment()
|
||||
AppScreen.SecurityAndPrivacy -> SecurityAndPrivacyFragment()
|
||||
AppScreen.ChangeAccessCode -> ChangeAccessCodeFragment()
|
||||
AppScreen.DetailsSecurity -> SecurityModeFragment()
|
||||
AppScreen.CardSettings -> CardSettingsFragment()
|
||||
AppScreen.AppSettings -> CardSettingsFragment()
|
||||
AppScreen.ResetToFactory -> ResetCardFragment()
|
||||
AppScreen.Disclaimer -> DisclaimerFragment()
|
||||
AppScreen.AddTokens -> AddTokensFragment()
|
||||
AppScreen.AddCustomToken -> AddCustomTokenFragment()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.common.redux.navigation
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import org.rekotlin.Action
|
||||
import java.lang.ref.WeakReference
|
||||
|
|
@ -15,6 +16,8 @@ sealed class NavigationAction : Action {
|
|||
|
||||
data class OpenUrl(val url: String) : NavigationAction()
|
||||
|
||||
data class OpenDocument(val url: Uri) : NavigationAction()
|
||||
|
||||
data class Share(val data: String) : NavigationAction()
|
||||
|
||||
data class ActivityCreated(val activity: WeakReference<FragmentActivity>) : NavigationAction()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.common.redux.navigation
|
||||
|
||||
import android.content.Intent
|
||||
import com.tangem.tap.common.CustomTabsManager
|
||||
import com.tangem.tap.common.extensions.openFragment
|
||||
import com.tangem.tap.common.extensions.popBackTo
|
||||
|
|
@ -32,6 +33,11 @@ val navigationMiddleware: Middleware<AppState> = { dispatch, state ->
|
|||
CustomTabsManager().openUrl(action.url, it)
|
||||
}
|
||||
}
|
||||
is NavigationAction.OpenDocument -> {
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.data = action.url
|
||||
navState?.activity?.get()?.startActivity(intent)
|
||||
}
|
||||
is NavigationAction.Share -> {
|
||||
navState?.activity?.get()?.let {
|
||||
it.shareText(action.data)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ enum class AppScreen {
|
|||
OnboardingNote, OnboardingWallet, OnboardingTwins, OnboardingOther,
|
||||
Wallet, WalletDetails,
|
||||
Send,
|
||||
Details, DetailsConfirm, DetailsSecurity, SecurityAndPrivacy, ChangeAccessCode,
|
||||
Details, DetailsSecurity, CardSettings, AppSettings, ResetToFactory,
|
||||
AddTokens, AddCustomToken,
|
||||
WalletConnectSessions,
|
||||
QrScan
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import com.tangem.common.extensions.ByteArrayKey
|
|||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.operations.CommandResponse
|
||||
import com.tangem.operations.ScanTask
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
|
||||
import com.tangem.operations.pins.CheckUserCodesCommand
|
||||
|
|
@ -144,6 +145,12 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
|
|||
initialMessage = Message(context.getString(R.string.initial_message_tap_header)))
|
||||
}
|
||||
|
||||
suspend fun scanCard(): CompletionResult<Card> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
ScanTask(),
|
||||
initialMessage = Message(context.getString(R.string.initial_message_tap_header)))
|
||||
}
|
||||
|
||||
suspend fun <T : CommandResponse> runTaskAsync(
|
||||
runnable: CardSessionRunnable<T>, cardId: String? = null, initialMessage: Message? = null,
|
||||
): CompletionResult<T> =
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import com.tangem.common.card.EllipticCurve
|
|||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.domain.common.TapWorkarounds
|
||||
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.domain.common.TapWorkarounds.isTangemNote
|
||||
import com.tangem.domain.common.TwinCardNumber
|
||||
import com.tangem.domain.common.getTwinCardNumber
|
||||
import com.tangem.domain.common.isTangemTwin
|
||||
|
|
@ -15,7 +15,6 @@ import com.tangem.operations.attestation.CardVerifyAndGetInfo
|
|||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
|
||||
|
||||
val Card.remainingSignatures: Int?
|
||||
get() = this.getSingleWallet()?.remainingSignatures
|
||||
|
||||
|
|
@ -24,7 +23,7 @@ val Card.isWalletDataSupported: Boolean
|
|||
|
||||
val Card.isMultiwalletAllowed: Boolean
|
||||
get() {
|
||||
return !isTangemTwin() && !isStart2Coin && !TapWorkarounds.isTangemNote(this)
|
||||
return !isTangemTwin() && !isStart2Coin && !isTangemNote()
|
||||
&& (firmwareVersion >= FirmwareVersion.MultiWalletAvailable ||
|
||||
getSingleWallet()?.curve == EllipticCurve.Secp256k1)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,14 @@ import com.tangem.common.extensions.guard
|
|||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.common.extensions.toMapKey
|
||||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.domain.common.*
|
||||
import com.tangem.domain.common.ProductType
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.TapWorkarounds.isExcluded
|
||||
import com.tangem.domain.common.TapWorkarounds.isNotSupportedInThatRelease
|
||||
import com.tangem.domain.common.TapWorkarounds.isTangemNote
|
||||
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
|
||||
import com.tangem.domain.common.TwinsHelper
|
||||
import com.tangem.domain.common.isTangemTwins
|
||||
import com.tangem.operations.PreflightReadMode
|
||||
import com.tangem.operations.PreflightReadTask
|
||||
import com.tangem.operations.ScanTask
|
||||
|
|
@ -56,7 +60,7 @@ class ScanProductTask(
|
|||
}
|
||||
|
||||
val commandProcessor = when {
|
||||
TapWorkarounds.isTangemNote(card) -> ScanNoteProcessor()
|
||||
card.isTangemNote() -> ScanNoteProcessor()
|
||||
card.isTangemTwins() -> ScanTwinProcessor()
|
||||
else -> ScanWalletProcessor(currenciesRepository, additionalBlockchainsToDerive)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,8 @@ package com.tangem.tap.features.details.redux
|
|||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.TwinCardNumber
|
||||
import com.tangem.operations.pins.CheckUserCodesResponse
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.domain.termsOfUse.CardTou
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.Action
|
||||
|
||||
sealed class DetailsAction : Action {
|
||||
|
|
@ -20,31 +16,23 @@ sealed class DetailsAction : Action {
|
|||
) : DetailsAction()
|
||||
|
||||
object ShowDisclaimer : DetailsAction()
|
||||
data class ReCreateTwinsWallet(val number: TwinCardNumber) : DetailsAction()
|
||||
object ReCreateTwinsWallet : DetailsAction()
|
||||
|
||||
sealed class ResetToFactory : DetailsAction() {
|
||||
object Check : ResetToFactory()
|
||||
object Proceed : ResetToFactory() {
|
||||
object NotAllowedByCard : ResetToFactory(), NotificationAction {
|
||||
override val messageResource = R.string.error_purge_prohibited
|
||||
}
|
||||
|
||||
object NotEmpty : ResetToFactory(), NotificationAction {
|
||||
override val messageResource = R.string.details_notification_erase_wallet_not_possible
|
||||
}
|
||||
}
|
||||
|
||||
object Confirm : ResetToFactory()
|
||||
object Cancel : ResetToFactory()
|
||||
object Proceed : ResetToFactory()
|
||||
data class Confirm(val confirmed: Boolean) : ResetToFactory()
|
||||
object Failure : ResetToFactory()
|
||||
object Success : ResetToFactory()
|
||||
}
|
||||
|
||||
object CreateBackup : DetailsAction()
|
||||
|
||||
object ScanCard : DetailsAction()
|
||||
|
||||
data class PrepareCardSettingsData(val card: Card) : DetailsAction()
|
||||
object ResetCardSettingsData : DetailsAction()
|
||||
|
||||
sealed class ManageSecurity : DetailsAction() {
|
||||
data class CheckCurrentSecurityOption(val card: Card) : ManageSecurity()
|
||||
data class SetCurrentOption(val userCodes: CheckUserCodesResponse) : ManageSecurity()
|
||||
object OpenSecurity : ManageSecurity()
|
||||
data class SelectOption(val option: SecurityOption) : ManageSecurity()
|
||||
object SaveChanges : ManageSecurity() {
|
||||
|
|
@ -52,19 +40,15 @@ sealed class DetailsAction : Action {
|
|||
object Failure : ManageSecurity()
|
||||
}
|
||||
|
||||
data class ConfirmSelection(val option: SecurityOption) : ManageSecurity() {
|
||||
object AlreadySet : ManageSecurity(), NotificationAction {
|
||||
override val messageResource = R.string.details_notification_security_option_already_active
|
||||
}
|
||||
}
|
||||
object ChangeAccessCode : ManageSecurity()
|
||||
}
|
||||
|
||||
sealed class ManagePrivacy : DetailsAction() {
|
||||
sealed class AppSettings : DetailsAction() {
|
||||
data class SwitchPrivacySetting(val allow: Boolean, val setting: PrivacySetting) :
|
||||
ManagePrivacy()
|
||||
AppSettings()
|
||||
|
||||
data class ConfirmSwitchingSetting(val allow: Boolean, val setting: PrivacySetting) :
|
||||
ManagePrivacy()
|
||||
AppSettings()
|
||||
}
|
||||
|
||||
data class ChangeAppCurrency(val fiatCurrency: FiatCurrency) : DetailsAction()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
package com.tangem.tap.features.details.redux
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.operations.pins.CheckUserCodesResponse
|
||||
import com.tangem.domain.common.isTangemTwins
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.AnalyticsParam
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
|
|
@ -36,10 +35,15 @@ class DetailsMiddleware {
|
|||
when (action) {
|
||||
is DetailsAction.ResetToFactory -> eraseWalletMiddleware.handle(action)
|
||||
is DetailsAction.ManageSecurity -> manageSecurityMiddleware.handle(action)
|
||||
is DetailsAction.ManagePrivacy -> managePrivacyMiddleware.handle(action)
|
||||
is DetailsAction.AppSettings -> managePrivacyMiddleware.handle(action)
|
||||
is DetailsAction.ShowDisclaimer -> {
|
||||
store.dispatch(DisclaimerAction.ShowAcceptedDisclaimer)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer))
|
||||
val uri = store.state.detailsState.cardTermsOfUseUrl
|
||||
if (uri !=null) {
|
||||
store.dispatch(NavigationAction.OpenDocument(uri))
|
||||
} else {
|
||||
store.dispatch(DisclaimerAction.ShowAcceptedDisclaimer)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer))
|
||||
}
|
||||
}
|
||||
is DetailsAction.ReCreateTwinsWallet -> {
|
||||
val wallet =
|
||||
|
|
@ -64,11 +68,23 @@ class DetailsMiddleware {
|
|||
store.dispatch(
|
||||
GlobalAction.Onboarding.Start(
|
||||
it,
|
||||
fromHomeScreen = false
|
||||
)
|
||||
fromHomeScreen = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
DetailsAction.ScanCard -> {
|
||||
scope.launch {
|
||||
when (val result = tangemSdkManager.scanCard()) {
|
||||
is CompletionResult.Success -> {
|
||||
val card = result.data
|
||||
store.dispatchOnMain(DetailsAction.PrepareCardSettingsData(card))
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
next(action)
|
||||
}
|
||||
|
|
@ -79,36 +95,31 @@ class DetailsMiddleware {
|
|||
fun handle(action: DetailsAction.ResetToFactory) {
|
||||
when (action) {
|
||||
is DetailsAction.ResetToFactory.Proceed -> {
|
||||
when (store.state.detailsState.eraseWalletState) {
|
||||
EraseWalletState.Allowed ->
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.DetailsConfirm))
|
||||
EraseWalletState.NotAllowedByCard ->
|
||||
store.dispatch(DetailsAction.ResetToFactory.Proceed.NotAllowedByCard)
|
||||
EraseWalletState.NotEmpty ->
|
||||
store.dispatch(DetailsAction.ResetToFactory.Proceed.NotEmpty)
|
||||
else -> { /* no-op */
|
||||
}
|
||||
val card = store.state.detailsState.cardSettingsState?.card ?: return
|
||||
if (card.isTangemTwins()) {
|
||||
store.dispatch(DetailsAction.ReCreateTwinsWallet)
|
||||
return
|
||||
}
|
||||
}
|
||||
is DetailsAction.ResetToFactory.Cancel -> {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
is DetailsAction.ResetToFactory.Confirm -> {
|
||||
val card = store.state.detailsState.scanResponse?.card ?: return
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.resetToFactorySettings(card)
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
currenciesRepository.removeCurrencies(card.cardId)
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
val returnScreen =
|
||||
if (card.cardId == store.state.globalState.scanResponse?.card?.cardId) {
|
||||
null
|
||||
} else {
|
||||
AppScreen.Home
|
||||
}
|
||||
store.dispatch(NavigationAction.PopBackTo(returnScreen))
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
store.state.globalState.analyticsHandlers?.logCardSdkError(
|
||||
error,
|
||||
Analytics.ActionToLog.PurgeWallet,
|
||||
card = store.state.detailsState.scanResponse?.card
|
||||
card = store.state.detailsState.scanResponse?.card,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -125,54 +136,13 @@ class DetailsMiddleware {
|
|||
class ManageSecurityMiddleware {
|
||||
fun handle(action: DetailsAction.ManageSecurity) {
|
||||
when (action) {
|
||||
is DetailsAction.ManageSecurity.CheckCurrentSecurityOption -> {
|
||||
if (action.card.firmwareVersion >= FirmwareVersion.IsAccessCodeStatusAvailable) {
|
||||
// for a card that meets this condition, we can get these statuses from it
|
||||
val simulatedResponse = CheckUserCodesResponse(
|
||||
action.card.isAccessCodeSet, action.card.isPasscodeSet ?: false
|
||||
)
|
||||
store.dispatch(
|
||||
DetailsAction.ManageSecurity.SetCurrentOption(
|
||||
simulatedResponse
|
||||
)
|
||||
)
|
||||
store.dispatch(DetailsAction.ManageSecurity.OpenSecurity)
|
||||
} else {
|
||||
scope.launch {
|
||||
when (val response =
|
||||
tangemSdkManager.checkUserCodes(action.card.cardId)) {
|
||||
is CompletionResult.Success -> {
|
||||
store.dispatchOnMain(
|
||||
DetailsAction.ManageSecurity.SetCurrentOption(
|
||||
response.data
|
||||
)
|
||||
)
|
||||
store.dispatchOnMain(DetailsAction.ManageSecurity.OpenSecurity)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is DetailsAction.ManageSecurity.OpenSecurity -> {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.DetailsSecurity))
|
||||
}
|
||||
is DetailsAction.ManageSecurity.ConfirmSelection -> {
|
||||
if (action.option != store.state.detailsState.securityScreenState?.currentOption) {
|
||||
if (action.option == SecurityOption.LongTap) {
|
||||
store.dispatch(DetailsAction.ManageSecurity.SaveChanges)
|
||||
} else {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.DetailsConfirm))
|
||||
}
|
||||
} else {
|
||||
store.dispatch(DetailsAction.ManageSecurity.ConfirmSelection.AlreadySet)
|
||||
}
|
||||
}
|
||||
is DetailsAction.ManageSecurity.SaveChanges -> {
|
||||
val cardId = store.state.detailsState.scanResponse?.card?.cardId
|
||||
val selectedOption =
|
||||
store.state.detailsState.securityScreenState?.selectedOption
|
||||
store.state.detailsState.cardSettingsState?.manageSecurityState?.selectedOption
|
||||
scope.launch {
|
||||
val result = when (selectedOption) {
|
||||
SecurityOption.LongTap -> tangemSdkManager.setLongTap(cardId)
|
||||
|
|
@ -186,9 +156,7 @@ class DetailsMiddleware {
|
|||
selectedOption?.let {
|
||||
store.dispatch(GlobalAction.UpdateSecurityOptions(it))
|
||||
}
|
||||
if (selectedOption != SecurityOption.LongTap) {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
store.dispatch(DetailsAction.ManageSecurity.SaveChanges.Success)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
|
|
@ -198,9 +166,9 @@ class DetailsMiddleware {
|
|||
actionToLog = Analytics.ActionToLog.ChangeSecOptions,
|
||||
parameters = mapOf(
|
||||
AnalyticsParam.NEW_SECURITY_OPTION to
|
||||
(selectedOption?.name ?: "")
|
||||
(selectedOption?.name ?: ""),
|
||||
),
|
||||
card = store.state.detailsState.scanResponse?.card
|
||||
card = store.state.detailsState.scanResponse?.card,
|
||||
)
|
||||
}
|
||||
store.dispatch(DetailsAction.ManageSecurity.SaveChanges.Failure)
|
||||
|
|
@ -211,6 +179,12 @@ class DetailsMiddleware {
|
|||
}
|
||||
}
|
||||
}
|
||||
is DetailsAction.ManageSecurity.ChangeAccessCode -> {
|
||||
val card = store.state.detailsState.cardSettingsState?.card ?: return
|
||||
scope.launch {
|
||||
tangemSdkManager.setAccessCode(card.cardId)
|
||||
}
|
||||
}
|
||||
else -> { /* no-op */
|
||||
}
|
||||
}
|
||||
|
|
@ -218,18 +192,18 @@ class DetailsMiddleware {
|
|||
}
|
||||
|
||||
class ManagePrivacyMiddleware {
|
||||
fun handle(action: DetailsAction.ManagePrivacy) {
|
||||
fun handle(action: DetailsAction.AppSettings) {
|
||||
when (action) {
|
||||
is DetailsAction.ManagePrivacy.ConfirmSwitchingSetting -> {
|
||||
is DetailsAction.AppSettings.ConfirmSwitchingSetting -> {
|
||||
// TODO()
|
||||
}
|
||||
is DetailsAction.ManagePrivacy.SwitchPrivacySetting -> {
|
||||
is DetailsAction.AppSettings.SwitchPrivacySetting -> {
|
||||
if (action.allow) {
|
||||
store.dispatch(
|
||||
DetailsAction.ManagePrivacy.ConfirmSwitchingSetting(
|
||||
DetailsAction.AppSettings.ConfirmSwitchingSetting(
|
||||
action.allow,
|
||||
action.setting
|
||||
)
|
||||
action.setting,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
store.dispatchDialogShow(DetailsDialog.ConfirmDisablingSaving(action.setting))
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
package com.tangem.tap.features.details.redux
|
||||
|
||||
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.domain.common.TapWorkarounds.isTangemNote
|
||||
import com.tangem.domain.common.isTangemTwin
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
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 org.rekotlin.Action
|
||||
import java.util.*
|
||||
|
|
@ -26,13 +25,17 @@ private fun internalReduce(action: Action, state: AppState): DetailsState {
|
|||
is DetailsAction.PrepareScreen -> {
|
||||
handlePrepareScreen(action, detailsState)
|
||||
}
|
||||
is DetailsAction.PrepareCardSettingsData -> {
|
||||
hanldePrepareCardSettingsScreen(action.card, detailsState)
|
||||
}
|
||||
is DetailsAction.ResetCardSettingsData -> detailsState.copy(cardSettingsState = null)
|
||||
is DetailsAction.ResetToFactory -> {
|
||||
handleEraseWallet(action, detailsState)
|
||||
}
|
||||
is DetailsAction.ManageSecurity -> {
|
||||
handleSecurityAction(action, detailsState)
|
||||
}
|
||||
is DetailsAction.ManagePrivacy -> {
|
||||
is DetailsAction.AppSettings -> {
|
||||
handlePrivacyAction(action, detailsState)
|
||||
}
|
||||
is DetailsAction.ChangeAppCurrency ->
|
||||
|
|
@ -50,43 +53,66 @@ private fun handlePrepareScreen(
|
|||
return DetailsState(
|
||||
scanResponse = action.scanResponse,
|
||||
wallets = action.wallets,
|
||||
cardInfo = action.scanResponse.card.toCardInfo(),
|
||||
cardTermsOfUseUrl = action.cardTou.getUrl(action.scanResponse.card),
|
||||
createBackupAllowed = action.scanResponse.card.backupStatus == Card.BackupStatus.NoBackup,
|
||||
appCurrency = store.state.globalState.appCurrency
|
||||
appCurrency = store.state.globalState.appCurrency,
|
||||
)
|
||||
}
|
||||
|
||||
private fun hanldePrepareCardSettingsScreen(
|
||||
card: Card, state: DetailsState,
|
||||
): DetailsState {
|
||||
val cardSettingsState = CardSettingsState(
|
||||
cardInfo = card.toCardInfo(),
|
||||
manageSecurityState = prepareSecurityOptions(card),
|
||||
card = card,
|
||||
resetCardAllowed = isResetToFactoryAllowedByCard(card)
|
||||
)
|
||||
return state.copy(cardSettingsState = cardSettingsState)
|
||||
}
|
||||
|
||||
private fun prepareSecurityOptions(card: Card): ManageSecurityState {
|
||||
val securityOption = when {
|
||||
card.isAccessCodeSet -> {
|
||||
SecurityOption.AccessCode
|
||||
}
|
||||
card.isPasscodeSet == true -> {
|
||||
SecurityOption.PassCode
|
||||
}
|
||||
else -> {
|
||||
SecurityOption.LongTap
|
||||
}
|
||||
}
|
||||
val allowedSecurityOptions = when {
|
||||
card.isStart2Coin || card.isTangemNote() -> {
|
||||
EnumSet.of(SecurityOption.LongTap)
|
||||
}
|
||||
card.settings.isBackupAllowed -> {
|
||||
EnumSet.of(securityOption)
|
||||
}
|
||||
else -> prepareAllowedSecurityOptions(card, securityOption)
|
||||
}
|
||||
return ManageSecurityState(
|
||||
currentOption = securityOption,
|
||||
allowedOptions = allowedSecurityOptions,
|
||||
selectedOption = securityOption,
|
||||
)
|
||||
}
|
||||
|
||||
private fun isResetToFactoryAllowedByCard(card: Card): Boolean {
|
||||
val notAllowedByAnyWallet = card.wallets.any { it.settings.isPermanent }
|
||||
val notAllowedByCard = notAllowedByAnyWallet ||
|
||||
(card.isWalletDataSupported && (!card.isTangemNote() && !card.settings.isBackupAllowed))
|
||||
return !notAllowedByCard
|
||||
}
|
||||
|
||||
private fun handleEraseWallet(
|
||||
action: DetailsAction.ResetToFactory,
|
||||
state: DetailsState,
|
||||
): DetailsState {
|
||||
return when (action) {
|
||||
DetailsAction.ResetToFactory.Check -> {
|
||||
val card = state.scanResponse?.card
|
||||
val notAllowedByAnyWallet = card?.wallets?.any { it.settings.isPermanent } ?: false
|
||||
val notAllowedByCard = notAllowedByAnyWallet ||
|
||||
(card?.isWalletDataSupported == true &&
|
||||
(!state.scanResponse.isTangemNote() && !state.scanResponse.supportsBackup()))
|
||||
|
||||
val notEmpty = state.wallets.any { it.hasSendableAmountsOrPendingTransactions() }
|
||||
val eraseWalletState = when {
|
||||
notAllowedByCard -> EraseWalletState.NotAllowedByCard
|
||||
notEmpty -> EraseWalletState.NotEmpty
|
||||
else -> EraseWalletState.Allowed
|
||||
}
|
||||
state.copy(eraseWalletState = eraseWalletState)
|
||||
}
|
||||
DetailsAction.ResetToFactory.Proceed -> {
|
||||
if (state.eraseWalletState == EraseWalletState.Allowed) {
|
||||
state.copy(confirmScreenState = ConfirmScreenState.EraseWallet)
|
||||
} else {
|
||||
state
|
||||
}
|
||||
}
|
||||
DetailsAction.ResetToFactory.Cancel -> state.copy(eraseWalletState = null)
|
||||
DetailsAction.ResetToFactory.Failure -> state.copy(eraseWalletState = null)
|
||||
DetailsAction.ResetToFactory.Success -> state.copy(eraseWalletState = null)
|
||||
is DetailsAction.ResetToFactory.Confirm ->
|
||||
state.copy(cardSettingsState = state.cardSettingsState?.copy(resetConfirmed = action.confirmed))
|
||||
else -> state
|
||||
}
|
||||
}
|
||||
|
|
@ -95,62 +121,27 @@ private fun handleSecurityAction(
|
|||
action: DetailsAction.ManageSecurity, state: DetailsState,
|
||||
): DetailsState {
|
||||
return when (action) {
|
||||
is DetailsAction.ManageSecurity.SetCurrentOption -> {
|
||||
val securityOption = when {
|
||||
action.userCodes.isAccessCodeSet -> {
|
||||
SecurityOption.AccessCode
|
||||
}
|
||||
action.userCodes.isPasscodeSet -> {
|
||||
SecurityOption.PassCode
|
||||
}
|
||||
else -> {
|
||||
SecurityOption.LongTap
|
||||
}
|
||||
}
|
||||
state.copy(securityScreenState = SecurityScreenState(currentOption = securityOption))
|
||||
}
|
||||
is DetailsAction.ManageSecurity.OpenSecurity -> {
|
||||
val allowedSecurityOptions = when {
|
||||
state.scanResponse?.card?.isStart2Coin == true ||
|
||||
state.scanResponse?.isTangemNote() == true -> {
|
||||
EnumSet.of(SecurityOption.LongTap)
|
||||
}
|
||||
state.scanResponse?.supportsBackup() == true -> {
|
||||
EnumSet.of(state.securityScreenState?.currentOption)
|
||||
}
|
||||
else -> prepareAllowedSecurityOptions(
|
||||
state.scanResponse?.card, state.securityScreenState?.currentOption
|
||||
)
|
||||
}
|
||||
state.copy(securityScreenState = state.securityScreenState?.copy(
|
||||
allowedOptions = allowedSecurityOptions,
|
||||
selectedOption = state.securityScreenState.currentOption
|
||||
))
|
||||
}
|
||||
is DetailsAction.ManageSecurity.SelectOption -> {
|
||||
state.copy(securityScreenState = state.securityScreenState?.copy(
|
||||
selectedOption = action.option
|
||||
))
|
||||
}
|
||||
is DetailsAction.ManageSecurity.ConfirmSelection -> {
|
||||
val confirmScreenState = when (action.option) {
|
||||
SecurityOption.LongTap -> ConfirmScreenState.LongTap
|
||||
SecurityOption.PassCode -> ConfirmScreenState.PassCode
|
||||
SecurityOption.AccessCode -> ConfirmScreenState.AccessCode
|
||||
}
|
||||
state.copy(confirmScreenState = confirmScreenState)
|
||||
val manageSecurityState = state.cardSettingsState?.manageSecurityState?.copy(
|
||||
selectedOption = action.option,
|
||||
)
|
||||
state.copy(cardSettingsState = state.cardSettingsState?.copy(manageSecurityState = manageSecurityState))
|
||||
}
|
||||
is DetailsAction.ManageSecurity.SaveChanges.Success -> {
|
||||
// Setting options to show only LongTap from now on for non-twins
|
||||
val manageSecurityState = state.cardSettingsState?.manageSecurityState?.copy(
|
||||
currentOption = state.cardSettingsState.manageSecurityState.selectedOption,
|
||||
allowedOptions = state.scanResponse?.card?.let {
|
||||
prepareAllowedSecurityOptions(
|
||||
it, state.cardSettingsState.manageSecurityState.selectedOption,
|
||||
)
|
||||
} ?: EnumSet.of(SecurityOption.LongTap),
|
||||
)
|
||||
state.copy(
|
||||
securityScreenState = state.securityScreenState?.copy(
|
||||
currentOption = state.securityScreenState.selectedOption,
|
||||
allowedOptions = state.scanResponse?.card?.let {
|
||||
prepareAllowedSecurityOptions(
|
||||
it, state.securityScreenState.selectedOption
|
||||
)
|
||||
} ?: EnumSet.of(SecurityOption.LongTap)
|
||||
))
|
||||
cardSettingsState = state.cardSettingsState?.copy(
|
||||
manageSecurityState = manageSecurityState,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
else -> state
|
||||
|
|
@ -158,16 +149,16 @@ private fun handleSecurityAction(
|
|||
}
|
||||
|
||||
private fun handlePrivacyAction(
|
||||
action: DetailsAction.ManagePrivacy, state: DetailsState,
|
||||
action: DetailsAction.AppSettings, state: DetailsState,
|
||||
): DetailsState {
|
||||
return when (action) {
|
||||
is DetailsAction.ManagePrivacy.ConfirmSwitchingSetting -> {
|
||||
is DetailsAction.AppSettings.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
|
||||
is DetailsAction.AppSettings.SwitchPrivacySetting -> state
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -188,7 +179,6 @@ private fun prepareAllowedSecurityOptions(
|
|||
return allowedSecurityOptions
|
||||
}
|
||||
|
||||
|
||||
private fun Card.toCardInfo(): CardInfo {
|
||||
val cardId = this.cardId.chunked(4).joinToString(separator = " ")
|
||||
val issuer = this.issuer.name
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.features.details.redux
|
|||
|
||||
import android.net.Uri
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.entities.Button
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
|
|
@ -15,11 +16,9 @@ import kotlin.properties.ReadOnlyProperty
|
|||
data class DetailsState(
|
||||
val scanResponse: ScanResponse? = null,
|
||||
val wallets: List<Wallet> = emptyList(),
|
||||
val cardInfo: CardInfo? = null,
|
||||
val eraseWalletState: EraseWalletState? = null,
|
||||
val confirmScreenState: ConfirmScreenState? = null,
|
||||
val securityScreenState: SecurityScreenState? = null,
|
||||
val cardSettingsState: CardSettingsState? = null,
|
||||
val cardTermsOfUseUrl: Uri? = null,
|
||||
val privacyPolicyUrl: String? = null,
|
||||
val createBackupAllowed: Boolean = false,
|
||||
val appCurrency: FiatCurrency = FiatCurrency.Default,
|
||||
val saveCards: Boolean = true,
|
||||
|
|
@ -42,9 +41,15 @@ data class CardInfo(
|
|||
val signedHashes: Int,
|
||||
)
|
||||
|
||||
enum class EraseWalletState { Allowed, NotAllowedByCard, NotEmpty }
|
||||
enum class ConfirmScreenState { EraseWallet, LongTap, AccessCode, PassCode }
|
||||
data class SecurityScreenState(
|
||||
data class CardSettingsState(
|
||||
val cardInfo: CardInfo,
|
||||
val card: Card,
|
||||
val manageSecurityState: ManageSecurityState?,
|
||||
val resetCardAllowed: Boolean,
|
||||
val resetConfirmed: Boolean = false
|
||||
)
|
||||
|
||||
data class ManageSecurityState(
|
||||
val currentOption: SecurityOption = SecurityOption.LongTap,
|
||||
val selectedOption: SecurityOption = currentOption,
|
||||
val allowedOptions: EnumSet<SecurityOption> = EnumSet.allOf(SecurityOption::class.java),
|
||||
|
|
@ -57,7 +62,7 @@ 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)) }
|
||||
{ store.dispatch(DetailsAction.AppSettings.ConfirmSwitchingSetting(false, setting)) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,88 +0,0 @@
|
|||
package com.tangem.tap.features.details.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.tangem.tap.common.extensions.getDrawable
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.details.redux.ConfirmScreenState
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.FragmentDetailsConfirmBinding
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
class DetailsConfirmFragment : Fragment(R.layout.fragment_details_confirm),
|
||||
StoreSubscriber<DetailsState> {
|
||||
|
||||
private val binding: FragmentDetailsConfirmBinding by viewBinding(FragmentDetailsConfirmBinding::bind)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
})
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
enterTransition = inflater.inflateTransition(R.transition.slide_right)
|
||||
exitTransition = inflater.inflateTransition(R.transition.fade)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
store.subscribe(this) { state ->
|
||||
state.skipRepeats { oldState, newState ->
|
||||
oldState.detailsState == newState.detailsState
|
||||
}.select { it.detailsState }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.toolbar.setNavigationOnClickListener {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun newState(state: DetailsState) {
|
||||
if (activity == null || view == null) return
|
||||
setState(state)
|
||||
}
|
||||
|
||||
private fun setState(state: DetailsState) = with(binding) {
|
||||
when (state.confirmScreenState) {
|
||||
ConfirmScreenState.EraseWallet -> {
|
||||
toolbar.title = getString(R.string.details_row_title_reset_factory_settings)
|
||||
tvWarningDescription.text = getString(R.string.details_row_title_reset_factory_settings_warning)
|
||||
btnConfirm.text = getString(R.string.details_row_title_reset_factory_settings)
|
||||
btnConfirm.setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||
null, null, getDrawable(R.drawable.ic_send), null
|
||||
)
|
||||
btnConfirm.setOnClickListener { store.dispatch(DetailsAction.ResetToFactory.Confirm) }
|
||||
}
|
||||
ConfirmScreenState.LongTap, ConfirmScreenState.AccessCode,
|
||||
ConfirmScreenState.PassCode -> {
|
||||
toolbar.title = getString(R.string.details_manage_security_title)
|
||||
tvWarningDescription.text = getString(R.string.details_security_management_warning)
|
||||
btnConfirm.text = getString(R.string.common_save_changes)
|
||||
btnConfirm.setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||
null, null, getDrawable(R.drawable.ic_save), null
|
||||
)
|
||||
btnConfirm.setOnClickListener { store.dispatch(DetailsAction.ManageSecurity.SaveChanges) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
package com.tangem.tap.features.details.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.tangem.domain.common.getTwinCardIdForUser
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.feedback.FeedbackEmail
|
||||
import com.tangem.tap.common.feedback.SupportInfo
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
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
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.FragmentDetailsBinding
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<DetailsState> {
|
||||
|
||||
private val binding: FragmentDetailsBinding by viewBinding(FragmentDetailsBinding::bind)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
})
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
enterTransition = inflater.inflateTransition(R.transition.slide_right)
|
||||
exitTransition = inflater.inflateTransition(R.transition.fade)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
store.subscribe(this) { state ->
|
||||
state.skipRepeats { oldState, newState ->
|
||||
oldState.detailsState == newState.detailsState
|
||||
}.select { it.detailsState }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.toolbar.setNavigationOnClickListener {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun newState(state: DetailsState) {
|
||||
if (activity == null || view == null) return
|
||||
setState(state)
|
||||
}
|
||||
|
||||
private fun setState(state: DetailsState) = with (binding) {
|
||||
|
||||
if (state.cardInfo != null) {
|
||||
val cardId = if (state.isTangemTwins) {
|
||||
state.scanResponse?.card?.getTwinCardIdForUser()
|
||||
} else {
|
||||
state.cardInfo.cardId
|
||||
}
|
||||
tvCardId.text = cardId
|
||||
tvIssuer.text = state.cardInfo.issuer
|
||||
tvSignedHashes.text = getString(
|
||||
R.string.details_row_subtitle_signed_hashes_format,
|
||||
state.cardInfo.signedHashes.toString()
|
||||
)
|
||||
}
|
||||
|
||||
tvSignedHashes.show(!state.isTangemTwins)
|
||||
tvSignedHashesTitle.show(!state.isTangemTwins)
|
||||
|
||||
tvDisclaimer.setOnClickListener { store.dispatch(DetailsAction.ShowDisclaimer) }
|
||||
|
||||
tvCardTou.show(state.cardTermsOfUseUrl != null)
|
||||
tvCardTou.setOnClickListener {
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.data = state.cardTermsOfUseUrl
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
if (state.isTangemTwins) {
|
||||
tvResetToFactory.show()
|
||||
tvResetToFactory.text = getText(R.string.details_row_title_twins_recreate)
|
||||
tvResetToFactory.setOnClickListener {
|
||||
store.dispatch(DetailsAction.ReCreateTwinsWallet(state.twinCardsState.cardNumber!!))
|
||||
}
|
||||
} else {
|
||||
tvResetToFactory.show()
|
||||
tvResetToFactory.text = getText(R.string.details_row_title_reset_factory_settings)
|
||||
tvResetToFactory.setOnClickListener {
|
||||
store.dispatch(DetailsAction.ResetToFactory.Check)
|
||||
store.dispatch(DetailsAction.ResetToFactory.Proceed)
|
||||
}
|
||||
}
|
||||
|
||||
tvCreateBackup.show(state.createBackupAllowed)
|
||||
tvCreateBackup.setOnClickListener {
|
||||
store.dispatch(DetailsAction.CreateBackup)
|
||||
}
|
||||
|
||||
tvAppCurrency.show(state.scanResponse?.card?.isMultiwalletAllowed != true)
|
||||
tvAppCurrencyTitle.show(state.scanResponse?.card?.isMultiwalletAllowed != true)
|
||||
tvAppCurrency.text = state.appCurrency.code
|
||||
|
||||
tvAppCurrency.setOnClickListener {
|
||||
store.dispatch(WalletAction.AppCurrencyAction.ChooseAppCurrency)
|
||||
}
|
||||
|
||||
tvSendFeedback.setOnClickListener {
|
||||
store.dispatch(GlobalAction.SendEmail(FeedbackEmail()))
|
||||
}
|
||||
|
||||
tvWalletConnect.show(state.scanResponse?.card?.isMultiwalletAllowed == true)
|
||||
tvWalletConnect.setOnClickListener {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.WalletConnectSessions))
|
||||
}
|
||||
|
||||
tvSupport.setOnClickListener {
|
||||
store.dispatch(GlobalAction.OpenChat(SupportInfo()))
|
||||
}
|
||||
|
||||
llManageSecurity.setOnClickListener {
|
||||
store.dispatch(DetailsAction.ManageSecurity.CheckCurrentSecurityOption(state.scanResponse!!.card))
|
||||
tvSecurityAndPrivacy.setOnClickListener {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.SecurityAndPrivacy))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
package com.tangem.tap.features.details.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
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.store
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.FragmentDetailsSecurityBinding
|
||||
import org.rekotlin.StoreSubscriber
|
||||
import java.util.*
|
||||
|
||||
class DetailsSecurityFragment : Fragment(R.layout.fragment_details_security),
|
||||
StoreSubscriber<DetailsState> {
|
||||
|
||||
private val binding: FragmentDetailsSecurityBinding by viewBinding(
|
||||
FragmentDetailsSecurityBinding::bind
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
})
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
enterTransition = inflater.inflateTransition(R.transition.slide_right)
|
||||
exitTransition = inflater.inflateTransition(R.transition.fade)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
store.subscribe(this) { state ->
|
||||
state.skipRepeats { oldState, newState ->
|
||||
oldState.detailsState == newState.detailsState
|
||||
}.select { it.detailsState }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.toolbar.setNavigationOnClickListener {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
setOnClickListeners()
|
||||
}
|
||||
|
||||
private fun setOnClickListeners() {
|
||||
binding.btnSaveChanges.setOnClickListener {
|
||||
store.state.detailsState.securityScreenState?.selectedOption?.let {
|
||||
store.dispatch(DetailsAction.ManageSecurity.ConfirmSelection(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun newState(state: DetailsState) {
|
||||
if (activity == null || view == null) return
|
||||
|
||||
selectSecurityOption(state.securityScreenState?.selectedOption)
|
||||
for (option in SecurityOption.values()) {
|
||||
setupOption(
|
||||
option,
|
||||
state.securityScreenState?.allowedOptions
|
||||
?: EnumSet.noneOf(SecurityOption::class.java)
|
||||
)
|
||||
}
|
||||
binding.tvAccessCodeUnavailableDisclaimer.show(
|
||||
state.scanResponse?.card?.backupStatus == Card.BackupStatus.NoBackup
|
||||
)
|
||||
}
|
||||
|
||||
private fun selectSecurityOption(securityOption: SecurityOption?) = with(binding) {
|
||||
radiobuttonLongTap.isChecked =
|
||||
securityOption == SecurityOption.LongTap && radiobuttonLongTap.isEnabled
|
||||
radiobuttonPasscode.isChecked =
|
||||
securityOption == SecurityOption.PassCode && radiobuttonPasscode.isEnabled
|
||||
radiobuttonAccessCode.isChecked =
|
||||
securityOption == SecurityOption.AccessCode && radiobuttonAccessCode.isEnabled
|
||||
}
|
||||
|
||||
private fun setupOption(option: SecurityOption, allowedOptions: EnumSet<SecurityOption>) {
|
||||
when (option) {
|
||||
SecurityOption.LongTap -> { enableLongTap(allowedOptions.contains(option)) }
|
||||
SecurityOption.PassCode -> { enablePasscode(allowedOptions.contains(option)) }
|
||||
SecurityOption.AccessCode -> { enableAccessCode(allowedOptions.contains(option)) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun enableLongTap(enable: Boolean) = with(binding) {
|
||||
groupLongTap.show(enable)
|
||||
val alpha = if (enable) 1f else 0.5f
|
||||
tvLongTapDescription.alpha = alpha
|
||||
tvLongTapTitle.alpha = alpha
|
||||
radiobuttonLongTap.alpha = alpha
|
||||
radiobuttonLongTap.isEnabled = enable
|
||||
vLongTap.isClickable = enable
|
||||
if (enable) {
|
||||
vLongTap.setOnClickListener {
|
||||
store.dispatch(DetailsAction.ManageSecurity.SelectOption(SecurityOption.LongTap))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun enablePasscode(enable: Boolean) = with(binding) {
|
||||
groupPasscode.show(enable)
|
||||
val alpha = if (enable) 1f else 0.5f
|
||||
tvPasscodeDescription.alpha = alpha
|
||||
tvPasscodeTitle.alpha = alpha
|
||||
radiobuttonPasscode.alpha = alpha
|
||||
radiobuttonPasscode.isEnabled = enable
|
||||
vPasscode.isClickable = enable
|
||||
if (enable) {
|
||||
vPasscode.setOnClickListener {
|
||||
store.dispatch(DetailsAction.ManageSecurity.SelectOption(SecurityOption.PassCode))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun enableAccessCode(enable: Boolean) = with(binding) {
|
||||
groupAccessCode.show(enable)
|
||||
val alpha = if (enable) 1f else 0.5f
|
||||
tvAccessCodeDescription.alpha = alpha
|
||||
tvAccessCodeTitle.alpha = alpha
|
||||
radiobuttonAccessCode.alpha = alpha
|
||||
radiobuttonAccessCode.isEnabled = enable
|
||||
vAccessCode.isClickable = enable
|
||||
if (enable) {
|
||||
vAccessCode.setOnClickListener {
|
||||
store.dispatch(DetailsAction.ManageSecurity.SelectOption(SecurityOption.AccessCode))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.tangem.tap.features.details.ui.appsettings
|
||||
|
||||
class AppSettingsFragment
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.tap.features.details.ui.appsettings
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@Composable
|
||||
fun AppSettingsScreen(
|
||||
|
||||
) {
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package com.tangem.tap.features.details.ui.cardsettings
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.google.accompanist.appcompattheme.AppCompatTheme
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
class CardSettingsFragment : Fragment(), StoreSubscriber<DetailsState> {
|
||||
|
||||
private val viewModel = CardSettingsViewModel(store)
|
||||
|
||||
private var screenState: MutableState<CardSettingsScreenState> =
|
||||
mutableStateOf(viewModel.updateState(store.state.detailsState.cardSettingsState, context))
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
enterTransition = inflater.inflateTransition(R.transition.slide_right)
|
||||
exitTransition = inflater.inflateTransition(R.transition.fade)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
return ComposeView(requireContext()).apply {
|
||||
setContent {
|
||||
isTransitionGroup = true
|
||||
AppCompatTheme {
|
||||
CardSettingsScreen(
|
||||
state = screenState.value,
|
||||
onBackPressed = {
|
||||
store.dispatch(DetailsAction.ResetCardSettingsData)
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
store.subscribe(this) { state ->
|
||||
state.skipRepeats { oldState, newState ->
|
||||
oldState.detailsState == newState.detailsState
|
||||
}.select { it.detailsState }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
override fun newState(state: DetailsState) {
|
||||
if (activity == null || view == null) return
|
||||
screenState.value = viewModel.updateState(state.cardSettingsState, context)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
package com.tangem.tap.features.details.ui.cardsettings
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.tap.common.compose.TangemTypography
|
||||
import com.tangem.tap.features.details.ui.common.DetailsMainButton
|
||||
import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
fun CardSettingsScreen(
|
||||
state: CardSettingsScreenState,
|
||||
onBackPressed: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
||||
SettingsScreensScaffold(
|
||||
content =
|
||||
if (state.cardDetails == null) {
|
||||
{ CardSettingsReadCard(state.onScanCardClick, modifier = modifier) }
|
||||
} else {
|
||||
{ CardSettings(state = state, modifier = modifier) }
|
||||
},
|
||||
titleRes = R.string.card_settings_title,
|
||||
onBackClick = onBackPressed,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CardSettingsReadCard(
|
||||
onScanCardClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth(),
|
||||
|
||||
) {
|
||||
Image(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 80.dp, end = 80.dp, top = 70.dp)
|
||||
.rotate(-15f),
|
||||
painter = painterResource(id = R.drawable.card_placeholder_secondary),
|
||||
contentDescription = "",
|
||||
contentScale = ContentScale.FillWidth,
|
||||
)
|
||||
Image(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 60.dp, end = 60.dp)
|
||||
.rotate(-1f),
|
||||
painter = painterResource(id = R.drawable.card_placeholder_black),
|
||||
contentDescription = "",
|
||||
contentScale = ContentScale.FillWidth,
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 32.dp),
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.scan_card_settings_title),
|
||||
color = colorResource(id = R.color.text_primary_1),
|
||||
style = TangemTypography.headline3,
|
||||
)
|
||||
Spacer(modifier = modifier.size(20.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.scan_card_settings_message),
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
style = TangemTypography.body1,
|
||||
)
|
||||
Spacer(modifier = modifier.size(29.dp))
|
||||
DetailsMainButton(
|
||||
title = stringResource(id = R.string.scan_card_settings_button),
|
||||
onClick = onScanCardClick,
|
||||
modifier = modifier,
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CardSettings(
|
||||
state: CardSettingsScreenState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
|
||||
state.cardDetails?.map {
|
||||
val paddingBottom = when (it) {
|
||||
is CardInfo.CardId, is CardInfo.Issuer -> 12.dp
|
||||
is CardInfo.SignedHashes -> 14.dp
|
||||
is CardInfo.SecurityMode -> 16.dp
|
||||
is CardInfo.ChangeAccessCode -> 16.dp
|
||||
is CardInfo.ResetToFactorySettings -> 28.dp
|
||||
}
|
||||
val paddingTop = when (it) {
|
||||
is CardInfo.CardId -> 0.dp
|
||||
is CardInfo.Issuer -> 12.dp
|
||||
is CardInfo.SignedHashes -> 12.dp
|
||||
is CardInfo.SecurityMode -> 14.dp
|
||||
is CardInfo.ChangeAccessCode -> 16.dp
|
||||
is CardInfo.ResetToFactorySettings -> 16.dp
|
||||
}
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
|
||||
.clickable(
|
||||
enabled = it.clickable,
|
||||
onClick = { state.onElementClick(it) },
|
||||
)
|
||||
.padding(start = 20.dp, end = 20.dp, bottom = paddingBottom, top = paddingTop),
|
||||
) {
|
||||
val titleColor = if (it.clickable) R.color.text_primary_1 else R.color.text_tertiary
|
||||
val subtitleColor = if (it.clickable) R.color.text_secondary else R.color.text_tertiary
|
||||
Text(
|
||||
text = stringResource(id = it.titleRes),
|
||||
color = colorResource(id = titleColor),
|
||||
style = TangemTypography.subtitle1,
|
||||
)
|
||||
Spacer(modifier = modifier.size(4.dp))
|
||||
Text(
|
||||
text = it.subtitle,
|
||||
color = colorResource(id = subtitleColor),
|
||||
style = TangemTypography.body2,
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun CardSettingsPreview() {
|
||||
CardSettingsScreen(state = CardSettingsScreenState(onScanCardClick = {}) {}, {})
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tangem.tap.features.details.ui.cardsettings
|
||||
|
||||
import com.tangem.wallet.R
|
||||
|
||||
data class CardSettingsScreenState(
|
||||
val cardDetails: List<CardInfo>? = null,
|
||||
val onScanCardClick: () -> Unit,
|
||||
val onElementClick: (CardInfo) -> Unit
|
||||
)
|
||||
|
||||
sealed class CardInfo(
|
||||
val titleRes: Int, val subtitle: String, val clickable: Boolean = false
|
||||
) {
|
||||
class CardId(subtitle: String) : CardInfo(R.string.details_row_title_cid, subtitle)
|
||||
|
||||
class Issuer(subtitle: String) : CardInfo(R.string.details_row_title_issuer, subtitle)
|
||||
|
||||
class SignedHashes(subtitle: String) : CardInfo(R.string.details_row_title_signed_hashes, subtitle)
|
||||
|
||||
class SecurityMode(
|
||||
subtitle: String,
|
||||
clickable: Boolean,
|
||||
) : CardInfo(R.string.card_settings_security_mode, subtitle, clickable)
|
||||
|
||||
class ChangeAccessCode(subtitle: String) : CardInfo(
|
||||
R.string.card_settings_change_access_code,
|
||||
subtitle,
|
||||
true,
|
||||
)
|
||||
|
||||
class ResetToFactorySettings(subtitle: String) : CardInfo(
|
||||
R.string.details_row_title_reset_factory_settings,
|
||||
subtitle,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package com.tangem.tap.features.details.ui.cardsettings
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.domain.common.getTwinCardIdForUser
|
||||
import com.tangem.domain.common.isTangemTwins
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.details.redux.CardSettingsState
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.ui.securitymode.toTitleRes
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.Store
|
||||
|
||||
class CardSettingsViewModel(private val store: Store<AppState>) {
|
||||
|
||||
fun updateState(state: CardSettingsState?, context: Context?): CardSettingsScreenState {
|
||||
|
||||
return if (state?.manageSecurityState == null) {
|
||||
CardSettingsScreenState(
|
||||
null, onElementClick = {},
|
||||
onScanCardClick = {
|
||||
store.dispatch(DetailsAction.ScanCard)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
val cardId = if (state.card.isTangemTwins()) {
|
||||
state.card.getTwinCardIdForUser()
|
||||
} else {
|
||||
state.cardInfo.cardId
|
||||
}
|
||||
val cardDetails: MutableList<CardInfo> = listOfNotNull(
|
||||
CardInfo.CardId(cardId),
|
||||
CardInfo.Issuer(state.cardInfo.issuer),
|
||||
).toMutableList()
|
||||
|
||||
context?.let { context ->
|
||||
if (!state.card.isTangemTwins()) {
|
||||
cardDetails.add(
|
||||
CardInfo.SignedHashes(
|
||||
context
|
||||
.getString(
|
||||
R.string.details_row_subtitle_signed_hashes_format,
|
||||
state.cardInfo.signedHashes.toString(),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
cardDetails.add(
|
||||
CardInfo.SecurityMode(
|
||||
subtitle = context.getString(state.manageSecurityState.currentOption.toTitleRes()),
|
||||
clickable = state.manageSecurityState.allowedOptions.size > 1,
|
||||
),
|
||||
)
|
||||
if (state.card.backupStatus?.isActive == true && state.card.isAccessCodeSet) {
|
||||
cardDetails.add(
|
||||
CardInfo.ChangeAccessCode(
|
||||
subtitle = context.getString(R.string.card_settings_change_access_code_footer),
|
||||
),
|
||||
)
|
||||
}
|
||||
if (state.resetCardAllowed) {
|
||||
cardDetails.add(
|
||||
CardInfo.ResetToFactorySettings(
|
||||
subtitle = context.getString(R.string.card_settings_reset_factory_footer),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
CardSettingsScreenState(
|
||||
cardDetails,
|
||||
onScanCardClick = { },
|
||||
onElementClick = {
|
||||
handleClickingItem(it)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleClickingItem(item: CardInfo) {
|
||||
when (item) {
|
||||
is CardInfo.ChangeAccessCode -> {
|
||||
store.dispatch(DetailsAction.ManageSecurity.ChangeAccessCode)
|
||||
}
|
||||
is CardInfo.ResetToFactorySettings -> {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.ResetToFactory))
|
||||
}
|
||||
is CardInfo.SecurityMode -> {
|
||||
store.dispatch(DetailsAction.ManageSecurity.OpenSecurity)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
package com.tangem.tap.features.details.ui.common
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.tap.common.compose.TangemTypography
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
fun SettingsScreensScaffold(
|
||||
content: @Composable () -> Unit,
|
||||
background: @Composable (() -> Unit)? = null,
|
||||
titleRes: Int,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BackHandler(true, onBackClick)
|
||||
|
||||
Scaffold(
|
||||
topBar = { EmptyTopBarWithNavigation(onBackClick = onBackClick) },
|
||||
modifier = modifier.systemBarsPadding(),
|
||||
backgroundColor = colorResource(id = R.color.background_primary),
|
||||
) {
|
||||
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
|
||||
background?.invoke()
|
||||
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = titleRes),
|
||||
modifier = modifier.padding(start = 20.dp, end = 20.dp, bottom = 52.dp),
|
||||
style = TangemTypography.headline1,
|
||||
color = colorResource(id = R.color.text_primary_1),
|
||||
)
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EmptyTopBarWithNavigation(
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
TopAppBar(
|
||||
title = { },
|
||||
navigationIcon =
|
||||
{
|
||||
IconButton(onClick = onBackClick) {
|
||||
Icon(
|
||||
painterResource(id = R.drawable.ic_back), "",
|
||||
tint = colorResource(id = R.color.icon_primary_1),
|
||||
)
|
||||
}
|
||||
},
|
||||
backgroundColor = colorResource(id = R.color.background_primary),
|
||||
elevation = 0.dp,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DetailsMainButton(
|
||||
title: String,
|
||||
enabled: Boolean = true,
|
||||
onClick: (() -> Unit),
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Button(
|
||||
onClick = onClick,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(48.dp),
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
enabled = enabled,
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
backgroundColor = colorResource(R.color.button_primary),
|
||||
contentColor = colorResource(R.color.text_primary_2),
|
||||
disabledBackgroundColor = colorResource(R.color.button_disabled),
|
||||
disabledContentColor = colorResource(R.color.text_disabled),
|
||||
),
|
||||
) {
|
||||
Text(text = title)
|
||||
Spacer(modifier = modifier.size(8.dp))
|
||||
Icon(painter = painterResource(id = R.drawable.ic_tangem), contentDescription = "")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
package com.tangem.tap.features.details.ui.details
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.google.accompanist.appcompattheme.AppCompatTheme
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
class DetailsFragment : Fragment(), StoreSubscriber<DetailsState> {
|
||||
|
||||
private val detailsViewModel = DetailsViewModel(store)
|
||||
|
||||
private var detailsScreenState: MutableState<DetailsScreenState> =
|
||||
mutableStateOf(detailsViewModel.updateState(store.state.detailsState))
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
enterTransition = inflater.inflateTransition(R.transition.slide_right)
|
||||
exitTransition = inflater.inflateTransition(R.transition.fade)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
return ComposeView(requireContext()).apply {
|
||||
setContent {
|
||||
isTransitionGroup = true
|
||||
AppCompatTheme {
|
||||
DetailsScreen(
|
||||
state = detailsScreenState.value,
|
||||
onBackPressed = { store.dispatch(NavigationAction.PopBackTo()) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
store.subscribe(this) { state ->
|
||||
state.skipRepeats { oldState, newState ->
|
||||
oldState.detailsState == newState.detailsState
|
||||
}.select { it.detailsState }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
// override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// super.onViewCreated(view, savedInstanceState)
|
||||
//
|
||||
// binding.toolbar.setNavigationOnClickListener {
|
||||
// store.dispatch(NavigationAction.PopBackTo())
|
||||
// }
|
||||
// }
|
||||
|
||||
override fun newState(state: DetailsState) {
|
||||
if (activity == null || view == null) return
|
||||
detailsScreenState.value = detailsViewModel.updateState(state)
|
||||
}
|
||||
|
||||
// private fun setState(state: DetailsState) = with(binding) {
|
||||
//
|
||||
// if (state.cardInfo != null) {
|
||||
// val cardId = if (state.isTangemTwins) {
|
||||
// state.scanResponse?.card?.getTwinCardIdForUser()
|
||||
// } else {
|
||||
// state.cardInfo.cardId
|
||||
// }
|
||||
// tvCardId.text = cardId
|
||||
// tvIssuer.text = state.cardInfo.issuer
|
||||
// tvSignedHashes.text = getString(
|
||||
// R.string.details_row_subtitle_signed_hashes_format,
|
||||
// state.cardInfo.signedHashes.toString()
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// tvSignedHashes.show(!state.isTangemTwins)
|
||||
// tvSignedHashesTitle.show(!state.isTangemTwins)
|
||||
//
|
||||
// tvDisclaimer.setOnClickListener { store.dispatch(DetailsAction.ShowDisclaimer) }
|
||||
//
|
||||
//
|
||||
// if (state.isTangemTwins) {
|
||||
// tvResetToFactory.show()
|
||||
// tvResetToFactory.text = getText(R.string.details_row_title_twins_recreate)
|
||||
// tvResetToFactory.setOnClickListener {
|
||||
// store.dispatch(DetailsAction.ReCreateTwinsWallet(state.twinCardsState.cardNumber!!))
|
||||
// }
|
||||
// } else {
|
||||
// tvResetToFactory.show()
|
||||
// tvResetToFactory.text = getText(R.string.details_row_title_reset_factory_settings)
|
||||
// tvResetToFactory.setOnClickListener {
|
||||
// store.dispatch(DetailsAction.ResetToFactory.Check)
|
||||
// store.dispatch(DetailsAction.ResetToFactory.Proceed)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// tvAppCurrency.show(state.scanResponse?.card?.isMultiwalletAllowed != true)
|
||||
// tvAppCurrencyTitle.show(state.scanResponse?.card?.isMultiwalletAllowed != true)
|
||||
// tvAppCurrency.text = state.appCurrency.code
|
||||
//
|
||||
// tvAppCurrency.setOnClickListener {
|
||||
// store.dispatch(WalletAction.AppCurrencyAction.ChooseAppCurrency)
|
||||
// }
|
||||
//
|
||||
// tvSecurityAndPrivacy.setOnClickListener {
|
||||
// store.dispatch(DetailsAction.ManageSecurity.CheckCurrentSecurityOption(state.scanResponse!!.card))
|
||||
// tvSecurityAndPrivacy.setOnClickListener {
|
||||
// store.dispatch(NavigationAction.NavigateTo(AppScreen.SecurityAndPrivacy))
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
package com.tangem.tap.features.details.ui.details
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.tap.common.compose.TangemTypography
|
||||
import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
fun DetailsScreen(
|
||||
state: DetailsScreenState,
|
||||
onBackPressed: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
||||
SettingsScreensScaffold(
|
||||
content = {
|
||||
Content(state = state, modifier = modifier)
|
||||
},
|
||||
titleRes = R.string.details_title,
|
||||
onBackClick = onBackPressed,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Content(
|
||||
state: DetailsScreenState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 40.dp),
|
||||
) {
|
||||
|
||||
state.elements.map {
|
||||
if (it == SettingsElement.WalletConnect) {
|
||||
WalletConnectDetailsItem(
|
||||
onItemsClick = state.onItemsClick, modifier = modifier
|
||||
)
|
||||
} else {
|
||||
DetailsItem(
|
||||
item = it, onItemsClick = state.onItemsClick,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
TangemSocialAccounts(state.tangemLinks, state.onSocialNetworkClick)
|
||||
Spacer(modifier = Modifier.size(12.dp))
|
||||
Text(
|
||||
text = "${stringResource(id = state.appNameRes)} ${state.tangemVersion}",
|
||||
style = TangemTypography.caption,
|
||||
color = colorResource(id = R.color.text_tertiary),
|
||||
modifier = modifier.padding(start = 16.dp, end = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WalletConnectDetailsItem(
|
||||
onItemsClick: (SettingsElement) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.height(84.dp)
|
||||
.clickable { onItemsClick(SettingsElement.WalletConnect) },
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_walletconnect),
|
||||
contentDescription = stringResource(id = R.string.wallet_connect_title),
|
||||
modifier = modifier.padding(start = 20.dp, end = 20.dp),
|
||||
tint = colorResource(id = R.color.all_colors_azure),
|
||||
)
|
||||
Column(
|
||||
modifier = modifier.height(56.dp),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.wallet_connect_title),
|
||||
modifier = modifier.padding(end = 20.dp, bottom = 4.dp),
|
||||
style = TangemTypography.headline3,
|
||||
color = colorResource(id = R.color.text_primary_1),
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.wallet_connect_subtitle),
|
||||
modifier = modifier.padding(end = 20.dp, bottom = 4.dp),
|
||||
style = TangemTypography.body1,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DetailsItem(
|
||||
item: SettingsElement,
|
||||
onItemsClick: (SettingsElement) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.height(56.dp)
|
||||
.fillMaxWidth()
|
||||
.clickable { onItemsClick(item) },
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = item.iconRes),
|
||||
contentDescription = stringResource(id = item.titleRes),
|
||||
modifier = modifier.padding(start = 20.dp, end = 20.dp),
|
||||
tint = colorResource(id = R.color.icon_secondary),
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = item.titleRes),
|
||||
modifier = modifier.padding(end = 20.dp),
|
||||
style = TangemTypography.subtitle1,
|
||||
color = colorResource(id = R.color.text_primary_1),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TangemSocialAccounts(
|
||||
links: List<TangemLink>,
|
||||
onSocialNetworkClick: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier.padding(start = 8.dp, end = 8.dp)
|
||||
) {
|
||||
|
||||
links.map {
|
||||
Icon(
|
||||
painter = painterResource(id = it.iconRes),
|
||||
contentDescription = "",
|
||||
modifier = modifier
|
||||
.padding(8.dp)
|
||||
.clickable { onSocialNetworkClick(it.url) }
|
||||
,
|
||||
tint = colorResource(id = R.color.icon_informative),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun Preview() {
|
||||
DetailsScreen(
|
||||
state = DetailsScreenState(
|
||||
SettingsElement.values().toList(),
|
||||
TangemSocialAccounts.accountsEn,
|
||||
"Tangem 2.14.12 (343)",
|
||||
{}, {}
|
||||
),
|
||||
{},
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package com.tangem.tap.features.details.ui.details
|
||||
|
||||
import com.tangem.wallet.R
|
||||
|
||||
data class DetailsScreenState(
|
||||
val elements: List<SettingsElement>,
|
||||
val tangemLinks: List<TangemLink>,
|
||||
val tangemVersion: String,
|
||||
val onItemsClick: (SettingsElement) -> Unit,
|
||||
val onSocialNetworkClick: (String) -> Unit,
|
||||
) {
|
||||
val appNameRes: Int = R.string.app_name
|
||||
}
|
||||
|
||||
|
||||
enum class SettingsElement(
|
||||
val iconRes: Int,
|
||||
val titleRes: Int,
|
||||
) {
|
||||
WalletConnect(R.drawable.ic_walletconnect, R.string.wallet_connect_title),
|
||||
Chat(R.drawable.ic_chat, R.string.details_ask_a_question),
|
||||
SendFeedback(R.drawable.ic_comment, R.string.details_row_title_send_feedback),
|
||||
CardSettings(R.drawable.ic_card_settings, R.string.card_settings_title),
|
||||
AppSettings(R.drawable.ic_settings, R.string.app_settings_title),
|
||||
LinkMoreCards(R.drawable.ic_more_cards, R.string.details_row_title_create_backup),
|
||||
TermsOfService(R.drawable.ic_text, R.string.disclaimer_title),
|
||||
PrivacyPolicy(R.drawable.ic_lock, R.string.details_row_privacy_policy),
|
||||
;
|
||||
}
|
||||
|
||||
data class TangemLink(
|
||||
val iconRes: Int,
|
||||
val url: String,
|
||||
)
|
||||
|
||||
object TangemSocialAccounts {
|
||||
val accountsEn: List<TangemLink> = listOf(
|
||||
TangemLink(R.drawable.ic_telegram, "https://t.me/TangemCards"),
|
||||
TangemLink(R.drawable.ic_twitter, "https://twitter.com/tangem"),
|
||||
TangemLink(R.drawable.ic_facebook, "https://m.facebook.com/TangemCards/"),
|
||||
TangemLink(R.drawable.ic_instagram, "https://instagram.com/tangemcards"),
|
||||
TangemLink(R.drawable.ic_github, "https://github.com/tangem"),
|
||||
TangemLink(R.drawable.ic_youtube, "https://youtube.com/channel/UCFGwLS7yggzVkP6ozte0m1w"),
|
||||
TangemLink(R.drawable.ic_linkedin, "https://www.linkedin.com/company/tangem"),
|
||||
)
|
||||
val accountsRu: List<TangemLink> = listOf(
|
||||
TangemLink(R.drawable.ic_telegram, "https://t.me/tangem_ru"),
|
||||
TangemLink(R.drawable.ic_twitter, "https://twitter.com/tangem"),
|
||||
TangemLink(R.drawable.ic_facebook, "https://m.facebook.com/TangemCards/"),
|
||||
TangemLink(R.drawable.ic_instagram, "https://instagram.com/tangemcards"),
|
||||
TangemLink(R.drawable.ic_github, "https://github.com/tangem"),
|
||||
TangemLink(R.drawable.ic_youtube, "https://youtube.com/channel/UCFGwLS7yggzVkP6ozte0m1w"),
|
||||
TangemLink(R.drawable.ic_linkedin, "https://www.linkedin.com/company/tangem"),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
package com.tangem.tap.features.details.ui.details
|
||||
|
||||
import com.tangem.tap.common.feedback.FeedbackEmail
|
||||
import com.tangem.tap.common.feedback.SupportInfo
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
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.home.LocaleRegionProvider
|
||||
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import org.rekotlin.Store
|
||||
|
||||
class DetailsViewModel(private val store: Store<AppState>) {
|
||||
|
||||
fun updateState(state: DetailsState): DetailsScreenState {
|
||||
val settings = SettingsElement.values().mapNotNull {
|
||||
when (it) {
|
||||
SettingsElement.WalletConnect -> {
|
||||
if (state.scanResponse?.card?.isMultiwalletAllowed == true) it else null
|
||||
}
|
||||
SettingsElement.LinkMoreCards -> {
|
||||
if (state.createBackupAllowed) it else null
|
||||
}
|
||||
SettingsElement.PrivacyPolicy -> {
|
||||
if (state.privacyPolicyUrl != null) it else null
|
||||
}
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
|
||||
return DetailsScreenState(
|
||||
settings,
|
||||
tangemLinks = getSocialLinks(),
|
||||
tangemVersion = getTangemAppVersion(),
|
||||
onItemsClick = { handleClickingSettingsItem(it) },
|
||||
onSocialNetworkClick = { handleSocialNetworkClick(it) },
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleSocialNetworkClick(url: String) {
|
||||
store.dispatch(NavigationAction.OpenUrl(url))
|
||||
}
|
||||
|
||||
private fun handleClickingSettingsItem(item: SettingsElement) {
|
||||
when (item) {
|
||||
SettingsElement.WalletConnect -> {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.WalletConnectSessions))
|
||||
}
|
||||
SettingsElement.Chat -> {
|
||||
store.dispatch(GlobalAction.OpenChat(SupportInfo()))
|
||||
}
|
||||
SettingsElement.SendFeedback -> {
|
||||
store.dispatch(GlobalAction.SendEmail(FeedbackEmail()))
|
||||
}
|
||||
SettingsElement.CardSettings -> {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.CardSettings))
|
||||
}
|
||||
SettingsElement.AppSettings -> {
|
||||
// store.dispatch(NavigationAction.NavigateTo()) TODO: To be available later
|
||||
}
|
||||
SettingsElement.LinkMoreCards -> {
|
||||
store.dispatch(DetailsAction.CreateBackup)
|
||||
}
|
||||
SettingsElement.TermsOfService -> {
|
||||
store.dispatch(DetailsAction.ShowDisclaimer)
|
||||
}
|
||||
SettingsElement.PrivacyPolicy -> {
|
||||
// TODO: To be available later
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSocialLinks(): List<TangemLink> {
|
||||
val locale = LocaleRegionProvider().getRegion()
|
||||
return if (locale.lowercase() == RUSSIA_COUNTRY_CODE) {
|
||||
TangemSocialAccounts.accountsRu
|
||||
} else {
|
||||
TangemSocialAccounts.accountsEn
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTangemAppVersion(): String {
|
||||
val versionCode: Int = BuildConfig.VERSION_CODE
|
||||
val versionName: String = BuildConfig.VERSION_NAME
|
||||
return "$versionName ($versionCode)"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package com.tangem.tap.features.details.ui.resetcard
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.google.accompanist.appcompattheme.AppCompatTheme
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
class ResetCardFragment : Fragment(), StoreSubscriber<DetailsState> {
|
||||
|
||||
private val viewModel = ResetCardViewModel(store)
|
||||
|
||||
private var screenState: MutableState<ResetCardScreenState> =
|
||||
mutableStateOf(viewModel.updateState(store.state.detailsState.cardSettingsState, context))
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
enterTransition = inflater.inflateTransition(R.transition.slide_right)
|
||||
exitTransition = inflater.inflateTransition(R.transition.fade)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
return ComposeView(requireContext()).apply {
|
||||
setContent {
|
||||
isTransitionGroup = true
|
||||
AppCompatTheme {
|
||||
ResetCardScreen(
|
||||
state = screenState.value,
|
||||
onBackPressed = { store.dispatch(NavigationAction.PopBackTo()) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
store.subscribe(this) { state ->
|
||||
state.skipRepeats { oldState, newState ->
|
||||
oldState.detailsState == newState.detailsState
|
||||
}.select { it.detailsState }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
override fun newState(state: DetailsState) {
|
||||
if (activity == null || view == null) return
|
||||
screenState.value = viewModel.updateState(state.cardSettingsState, context)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
package com.tangem.tap.features.details.ui.resetcard
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconToggleButton
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.tap.common.compose.TangemTypography
|
||||
import com.tangem.tap.features.details.ui.common.DetailsMainButton
|
||||
import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
fun ResetCardScreen(
|
||||
state: ResetCardScreenState,
|
||||
onBackPressed: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
||||
SettingsScreensScaffold(
|
||||
content = { ResetCardView(state = state, modifier = modifier) },
|
||||
background =
|
||||
{ Image(painter = painterResource(id = R.drawable.ic_reset_background), contentDescription = "") },
|
||||
titleRes = R.string.details_row_title_reset_factory_settings,
|
||||
onBackClick = onBackPressed,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ResetCardView(
|
||||
state: ResetCardScreenState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Bottom,
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.common_attention),
|
||||
modifier = modifier.padding(start = 20.dp, end = 20.dp),
|
||||
style = TangemTypography.headline3,
|
||||
color = colorResource(id = R.color.text_primary_1),
|
||||
)
|
||||
|
||||
Spacer(modifier = modifier.size(24.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.reset_factory_warning),
|
||||
modifier = modifier.padding(start = 20.dp, end = 20.dp),
|
||||
style = TangemTypography.body1,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
)
|
||||
|
||||
Spacer(modifier = modifier.size(44.dp))
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(end = 20.dp),
|
||||
) {
|
||||
IconToggleButton(
|
||||
checked = state.accepted,
|
||||
onCheckedChange = state.onAcceptWarningToggleClick,
|
||||
modifier = modifier.padding(start = 20.dp, end = 20.dp),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(
|
||||
if (state.accepted) {
|
||||
R.drawable.ic_accepted
|
||||
} else {
|
||||
R.drawable.ic_unticked
|
||||
},
|
||||
),
|
||||
contentDescription = null,
|
||||
tint = if (state.accepted) {
|
||||
colorResource(id = R.color.icon_accent)
|
||||
} else {
|
||||
colorResource(id = R.color.icon_secondary)
|
||||
},
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringResource(id = R.string.reset_factory_confirmation),
|
||||
style = TangemTypography.body2,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = modifier.size(32.dp))
|
||||
Box(
|
||||
modifier = modifier
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 32.dp),
|
||||
) {
|
||||
DetailsMainButton(
|
||||
title = stringResource(id = R.string.reset_factory_button),
|
||||
onClick = state.onResetButtonClick,
|
||||
enabled = state.resetButtonEnabled
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun ResetCardScreenPreview(
|
||||
|
||||
) {
|
||||
ResetCardScreen(state = ResetCardScreenState(onAcceptWarningToggleClick = {}, accepted = true) {}, {})
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.tap.features.details.ui.resetcard
|
||||
|
||||
data class ResetCardScreenState(
|
||||
val accepted: Boolean = false,
|
||||
val onAcceptWarningToggleClick: (Boolean) -> Unit,
|
||||
val onResetButtonClick: () -> Unit,
|
||||
) {
|
||||
val resetButtonEnabled: Boolean
|
||||
get() = accepted
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.tap.features.details.ui.resetcard
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.details.redux.CardSettingsState
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import org.rekotlin.Store
|
||||
|
||||
class ResetCardViewModel(private val store: Store<AppState>) {
|
||||
|
||||
fun updateState(state: CardSettingsState?, context: Context?): ResetCardScreenState {
|
||||
return ResetCardScreenState(
|
||||
accepted = state?.resetConfirmed ?: false,
|
||||
onAcceptWarningToggleClick = { store.dispatch(DetailsAction.ResetToFactory.Confirm(it)) },
|
||||
onResetButtonClick = { store.dispatch(DetailsAction.ResetToFactory.Proceed) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package com.tangem.tap.features.details.ui.securitymode
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.google.accompanist.appcompattheme.AppCompatTheme
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
class SecurityModeFragment : Fragment(), StoreSubscriber<DetailsState> {
|
||||
|
||||
private val viewModel = SecurityModeViewModel(store)
|
||||
|
||||
private var screenState: MutableState<SecurityModeScreenState> =
|
||||
mutableStateOf(viewModel.updateState(store.state.detailsState.cardSettingsState?.manageSecurityState))
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
enterTransition = inflater.inflateTransition(R.transition.slide_right)
|
||||
exitTransition = inflater.inflateTransition(R.transition.fade)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
return ComposeView(requireContext()).apply {
|
||||
setContent {
|
||||
isTransitionGroup = true
|
||||
AppCompatTheme {
|
||||
SecurityModeScreen(
|
||||
state = screenState.value,
|
||||
onBackPressed = { store.dispatch(NavigationAction.PopBackTo()) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
store.subscribe(this) { state ->
|
||||
state.skipRepeats { oldState, newState ->
|
||||
oldState.detailsState == newState.detailsState
|
||||
}.select { it.detailsState }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
override fun newState(state: DetailsState) {
|
||||
if (activity == null || view == null) return
|
||||
screenState.value = viewModel.updateState(state.cardSettingsState?.manageSecurityState)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
package com.tangem.tap.features.details.ui.securitymode
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.material.RadioButton
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.tap.common.compose.TangemTypography
|
||||
import com.tangem.tap.features.details.redux.SecurityOption
|
||||
import com.tangem.tap.features.details.ui.common.DetailsMainButton
|
||||
import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
fun SecurityModeScreen(
|
||||
state: SecurityModeScreenState,
|
||||
onBackPressed: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
||||
SettingsScreensScaffold(
|
||||
content = { SecurityModeOptions(state = state, modifier = modifier) },
|
||||
titleRes = R.string.card_settings_security_mode,
|
||||
onBackClick = onBackPressed
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SecurityModeOptions(
|
||||
state: SecurityModeScreenState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(bottom = 28.dp),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
|
||||
Column(modifier = modifier.fillMaxWidth()) {
|
||||
|
||||
state.availableOptions.map {
|
||||
|
||||
val selected = it == state.selectedSecurityMode
|
||||
|
||||
val title = it.toTitleRes()
|
||||
|
||||
val subtitle = when (it) {
|
||||
SecurityOption.LongTap -> R.string.details_manage_security_long_tap_description
|
||||
SecurityOption.PassCode -> R.string.details_manage_security_passcode_description
|
||||
SecurityOption.AccessCode -> R.string.details_manage_security_access_code_description
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.selectable(
|
||||
selected = selected, onClick = { state.onNewModeSelected(it) },
|
||||
)
|
||||
.padding(start = 20.dp, end = 20.dp, bottom = 32.dp),
|
||||
) {
|
||||
|
||||
RadioButton(
|
||||
selected = selected, onClick = null,
|
||||
modifier = modifier.padding(end = 20.dp),
|
||||
)
|
||||
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(id = title),
|
||||
style = TangemTypography.subtitle1,
|
||||
color = colorResource(id = R.color.text_primary_1),
|
||||
)
|
||||
Spacer(modifier = modifier.size(4.dp))
|
||||
Text(
|
||||
text = stringResource(id = subtitle),
|
||||
style = TangemTypography.body2,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DetailsMainButton(
|
||||
title = stringResource(id = R.string.common_save_changes),
|
||||
enabled = state.isSaveChangesEnabled,
|
||||
onClick = state.onSaveChangesClicked,
|
||||
modifier = modifier
|
||||
.padding(start = 20.dp, end = 20.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun SecurityModeScreenPreview() {
|
||||
SecurityModeScreen(
|
||||
state = SecurityModeScreenState(
|
||||
availableOptions = SecurityOption.values().toList(),
|
||||
selectedSecurityMode = SecurityOption.LongTap,
|
||||
isSaveChangesEnabled = false,
|
||||
onNewModeSelected = {},
|
||||
onSaveChangesClicked = {},
|
||||
),
|
||||
{}
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.tap.features.details.ui.securitymode
|
||||
|
||||
import com.tangem.tap.features.details.redux.SecurityOption
|
||||
import com.tangem.wallet.R
|
||||
|
||||
data class SecurityModeScreenState(
|
||||
val availableOptions: List<SecurityOption>,
|
||||
val selectedSecurityMode: SecurityOption,
|
||||
val isSaveChangesEnabled: Boolean,
|
||||
val onNewModeSelected: (SecurityOption) -> Unit,
|
||||
val onSaveChangesClicked: () -> Unit,
|
||||
)
|
||||
|
||||
fun SecurityOption.toTitleRes(): Int {
|
||||
return when (this) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.tap.features.details.ui.securitymode
|
||||
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.ManageSecurityState
|
||||
import com.tangem.tap.features.details.redux.SecurityOption
|
||||
import org.rekotlin.Store
|
||||
|
||||
class SecurityModeViewModel(val store: Store<AppState>) {
|
||||
|
||||
fun updateState(state: ManageSecurityState?): SecurityModeScreenState {
|
||||
|
||||
if (state == null) return SecurityModeScreenState(
|
||||
emptyList(), SecurityOption.LongTap, false, {},
|
||||
{},
|
||||
)
|
||||
return SecurityModeScreenState(
|
||||
state.allowedOptions.toList(),
|
||||
selectedSecurityMode = state.selectedOption,
|
||||
isSaveChangesEnabled = state.selectedOption != state.currentOption,
|
||||
onNewModeSelected = { store.dispatch(DetailsAction.ManageSecurity.SelectOption(it)) },
|
||||
onSaveChangesClicked = { store.dispatch(DetailsAction.ManageSecurity.SaveChanges) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
package com.tangem.tap.features.details.ui.securityprivacy
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
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 com.tangem.wallet.databinding.ViewComposeFragmentBinding
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
class ChangeAccessCodeFragment : BaseStoreFragment(R.layout.view_compose_fragment),
|
||||
StoreSubscriber<DetailsState> {
|
||||
|
||||
private val binding: ViewComposeFragmentBinding
|
||||
by viewBinding(ViewComposeFragmentBinding::bind)
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.toolbar.setNavigationOnClickListener {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
package com.tangem.tap.features.details.ui.securityprivacy
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.tangem.tap.common.extensions.show
|
||||
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 onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.toolbar.setNavigationOnClickListener {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
setListeners()
|
||||
|
||||
}
|
||||
|
||||
private fun setListeners() = with(binding) {
|
||||
tvAccessCode.setOnClickListener {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.ChangeAccessCode))
|
||||
}
|
||||
|
||||
switchSaveCards.setOnCheckedChangeListener { _, isChecked ->
|
||||
store.dispatch(
|
||||
DetailsAction.ManagePrivacy.SwitchPrivacySetting(
|
||||
allow = isChecked,
|
||||
setting = PrivacySetting.SAVE_CARDS
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
switchSavePasswords.setOnCheckedChangeListener { _, isChecked ->
|
||||
store.dispatch(
|
||||
DetailsAction.ManagePrivacy.SwitchPrivacySetting(
|
||||
allow = isChecked,
|
||||
setting = PrivacySetting.SAVE_ACCESS_CODE
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun newState(state: DetailsState) {
|
||||
with(binding) {
|
||||
|
||||
val card = state.scanResponse?.card
|
||||
llAccessManagement.show(card != null)
|
||||
if (card != null) {
|
||||
llAccessManagement.setOnClickListener {
|
||||
store.dispatch(
|
||||
DetailsAction.ManageSecurity.CheckCurrentSecurityOption(
|
||||
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
|
||||
|
||||
tvAccessCode.show(state.scanResponse?.card?.isAccessCodeSet == true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun subscribeToStore() {
|
||||
store.subscribe(this) { state ->
|
||||
state.skipRepeats { oldState, newState ->
|
||||
oldState.detailsState == newState.detailsState
|
||||
}.select { it.detailsState }
|
||||
}
|
||||
storeSubscribersList.add(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.tap.features.details.ui.walletconnect
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
@Composable
|
||||
fun WalletConnectScreen(
|
||||
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
//data class WalletConnectScreenState(
|
||||
// sessions: WcSessionScreenState
|
||||
//)
|
||||
//
|
||||
//data class WcSessionScreenState(
|
||||
// val icon: Bitmap,
|
||||
//
|
||||
//)
|
||||
Loading…
Add table
Add a link
Reference in a new issue