Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ABW-3919] Security factors screen redesign #1280

Merged
merged 4 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,43 +64,63 @@ sealed interface SettingsItem {
LinkedConnectors -> DSR.ic_desktop_connection
is SecurityCenter -> DSR.ic_security_center
Troubleshooting -> DSR.ic_troubleshooting
else -> null
}
}
}

sealed interface SecurityFactorsSettingsItem {

val count: Int
enum class SecurityFactorCategory {
Own, Hardware, Information;

data class SeedPhrases(
override val count: Int,
@StringRes
fun titleRes(): Int? {
return when (this) {
Own -> null
Hardware -> R.string.securityFactors_hardware
Information -> R.string.securityFactors_information
}
}
}

data class BiometricsPin(
val securityProblems: ImmutableSet<SecurityProblem> = persistentSetOf()
) : SecurityFactorsSettingsItem

data class LedgerHardwareWallets(override val count: Int) : SecurityFactorsSettingsItem
data object LedgerNano : SecurityFactorsSettingsItem
data object ArculusCard : SecurityFactorsSettingsItem
data object Password : SecurityFactorsSettingsItem
data object Passphrase : SecurityFactorsSettingsItem

@StringRes
fun titleRes(): Int {
return when (this) {
is SeedPhrases -> R.string.securityFactors_seedPhrases_title
is LedgerHardwareWallets -> R.string.securityFactors_ledgerWallet_title
is BiometricsPin -> R.string.factorSources_card_deviceTitle
LedgerNano -> R.string.factorSources_card_ledgerTitle
ArculusCard -> R.string.factorSources_card_arculusCardTitle
Passphrase -> R.string.factorSources_card_passphraseTitle
Password -> R.string.factorSources_card_passwordTitle
}
}

@StringRes
fun subtitleRes(): Int {
return when (this) {
is SeedPhrases -> R.string.securityFactors_seedPhrases_subtitle
is LedgerHardwareWallets -> R.string.securityFactors_ledgerWallet_subtitle
is BiometricsPin -> R.string.factorSources_card_deviceDescription
LedgerNano -> R.string.factorSources_card_ledgerDescription
ArculusCard -> R.string.factorSources_card_arculusCardDescription
Passphrase -> R.string.factorSources_card_passphraseDescription
Password -> R.string.factorSources_card_passwordDescription
}
}

@DrawableRes
fun getIcon(): Int? { // add rest of icons
return when (this) {
is SeedPhrases -> DSR.ic_seed_phrases
is LedgerHardwareWallets -> DSR.ic_ledger_hardware_wallets
is BiometricsPin -> DSR.ic_device_biometric_pin
LedgerNano -> DSR.ic_ledger_nano
ArculusCard -> DSR.ic_arculus_card
Password -> DSR.ic_password
Passphrase -> DSR.ic_passphrase
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,23 @@ fun NavGraphBuilder.securityCenterNavGraph(
},
onSecurityFactorSettingItemClick = { item ->
when (item) {
is SettingsItem.SecurityFactorsSettingsItem.LedgerHardwareWallets -> {
is SettingsItem.SecurityFactorsSettingsItem.LedgerNano -> {
navController.ledgerHardwareWalletsScreen()
}

is SettingsItem.SecurityFactorsSettingsItem.SeedPhrases -> {
is SettingsItem.SecurityFactorsSettingsItem.BiometricsPin -> {
navController.seedPhrases()
}

SettingsItem.SecurityFactorsSettingsItem.ArculusCard -> {
// TODO
}
SettingsItem.SecurityFactorsSettingsItem.Passphrase -> {
// TODO
}
SettingsItem.SecurityFactorsSettingsItem.Password -> {
// TODO
}
}
}
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.babylon.wallet.android.presentation.settings.securitycenter.securityfactors

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
Expand All @@ -18,26 +21,31 @@ import com.babylon.wallet.android.R
import com.babylon.wallet.android.designsystem.theme.RadixTheme
import com.babylon.wallet.android.designsystem.theme.RadixWalletTheme
import com.babylon.wallet.android.domain.model.SecurityProblem
import com.babylon.wallet.android.presentation.settings.SettingsItem
import com.babylon.wallet.android.presentation.settings.SettingsItem.SecurityFactorsSettingsItem
import com.babylon.wallet.android.presentation.settings.SettingsItem.SecurityFactorsSettingsItem.SecurityFactorCategory
import com.babylon.wallet.android.presentation.settings.securitycenter.securityfactors.SecurityFactorsViewModel.Companion.currentSecurityFactorsSettings
import com.babylon.wallet.android.presentation.ui.composables.DefaultSettingsItem
import com.babylon.wallet.android.presentation.ui.composables.RadixCenteredTopAppBar
import com.babylon.wallet.android.presentation.ui.composables.statusBarsAndBanner
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.persistentSetOf
import kotlinx.collections.immutable.toPersistentList
import kotlinx.collections.immutable.toPersistentSet

@Composable
fun SecurityFactorsScreen(
modifier: Modifier = Modifier,
viewModel: SecurityFactorsViewModel,
onSecurityFactorSettingItemClick: (SettingsItem.SecurityFactorsSettingsItem) -> Unit,
onSecurityFactorSettingItemClick: (SecurityFactorsSettingsItem) -> Unit,
onBackClick: () -> Unit
) {
val state by viewModel.state.collectAsStateWithLifecycle()
SecurityFactorsContent(
modifier = modifier.fillMaxSize(),
securityFactorsSettings = state.settings,
securityFactorSettingItems = state.securityFactorSettingItems,
onSecurityFactorSettingItemClick = onSecurityFactorSettingItemClick,
onBackClick = onBackClick,
)
Expand All @@ -46,8 +54,8 @@ fun SecurityFactorsScreen(
@Composable
private fun SecurityFactorsContent(
modifier: Modifier = Modifier,
securityFactorsSettings: ImmutableSet<SettingsItem.SecurityFactorsSettingsItem>,
onSecurityFactorSettingItemClick: (SettingsItem.SecurityFactorsSettingsItem) -> Unit,
securityFactorSettingItems: ImmutableMap<SecurityFactorCategory, ImmutableSet<SecurityFactorsSettingsItem>>,
onSecurityFactorSettingItemClick: (SecurityFactorsSettingsItem) -> Unit,
onBackClick: () -> Unit,
) {
Scaffold(
Expand Down Expand Up @@ -77,56 +85,56 @@ private fun SecurityFactorsContent(
modifier = Modifier.padding(RadixTheme.dimensions.paddingDefault)
)
}
securityFactorsSettings.forEach { securityFactorsSettingsItem ->
val lastItem = securityFactorsSettings.last() == securityFactorsSettingsItem
item {
securityFactorSettingItems.forEach { (category, securityFactorsItems) ->
// Add a header for the security factor category if any
val categoryTitleRes = category.titleRes()
categoryTitleRes?.let {
item {
Text(
text = stringResource(id = categoryTitleRes),
style = RadixTheme.typography.body1Header,
color = RadixTheme.colors.gray2,
modifier = Modifier.padding(RadixTheme.dimensions.paddingDefault)
)
}
}
// Add security factor items
items(securityFactorsItems.toList()) { securityFactorsItem ->
val isLastItem = securityFactorsItem == securityFactorsItems.last()
DefaultSettingsItem(
title = stringResource(id = securityFactorsSettingsItem.titleRes()),
subtitle = stringResource(id = securityFactorsSettingsItem.subtitleRes()),
leadingIconRes = securityFactorsSettingsItem.getIcon(),
title = stringResource(id = securityFactorsItem.titleRes()),
subtitle = stringResource(id = securityFactorsItem.subtitleRes()),
leadingIconRes = securityFactorsItem.getIcon(),
onClick = {
onSecurityFactorSettingItemClick(securityFactorsSettingsItem)
onSecurityFactorSettingItemClick(securityFactorsItem)
},
info = when (securityFactorsSettingsItem) {
is SettingsItem.SecurityFactorsSettingsItem.LedgerHardwareWallets -> {
if (securityFactorsSettingsItem.count == 1) {
stringResource(id = R.string.securityFactors_ledgerWallet_counterSingular)
} else {
stringResource(
id = R.string.securityFactors_ledgerWallet_counterPlural,
securityFactorsSettingsItem.count
)
}
}

is SettingsItem.SecurityFactorsSettingsItem.SeedPhrases -> {
if (securityFactorsSettingsItem.count == 1) {
stringResource(id = R.string.securityFactors_seedPhrases_counterSingular)
} else {
stringResource(
id = R.string.securityFactors_seedPhrases_counterPlural,
securityFactorsSettingsItem.count
)
}
}
},
warnings = if (securityFactorsSettingsItem is SettingsItem.SecurityFactorsSettingsItem.SeedPhrases) {
getSecurityWarnings(securityFactorsSettingsItem = securityFactorsSettingsItem)
warnings = if (securityFactorsItem is SecurityFactorsSettingsItem.BiometricsPin) {
getSecurityWarnings(securityFactorsSettingsItem = securityFactorsItem)
} else {
null
}
)
if (!lastItem) {
HorizontalDivider(color = RadixTheme.colors.gray5)
if (isLastItem.not()) {
HorizontalDivider(
modifier = Modifier
.background(color = RadixTheme.colors.defaultBackground)
.padding(horizontal = RadixTheme.dimensions.paddingSemiLarge),
color = RadixTheme.colors.gray4
)
} else {
HorizontalDivider(color = RadixTheme.colors.gray4)
}
}
}
item {
Spacer(modifier = Modifier.padding(RadixTheme.dimensions.paddingDefault))
}
}
}
}

@Composable
fun getSecurityWarnings(securityFactorsSettingsItem: SettingsItem.SecurityFactorsSettingsItem.SeedPhrases): PersistentList<String> {
fun getSecurityWarnings(securityFactorsSettingsItem: SecurityFactorsSettingsItem.BiometricsPin): PersistentList<String> {
return mutableListOf<String>().apply {
securityFactorsSettingsItem.securityProblems.forEach { problem ->
when (problem) {
Expand All @@ -135,7 +143,7 @@ fun getSecurityWarnings(securityFactorsSettingsItem: SettingsItem.SecurityFactor
}

is SecurityProblem.SeedPhraseNeedRecovery -> {
add(stringResource(id = R.string.securityProblems_no9_seedPhrases))
add(stringResource(id = R.string.securityProblems_no9_securityFactors))
}

else -> {}
Expand All @@ -146,11 +154,41 @@ fun getSecurityWarnings(securityFactorsSettingsItem: SettingsItem.SecurityFactor

@Preview(showBackground = true)
@Composable
fun SecurityFactorsContentPreview() {
private fun SecurityFactorsPreview() {
RadixWalletTheme {
SecurityFactorsContent(
modifier = Modifier,
securityFactorsSettings = persistentSetOf(),
securityFactorSettingItems = currentSecurityFactorsSettings,
onSecurityFactorSettingItemClick = {},
onBackClick = {}
)
}
}

@Preview(showBackground = true)
@Composable
private fun SecurityFactorsWithSecurityProblemsPreview() {
RadixWalletTheme {
SecurityFactorsContent(
modifier = Modifier,
securityFactorSettingItems = persistentMapOf(
SecurityFactorCategory.Own to persistentSetOf(
SecurityFactorsSettingsItem.BiometricsPin(
securityProblems = setOf(
SecurityProblem.SeedPhraseNeedRecovery(isAnyActivePersonaAffected = true),
SecurityProblem.EntitiesNotRecoverable(
accountsNeedBackup = 7,
personasNeedBackup = 2,
hiddenAccountsNeedBackup = 1,
hiddenPersonasNeedBackup = 3
)
).toPersistentSet()
)
),
SecurityFactorCategory.Hardware to persistentSetOf(
SecurityFactorsSettingsItem.LedgerNano
)
),
onSecurityFactorSettingItemClick = {},
onBackClick = {}
)
Expand Down
Loading
Loading