[#1286] Remove Seed Copy Button

* [#1286] New wallet screen: Copy seed only in debug

* [#1286] Seed screen: Copy seed only in debug

* Changelog update
This commit is contained in:
Honza Rychnovský 2024-03-13 15:49:10 +01:00 committed by GitHub
parent fd7703e005
commit d309da9287
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 164 additions and 134 deletions

View File

@ -22,6 +22,9 @@ directly impact users rather than highlighting other key architectural updates.*
### Fixed ### Fixed
- Button sizing has been updated to align with the design guidelines and preserve stretching if necessary - Button sizing has been updated to align with the design guidelines and preserve stretching if necessary
### Removed
- The seed copy feature from the New wallet recovery and Seed recovery screens has been removed for security reasons
## [0.2.0 (560)] - 2024-02-27 ## [0.2.0 (560)] - 2024-02-27
### Added ### Added

View File

@ -38,8 +38,9 @@ private fun ComposablePreview() {
@Composable @Composable
fun ChipGrid( fun ChipGrid(
wordList: ImmutableList<String>, wordList: ImmutableList<String>,
onGridClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onGridClick: () -> Unit allowCopy: Boolean = false,
) { ) {
val interactionSource = remember { MutableInteractionSource() } val interactionSource = remember { MutableInteractionSource() }
@ -51,13 +52,20 @@ fun ChipGrid(
modifier = modifier =
Modifier Modifier
.wrapContentWidth() .wrapContentWidth()
.clickable(
interactionSource = interactionSource,
// Disable ripple
indication = null,
onClick = onGridClick
)
.testTag(CommonTag.CHIP_LAYOUT) .testTag(CommonTag.CHIP_LAYOUT)
.then(
if (allowCopy) {
Modifier
.clickable(
interactionSource = interactionSource,
// Disable ripple
indication = null,
onClick = onGridClick
)
} else {
Modifier
}
)
) { ) {
wordList.chunked(CHIP_GRID_COLUMN_SIZE).forEachIndexed { chunkIndex, chunk -> wordList.chunked(CHIP_GRID_COLUMN_SIZE).forEachIndexed { chunkIndex, chunk ->
// TODO [#1043]: Correctly align numbers and words on Recovery screen // TODO [#1043]: Correctly align numbers and words on Recovery screen

View File

@ -3,23 +3,18 @@ package co.electriccoin.zcash.ui.screen.newwalletrecovery.view
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.test.junit4.ComposeContentTestRule import androidx.compose.ui.test.junit4.ComposeContentTestRule
import cash.z.ecc.sdk.fixture.PersistableWalletFixture import cash.z.ecc.sdk.fixture.PersistableWalletFixture
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
class NewWalletRecoveryTestSetup( class NewWalletRecoveryTestSetup(
private val composeTestRule: ComposeContentTestRule, private val composeTestRule: ComposeContentTestRule,
private val versionInfo: VersionInfo,
) { ) {
private val onSeedCopyCount = AtomicInteger(0)
private val onBirthdayCopyCount = AtomicInteger(0) private val onBirthdayCopyCount = AtomicInteger(0)
private val onCompleteCallbackCount = AtomicInteger(0) private val onCompleteCallbackCount = AtomicInteger(0)
fun getOnSeedCopyCount(): Int {
composeTestRule.waitForIdle()
return onSeedCopyCount.get()
}
fun getOnBirthdayCopyCount(): Int { fun getOnBirthdayCopyCount(): Int {
composeTestRule.waitForIdle() composeTestRule.waitForIdle()
return onBirthdayCopyCount.get() return onBirthdayCopyCount.get()
@ -36,9 +31,10 @@ class NewWalletRecoveryTestSetup(
ZcashTheme { ZcashTheme {
NewWalletRecovery( NewWalletRecovery(
PersistableWalletFixture.new(), PersistableWalletFixture.new(),
onSeedCopy = { onSeedCopyCount.incrementAndGet() }, onSeedCopy = { /* Not tested - debug mode feature only */ },
onBirthdayCopy = { onBirthdayCopyCount.incrementAndGet() }, onBirthdayCopy = { onBirthdayCopyCount.incrementAndGet() },
onComplete = { onCompleteCallbackCount.incrementAndGet() }, onComplete = { onCompleteCallbackCount.incrementAndGet() },
versionInfo = versionInfo,
) )
} }
} }

View File

@ -11,6 +11,7 @@ import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.test.CommonTag.WALLET_BIRTHDAY import co.electriccoin.zcash.ui.common.test.CommonTag.WALLET_BIRTHDAY
import co.electriccoin.zcash.ui.design.component.CommonTag import co.electriccoin.zcash.ui.design.component.CommonTag
import co.electriccoin.zcash.ui.fixture.VersionInfoFixture
import co.electriccoin.zcash.ui.test.getStringResource import co.electriccoin.zcash.ui.test.getStringResource
import org.junit.Rule import org.junit.Rule
import kotlin.test.Test import kotlin.test.Test
@ -21,7 +22,10 @@ class NewWalletRecoveryViewTest : UiTestPrerequisites() {
val composeTestRule = createComposeRule() val composeTestRule = createComposeRule()
private fun newTestSetup(): NewWalletRecoveryTestSetup { private fun newTestSetup(): NewWalletRecoveryTestSetup {
return NewWalletRecoveryTestSetup(composeTestRule).apply { return NewWalletRecoveryTestSetup(
composeTestRule,
VersionInfoFixture.new()
).apply {
setDefaultContent() setDefaultContent()
} }
} }
@ -31,14 +35,9 @@ class NewWalletRecoveryViewTest : UiTestPrerequisites() {
fun default_ui_state_test() { fun default_ui_state_test() {
val testSetup = newTestSetup() val testSetup = newTestSetup()
assertEquals(0, testSetup.getOnSeedCopyCount())
assertEquals(0, testSetup.getOnBirthdayCopyCount()) assertEquals(0, testSetup.getOnBirthdayCopyCount())
assertEquals(0, testSetup.getOnCompleteCount()) assertEquals(0, testSetup.getOnCompleteCount())
composeTestRule.onNodeWithText(getStringResource(R.string.new_wallet_recovery_copy)).also {
it.assertExists()
}
composeTestRule.onNodeWithContentDescription( composeTestRule.onNodeWithContentDescription(
label = getStringResource(R.string.zcash_logo_content_description) label = getStringResource(R.string.zcash_logo_content_description)
).also { ).also {
@ -69,40 +68,10 @@ class NewWalletRecoveryViewTest : UiTestPrerequisites() {
it.assertExists() it.assertExists()
} }
assertEquals(0, testSetup.getOnSeedCopyCount())
assertEquals(0, testSetup.getOnBirthdayCopyCount()) assertEquals(0, testSetup.getOnBirthdayCopyCount())
assertEquals(0, testSetup.getOnCompleteCount()) assertEquals(0, testSetup.getOnCompleteCount())
} }
@Test
@MediumTest
fun copy_seed_to_clipboard_from_app_bar_test() {
val testSetup = newTestSetup()
assertEquals(0, testSetup.getOnSeedCopyCount())
composeTestRule.onNodeWithText(getStringResource(R.string.new_wallet_recovery_copy)).also { menuButton ->
menuButton.performClick()
}
assertEquals(1, testSetup.getOnSeedCopyCount())
}
@Test
@MediumTest
fun copy_seed_to_clipboard_content_test() {
val testSetup = newTestSetup()
assertEquals(0, testSetup.getOnSeedCopyCount())
composeTestRule.onNodeWithTag(CommonTag.CHIP_LAYOUT).also {
it.performScrollTo()
it.performClick()
}
assertEquals(1, testSetup.getOnSeedCopyCount())
}
@Test @Test
@MediumTest @MediumTest
fun copy_birthday_to_clipboard_content_test() { fun copy_birthday_to_clipboard_content_test() {
@ -123,7 +92,6 @@ class NewWalletRecoveryViewTest : UiTestPrerequisites() {
fun click_finish_test() { fun click_finish_test() {
val testSetup = newTestSetup() val testSetup = newTestSetup()
assertEquals(0, testSetup.getOnSeedCopyCount())
assertEquals(0, testSetup.getOnBirthdayCopyCount()) assertEquals(0, testSetup.getOnBirthdayCopyCount())
assertEquals(0, testSetup.getOnCompleteCount()) assertEquals(0, testSetup.getOnCompleteCount())
@ -135,7 +103,6 @@ class NewWalletRecoveryViewTest : UiTestPrerequisites() {
it.performClick() it.performClick()
} }
assertEquals(0, testSetup.getOnSeedCopyCount())
assertEquals(0, testSetup.getOnBirthdayCopyCount()) assertEquals(0, testSetup.getOnBirthdayCopyCount())
assertEquals(1, testSetup.getOnCompleteCount()) assertEquals(1, testSetup.getOnCompleteCount())
} }

View File

@ -9,6 +9,7 @@ import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.common.compose.LocalScreenSecurity import co.electriccoin.zcash.ui.common.compose.LocalScreenSecurity
import co.electriccoin.zcash.ui.common.compose.ScreenSecurity import co.electriccoin.zcash.ui.common.compose.ScreenSecurity
import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.fixture.VersionInfoFixture
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -45,7 +46,8 @@ class NewWalletRecoveryViewsSecuredScreenTest : UiTestPrerequisites() {
PersistableWalletFixture.new(), PersistableWalletFixture.new(),
onSeedCopy = {}, onSeedCopy = {},
onBirthdayCopy = {}, onBirthdayCopy = {},
onComplete = {} onComplete = {},
versionInfo = VersionInfoFixture.new()
) )
} }
} }

View File

@ -3,25 +3,20 @@ package co.electriccoin.zcash.ui.screen.seedrecovery.view
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.test.junit4.ComposeContentTestRule import androidx.compose.ui.test.junit4.ComposeContentTestRule
import cash.z.ecc.sdk.fixture.PersistableWalletFixture import cash.z.ecc.sdk.fixture.PersistableWalletFixture
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
class SeedRecoveryTestSetup( class SeedRecoveryTestSetup(
private val composeTestRule: ComposeContentTestRule, private val composeTestRule: ComposeContentTestRule,
private val versionInfo: VersionInfo,
) { ) {
private val onSeedCopyCount = AtomicInteger(0)
private val onBirthdayCopyCount = AtomicInteger(0) private val onBirthdayCopyCount = AtomicInteger(0)
private val onCompleteCallbackCount = AtomicInteger(0) private val onCompleteCallbackCount = AtomicInteger(0)
private val onBackCount = AtomicInteger(0) private val onBackCount = AtomicInteger(0)
fun getOnSeedCopyCount(): Int {
composeTestRule.waitForIdle()
return onSeedCopyCount.get()
}
fun getOnBirthdayCopyCount(): Int { fun getOnBirthdayCopyCount(): Int {
composeTestRule.waitForIdle() composeTestRule.waitForIdle()
return onBirthdayCopyCount.get() return onBirthdayCopyCount.get()
@ -44,9 +39,10 @@ class SeedRecoveryTestSetup(
SeedRecovery( SeedRecovery(
PersistableWalletFixture.new(), PersistableWalletFixture.new(),
onBack = { onBackCount.incrementAndGet() }, onBack = { onBackCount.incrementAndGet() },
onSeedCopy = { onSeedCopyCount.incrementAndGet() }, onSeedCopy = { /* Not tested - debug mode feature only */ },
onBirthdayCopy = { onBirthdayCopyCount.incrementAndGet() }, onBirthdayCopy = { onBirthdayCopyCount.incrementAndGet() },
onDone = { onCompleteCallbackCount.incrementAndGet() }, onDone = { onCompleteCallbackCount.incrementAndGet() },
versionInfo = versionInfo,
) )
} }
} }

View File

@ -11,6 +11,7 @@ import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.test.CommonTag.WALLET_BIRTHDAY import co.electriccoin.zcash.ui.common.test.CommonTag.WALLET_BIRTHDAY
import co.electriccoin.zcash.ui.design.component.CommonTag import co.electriccoin.zcash.ui.design.component.CommonTag
import co.electriccoin.zcash.ui.fixture.VersionInfoFixture
import co.electriccoin.zcash.ui.test.getStringResource import co.electriccoin.zcash.ui.test.getStringResource
import org.junit.Rule import org.junit.Rule
import kotlin.test.Test import kotlin.test.Test
@ -21,7 +22,10 @@ class SeedRecoveryViewTest : UiTestPrerequisites() {
val composeTestRule = createComposeRule() val composeTestRule = createComposeRule()
private fun newTestSetup(): SeedRecoveryTestSetup { private fun newTestSetup(): SeedRecoveryTestSetup {
return SeedRecoveryTestSetup(composeTestRule).apply { return SeedRecoveryTestSetup(
composeTestRule,
VersionInfoFixture.new()
).apply {
setDefaultContent() setDefaultContent()
} }
} }
@ -31,7 +35,6 @@ class SeedRecoveryViewTest : UiTestPrerequisites() {
fun default_ui_state_test() { fun default_ui_state_test() {
val testSetup = newTestSetup() val testSetup = newTestSetup()
assertEquals(0, testSetup.getOnSeedCopyCount())
assertEquals(0, testSetup.getOnBirthdayCopyCount()) assertEquals(0, testSetup.getOnBirthdayCopyCount())
assertEquals(0, testSetup.getOnCompleteCount()) assertEquals(0, testSetup.getOnCompleteCount())
assertEquals(0, testSetup.getOnBackCount()) assertEquals(0, testSetup.getOnBackCount())
@ -41,10 +44,6 @@ class SeedRecoveryViewTest : UiTestPrerequisites() {
it.assertExists() it.assertExists()
} }
composeTestRule.onNodeWithText(getStringResource(R.string.seed_recovery_copy)).also {
it.assertExists()
}
composeTestRule.onNodeWithContentDescription( composeTestRule.onNodeWithContentDescription(
label = getStringResource(R.string.zcash_logo_content_description) label = getStringResource(R.string.zcash_logo_content_description)
).also { ).also {
@ -75,7 +74,6 @@ class SeedRecoveryViewTest : UiTestPrerequisites() {
it.assertExists() it.assertExists()
} }
assertEquals(0, testSetup.getOnSeedCopyCount())
assertEquals(0, testSetup.getOnBirthdayCopyCount()) assertEquals(0, testSetup.getOnBirthdayCopyCount())
assertEquals(0, testSetup.getOnCompleteCount()) assertEquals(0, testSetup.getOnCompleteCount())
assertEquals(0, testSetup.getOnBackCount()) assertEquals(0, testSetup.getOnBackCount())
@ -96,35 +94,6 @@ class SeedRecoveryViewTest : UiTestPrerequisites() {
assertEquals(1, testSetup.getOnBackCount()) assertEquals(1, testSetup.getOnBackCount())
} }
@Test
@MediumTest
fun copy_seed_to_clipboard_from_app_bar_test() {
val testSetup = newTestSetup()
assertEquals(0, testSetup.getOnSeedCopyCount())
composeTestRule.onNodeWithText(getStringResource(R.string.seed_recovery_copy)).also { menuButton ->
menuButton.performClick()
}
assertEquals(1, testSetup.getOnSeedCopyCount())
}
@Test
@MediumTest
fun copy_seed_to_clipboard_content_test() {
val testSetup = newTestSetup()
assertEquals(0, testSetup.getOnSeedCopyCount())
composeTestRule.onNodeWithTag(CommonTag.CHIP_LAYOUT).also {
it.performScrollTo()
it.performClick()
}
assertEquals(1, testSetup.getOnSeedCopyCount())
}
@Test @Test
@MediumTest @MediumTest
fun copy_birthday_to_clipboard_content_test() { fun copy_birthday_to_clipboard_content_test() {
@ -145,7 +114,6 @@ class SeedRecoveryViewTest : UiTestPrerequisites() {
fun click_finish_test() { fun click_finish_test() {
val testSetup = newTestSetup() val testSetup = newTestSetup()
assertEquals(0, testSetup.getOnSeedCopyCount())
assertEquals(0, testSetup.getOnBirthdayCopyCount()) assertEquals(0, testSetup.getOnBirthdayCopyCount())
assertEquals(0, testSetup.getOnCompleteCount()) assertEquals(0, testSetup.getOnCompleteCount())
@ -157,7 +125,6 @@ class SeedRecoveryViewTest : UiTestPrerequisites() {
it.performClick() it.performClick()
} }
assertEquals(0, testSetup.getOnSeedCopyCount())
assertEquals(0, testSetup.getOnBirthdayCopyCount()) assertEquals(0, testSetup.getOnBirthdayCopyCount())
assertEquals(1, testSetup.getOnCompleteCount()) assertEquals(1, testSetup.getOnCompleteCount())
} }

View File

@ -9,6 +9,7 @@ import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.common.compose.LocalScreenSecurity import co.electriccoin.zcash.ui.common.compose.LocalScreenSecurity
import co.electriccoin.zcash.ui.common.compose.ScreenSecurity import co.electriccoin.zcash.ui.common.compose.ScreenSecurity
import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.fixture.VersionInfoFixture
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -46,7 +47,8 @@ class SeedRecoveryViewsSecuredScreenTest : UiTestPrerequisites() {
onBack = {}, onBack = {},
onSeedCopy = {}, onSeedCopy = {},
onBirthdayCopy = {}, onBirthdayCopy = {},
onDone = {} onDone = {},
versionInfo = VersionInfoFixture.new()
) )
} }
} }

View File

@ -6,6 +6,7 @@ import cash.z.ecc.android.sdk.model.PersistableWallet
import co.electriccoin.zcash.spackle.ClipboardManagerUtil import co.electriccoin.zcash.spackle.ClipboardManagerUtil
import co.electriccoin.zcash.ui.MainActivity import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.screen.newwalletrecovery.view.NewWalletRecovery import co.electriccoin.zcash.ui.screen.newwalletrecovery.view.NewWalletRecovery
@Composable @Composable
@ -22,6 +23,8 @@ private fun WrapNewWalletRecovery(
persistableWallet: PersistableWallet, persistableWallet: PersistableWallet,
onBackupComplete: () -> Unit onBackupComplete: () -> Unit
) { ) {
val versionInfo = VersionInfo.new(activity.applicationContext)
NewWalletRecovery( NewWalletRecovery(
persistableWallet, persistableWallet,
onSeedCopy = { onSeedCopy = {
@ -38,6 +41,7 @@ private fun WrapNewWalletRecovery(
persistableWallet.birthday?.value.toString() persistableWallet.birthday?.value.toString()
) )
}, },
onComplete = onBackupComplete onComplete = onBackupComplete,
versionInfo = versionInfo
) )
} }

View File

@ -4,5 +4,5 @@ package co.electriccoin.zcash.ui.screen.newwalletrecovery.view
* These are only used for automated testing. * These are only used for automated testing.
*/ */
object NewWalletRecoveryTag { object NewWalletRecoveryTag {
const val WALLET_BIRTHDAY = "wallet_birthday" const val DEBUG_MENU_TAG = "debug_menu"
} }

View File

@ -15,9 +15,20 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
@ -30,16 +41,17 @@ import cash.z.ecc.sdk.fixture.PersistableWalletFixture
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.SecureScreen import co.electriccoin.zcash.ui.common.compose.SecureScreen
import co.electriccoin.zcash.ui.common.compose.shouldSecureScreen import co.electriccoin.zcash.ui.common.compose.shouldSecureScreen
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.common.test.CommonTag.WALLET_BIRTHDAY import co.electriccoin.zcash.ui.common.test.CommonTag.WALLET_BIRTHDAY
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
import co.electriccoin.zcash.ui.design.component.BodySmall import co.electriccoin.zcash.ui.design.component.BodySmall
import co.electriccoin.zcash.ui.design.component.ChipGrid import co.electriccoin.zcash.ui.design.component.ChipGrid
import co.electriccoin.zcash.ui.design.component.GradientSurface import co.electriccoin.zcash.ui.design.component.GradientSurface
import co.electriccoin.zcash.ui.design.component.PrimaryButton import co.electriccoin.zcash.ui.design.component.PrimaryButton
import co.electriccoin.zcash.ui.design.component.Reference
import co.electriccoin.zcash.ui.design.component.SmallTopAppBar import co.electriccoin.zcash.ui.design.component.SmallTopAppBar
import co.electriccoin.zcash.ui.design.component.TopScreenLogoTitle import co.electriccoin.zcash.ui.design.component.TopScreenLogoTitle
import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.fixture.VersionInfoFixture
import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.toPersistentList
@Preview(name = "NewWalletRecovery", device = Devices.PIXEL_4) @Preview(name = "NewWalletRecovery", device = Devices.PIXEL_4)
@ -52,6 +64,7 @@ private fun ComposablePreview() {
onSeedCopy = {}, onSeedCopy = {},
onBirthdayCopy = {}, onBirthdayCopy = {},
onComplete = {}, onComplete = {},
versionInfo = VersionInfoFixture.new(),
) )
} }
} }
@ -69,11 +82,13 @@ fun NewWalletRecovery(
onSeedCopy: () -> Unit, onSeedCopy: () -> Unit,
onBirthdayCopy: () -> Unit, onBirthdayCopy: () -> Unit,
onComplete: () -> Unit, onComplete: () -> Unit,
versionInfo: VersionInfo,
) { ) {
Scaffold( Scaffold(
topBar = { topBar = {
NewWalletRecoveryTopAppBar( NewWalletRecoveryTopAppBar(
onSeedCopy = onSeedCopy, onSeedCopy = onSeedCopy,
versionInfo = versionInfo,
) )
} }
) { paddingValues -> ) { paddingValues ->
@ -82,6 +97,7 @@ fun NewWalletRecovery(
onComplete = onComplete, onComplete = onComplete,
onSeedCopy = onSeedCopy, onSeedCopy = onSeedCopy,
onBirthdayCopy = onBirthdayCopy, onBirthdayCopy = onBirthdayCopy,
versionInfo = versionInfo,
// Horizontal paddings will be part of each UI element to minimize a possible truncation on very // Horizontal paddings will be part of each UI element to minimize a possible truncation on very
// small screens // small screens
modifier = modifier =
@ -95,38 +111,54 @@ fun NewWalletRecovery(
@Composable @Composable
private fun NewWalletRecoveryTopAppBar( private fun NewWalletRecoveryTopAppBar(
versionInfo: VersionInfo,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onSeedCopy: () -> Unit onSeedCopy: () -> Unit
) { ) {
SmallTopAppBar( SmallTopAppBar(
modifier = modifier, modifier = modifier,
regularActions = { regularActions = {
NewWalletRecoveryCopyToBufferMenuItem( if (versionInfo.isDebuggable && !versionInfo.isRunningUnderTestService) {
onCopyToClipboard = onSeedCopy DebugMenu(onCopyToClipboard = onSeedCopy)
) }
} }
) )
} }
@Composable @Composable
private fun NewWalletRecoveryCopyToBufferMenuItem( private fun DebugMenu(onCopyToClipboard: () -> Unit) {
modifier: Modifier = Modifier, Column(
onCopyToClipboard: () -> Unit, modifier = Modifier.testTag(NewWalletRecoveryTag.DEBUG_MENU_TAG)
) { ) {
Reference( var expanded by rememberSaveable { mutableStateOf(false) }
text = stringResource(id = R.string.new_wallet_recovery_copy), IconButton(onClick = { expanded = true }) {
onClick = onCopyToClipboard, Icon(Icons.Default.MoreVert, contentDescription = null)
textAlign = TextAlign.Center, }
modifier = modifier.padding(all = ZcashTheme.dimens.spacingDefault) DropdownMenu(
) expanded = expanded,
onDismissRequest = { expanded = false }
) {
DropdownMenuItem(
text = {
Text(stringResource(id = R.string.new_wallet_recovery_copy))
},
onClick = {
onCopyToClipboard()
expanded = false
}
)
}
}
} }
@Composable @Composable
@Suppress("LongParameterList")
private fun NewWalletRecoveryMainContent( private fun NewWalletRecoveryMainContent(
wallet: PersistableWallet, wallet: PersistableWallet,
onSeedCopy: () -> Unit, onSeedCopy: () -> Unit,
onBirthdayCopy: () -> Unit, onBirthdayCopy: () -> Unit,
onComplete: () -> Unit, onComplete: () -> Unit,
versionInfo: VersionInfo,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
Column( Column(
@ -156,6 +188,7 @@ private fun NewWalletRecoveryMainContent(
persistableWallet = wallet, persistableWallet = wallet,
onSeedCopy = onSeedCopy, onSeedCopy = onSeedCopy,
onBirthdayCopy = onBirthdayCopy, onBirthdayCopy = onBirthdayCopy,
versionInfo = versionInfo
) )
Spacer( Spacer(
@ -187,6 +220,7 @@ private fun NewWalletRecoverySeedPhrase(
persistableWallet: PersistableWallet, persistableWallet: PersistableWallet,
onSeedCopy: () -> Unit, onSeedCopy: () -> Unit,
onBirthdayCopy: () -> Unit, onBirthdayCopy: () -> Unit,
versionInfo: VersionInfo,
) { ) {
if (shouldSecureScreen) { if (shouldSecureScreen) {
SecureScreen() SecureScreen()
@ -195,7 +229,8 @@ private fun NewWalletRecoverySeedPhrase(
Column { Column {
ChipGrid( ChipGrid(
wordList = persistableWallet.seedPhrase.split.toPersistentList(), wordList = persistableWallet.seedPhrase.split.toPersistentList(),
onGridClick = onSeedCopy onGridClick = onSeedCopy,
allowCopy = versionInfo.isDebuggable && !versionInfo.isRunningUnderTestService,
) )
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault)) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))

View File

@ -7,6 +7,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.spackle.ClipboardManagerUtil import co.electriccoin.zcash.spackle.ClipboardManagerUtil
import co.electriccoin.zcash.ui.MainActivity import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.common.viewmodel.SecretState import co.electriccoin.zcash.ui.common.viewmodel.SecretState
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
@ -26,6 +27,8 @@ private fun WrapSeedRecovery(
goBack: () -> Unit, goBack: () -> Unit,
onDone: () -> Unit onDone: () -> Unit
) { ) {
val versionInfo = VersionInfo.new(activity.applicationContext)
val walletViewModel by activity.viewModels<WalletViewModel>() val walletViewModel by activity.viewModels<WalletViewModel>()
val persistableWallet = val persistableWallet =
@ -62,7 +65,8 @@ private fun WrapSeedRecovery(
persistableWallet.birthday?.value.toString() persistableWallet.birthday?.value.toString()
) )
}, },
onDone = onDone onDone = onDone,
versionInfo = versionInfo,
) )
} }
} }

View File

@ -0,0 +1,8 @@
package co.electriccoin.zcash.ui.screen.seedrecovery.view
/**
* These are only used for automated testing.
*/
object SeedRecoveryTag {
const val DEBUG_MENU_TAG = "debug_menu"
}

View File

@ -15,9 +15,20 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
@ -30,16 +41,17 @@ import cash.z.ecc.sdk.fixture.PersistableWalletFixture
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.SecureScreen import co.electriccoin.zcash.ui.common.compose.SecureScreen
import co.electriccoin.zcash.ui.common.compose.shouldSecureScreen import co.electriccoin.zcash.ui.common.compose.shouldSecureScreen
import co.electriccoin.zcash.ui.common.model.VersionInfo
import co.electriccoin.zcash.ui.common.test.CommonTag.WALLET_BIRTHDAY import co.electriccoin.zcash.ui.common.test.CommonTag.WALLET_BIRTHDAY
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
import co.electriccoin.zcash.ui.design.component.BodySmall import co.electriccoin.zcash.ui.design.component.BodySmall
import co.electriccoin.zcash.ui.design.component.ChipGrid import co.electriccoin.zcash.ui.design.component.ChipGrid
import co.electriccoin.zcash.ui.design.component.GradientSurface import co.electriccoin.zcash.ui.design.component.GradientSurface
import co.electriccoin.zcash.ui.design.component.PrimaryButton import co.electriccoin.zcash.ui.design.component.PrimaryButton
import co.electriccoin.zcash.ui.design.component.Reference
import co.electriccoin.zcash.ui.design.component.SmallTopAppBar import co.electriccoin.zcash.ui.design.component.SmallTopAppBar
import co.electriccoin.zcash.ui.design.component.TopScreenLogoTitle import co.electriccoin.zcash.ui.design.component.TopScreenLogoTitle
import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.fixture.VersionInfoFixture
import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.toPersistentList
@Preview(name = "SeedRecovery", device = Devices.PIXEL_4) @Preview(name = "SeedRecovery", device = Devices.PIXEL_4)
@ -53,6 +65,7 @@ private fun ComposablePreview() {
onBirthdayCopy = {}, onBirthdayCopy = {},
onDone = {}, onDone = {},
onSeedCopy = {}, onSeedCopy = {},
versionInfo = VersionInfoFixture.new(),
) )
} }
} }
@ -65,18 +78,21 @@ private fun ComposablePreview() {
* @param onDone Callback when the user has confirmed viewing the seed phrase. * @param onDone Callback when the user has confirmed viewing the seed phrase.
*/ */
@Composable @Composable
@Suppress("LongParameterList")
fun SeedRecovery( fun SeedRecovery(
wallet: PersistableWallet, wallet: PersistableWallet,
onBack: () -> Unit, onBack: () -> Unit,
onBirthdayCopy: () -> Unit, onBirthdayCopy: () -> Unit,
onDone: () -> Unit, onDone: () -> Unit,
onSeedCopy: () -> Unit, onSeedCopy: () -> Unit,
versionInfo: VersionInfo,
) { ) {
Scaffold( Scaffold(
topBar = { topBar = {
SeedRecoveryTopAppBar( SeedRecoveryTopAppBar(
onBack = onBack, onBack = onBack,
onSeedCopy = onSeedCopy, onSeedCopy = onSeedCopy,
versionInfo = versionInfo,
) )
} }
) { paddingValues -> ) { paddingValues ->
@ -85,6 +101,7 @@ fun SeedRecovery(
onDone = onDone, onDone = onDone,
onSeedCopy = onSeedCopy, onSeedCopy = onSeedCopy,
onBirthdayCopy = onBirthdayCopy, onBirthdayCopy = onBirthdayCopy,
versionInfo = versionInfo,
// Horizontal paddings will be part of each UI element to minimize a possible truncation on very // Horizontal paddings will be part of each UI element to minimize a possible truncation on very
// small screens // small screens
modifier = modifier =
@ -100,6 +117,7 @@ fun SeedRecovery(
private fun SeedRecoveryTopAppBar( private fun SeedRecoveryTopAppBar(
onBack: () -> Unit, onBack: () -> Unit,
onSeedCopy: () -> Unit, onSeedCopy: () -> Unit,
versionInfo: VersionInfo,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
SmallTopAppBar( SmallTopAppBar(
@ -108,32 +126,49 @@ private fun SeedRecoveryTopAppBar(
backContentDescriptionText = stringResource(R.string.seed_recovery_back_content_description), backContentDescriptionText = stringResource(R.string.seed_recovery_back_content_description),
onBack = onBack, onBack = onBack,
regularActions = { regularActions = {
SeedRecoveryCopyToBufferMenuItem( if (versionInfo.isDebuggable && !versionInfo.isRunningUnderTestService) {
onCopyToClipboard = onSeedCopy DebugMenu(
) onCopyToClipboard = onSeedCopy
)
}
} }
) )
} }
@Composable @Composable
private fun SeedRecoveryCopyToBufferMenuItem( private fun DebugMenu(onCopyToClipboard: () -> Unit) {
modifier: Modifier = Modifier, Column(
onCopyToClipboard: () -> Unit, modifier = Modifier.testTag(SeedRecoveryTag.DEBUG_MENU_TAG)
) { ) {
Reference( var expanded by rememberSaveable { mutableStateOf(false) }
text = stringResource(id = R.string.seed_recovery_copy), IconButton(onClick = { expanded = true }) {
onClick = onCopyToClipboard, Icon(Icons.Default.MoreVert, contentDescription = null)
textAlign = TextAlign.Center, }
modifier = modifier.padding(all = ZcashTheme.dimens.spacingDefault) DropdownMenu(
) expanded = expanded,
onDismissRequest = { expanded = false }
) {
DropdownMenuItem(
text = {
Text(stringResource(id = R.string.seed_recovery_copy))
},
onClick = {
onCopyToClipboard()
expanded = false
}
)
}
}
} }
@Composable @Composable
@Suppress("LongParameterList")
private fun SeedRecoveryMainContent( private fun SeedRecoveryMainContent(
wallet: PersistableWallet, wallet: PersistableWallet,
onSeedCopy: () -> Unit, onSeedCopy: () -> Unit,
onBirthdayCopy: () -> Unit, onBirthdayCopy: () -> Unit,
onDone: () -> Unit, onDone: () -> Unit,
versionInfo: VersionInfo,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
Column( Column(
@ -163,6 +198,7 @@ private fun SeedRecoveryMainContent(
persistableWallet = wallet, persistableWallet = wallet,
onSeedCopy = onSeedCopy, onSeedCopy = onSeedCopy,
onBirthdayCopy = onBirthdayCopy, onBirthdayCopy = onBirthdayCopy,
versionInfo = versionInfo,
) )
Spacer( Spacer(
@ -194,6 +230,7 @@ private fun SeedRecoverySeedPhrase(
persistableWallet: PersistableWallet, persistableWallet: PersistableWallet,
onSeedCopy: () -> Unit, onSeedCopy: () -> Unit,
onBirthdayCopy: () -> Unit, onBirthdayCopy: () -> Unit,
versionInfo: VersionInfo,
) { ) {
if (shouldSecureScreen) { if (shouldSecureScreen) {
SecureScreen() SecureScreen()
@ -202,7 +239,8 @@ private fun SeedRecoverySeedPhrase(
Column { Column {
ChipGrid( ChipGrid(
wordList = persistableWallet.seedPhrase.split.toPersistentList(), wordList = persistableWallet.seedPhrase.split.toPersistentList(),
onGridClick = onSeedCopy onGridClick = onSeedCopy,
allowCopy = versionInfo.isDebuggable && !versionInfo.isRunningUnderTestService,
) )
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault)) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))