From 450cd11aac09e8ac42c77462824f5911c1dc8b2d Mon Sep 17 00:00:00 2001 From: Sergiu Puhalschi Date: Tue, 3 Dec 2024 12:52:33 +0200 Subject: [PATCH 1/5] WIP --- .../domain/model/factors/FactorSourceCard.kt | 13 + .../domain/model/factors/StatusMessage.kt | 13 + .../settings/debug/DebugSettingsNav.kt | 6 + .../debug/factors/SecurityFactorSamplesNav.kt | 40 ++ .../factors/SecurityFactorSamplesScreen.kt | 68 +++ .../factors/SecurityFactorSamplesViewModel.kt | 17 + .../ui/composables/SimpleAccountCard.kt | 8 +- .../ui/composables/StatusMessageText.kt | 37 ++ .../composables/card/FactorSourceCardView.kt | 413 ++++++++++++++++++ .../card/SelectableFactorSourceCard.kt | 183 ++++++++ .../android/designsystem/theme/RadixShapes.kt | 1 + .../main/res/drawable/ic_factor_arculus.xml | 22 + .../main/res/drawable/ic_factor_device.xml | 18 + .../drawable/ic_factor_ledger_hardware.xml | 22 + .../res/drawable/ic_factor_passphrase.xml | 14 + .../main/res/drawable/ic_factor_password.xml | 22 + 16 files changed, 894 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/babylon/wallet/android/domain/model/factors/FactorSourceCard.kt create mode 100644 app/src/main/java/com/babylon/wallet/android/domain/model/factors/StatusMessage.kt create mode 100644 app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesNav.kt create mode 100644 app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesScreen.kt create mode 100644 app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt create mode 100644 app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/StatusMessageText.kt create mode 100644 app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/card/FactorSourceCardView.kt create mode 100644 app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/card/SelectableFactorSourceCard.kt create mode 100644 designsystem/src/main/res/drawable/ic_factor_arculus.xml create mode 100644 designsystem/src/main/res/drawable/ic_factor_device.xml create mode 100644 designsystem/src/main/res/drawable/ic_factor_ledger_hardware.xml create mode 100644 designsystem/src/main/res/drawable/ic_factor_passphrase.xml create mode 100644 designsystem/src/main/res/drawable/ic_factor_password.xml diff --git a/app/src/main/java/com/babylon/wallet/android/domain/model/factors/FactorSourceCard.kt b/app/src/main/java/com/babylon/wallet/android/domain/model/factors/FactorSourceCard.kt new file mode 100644 index 0000000000..43d7269672 --- /dev/null +++ b/app/src/main/java/com/babylon/wallet/android/domain/model/factors/FactorSourceCard.kt @@ -0,0 +1,13 @@ +package com.babylon.wallet.android.domain.model.factors + +import com.radixdlt.sargon.Account +import com.radixdlt.sargon.FactorSourceKind +import com.radixdlt.sargon.Persona + +data class FactorSourceCard( + val kind: FactorSourceKind, + val lastUsedOn: String?, + val messages: List, + val accounts: List, + val personas: List +) \ No newline at end of file diff --git a/app/src/main/java/com/babylon/wallet/android/domain/model/factors/StatusMessage.kt b/app/src/main/java/com/babylon/wallet/android/domain/model/factors/StatusMessage.kt new file mode 100644 index 0000000000..8697716eca --- /dev/null +++ b/app/src/main/java/com/babylon/wallet/android/domain/model/factors/StatusMessage.kt @@ -0,0 +1,13 @@ +package com.babylon.wallet.android.domain.model.factors + +data class StatusMessage( + val message: String, + val type: Type +) { + + enum class Type { + SUCCESS, + WARNING, + ERROR + } +} diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/DebugSettingsNav.kt b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/DebugSettingsNav.kt index e0ce6357e7..583e76ed6e 100644 --- a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/DebugSettingsNav.kt +++ b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/DebugSettingsNav.kt @@ -11,6 +11,7 @@ import androidx.navigation.navigation import com.babylon.wallet.android.presentation.settings.SettingsItem import com.babylon.wallet.android.presentation.settings.SettingsItem.DebugSettingsItem.InspectProfile import com.babylon.wallet.android.presentation.settings.debug.backups.inspectGoogleBackups +import com.babylon.wallet.android.presentation.settings.debug.factors.securityFactorSamples import com.babylon.wallet.android.presentation.settings.debug.profile.inspectProfile const val ROUTE_DEBUG_SETTINGS_SCREEN = "settings_debug_settings_screen" @@ -68,5 +69,10 @@ fun NavGraphBuilder.debugSettings( navController.popBackStack() } ) + securityFactorSamples( + onBackClick = { + navController.popBackStack() + } + ) } } diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesNav.kt b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesNav.kt new file mode 100644 index 0000000000..f35194859d --- /dev/null +++ b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesNav.kt @@ -0,0 +1,40 @@ +package com.babylon.wallet.android.presentation.settings.debug.factors + +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable + +private const val ROUTE = "security_factor_samples" + +fun NavController.securityFactorSamples() { + navigate(route = ROUTE) +} + +fun NavGraphBuilder.securityFactorSamples( + onBackClick: () -> Unit +) { + composable( + route = ROUTE, + enterTransition = { + slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Left) + }, + exitTransition = { + ExitTransition.None + }, + popEnterTransition = { + EnterTransition.None + }, + popExitTransition = { + slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Right) + } + ) { + SecurityFactorSamplesScreen( + viewModel = hiltViewModel(), + onBackClick = onBackClick + ) + } +} diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesScreen.kt b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesScreen.kt new file mode 100644 index 0000000000..536017bb39 --- /dev/null +++ b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesScreen.kt @@ -0,0 +1,68 @@ +package com.babylon.wallet.android.presentation.settings.debug.factors + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import com.babylon.wallet.android.R +import com.babylon.wallet.android.designsystem.theme.RadixTheme +import com.babylon.wallet.android.presentation.ui.RadixWalletPreviewTheme +import com.babylon.wallet.android.presentation.ui.composables.RadixCenteredTopAppBar +import com.babylon.wallet.android.presentation.ui.composables.statusBarsAndBanner + +@Composable +fun SecurityFactorSamplesScreen( + viewModel: SecurityFactorSamplesViewModel, + onBackClick: () -> Unit +) { + val state = viewModel.state.collectAsState().value + + SecurityFactorSamplesContent( + state = state, + onBackClick = onBackClick + ) +} + +@Composable +private fun SecurityFactorSamplesContent( + modifier: Modifier = Modifier, + state: SecurityFactorSamplesViewModel.State, + onBackClick: () -> Unit +) { + Scaffold( + modifier = modifier.fillMaxSize(), + topBar = { + RadixCenteredTopAppBar( + title = stringResource(R.string.securityFactors_title), + onBackClick = onBackClick, + windowInsets = WindowInsets.statusBarsAndBanner + ) + }, + containerColor = RadixTheme.colors.defaultBackground + ) { padding -> + Column( + modifier = Modifier.padding(padding), + horizontalAlignment = Alignment.Start + ) { + + } + } +} + +@Composable +@Preview +private fun SecurityFactorSamplesPreview() { + RadixWalletPreviewTheme { + SecurityFactorSamplesContent( + state = SecurityFactorSamplesViewModel.State(), + onBackClick = {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt new file mode 100644 index 0000000000..651b1e8982 --- /dev/null +++ b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt @@ -0,0 +1,17 @@ +package com.babylon.wallet.android.presentation.settings.debug.factors + +import com.babylon.wallet.android.domain.model.factors.FactorSourceCard +import com.babylon.wallet.android.presentation.common.StateViewModel +import com.babylon.wallet.android.presentation.common.UiState +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class SecurityFactorSamplesViewModel @Inject constructor() : StateViewModel() { + + override fun initialState(): State = State() + + data class State( + val items: List = emptyList() + ) : UiState +} \ No newline at end of file diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/SimpleAccountCard.kt b/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/SimpleAccountCard.kt index b56fe35f15..a632ba8a0f 100644 --- a/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/SimpleAccountCard.kt +++ b/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/SimpleAccountCard.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Shape import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import com.babylon.wallet.android.designsystem.theme.RadixTheme @@ -76,13 +77,14 @@ fun SimpleAccountCard( @Composable fun SimpleAccountCard( modifier: Modifier = Modifier, - account: Account + account: Account, + shape: Shape = RadixTheme.shapes.roundedRectSmall ) { Row( modifier = modifier .background( - account.appearanceId.gradient(), - RadixTheme.shapes.roundedRectSmall + brush = account.appearanceId.gradient(), + shape = shape ) .padding( horizontal = RadixTheme.dimensions.paddingLarge, diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/StatusMessageText.kt b/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/StatusMessageText.kt new file mode 100644 index 0000000000..db407b5fdd --- /dev/null +++ b/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/StatusMessageText.kt @@ -0,0 +1,37 @@ +package com.babylon.wallet.android.presentation.ui.composables + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.AnnotatedString +import com.babylon.wallet.android.designsystem.theme.RadixTheme +import com.babylon.wallet.android.domain.model.factors.StatusMessage + +@Composable +fun StatusMessageText( + message: StatusMessage +) { + WarningText( + text = AnnotatedString(message.message), + textStyle = RadixTheme.typography.body2HighImportance, + contentColor = message.type.color(), + iconRes = message.type.iconRes() + ) +} + +@Composable +private fun StatusMessage.Type.iconRes(): Int { + return when (this) { + StatusMessage.Type.SUCCESS -> com.babylon.wallet.android.designsystem.R.drawable.ic_check_circle_outline + StatusMessage.Type.WARNING -> com.babylon.wallet.android.designsystem.R.drawable.ic_warning_error + StatusMessage.Type.ERROR -> com.babylon.wallet.android.designsystem.R.drawable.ic_warning_error + } +} + +@Composable +private fun StatusMessage.Type.color(): Color { + return when (this) { + StatusMessage.Type.SUCCESS -> RadixTheme.colors.green1 + StatusMessage.Type.WARNING -> RadixTheme.colors.orange1 + StatusMessage.Type.ERROR -> RadixTheme.colors.red1 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/card/FactorSourceCardView.kt b/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/card/FactorSourceCardView.kt new file mode 100644 index 0000000000..7909ec730f --- /dev/null +++ b/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/card/FactorSourceCardView.kt @@ -0,0 +1,413 @@ +package com.babylon.wallet.android.presentation.ui.composables.card + +import androidx.annotation.DrawableRes +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.background +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.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.dp +import com.babylon.wallet.android.R +import com.babylon.wallet.android.designsystem.theme.RadixTheme +import com.babylon.wallet.android.domain.model.factors.FactorSourceCard +import com.babylon.wallet.android.domain.model.factors.StatusMessage +import com.babylon.wallet.android.presentation.ui.RadixWalletPreviewTheme +import com.babylon.wallet.android.presentation.ui.composables.SimpleAccountCard +import com.babylon.wallet.android.presentation.ui.composables.StatusMessageText +import com.babylon.wallet.android.presentation.ui.modifier.defaultCardShadow +import com.babylon.wallet.android.presentation.ui.modifier.throttleClickable +import com.babylon.wallet.android.utils.formattedSpans +import com.radixdlt.sargon.Account +import com.radixdlt.sargon.FactorSourceKind +import com.radixdlt.sargon.Persona +import com.radixdlt.sargon.annotation.UsesSampleValues +import com.radixdlt.sargon.samples.sampleMainnet +import com.radixdlt.sargon.samples.sampleStokenet + +@Composable +fun FactorSourceCardView( + item: FactorSourceCard, + modifier: Modifier = Modifier, + endContent: (@Composable () -> Unit)? = null +) { + FactorSourceCardView( + iconRes = item.kind.iconRes(), + title = item.kind.title(), + subtitle = item.kind.subtitle(), + lastUsedOn = item.lastUsedOn, + messages = item.messages, + accounts = item.accounts, + personas = item.personas, + modifier = modifier, + endContent = endContent + ) +} + +@Composable +fun FactorSourceCardView( + iconRes: Int, + title: String, + subtitle: String?, + lastUsedOn: String?, + messages: List, + accounts: List, + personas: List, + modifier: Modifier = Modifier, + endContent: (@Composable () -> Unit)? = null +) { + Column( + modifier = modifier + .defaultCardShadow(elevation = 2.dp) + .clip(RadixTheme.shapes.roundedRectMedium) + .fillMaxWidth() + .background( + color = RadixTheme.colors.white, + shape = RadixTheme.shapes.roundedRectDefault + ) + ) { + SimpleFactorSourceCard( + iconRes = iconRes, + title = title, + subtitle = subtitle, + description = lastUsedOn?.let { + stringResource(id = R.string.factorSources_card_lastUsed, it) + .formattedSpans(SpanStyle(fontWeight = FontWeight.Bold)) + }, + endContent = endContent + ) + + if (messages.isNotEmpty()) { + Column( + modifier = Modifier.padding( + start = RadixTheme.dimensions.paddingXXLarge, + end = RadixTheme.dimensions.paddingXXLarge, + top = RadixTheme.dimensions.paddingXSmall, + bottom = RadixTheme.dimensions.paddingDefault + ), + verticalArrangement = Arrangement.spacedBy(RadixTheme.dimensions.paddingXSmall) + ) { + messages.forEach { + StatusMessageText( + message = it + ) + } + } + } + + if (accounts.isNotEmpty() || personas.isNotEmpty()) { + LinkedEntitiesView( + accounts = accounts, + personas = personas + ) + } + } +} + +@Composable +fun SimpleFactorSourceCard( + @DrawableRes iconRes: Int, + title: String, + modifier: Modifier = Modifier, + subtitle: String? = null, + description: AnnotatedString? = null, + endContent: @Composable (() -> Unit)? = null +) { + Row( + modifier = modifier.padding( + vertical = RadixTheme.dimensions.paddingDefault + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Row( + modifier = Modifier + .weight(1f) + .padding(horizontal = RadixTheme.dimensions.paddingDefault), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + painter = painterResource( + id = iconRes + ), + contentDescription = null, + tint = RadixTheme.colors.gray1 + ) + + Spacer(modifier = Modifier.width(RadixTheme.dimensions.paddingMedium)) + + Column { + Text( + text = title, + style = RadixTheme.typography.body1Header, + color = RadixTheme.colors.gray1 + ) + + subtitle?.let { + Spacer(modifier = Modifier.height(RadixTheme.dimensions.paddingXSmall)) + + Text( + text = it, + style = RadixTheme.typography.body2Regular, + color = RadixTheme.colors.gray2 + ) + } + + description?.let { + Spacer(modifier = Modifier.height(RadixTheme.dimensions.paddingXSmall)) + + Text( + text = it, + style = RadixTheme.typography.body2Regular, + color = RadixTheme.colors.gray2 + ) + } + } + } + + endContent?.invoke() + } +} + +@Composable +private fun LinkedEntitiesView( + accounts: List, + personas: List +) { + val accountsText = when { + accounts.isEmpty() -> null + accounts.size == 1 -> stringResource(id = R.string.factorSources_card_accountSingular) + else -> stringResource(id = R.string.factorSources_card_accountPlural, accounts.size) + } + val personasText = when { + personas.isEmpty() -> null + personas.size == 1 -> stringResource(id = R.string.factorSources_card_personaSingular) + else -> stringResource(id = R.string.factorSources_card_personaPlural, personas.size) + } + val linkedText = when { + accountsText != null && personasText != null -> stringResource( + id = R.string.factorSources_card_linkedAccountsAndPersonas, + accountsText, + personasText + ) + accountsText != null -> stringResource(id = R.string.factorSources_card_linkedAccountsOrPersonas, accounts) + personasText != null -> stringResource(id = R.string.factorSources_card_linkedAccountsOrPersonas, personas) + else -> "" + } + + var isExpanded by rememberSaveable { mutableStateOf(false) } + + Column( + modifier = Modifier + .fillMaxWidth() + .background( + color = RadixTheme.colors.gray5, + shape = RadixTheme.shapes.roundedRectBottomDefault + ) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .throttleClickable { isExpanded = !isExpanded } + .padding( + horizontal = RadixTheme.dimensions.paddingDefault, + vertical = RadixTheme.dimensions.paddingMedium + ), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = linkedText, + style = RadixTheme.typography.body2Regular, + color = RadixTheme.colors.gray2 + ) + + Icon( + modifier = Modifier.size(18.dp), + painter = painterResource( + id = if (isExpanded) { + com.babylon.wallet.android.designsystem.R.drawable.ic_arrow_up + } else { + com.babylon.wallet.android.designsystem.R.drawable.ic_arrow_down + } + ), + contentDescription = null, + tint = RadixTheme.colors.gray2 + ) + } + + AnimatedVisibility(visible = isExpanded) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding( + start = RadixTheme.dimensions.paddingDefault, + end = RadixTheme.dimensions.paddingDefault, + top = RadixTheme.dimensions.paddingXSmall, + bottom = RadixTheme.dimensions.paddingDefault + ), + verticalArrangement = Arrangement.spacedBy(RadixTheme.dimensions.paddingSmall) + ) { + accounts.forEach { + SimpleAccountCard( + account = it, + shape = RadixTheme.shapes.roundedRectMedium + ) + } + + personas.forEach { + SimplePersonaCard( + modifier = Modifier + .fillMaxWidth() + .background( + color = RadixTheme.colors.white, + shape = RadixTheme.shapes.roundedRectMedium + ), + persona = it + ) + } + } + } + } +} + +@Composable +fun FactorSourceKind.iconRes(): Int { + return when (this) { + FactorSourceKind.DEVICE -> com.babylon.wallet.android.designsystem.R.drawable.ic_factor_device + FactorSourceKind.LEDGER_HQ_HARDWARE_WALLET -> com.babylon.wallet.android.designsystem.R.drawable.ic_factor_ledger_hardware + FactorSourceKind.OFF_DEVICE_MNEMONIC -> com.babylon.wallet.android.designsystem.R.drawable.ic_factor_passphrase + FactorSourceKind.ARCULUS_CARD -> com.babylon.wallet.android.designsystem.R.drawable.ic_factor_arculus + FactorSourceKind.PASSPHRASE -> com.babylon.wallet.android.designsystem.R.drawable.ic_factor_password + FactorSourceKind.TRUSTED_CONTACT, + FactorSourceKind.SECURITY_QUESTIONS -> error("Not supported yet") + } +} + +@Composable +fun FactorSourceKind.title(): String { + return stringResource( + id = when (this) { + FactorSourceKind.DEVICE -> R.string.factorSources_card_deviceTitle + FactorSourceKind.LEDGER_HQ_HARDWARE_WALLET -> R.string.factorSources_card_ledgerTitle + FactorSourceKind.OFF_DEVICE_MNEMONIC -> R.string.factorSources_card_passphraseTitle + FactorSourceKind.ARCULUS_CARD -> R.string.factorSources_card_arculusCardTitle + FactorSourceKind.PASSPHRASE -> R.string.factorSources_card_passwordTitle + FactorSourceKind.TRUSTED_CONTACT, + FactorSourceKind.SECURITY_QUESTIONS -> error("Not supported yet") + } + ) +} + +@Composable +fun FactorSourceKind.subtitle(): String { + return stringResource( + id = when (this) { + FactorSourceKind.DEVICE -> R.string.factorSources_card_deviceDescription + FactorSourceKind.LEDGER_HQ_HARDWARE_WALLET -> R.string.factorSources_card_ledgerDescription + FactorSourceKind.OFF_DEVICE_MNEMONIC -> R.string.factorSources_card_passphraseDescription + FactorSourceKind.ARCULUS_CARD -> R.string.factorSources_card_arculusCardDescription + FactorSourceKind.PASSPHRASE -> R.string.factorSources_card_passwordDescription + FactorSourceKind.TRUSTED_CONTACT, + FactorSourceKind.SECURITY_QUESTIONS -> error("Not supported yet") + } + ) +} + +@Composable +@Preview +@UsesSampleValues +private fun FactorSourceCardPreview( + @PreviewParameter(FactorSourceCardPreviewProvider::class) item: FactorSourceCard +) { + RadixWalletPreviewTheme { + FactorSourceCardView( + item = item + ) + } +} + +@UsesSampleValues +class FactorSourceCardPreviewProvider : PreviewParameterProvider { + + override val values: Sequence + get() = sequenceOf( + FactorSourceCard( + kind = FactorSourceKind.DEVICE, + lastUsedOn = "Today", + messages = listOf( + StatusMessage( + message = "Choosing a passphrase is only recommended for advanced users", + type = StatusMessage.Type.WARNING + ), + StatusMessage( + message = "Warning text", + type = StatusMessage.Type.WARNING + ) + ), + accounts = listOf( + Account.sampleMainnet() + ), + personas = listOf( + Persona.sampleMainnet(), + Persona.sampleStokenet() + ) + ), + FactorSourceCard( + kind = FactorSourceKind.LEDGER_HQ_HARDWARE_WALLET, + lastUsedOn = "Today", + messages = emptyList(), + accounts = emptyList(), + personas = emptyList() + ), + FactorSourceCard( + kind = FactorSourceKind.ARCULUS_CARD, + lastUsedOn = "Today", + messages = emptyList(), + accounts = emptyList(), + personas = emptyList() + ), + FactorSourceCard( + kind = FactorSourceKind.PASSPHRASE, + lastUsedOn = "Today", + messages = emptyList(), + accounts = emptyList(), + personas = emptyList() + ), + FactorSourceCard( + kind = FactorSourceKind.OFF_DEVICE_MNEMONIC, + lastUsedOn = null, + messages = listOf( + StatusMessage( + message = "This seed phrase has been written down", + type = StatusMessage.Type.SUCCESS + ) + ), + accounts = emptyList(), + personas = emptyList() + ) + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/card/SelectableFactorSourceCard.kt b/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/card/SelectableFactorSourceCard.kt new file mode 100644 index 0000000000..881ec490ba --- /dev/null +++ b/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/card/SelectableFactorSourceCard.kt @@ -0,0 +1,183 @@ +package com.babylon.wallet.android.presentation.ui.composables.card + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Checkbox +import androidx.compose.material3.CheckboxDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import com.babylon.wallet.android.designsystem.theme.RadixTheme +import com.babylon.wallet.android.domain.model.factors.FactorSourceCard +import com.babylon.wallet.android.presentation.ui.RadixWalletPreviewTheme +import com.babylon.wallet.android.presentation.ui.composables.RadixRadioButton +import com.radixdlt.sargon.FactorSourceKind +import com.radixdlt.sargon.annotation.UsesSampleValues + +@Composable +fun SelectableFactorSourceCard( + modifier: Modifier = Modifier, + item: FactorSourceCard, + isSelected: Boolean, + isSingleChoice: Boolean, + onSelectedChange: (Boolean) -> Unit +) { + FactorSourceCardView( + modifier = modifier, + item = item, + endContent = { + SelectorView( + isSelected = isSelected, + isSingleChoice = isSingleChoice, + onSelectedChange = onSelectedChange + ) + } + ) +} + +@Composable +fun RemovableFactorSourceCard( + modifier: Modifier = Modifier, + item: FactorSourceCard, + onRemoveClick: () -> Unit +) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically + ) { + FactorSourceCardView( + modifier = Modifier.weight(1f), + item = item + ) + + IconButton(onClick = onRemoveClick) { + Icon( + painter = painterResource(id = com.babylon.wallet.android.designsystem.R.drawable.ic_close), + contentDescription = null, + tint = RadixTheme.colors.gray2 + ) + } + } +} + +@Composable +fun SimpleSelectableFactorSourceCard( + modifier: Modifier = Modifier, + @DrawableRes iconRes: Int, + title: String, + isSelected: Boolean, + isSingleChoice: Boolean, + onSelectedChange: (Boolean) -> Unit +) { + SimpleFactorSourceCard( + modifier = modifier + .background( + color = RadixTheme.colors.white, + shape = RadixTheme.shapes.roundedRectMedium + ), + iconRes = iconRes, + title = title, + endContent = { + SelectorView( + isSelected = isSelected, + isSingleChoice = isSingleChoice, + onSelectedChange = onSelectedChange + ) + } + ) +} + +@Composable +private fun SelectorView( + isSelected: Boolean, + isSingleChoice: Boolean, + onSelectedChange: (Boolean) -> Unit +) { + Row { + if (isSingleChoice) { + Spacer(modifier = Modifier.width(RadixTheme.dimensions.paddingSmall)) + + RadixRadioButton( + selected = isSelected, + onClick = { onSelectedChange(true) } + ) + + Spacer(modifier = Modifier.width(RadixTheme.dimensions.paddingDefault)) + } else { + Spacer(modifier = Modifier.width(RadixTheme.dimensions.paddingSmall)) + + Checkbox( + checked = isSelected, + colors = CheckboxDefaults.colors().copy( + checkedCheckmarkColor = RadixTheme.colors.white, + checkedBorderColor = RadixTheme.colors.gray1, + checkedBoxColor = RadixTheme.colors.gray1, + uncheckedCheckmarkColor = Color.Transparent, + uncheckedBorderColor = RadixTheme.colors.gray2, + uncheckedBoxColor = RadixTheme.colors.gray5 + ), + onCheckedChange = onSelectedChange + ) + + Spacer(modifier = Modifier.width(RadixTheme.dimensions.paddingSmall)) + } + } +} + +@Composable +@Preview +@UsesSampleValues +private fun SelectableFactorSourceCardPreview( + @PreviewParameter(FactorSourceCardPreviewProvider::class) item: FactorSourceCard +) { + RadixWalletPreviewTheme { + SelectableFactorSourceCard( + item = item, + isSelected = false, + isSingleChoice = true, + onSelectedChange = {} + ) + } +} + +@Composable +@Preview +@UsesSampleValues +private fun SimpleSelectableFactorSourceCardPreview() { + RadixWalletPreviewTheme { + SimpleSelectableFactorSourceCard( + iconRes = FactorSourceKind.DEVICE.iconRes(), + title = "My Phone", + isSelected = false, + isSingleChoice = false, + onSelectedChange = {} + ) + } +} + +@Composable +@Preview +@UsesSampleValues +private fun RemovableFactorSourceCardPreview() { + RadixWalletPreviewTheme { + RemovableFactorSourceCard( + item = FactorSourceCard( + kind = FactorSourceKind.DEVICE, + lastUsedOn = null, + messages = emptyList(), + accounts = emptyList(), + personas = emptyList() + ), + onRemoveClick = {} + ) + } +} \ No newline at end of file diff --git a/designsystem/src/main/java/com/babylon/wallet/android/designsystem/theme/RadixShapes.kt b/designsystem/src/main/java/com/babylon/wallet/android/designsystem/theme/RadixShapes.kt index 73aac074db..01effb2c5c 100644 --- a/designsystem/src/main/java/com/babylon/wallet/android/designsystem/theme/RadixShapes.kt +++ b/designsystem/src/main/java/com/babylon/wallet/android/designsystem/theme/RadixShapes.kt @@ -23,6 +23,7 @@ data class RadixShapes( val roundedRectTopMedium: CornerBasedShape = RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp), val roundedRectBottomMedium: CornerBasedShape = RoundedCornerShape(bottomEnd = 12.dp, bottomStart = 12.dp), val roundedRectTopDefault: CornerBasedShape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp), + val roundedRectBottomDefault: CornerBasedShape = RoundedCornerShape(bottomStart = 16.dp, bottomEnd = 16.dp), val roundedTag: CornerBasedShape = RoundedCornerShape( topStart = 4.dp, topEnd = 36.dp, diff --git a/designsystem/src/main/res/drawable/ic_factor_arculus.xml b/designsystem/src/main/res/drawable/ic_factor_arculus.xml new file mode 100644 index 0000000000..d097ecc838 --- /dev/null +++ b/designsystem/src/main/res/drawable/ic_factor_arculus.xml @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/designsystem/src/main/res/drawable/ic_factor_device.xml b/designsystem/src/main/res/drawable/ic_factor_device.xml new file mode 100644 index 0000000000..cab66a55c6 --- /dev/null +++ b/designsystem/src/main/res/drawable/ic_factor_device.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/designsystem/src/main/res/drawable/ic_factor_ledger_hardware.xml b/designsystem/src/main/res/drawable/ic_factor_ledger_hardware.xml new file mode 100644 index 0000000000..7c3a19f403 --- /dev/null +++ b/designsystem/src/main/res/drawable/ic_factor_ledger_hardware.xml @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/designsystem/src/main/res/drawable/ic_factor_passphrase.xml b/designsystem/src/main/res/drawable/ic_factor_passphrase.xml new file mode 100644 index 0000000000..20a265178c --- /dev/null +++ b/designsystem/src/main/res/drawable/ic_factor_passphrase.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/designsystem/src/main/res/drawable/ic_factor_password.xml b/designsystem/src/main/res/drawable/ic_factor_password.xml new file mode 100644 index 0000000000..c88b8e3dc4 --- /dev/null +++ b/designsystem/src/main/res/drawable/ic_factor_password.xml @@ -0,0 +1,22 @@ + + + + + + + + + + From f32c4b27829764ab8016658c7063fbb60764dfd9 Mon Sep 17 00:00:00 2001 From: Sergiu Puhalschi Date: Wed, 4 Dec 2024 10:50:26 +0200 Subject: [PATCH 2/5] Add samples --- .../domain/model/factors/FactorSourceCard.kt | 17 +- .../presentation/settings/SettingsItem.kt | 7 +- .../settings/debug/DebugSettingsNav.kt | 1 + .../factors/SecurityFactorSamplesScreen.kt | 60 ++++- .../factors/SecurityFactorSamplesViewModel.kt | 234 +++++++++++++++++- .../composables/card/FactorSourceCardView.kt | 62 +++-- .../card/SelectableFactorSourceCard.kt | 154 +++++++----- app/src/main/res/values/constants.xml | 1 + 8 files changed, 447 insertions(+), 89 deletions(-) diff --git a/app/src/main/java/com/babylon/wallet/android/domain/model/factors/FactorSourceCard.kt b/app/src/main/java/com/babylon/wallet/android/domain/model/factors/FactorSourceCard.kt index 43d7269672..074e1daa15 100644 --- a/app/src/main/java/com/babylon/wallet/android/domain/model/factors/FactorSourceCard.kt +++ b/app/src/main/java/com/babylon/wallet/android/domain/model/factors/FactorSourceCard.kt @@ -1,13 +1,26 @@ package com.babylon.wallet.android.domain.model.factors import com.radixdlt.sargon.Account +import com.radixdlt.sargon.FactorSourceId import com.radixdlt.sargon.FactorSourceKind import com.radixdlt.sargon.Persona data class FactorSourceCard( val kind: FactorSourceKind, - val lastUsedOn: String?, + val header: Header, val messages: List, val accounts: List, val personas: List -) \ No newline at end of file +) { + + sealed interface Header { + + data object New : Header + + data class Added( + val id: FactorSourceId.Hash, + val name: String, + val lastUsedOn: String? + ) : Header + } +} \ No newline at end of file diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/settings/SettingsItem.kt b/app/src/main/java/com/babylon/wallet/android/presentation/settings/SettingsItem.kt index 948976b412..7522e567a3 100644 --- a/app/src/main/java/com/babylon/wallet/android/presentation/settings/SettingsItem.kt +++ b/app/src/main/java/com/babylon/wallet/android/presentation/settings/SettingsItem.kt @@ -207,12 +207,15 @@ sealed interface SettingsItem { data object InspectCloudBackups : DebugSettingsItem + data object SecurityFactorSamples : DebugSettingsItem + @StringRes fun titleRes(): Int { return when (this) { InspectProfile -> R.string.settings_debugSettings_inspectProfile LinkConnectionStatusIndicator -> R.string.linkedConnectors_title InspectCloudBackups -> R.string.settings_debugSettings_inspectCloudBackups + SecurityFactorSamples -> R.string.settings_debugSettings_securityFactorSamples } } @@ -222,6 +225,7 @@ sealed interface SettingsItem { InspectProfile -> com.babylon.wallet.android.designsystem.R.drawable.ic_personas LinkConnectionStatusIndicator -> com.babylon.wallet.android.designsystem.R.drawable.ic_desktop_connection InspectCloudBackups -> com.babylon.wallet.android.designsystem.R.drawable.ic_backup + SecurityFactorSamples -> com.babylon.wallet.android.designsystem.R.drawable.ic_security } } @@ -229,7 +233,8 @@ sealed interface SettingsItem { fun values() = setOf( InspectProfile, LinkConnectionStatusIndicator, - InspectCloudBackups + InspectCloudBackups, + SecurityFactorSamples ) } } diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/DebugSettingsNav.kt b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/DebugSettingsNav.kt index 583e76ed6e..cea7c2a1de 100644 --- a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/DebugSettingsNav.kt +++ b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/DebugSettingsNav.kt @@ -55,6 +55,7 @@ fun NavGraphBuilder.debugSettings( InspectProfile -> navController.inspectProfile() SettingsItem.DebugSettingsItem.LinkConnectionStatusIndicator -> {} SettingsItem.DebugSettingsItem.InspectCloudBackups -> navController.inspectGoogleBackups() + SettingsItem.DebugSettingsItem.SecurityFactorSamples -> navController.securityFactorSamples() } } ) diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesScreen.kt b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesScreen.kt index 536017bb39..772cc64859 100644 --- a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesScreen.kt +++ b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesScreen.kt @@ -1,20 +1,27 @@ package com.babylon.wallet.android.presentation.settings.debug.factors -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues 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.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.babylon.wallet.android.R import com.babylon.wallet.android.designsystem.theme.RadixTheme +import com.babylon.wallet.android.domain.model.factors.FactorSourceCard import com.babylon.wallet.android.presentation.ui.RadixWalletPreviewTheme import com.babylon.wallet.android.presentation.ui.composables.RadixCenteredTopAppBar +import com.babylon.wallet.android.presentation.ui.composables.card.FactorSourceCardView +import com.babylon.wallet.android.presentation.ui.composables.card.RemovableFactorSourceCard +import com.babylon.wallet.android.presentation.ui.composables.card.SelectableMultiChoiceFactorSourceCard +import com.babylon.wallet.android.presentation.ui.composables.card.SelectableSingleChoiceFactorSourceCard import com.babylon.wallet.android.presentation.ui.composables.statusBarsAndBanner @Composable @@ -26,7 +33,10 @@ fun SecurityFactorSamplesScreen( SecurityFactorSamplesContent( state = state, - onBackClick = onBackClick + onBackClick = onBackClick, + onSelect = viewModel::onSelect, + onCheckedChange = viewModel::onCheckedChange, + onRemoveClick = viewModel::onRemoveClick ) } @@ -34,7 +44,10 @@ fun SecurityFactorSamplesScreen( private fun SecurityFactorSamplesContent( modifier: Modifier = Modifier, state: SecurityFactorSamplesViewModel.State, - onBackClick: () -> Unit + onBackClick: () -> Unit, + onSelect: (FactorSourceCard) -> Unit, + onCheckedChange: (FactorSourceCard, Boolean) -> Unit, + onRemoveClick: (FactorSourceCard) -> Unit ) { Scaffold( modifier = modifier.fillMaxSize(), @@ -45,13 +58,41 @@ private fun SecurityFactorSamplesContent( windowInsets = WindowInsets.statusBarsAndBanner ) }, - containerColor = RadixTheme.colors.defaultBackground + containerColor = RadixTheme.colors.gray5 ) { padding -> - Column( + LazyColumn( modifier = Modifier.padding(padding), - horizontalAlignment = Alignment.Start + contentPadding = PaddingValues(RadixTheme.dimensions.paddingDefault), + verticalArrangement = Arrangement.spacedBy(RadixTheme.dimensions.paddingMedium) ) { + items(state.displayOnlyItems) { + FactorSourceCardView( + item = it + ) + } + + items(state.singleChoiceItems) { + SelectableSingleChoiceFactorSourceCard( + item = it.item, + isSelected = it.isSelected, + onSelect = onSelect + ) + } + + items(state.multiChoiceItems) { + SelectableMultiChoiceFactorSourceCard( + item = it.item, + isChecked = it.isSelected, + onCheckedChange = onCheckedChange + ) + } + items(state.removableItems) { + RemovableFactorSourceCard( + item = it, + onRemoveClick = onRemoveClick + ) + } } } } @@ -62,7 +103,10 @@ private fun SecurityFactorSamplesPreview() { RadixWalletPreviewTheme { SecurityFactorSamplesContent( state = SecurityFactorSamplesViewModel.State(), - onBackClick = {} + onBackClick = {}, + onSelect = {}, + onCheckedChange = { _, _ -> }, + onRemoveClick = {} ) } } \ No newline at end of file diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt index 651b1e8982..b0ea5d689b 100644 --- a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt +++ b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt @@ -1,17 +1,247 @@ package com.babylon.wallet.android.presentation.settings.debug.factors +import android.text.format.DateUtils import com.babylon.wallet.android.domain.model.factors.FactorSourceCard +import com.babylon.wallet.android.domain.model.factors.StatusMessage import com.babylon.wallet.android.presentation.common.StateViewModel import com.babylon.wallet.android.presentation.common.UiState +import com.radixdlt.sargon.Account +import com.radixdlt.sargon.DeviceFactorSource +import com.radixdlt.sargon.FactorSourceKind +import com.radixdlt.sargon.LedgerHardwareWalletFactorSource +import com.radixdlt.sargon.Persona +import com.radixdlt.sargon.Timestamp +import com.radixdlt.sargon.extensions.asGeneral +import com.radixdlt.sargon.extensions.kind +import com.radixdlt.sargon.newAccountSampleMainnetAlice +import com.radixdlt.sargon.newAccountSampleMainnetBob +import com.radixdlt.sargon.newAccountSampleMainnetCarol +import com.radixdlt.sargon.newAccountSampleStokenetNadia +import com.radixdlt.sargon.newAccountSampleStokenetOlivia +import com.radixdlt.sargon.newAccountSampleStokenetPaige +import com.radixdlt.sargon.newDeviceFactorSourceSample +import com.radixdlt.sargon.newLedgerHardwareWalletFactorSourceSample +import com.radixdlt.sargon.newPersonaSampleMainnetBatman +import com.radixdlt.sargon.newPersonaSampleMainnetRipley +import com.radixdlt.sargon.newPersonaSampleMainnetSatoshi +import com.radixdlt.sargon.newPersonaSampleStokenetConnor +import com.radixdlt.sargon.newPersonaSampleStokenetHermione +import com.radixdlt.sargon.newPersonaSampleStokenetLeiaSkywalker import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.update +import rdx.works.core.mapWhen +import timber.log.Timber import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds @HiltViewModel class SecurityFactorSamplesViewModel @Inject constructor() : StateViewModel() { - override fun initialState(): State = State() + override fun initialState(): State = State( + displayOnlyItems = listOf( + newLedgerHardwareWalletFactorSourceSample().toCard( + accounts = listOf(newAccountSampleStokenetNadia()), + personas = listOf(newPersonaSampleStokenetLeiaSkywalker()) + ), + newLedgerHardwareWalletFactorSourceSample().toCard( + accounts = listOf( + newAccountSampleMainnetAlice(), + newAccountSampleMainnetBob(), + newAccountSampleMainnetCarol(), + newAccountSampleStokenetNadia(), + newAccountSampleStokenetOlivia(), + newAccountSampleStokenetPaige() + ), + personas = listOf( + newPersonaSampleMainnetSatoshi(), + newPersonaSampleMainnetBatman(), + newPersonaSampleMainnetRipley(), + newPersonaSampleStokenetLeiaSkywalker(), + newPersonaSampleStokenetHermione(), + newPersonaSampleStokenetConnor() + ) + ), + newLedgerHardwareWalletFactorSourceSample().toCard( + accounts = listOf( + newAccountSampleMainnetAlice(), + newAccountSampleMainnetBob(), + newAccountSampleMainnetCarol(), + newAccountSampleStokenetNadia(), + newAccountSampleStokenetOlivia(), + newAccountSampleStokenetPaige() + ) + ), + newLedgerHardwareWalletFactorSourceSample().toCard( + personas = listOf( + newPersonaSampleMainnetSatoshi(), + newPersonaSampleMainnetBatman(), + newPersonaSampleMainnetRipley(), + newPersonaSampleStokenetLeiaSkywalker(), + newPersonaSampleStokenetHermione(), + newPersonaSampleStokenetConnor() + ) + ), + FactorSourceCard( + kind = FactorSourceKind.OFF_DEVICE_MNEMONIC, + header = FactorSourceCard.Header.New, + messages = listOf( + StatusMessage( + message = "Some warning", + type = StatusMessage.Type.WARNING + ), + StatusMessage( + message = "This seed phrase has been written down", + type = StatusMessage.Type.SUCCESS + ) + ), + accounts = emptyList(), + personas = emptyList() + ), + newDeviceFactorSourceSample().toCard( + messages = listOf( + StatusMessage( + message = "Some error", + type = StatusMessage.Type.ERROR + ) + ) + ) + ), + singleChoiceItems = getSupportedKinds().map { + SelectableFactorSourceCard( + item = FactorSourceCard( + kind = it, + header = FactorSourceCard.Header.New, + messages = emptyList(), + accounts = emptyList(), + personas = emptyList() + ), + isSelected = false + ) + }, + multiChoiceItems = getSupportedKinds().map { + SelectableFactorSourceCard( + item = FactorSourceCard( + kind = it, + header = FactorSourceCard.Header.New, + messages = emptyList(), + accounts = emptyList(), + personas = emptyList() + ), + isSelected = false + ) + }, + removableItems = listOf( + FactorSourceCard( + kind = FactorSourceKind.DEVICE, + header = FactorSourceCard.Header.New, + messages = emptyList(), + accounts = emptyList(), + personas = emptyList() + ), + FactorSourceCard( + kind = FactorSourceKind.ARCULUS_CARD, + header = FactorSourceCard.Header.New, + messages = emptyList(), + accounts = emptyList(), + personas = emptyList() + ) + ) + ) + + fun onSelect(item: FactorSourceCard) { + _state.update { state -> + state.copy( + singleChoiceItems = state.singleChoiceItems.map { selectableItem -> + selectableItem.copy( + isSelected = selectableItem.item.kind == item.kind + ) + } + ) + } + } + + fun onCheckedChange(item: FactorSourceCard, isChecked: Boolean) { + _state.update { state -> + state.copy( + multiChoiceItems = state.multiChoiceItems.mapWhen( + predicate = { it.item.kind == item.kind }, + mutation = { it.copy(isSelected = isChecked) } + ) + ) + } + } + + fun onRemoveClick(item: FactorSourceCard) { + Timber.d("Remove clicked: $item") + } + + private fun getSupportedKinds(): List { + return FactorSourceKind.entries.filter { it.isSupported() } + } + + private fun FactorSourceKind.isSupported(): Boolean { + return this !in unsupportedKinds + } + + private fun DeviceFactorSource.toCard( + messages: List = emptyList(), + accounts: List = emptyList(), + personas: List = emptyList() + ): FactorSourceCard { + return FactorSourceCard( + kind = kind, + header = FactorSourceCard.Header.Added( + id = id.asGeneral(), + name = hint.name, + lastUsedOn = common.lastUsedOn.formatted() + ), + messages = messages, + accounts = accounts, + personas = personas + ) + } + + private fun LedgerHardwareWalletFactorSource.toCard( + messages: List = emptyList(), + accounts: List = emptyList(), + personas: List = emptyList() + ): FactorSourceCard { + return FactorSourceCard( + kind = kind, + header = FactorSourceCard.Header.Added( + id = id.asGeneral(), + name = hint.name, + lastUsedOn = common.lastUsedOn.formatted() + ), + messages = messages, + accounts = accounts, + personas = personas + ) + } + + private fun Timestamp.formatted(): String { + val millis = toEpochSecond().seconds.inWholeMilliseconds + return DateUtils.getRelativeTimeSpanString(millis).toString() + } + + data class SelectableFactorSourceCard( + val item: FactorSourceCard, + val isSelected: Boolean + ) data class State( - val items: List = emptyList() + val displayOnlyItems: List = emptyList(), + val singleChoiceItems: List = emptyList(), + val multiChoiceItems: List = emptyList(), + val removableItems: List = emptyList() ) : UiState + + companion object { + + private val unsupportedKinds = setOf( + FactorSourceKind.TRUSTED_CONTACT, + FactorSourceKind.SECURITY_QUESTIONS + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/card/FactorSourceCardView.kt b/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/card/FactorSourceCardView.kt index 7909ec730f..e1b81e0cdb 100644 --- a/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/card/FactorSourceCardView.kt +++ b/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/card/FactorSourceCardView.kt @@ -42,9 +42,13 @@ import com.babylon.wallet.android.presentation.ui.modifier.defaultCardShadow import com.babylon.wallet.android.presentation.ui.modifier.throttleClickable import com.babylon.wallet.android.utils.formattedSpans import com.radixdlt.sargon.Account +import com.radixdlt.sargon.FactorSourceId import com.radixdlt.sargon.FactorSourceKind +import com.radixdlt.sargon.MnemonicWithPassphrase import com.radixdlt.sargon.Persona import com.radixdlt.sargon.annotation.UsesSampleValues +import com.radixdlt.sargon.extensions.init +import com.radixdlt.sargon.samples.sample import com.radixdlt.sargon.samples.sampleMainnet import com.radixdlt.sargon.samples.sampleStokenet @@ -54,17 +58,30 @@ fun FactorSourceCardView( modifier: Modifier = Modifier, endContent: (@Composable () -> Unit)? = null ) { - FactorSourceCardView( - iconRes = item.kind.iconRes(), - title = item.kind.title(), - subtitle = item.kind.subtitle(), - lastUsedOn = item.lastUsedOn, - messages = item.messages, - accounts = item.accounts, - personas = item.personas, - modifier = modifier, - endContent = endContent - ) + when (item.header) { + is FactorSourceCard.Header.Added -> FactorSourceCardView( + iconRes = item.kind.iconRes(), + title = item.header.name, + subtitle = null, + lastUsedOn = item.header.lastUsedOn, + messages = item.messages, + accounts = item.accounts, + personas = item.personas, + modifier = modifier, + endContent = endContent + ) + FactorSourceCard.Header.New -> FactorSourceCardView( + iconRes = item.kind.iconRes(), + title = item.kind.title(), + subtitle = item.kind.subtitle(), + lastUsedOn = null, + messages = item.messages, + accounts = item.accounts, + personas = item.personas, + modifier = modifier, + endContent = endContent + ) + } } @Composable @@ -213,8 +230,8 @@ private fun LinkedEntitiesView( accountsText, personasText ) - accountsText != null -> stringResource(id = R.string.factorSources_card_linkedAccountsOrPersonas, accounts) - personasText != null -> stringResource(id = R.string.factorSources_card_linkedAccountsOrPersonas, personas) + accountsText != null -> stringResource(id = R.string.factorSources_card_linkedAccountsOrPersonas, accountsText) + personasText != null -> stringResource(id = R.string.factorSources_card_linkedAccountsOrPersonas, personasText) else -> "" } @@ -266,7 +283,7 @@ private fun LinkedEntitiesView( .padding( start = RadixTheme.dimensions.paddingDefault, end = RadixTheme.dimensions.paddingDefault, - top = RadixTheme.dimensions.paddingXSmall, + top = RadixTheme.dimensions.paddingSmall, bottom = RadixTheme.dimensions.paddingDefault ), verticalArrangement = Arrangement.spacedBy(RadixTheme.dimensions.paddingSmall) @@ -357,7 +374,14 @@ class FactorSourceCardPreviewProvider : PreviewParameterProvider Unit + onSelect: (FactorSourceCard) -> Unit ) { FactorSourceCardView( modifier = modifier, item = item, endContent = { - SelectorView( + RadioButtonSelectorView( isSelected = isSelected, - isSingleChoice = isSingleChoice, - onSelectedChange = onSelectedChange + onSelectedChange = { onSelect(item) } + ) + } + ) +} + +@Composable +fun SelectableMultiChoiceFactorSourceCard( + modifier: Modifier = Modifier, + item: FactorSourceCard, + isChecked: Boolean, + onCheckedChange: (FactorSourceCard, Boolean) -> Unit +) { + FactorSourceCardView( + modifier = modifier, + item = item, + endContent = { + CheckboxSelectorView( + isChecked = isChecked, + onCheckedChange = { onCheckedChange(item, it) } ) } ) @@ -48,7 +64,7 @@ fun SelectableFactorSourceCard( fun RemovableFactorSourceCard( modifier: Modifier = Modifier, item: FactorSourceCard, - onRemoveClick: () -> Unit + onRemoveClick: (FactorSourceCard) -> Unit ) { Row( modifier = modifier, @@ -59,7 +75,9 @@ fun RemovableFactorSourceCard( item = item ) - IconButton(onClick = onRemoveClick) { + IconButton( + onClick = { onRemoveClick(item) } + ) { Icon( painter = painterResource(id = com.babylon.wallet.android.designsystem.R.drawable.ic_close), contentDescription = null, @@ -70,13 +88,12 @@ fun RemovableFactorSourceCard( } @Composable -fun SimpleSelectableFactorSourceCard( +fun SimpleSelectableMultiChoiceFactorSourceCard( modifier: Modifier = Modifier, @DrawableRes iconRes: Int, title: String, - isSelected: Boolean, - isSingleChoice: Boolean, - onSelectedChange: (Boolean) -> Unit + isChecked: Boolean, + onCheckedChange: (Boolean) -> Unit ) { SimpleFactorSourceCard( modifier = modifier @@ -87,64 +104,88 @@ fun SimpleSelectableFactorSourceCard( iconRes = iconRes, title = title, endContent = { - SelectorView( - isSelected = isSelected, - isSingleChoice = isSingleChoice, - onSelectedChange = onSelectedChange + CheckboxSelectorView( + isChecked = isChecked, + onCheckedChange = onCheckedChange ) } ) } @Composable -private fun SelectorView( +private fun RadioButtonSelectorView( isSelected: Boolean, - isSingleChoice: Boolean, - onSelectedChange: (Boolean) -> Unit + onSelectedChange: () -> Unit ) { Row { - if (isSingleChoice) { - Spacer(modifier = Modifier.width(RadixTheme.dimensions.paddingSmall)) + Spacer(modifier = Modifier.width(RadixTheme.dimensions.paddingSmall)) - RadixRadioButton( - selected = isSelected, - onClick = { onSelectedChange(true) } - ) + RadixRadioButton( + selected = isSelected, + onClick = onSelectedChange + ) - Spacer(modifier = Modifier.width(RadixTheme.dimensions.paddingDefault)) - } else { - Spacer(modifier = Modifier.width(RadixTheme.dimensions.paddingSmall)) - - Checkbox( - checked = isSelected, - colors = CheckboxDefaults.colors().copy( - checkedCheckmarkColor = RadixTheme.colors.white, - checkedBorderColor = RadixTheme.colors.gray1, - checkedBoxColor = RadixTheme.colors.gray1, - uncheckedCheckmarkColor = Color.Transparent, - uncheckedBorderColor = RadixTheme.colors.gray2, - uncheckedBoxColor = RadixTheme.colors.gray5 - ), - onCheckedChange = onSelectedChange - ) + Spacer(modifier = Modifier.width(RadixTheme.dimensions.paddingDefault)) + } +} - Spacer(modifier = Modifier.width(RadixTheme.dimensions.paddingSmall)) - } +@Composable +private fun CheckboxSelectorView( + isChecked: Boolean, + onCheckedChange: (Boolean) -> Unit +) { + Row { + Spacer(modifier = Modifier.width(RadixTheme.dimensions.paddingSmall)) + + Checkbox( + checked = isChecked, + colors = CheckboxDefaults.colors().copy( + checkedCheckmarkColor = RadixTheme.colors.white, + checkedBorderColor = RadixTheme.colors.gray1, + checkedBoxColor = RadixTheme.colors.gray1, + uncheckedCheckmarkColor = Color.Transparent, + uncheckedBorderColor = RadixTheme.colors.gray2, + uncheckedBoxColor = RadixTheme.colors.gray5 + ), + onCheckedChange = onCheckedChange + ) + + Spacer(modifier = Modifier.width(RadixTheme.dimensions.paddingSmall)) } } @Composable @Preview -@UsesSampleValues -private fun SelectableFactorSourceCardPreview( - @PreviewParameter(FactorSourceCardPreviewProvider::class) item: FactorSourceCard -) { +private fun SelectableSingleChoiceFactorSourceCardPreview() { RadixWalletPreviewTheme { - SelectableFactorSourceCard( - item = item, + SelectableSingleChoiceFactorSourceCard( + item = FactorSourceCard( + kind = FactorSourceKind.DEVICE, + header = FactorSourceCard.Header.New, + messages = emptyList(), + accounts = emptyList(), + personas = emptyList() + ), isSelected = false, - isSingleChoice = true, - onSelectedChange = {} + onSelect = {} + ) + } +} + +@Composable +@Preview +private fun SelectableMultiChoiceFactorSourceCardPreview() { + RadixWalletPreviewTheme { + SelectableMultiChoiceFactorSourceCard( + item = FactorSourceCard( + kind = FactorSourceKind.ARCULUS_CARD, + header = FactorSourceCard.Header.New, + messages = emptyList(), + accounts = emptyList(), + personas = emptyList() + ), + isChecked = true, + onCheckedChange = { _, _ -> } ) } } @@ -154,12 +195,11 @@ private fun SelectableFactorSourceCardPreview( @UsesSampleValues private fun SimpleSelectableFactorSourceCardPreview() { RadixWalletPreviewTheme { - SimpleSelectableFactorSourceCard( + SimpleSelectableMultiChoiceFactorSourceCard( iconRes = FactorSourceKind.DEVICE.iconRes(), title = "My Phone", - isSelected = false, - isSingleChoice = false, - onSelectedChange = {} + isChecked = false, + onCheckedChange = {} ) } } @@ -172,7 +212,7 @@ private fun RemovableFactorSourceCardPreview() { RemovableFactorSourceCard( item = FactorSourceCard( kind = FactorSourceKind.DEVICE, - lastUsedOn = null, + header = FactorSourceCard.Header.New, messages = emptyList(), accounts = emptyList(), personas = emptyList() diff --git a/app/src/main/res/values/constants.xml b/app/src/main/res/values/constants.xml index a5c110d96c..112b545273 100644 --- a/app/src/main/res/values/constants.xml +++ b/app/src/main/res/values/constants.xml @@ -8,6 +8,7 @@ Inspect Profile Mobile Connect Delay Seconds Inspect Cloud Backups + Security Factor Samples No email client installed Radix Mainnet Gateway Stokenet (testnet) Gateway From fae427d4d170843f296cb26bed1586776d173e7e Mon Sep 17 00:00:00 2001 From: Sergiu Puhalschi Date: Wed, 4 Dec 2024 11:45:15 +0200 Subject: [PATCH 3/5] Fix detekt --- .../domain/model/factors/FactorSourceCard.kt | 9 +- .../factors/SecurityFactorSamplesScreen.kt | 2 +- .../factors/SecurityFactorSamplesViewModel.kt | 83 ++++++++++--------- .../ui/composables/StatusMessageText.kt | 2 +- .../composables/card/FactorSourceCardView.kt | 44 +++++----- .../card/SelectableFactorSourceCard.kt | 21 ++--- 6 files changed, 84 insertions(+), 77 deletions(-) diff --git a/app/src/main/java/com/babylon/wallet/android/domain/model/factors/FactorSourceCard.kt b/app/src/main/java/com/babylon/wallet/android/domain/model/factors/FactorSourceCard.kt index 074e1daa15..7fd5ea6029 100644 --- a/app/src/main/java/com/babylon/wallet/android/domain/model/factors/FactorSourceCard.kt +++ b/app/src/main/java/com/babylon/wallet/android/domain/model/factors/FactorSourceCard.kt @@ -4,13 +4,14 @@ import com.radixdlt.sargon.Account import com.radixdlt.sargon.FactorSourceId import com.radixdlt.sargon.FactorSourceKind import com.radixdlt.sargon.Persona +import kotlinx.collections.immutable.PersistentList data class FactorSourceCard( val kind: FactorSourceKind, val header: Header, - val messages: List, - val accounts: List, - val personas: List + val messages: PersistentList, + val accounts: PersistentList, + val personas: PersistentList ) { sealed interface Header { @@ -23,4 +24,4 @@ data class FactorSourceCard( val lastUsedOn: String? ) : Header } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesScreen.kt b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesScreen.kt index 772cc64859..38e05e3c40 100644 --- a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesScreen.kt +++ b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesScreen.kt @@ -109,4 +109,4 @@ private fun SecurityFactorSamplesPreview() { onRemoveClick = {} ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt index b0ea5d689b..b185378a67 100644 --- a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt +++ b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt @@ -28,24 +28,27 @@ import com.radixdlt.sargon.newPersonaSampleStokenetConnor import com.radixdlt.sargon.newPersonaSampleStokenetHermione import com.radixdlt.sargon.newPersonaSampleStokenetLeiaSkywalker import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.flow.update import rdx.works.core.mapWhen import timber.log.Timber import javax.inject.Inject -import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds @HiltViewModel class SecurityFactorSamplesViewModel @Inject constructor() : StateViewModel() { + @Suppress("LongMethod") override fun initialState(): State = State( - displayOnlyItems = listOf( + displayOnlyItems = persistentListOf( newLedgerHardwareWalletFactorSourceSample().toCard( - accounts = listOf(newAccountSampleStokenetNadia()), - personas = listOf(newPersonaSampleStokenetLeiaSkywalker()) + accounts = persistentListOf(newAccountSampleStokenetNadia()), + personas = persistentListOf(newPersonaSampleStokenetLeiaSkywalker()) ), newLedgerHardwareWalletFactorSourceSample().toCard( - accounts = listOf( + accounts = persistentListOf( newAccountSampleMainnetAlice(), newAccountSampleMainnetBob(), newAccountSampleMainnetCarol(), @@ -53,7 +56,7 @@ class SecurityFactorSamplesViewModel @Inject constructor() : StateViewModel = emptyList(), - accounts: List = emptyList(), - personas: List = emptyList() + messages: PersistentList = persistentListOf(), + accounts: PersistentList = persistentListOf(), + personas: PersistentList = persistentListOf() ): FactorSourceCard { return FactorSourceCard( kind = kind, @@ -203,9 +206,9 @@ class SecurityFactorSamplesViewModel @Inject constructor() : StateViewModel = emptyList(), - accounts: List = emptyList(), - personas: List = emptyList() + messages: PersistentList = persistentListOf(), + accounts: PersistentList = persistentListOf(), + personas: PersistentList = persistentListOf() ): FactorSourceCard { return FactorSourceCard( kind = kind, @@ -231,10 +234,10 @@ class SecurityFactorSamplesViewModel @Inject constructor() : StateViewModel = emptyList(), - val singleChoiceItems: List = emptyList(), - val multiChoiceItems: List = emptyList(), - val removableItems: List = emptyList() + val displayOnlyItems: PersistentList = persistentListOf(), + val singleChoiceItems: PersistentList = persistentListOf(), + val multiChoiceItems: PersistentList = persistentListOf(), + val removableItems: PersistentList = persistentListOf() ) : UiState companion object { @@ -244,4 +247,4 @@ class SecurityFactorSamplesViewModel @Inject constructor() : StateViewModel RadixTheme.colors.orange1 StatusMessage.Type.ERROR -> RadixTheme.colors.red1 } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/card/FactorSourceCardView.kt b/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/card/FactorSourceCardView.kt index e1b81e0cdb..57456ee25e 100644 --- a/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/card/FactorSourceCardView.kt +++ b/app/src/main/java/com/babylon/wallet/android/presentation/ui/composables/card/FactorSourceCardView.kt @@ -51,6 +51,8 @@ import com.radixdlt.sargon.extensions.init import com.radixdlt.sargon.samples.sample import com.radixdlt.sargon.samples.sampleMainnet import com.radixdlt.sargon.samples.sampleStokenet +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.persistentListOf @Composable fun FactorSourceCardView( @@ -90,9 +92,9 @@ fun FactorSourceCardView( title: String, subtitle: String?, lastUsedOn: String?, - messages: List, - accounts: List, - personas: List, + messages: PersistentList, + accounts: PersistentList, + personas: PersistentList, modifier: Modifier = Modifier, endContent: (@Composable () -> Unit)? = null ) { @@ -211,8 +213,8 @@ fun SimpleFactorSourceCard( @Composable private fun LinkedEntitiesView( - accounts: List, - personas: List + accounts: PersistentList, + personas: PersistentList ) { val accountsText = when { accounts.isEmpty() -> null @@ -382,7 +384,7 @@ class FactorSourceCardPreviewProvider : PreviewParameterProvider } @@ -213,11 +214,11 @@ private fun RemovableFactorSourceCardPreview() { item = FactorSourceCard( kind = FactorSourceKind.DEVICE, header = FactorSourceCard.Header.New, - messages = emptyList(), - accounts = emptyList(), - personas = emptyList() + messages = persistentListOf(), + accounts = persistentListOf(), + personas = persistentListOf() ), onRemoveClick = {} ) } -} \ No newline at end of file +} From fa9a2e01261f961d3edad438f0f31afb949e2990 Mon Sep 17 00:00:00 2001 From: Sergiu Puhalschi Date: Wed, 4 Dec 2024 19:36:49 +0200 Subject: [PATCH 4/5] Update naming --- .../android/domain/model/factors/FactorSourceCard.kt | 2 +- .../debug/factors/SecurityFactorSamplesViewModel.kt | 8 ++++---- .../ui/composables/card/FactorSourceCardView.kt | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/babylon/wallet/android/domain/model/factors/FactorSourceCard.kt b/app/src/main/java/com/babylon/wallet/android/domain/model/factors/FactorSourceCard.kt index 7fd5ea6029..23b1dd4b35 100644 --- a/app/src/main/java/com/babylon/wallet/android/domain/model/factors/FactorSourceCard.kt +++ b/app/src/main/java/com/babylon/wallet/android/domain/model/factors/FactorSourceCard.kt @@ -18,7 +18,7 @@ data class FactorSourceCard( data object New : Header - data class Added( + data class Instance( val id: FactorSourceId.Hash, val name: String, val lastUsedOn: String? diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt index b185378a67..8848852686 100644 --- a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt +++ b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt @@ -194,9 +194,9 @@ class SecurityFactorSamplesViewModel @Inject constructor() : StateViewModel Unit)? = null ) { when (item.header) { - is FactorSourceCard.Header.Added -> FactorSourceCardView( + is FactorSourceCard.Header.Instance -> FactorSourceCardView( iconRes = item.kind.iconRes(), title = item.header.name, subtitle = null, @@ -320,7 +320,7 @@ fun FactorSourceKind.iconRes(): Int { FactorSourceKind.LEDGER_HQ_HARDWARE_WALLET -> com.babylon.wallet.android.designsystem.R.drawable.ic_factor_ledger_hardware FactorSourceKind.OFF_DEVICE_MNEMONIC -> com.babylon.wallet.android.designsystem.R.drawable.ic_factor_passphrase FactorSourceKind.ARCULUS_CARD -> com.babylon.wallet.android.designsystem.R.drawable.ic_factor_arculus - FactorSourceKind.PASSPHRASE -> com.babylon.wallet.android.designsystem.R.drawable.ic_factor_password + FactorSourceKind.PASSWORD -> com.babylon.wallet.android.designsystem.R.drawable.ic_factor_password FactorSourceKind.TRUSTED_CONTACT, FactorSourceKind.SECURITY_QUESTIONS -> error("Not supported yet") } @@ -334,7 +334,7 @@ fun FactorSourceKind.title(): String { FactorSourceKind.LEDGER_HQ_HARDWARE_WALLET -> R.string.factorSources_card_ledgerTitle FactorSourceKind.OFF_DEVICE_MNEMONIC -> R.string.factorSources_card_passphraseTitle FactorSourceKind.ARCULUS_CARD -> R.string.factorSources_card_arculusCardTitle - FactorSourceKind.PASSPHRASE -> R.string.factorSources_card_passwordTitle + FactorSourceKind.PASSWORD -> R.string.factorSources_card_passwordTitle FactorSourceKind.TRUSTED_CONTACT, FactorSourceKind.SECURITY_QUESTIONS -> error("Not supported yet") } @@ -349,7 +349,7 @@ fun FactorSourceKind.subtitle(): String { FactorSourceKind.LEDGER_HQ_HARDWARE_WALLET -> R.string.factorSources_card_ledgerDescription FactorSourceKind.OFF_DEVICE_MNEMONIC -> R.string.factorSources_card_passphraseDescription FactorSourceKind.ARCULUS_CARD -> R.string.factorSources_card_arculusCardDescription - FactorSourceKind.PASSPHRASE -> R.string.factorSources_card_passwordDescription + FactorSourceKind.PASSWORD -> R.string.factorSources_card_passwordDescription FactorSourceKind.TRUSTED_CONTACT, FactorSourceKind.SECURITY_QUESTIONS -> error("Not supported yet") } @@ -376,7 +376,7 @@ class FactorSourceCardPreviewProvider : PreviewParameterProvider Date: Thu, 5 Dec 2024 13:51:26 +0200 Subject: [PATCH 5/5] Address PR comments --- .../factors/SecurityFactorSamplesScreen.kt | 2 +- .../factors/SecurityFactorSamplesViewModel.kt | 85 +++++++++---------- .../ui/composables/StatusMessageText.kt | 2 +- .../composables/card/FactorSourceCardView.kt | 4 +- .../card/SelectableFactorSourceCard.kt | 2 +- .../ui}/model/factors/FactorSourceCard.kt | 2 +- .../ui}/model/factors/StatusMessage.kt | 2 +- 7 files changed, 45 insertions(+), 54 deletions(-) rename app/src/main/java/com/babylon/wallet/android/{domain => presentation/ui}/model/factors/FactorSourceCard.kt (91%) rename app/src/main/java/com/babylon/wallet/android/{domain => presentation/ui}/model/factors/StatusMessage.kt (70%) diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesScreen.kt b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesScreen.kt index 38e05e3c40..4195409465 100644 --- a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesScreen.kt +++ b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesScreen.kt @@ -15,7 +15,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.babylon.wallet.android.R import com.babylon.wallet.android.designsystem.theme.RadixTheme -import com.babylon.wallet.android.domain.model.factors.FactorSourceCard import com.babylon.wallet.android.presentation.ui.RadixWalletPreviewTheme import com.babylon.wallet.android.presentation.ui.composables.RadixCenteredTopAppBar import com.babylon.wallet.android.presentation.ui.composables.card.FactorSourceCardView @@ -23,6 +22,7 @@ import com.babylon.wallet.android.presentation.ui.composables.card.RemovableFact import com.babylon.wallet.android.presentation.ui.composables.card.SelectableMultiChoiceFactorSourceCard import com.babylon.wallet.android.presentation.ui.composables.card.SelectableSingleChoiceFactorSourceCard import com.babylon.wallet.android.presentation.ui.composables.statusBarsAndBanner +import com.babylon.wallet.android.presentation.ui.model.factors.FactorSourceCard @Composable fun SecurityFactorSamplesScreen( diff --git a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt index 8848852686..44e956416f 100644 --- a/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt +++ b/app/src/main/java/com/babylon/wallet/android/presentation/settings/debug/factors/SecurityFactorSamplesViewModel.kt @@ -1,32 +1,22 @@ package com.babylon.wallet.android.presentation.settings.debug.factors import android.text.format.DateUtils -import com.babylon.wallet.android.domain.model.factors.FactorSourceCard -import com.babylon.wallet.android.domain.model.factors.StatusMessage import com.babylon.wallet.android.presentation.common.StateViewModel import com.babylon.wallet.android.presentation.common.UiState +import com.babylon.wallet.android.presentation.ui.model.factors.FactorSourceCard +import com.babylon.wallet.android.presentation.ui.model.factors.StatusMessage import com.radixdlt.sargon.Account import com.radixdlt.sargon.DeviceFactorSource import com.radixdlt.sargon.FactorSourceKind import com.radixdlt.sargon.LedgerHardwareWalletFactorSource import com.radixdlt.sargon.Persona import com.radixdlt.sargon.Timestamp +import com.radixdlt.sargon.annotation.UsesSampleValues import com.radixdlt.sargon.extensions.asGeneral import com.radixdlt.sargon.extensions.kind -import com.radixdlt.sargon.newAccountSampleMainnetAlice -import com.radixdlt.sargon.newAccountSampleMainnetBob -import com.radixdlt.sargon.newAccountSampleMainnetCarol -import com.radixdlt.sargon.newAccountSampleStokenetNadia -import com.radixdlt.sargon.newAccountSampleStokenetOlivia -import com.radixdlt.sargon.newAccountSampleStokenetPaige -import com.radixdlt.sargon.newDeviceFactorSourceSample -import com.radixdlt.sargon.newLedgerHardwareWalletFactorSourceSample -import com.radixdlt.sargon.newPersonaSampleMainnetBatman -import com.radixdlt.sargon.newPersonaSampleMainnetRipley -import com.radixdlt.sargon.newPersonaSampleMainnetSatoshi -import com.radixdlt.sargon.newPersonaSampleStokenetConnor -import com.radixdlt.sargon.newPersonaSampleStokenetHermione -import com.radixdlt.sargon.newPersonaSampleStokenetLeiaSkywalker +import com.radixdlt.sargon.samples.sample +import com.radixdlt.sargon.samples.sampleMainnet +import com.radixdlt.sargon.samples.sampleStokenet import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf @@ -41,48 +31,49 @@ import kotlin.time.Duration.Companion.seconds class SecurityFactorSamplesViewModel @Inject constructor() : StateViewModel() { @Suppress("LongMethod") + @OptIn(UsesSampleValues::class) override fun initialState(): State = State( displayOnlyItems = persistentListOf( - newLedgerHardwareWalletFactorSourceSample().toCard( - accounts = persistentListOf(newAccountSampleStokenetNadia()), - personas = persistentListOf(newPersonaSampleStokenetLeiaSkywalker()) + LedgerHardwareWalletFactorSource.sample().toCard( + accounts = persistentListOf(Account.sampleStokenet.nadia), + personas = persistentListOf(Persona.sampleStokenet.leiaSkywalker) ), - newLedgerHardwareWalletFactorSourceSample().toCard( + LedgerHardwareWalletFactorSource.sample().toCard( accounts = persistentListOf( - newAccountSampleMainnetAlice(), - newAccountSampleMainnetBob(), - newAccountSampleMainnetCarol(), - newAccountSampleStokenetNadia(), - newAccountSampleStokenetOlivia(), - newAccountSampleStokenetPaige() + Account.sampleMainnet.alice, + Account.sampleMainnet.bob, + Account.sampleMainnet.carol, + Account.sampleStokenet.nadia, + Account.sampleStokenet.olivia, + Account.sampleStokenet.paige ), personas = persistentListOf( - newPersonaSampleMainnetSatoshi(), - newPersonaSampleMainnetBatman(), - newPersonaSampleMainnetRipley(), - newPersonaSampleStokenetLeiaSkywalker(), - newPersonaSampleStokenetHermione(), - newPersonaSampleStokenetConnor() + Persona.sampleMainnet.satoshi, + Persona.sampleMainnet.batman, + Persona.sampleMainnet.ripley, + Persona.sampleStokenet.leiaSkywalker, + Persona.sampleStokenet.hermione, + Persona.sampleStokenet.connor ) ), - newLedgerHardwareWalletFactorSourceSample().toCard( + LedgerHardwareWalletFactorSource.sample().toCard( accounts = persistentListOf( - newAccountSampleMainnetAlice(), - newAccountSampleMainnetBob(), - newAccountSampleMainnetCarol(), - newAccountSampleStokenetNadia(), - newAccountSampleStokenetOlivia(), - newAccountSampleStokenetPaige() + Account.sampleMainnet.alice, + Account.sampleMainnet.bob, + Account.sampleMainnet.carol, + Account.sampleStokenet.nadia, + Account.sampleStokenet.olivia, + Account.sampleStokenet.paige ) ), - newLedgerHardwareWalletFactorSourceSample().toCard( + LedgerHardwareWalletFactorSource.sample().toCard( personas = persistentListOf( - newPersonaSampleMainnetSatoshi(), - newPersonaSampleMainnetBatman(), - newPersonaSampleMainnetRipley(), - newPersonaSampleStokenetLeiaSkywalker(), - newPersonaSampleStokenetHermione(), - newPersonaSampleStokenetConnor() + Persona.sampleMainnet.satoshi, + Persona.sampleMainnet.batman, + Persona.sampleMainnet.ripley, + Persona.sampleStokenet.leiaSkywalker, + Persona.sampleStokenet.hermione, + Persona.sampleStokenet.connor ) ), FactorSourceCard( @@ -101,7 +92,7 @@ class SecurityFactorSamplesViewModel @Inject constructor() : StateViewModel