[#763] Configure simplified onboarding
This commit is contained in:
parent
9f9ee0fcc1
commit
a806d2defa
|
@ -11,8 +11,17 @@ import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
class OnboardingTestSetup(
|
class OnboardingTestSetup(
|
||||||
private val composeTestRule: ComposeContentTestRule,
|
private val composeTestRule: ComposeContentTestRule,
|
||||||
|
private val isFullOnboardingEnabled: Boolean,
|
||||||
initialStage: OnboardingStage
|
initialStage: OnboardingStage
|
||||||
) {
|
) {
|
||||||
|
init {
|
||||||
|
if (!isFullOnboardingEnabled) {
|
||||||
|
require(initialStage == OnboardingStage.Wallet) {
|
||||||
|
"When full onboarding is disabled, the initial stage must be Wallet"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val onboardingState = OnboardingState(initialStage)
|
private val onboardingState = OnboardingState(initialStage)
|
||||||
|
|
||||||
private val onCreateWalletCallbackCount = AtomicInteger(0)
|
private val onCreateWalletCallbackCount = AtomicInteger(0)
|
||||||
|
@ -38,6 +47,7 @@ class OnboardingTestSetup(
|
||||||
fun getDefaultContent() {
|
fun getDefaultContent() {
|
||||||
ZcashTheme {
|
ZcashTheme {
|
||||||
Onboarding(
|
Onboarding(
|
||||||
|
isFullOnboardingEnabled,
|
||||||
onboardingState,
|
onboardingState,
|
||||||
isDebugMenuEnabled = false,
|
isDebugMenuEnabled = false,
|
||||||
onCreateWallet = { onCreateWalletCallbackCount.incrementAndGet() },
|
onCreateWallet = { onCreateWalletCallbackCount.incrementAndGet() },
|
||||||
|
|
|
@ -18,7 +18,11 @@ class OnboardingActivityTest : UiTestPrerequisites() {
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val composeTestRule = createAndroidComposeRule<UiTestingActivity>()
|
val composeTestRule = createAndroidComposeRule<UiTestingActivity>()
|
||||||
|
|
||||||
private fun newTestSetup() = OnboardingTestSetup(composeTestRule, OnboardingStage.ShieldedByDefault)
|
private fun newTestSetup() = OnboardingTestSetup(
|
||||||
|
composeTestRule,
|
||||||
|
isFullOnboardingEnabled = true,
|
||||||
|
OnboardingStage.ShieldedByDefault
|
||||||
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
|
|
|
@ -18,7 +18,11 @@ class OnboardingIntegrationTest : UiTestPrerequisites() {
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val composeTestRule = createComposeRule()
|
val composeTestRule = createComposeRule()
|
||||||
|
|
||||||
private fun newTestSetup(initialStage: OnboardingStage) = OnboardingTestSetup(composeTestRule, initialStage)
|
private fun newTestSetup(initialStage: OnboardingStage) = OnboardingTestSetup(
|
||||||
|
composeTestRule,
|
||||||
|
isFullOnboardingEnabled = true,
|
||||||
|
initialStage
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The test semantics are built upon StateRestorationTester component. We simulate screen state
|
* The test semantics are built upon StateRestorationTester component. We simulate screen state
|
||||||
|
|
|
@ -20,8 +20,8 @@ class OnboardingViewTest : UiTestPrerequisites() {
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val composeTestRule = createComposeRule()
|
val composeTestRule = createComposeRule()
|
||||||
|
|
||||||
private fun newTestSetup(initialStage: OnboardingStage): OnboardingTestSetup {
|
private fun newTestSetup(isFullOnboardingEnabled: Boolean = true, initialStage: OnboardingStage): OnboardingTestSetup {
|
||||||
return OnboardingTestSetup(composeTestRule, initialStage).apply {
|
return OnboardingTestSetup(composeTestRule, isFullOnboardingEnabled, initialStage).apply {
|
||||||
setDefaultContent()
|
setDefaultContent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ class OnboardingViewTest : UiTestPrerequisites() {
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun verify_test_setup_stage_1() {
|
fun verify_test_setup_stage_1() {
|
||||||
val testSetup = newTestSetup(OnboardingStage.ShieldedByDefault)
|
val testSetup = newTestSetup(initialStage = OnboardingStage.ShieldedByDefault)
|
||||||
|
|
||||||
assertEquals(OnboardingStage.ShieldedByDefault, testSetup.getOnboardingStage())
|
assertEquals(OnboardingStage.ShieldedByDefault, testSetup.getOnboardingStage())
|
||||||
assertEquals(0, testSetup.getOnImportWalletCallbackCount())
|
assertEquals(0, testSetup.getOnImportWalletCallbackCount())
|
||||||
|
@ -40,7 +40,7 @@ class OnboardingViewTest : UiTestPrerequisites() {
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun verify_test_setup_stage_4() {
|
fun verify_test_setup_stage_4() {
|
||||||
val testSetup = newTestSetup(OnboardingStage.Wallet)
|
val testSetup = newTestSetup(initialStage = OnboardingStage.Wallet)
|
||||||
|
|
||||||
assertEquals(OnboardingStage.Wallet, testSetup.getOnboardingStage())
|
assertEquals(OnboardingStage.Wallet, testSetup.getOnboardingStage())
|
||||||
assertEquals(0, testSetup.getOnImportWalletCallbackCount())
|
assertEquals(0, testSetup.getOnImportWalletCallbackCount())
|
||||||
|
@ -50,7 +50,7 @@ class OnboardingViewTest : UiTestPrerequisites() {
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun stage_1_layout() {
|
fun stage_1_layout() {
|
||||||
newTestSetup(OnboardingStage.ShieldedByDefault)
|
newTestSetup(initialStage = OnboardingStage.ShieldedByDefault)
|
||||||
|
|
||||||
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_next)).also {
|
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_next)).also {
|
||||||
it.assertExists()
|
it.assertExists()
|
||||||
|
@ -77,7 +77,7 @@ class OnboardingViewTest : UiTestPrerequisites() {
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun stage_2_layout() {
|
fun stage_2_layout() {
|
||||||
newTestSetup(OnboardingStage.UnifiedAddresses)
|
newTestSetup(initialStage = OnboardingStage.UnifiedAddresses)
|
||||||
|
|
||||||
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_skip)).also {
|
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_skip)).also {
|
||||||
it.assertExists()
|
it.assertExists()
|
||||||
|
@ -114,7 +114,7 @@ class OnboardingViewTest : UiTestPrerequisites() {
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun stage_3_layout() {
|
fun stage_3_layout() {
|
||||||
newTestSetup(OnboardingStage.More)
|
newTestSetup(initialStage = OnboardingStage.More)
|
||||||
|
|
||||||
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_skip)).also {
|
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_skip)).also {
|
||||||
it.assertExists()
|
it.assertExists()
|
||||||
|
@ -151,7 +151,7 @@ class OnboardingViewTest : UiTestPrerequisites() {
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun stage_4_layout() {
|
fun stage_4_layout() {
|
||||||
newTestSetup(OnboardingStage.Wallet)
|
newTestSetup(initialStage = OnboardingStage.Wallet)
|
||||||
|
|
||||||
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_skip)).also {
|
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_skip)).also {
|
||||||
it.assertDoesNotExist()
|
it.assertDoesNotExist()
|
||||||
|
@ -180,10 +180,40 @@ class OnboardingViewTest : UiTestPrerequisites() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@MediumTest
|
||||||
|
fun stage_4_layout_short_onboarding() {
|
||||||
|
newTestSetup(isFullOnboardingEnabled = false, initialStage = OnboardingStage.Wallet)
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_skip)).also {
|
||||||
|
it.assertDoesNotExist()
|
||||||
|
}
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_next)).also {
|
||||||
|
it.assertDoesNotExist()
|
||||||
|
}
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithContentDescription(getStringResource(R.string.onboarding_back)).also {
|
||||||
|
it.assertDoesNotExist()
|
||||||
|
}
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_4_create_new_wallet)).also {
|
||||||
|
it.assertExists()
|
||||||
|
it.assertIsEnabled()
|
||||||
|
it.assertHasClickAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_4_import_existing_wallet)).also {
|
||||||
|
it.assertExists()
|
||||||
|
it.assertIsEnabled()
|
||||||
|
it.assertHasClickAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun stage_1_skip() {
|
fun stage_1_skip() {
|
||||||
val testSetup = newTestSetup(OnboardingStage.ShieldedByDefault)
|
val testSetup = newTestSetup(initialStage = OnboardingStage.ShieldedByDefault)
|
||||||
|
|
||||||
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_skip)).also {
|
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_skip)).also {
|
||||||
it.performClick()
|
it.performClick()
|
||||||
|
@ -195,7 +225,7 @@ class OnboardingViewTest : UiTestPrerequisites() {
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun last_stage_click_create_wallet() {
|
fun last_stage_click_create_wallet() {
|
||||||
val testSetup = newTestSetup(OnboardingStage.Wallet)
|
val testSetup = newTestSetup(initialStage = OnboardingStage.Wallet)
|
||||||
|
|
||||||
val newWalletButton = composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_4_create_new_wallet))
|
val newWalletButton = composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_4_create_new_wallet))
|
||||||
newWalletButton.performClick()
|
newWalletButton.performClick()
|
||||||
|
@ -207,7 +237,7 @@ class OnboardingViewTest : UiTestPrerequisites() {
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun last_stage_click_import_wallet() {
|
fun last_stage_click_import_wallet() {
|
||||||
val testSetup = newTestSetup(OnboardingStage.Wallet)
|
val testSetup = newTestSetup(initialStage = OnboardingStage.Wallet)
|
||||||
|
|
||||||
val newWalletButton = composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_4_import_existing_wallet))
|
val newWalletButton = composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_4_import_existing_wallet))
|
||||||
newWalletButton.performClick()
|
newWalletButton.performClick()
|
||||||
|
@ -219,7 +249,7 @@ class OnboardingViewTest : UiTestPrerequisites() {
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun multi_stage_progression() {
|
fun multi_stage_progression() {
|
||||||
val testSetup = newTestSetup(OnboardingStage.ShieldedByDefault)
|
val testSetup = newTestSetup(initialStage = OnboardingStage.ShieldedByDefault)
|
||||||
|
|
||||||
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_next)).also {
|
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_next)).also {
|
||||||
it.performClick()
|
it.performClick()
|
||||||
|
|
|
@ -8,7 +8,11 @@ object ConfigurationEntries {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Disabled because we don't have the URI parser support in the SDK yet.
|
* Disabled because we don't have the URI parser support in the SDK yet.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
val IS_REQUEST_ZEC_ENABLED = BooleanConfigurationEntry(ConfigKey("is_request_zec_enabled"), false)
|
val IS_REQUEST_ZEC_ENABLED = BooleanConfigurationEntry(ConfigKey("is_request_zec_enabled"), false)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The full onboarding flow is functional and tested, but it is disabled by default for an initially minimal feature set.
|
||||||
|
*/
|
||||||
|
val IS_FULL_ONBOARDING_ENABLED = BooleanConfigurationEntry(ConfigKey("is_full_onboarding_enabled"), false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,11 @@ import co.electriccoin.zcash.spackle.EmulatorWtfUtil
|
||||||
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
|
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
|
||||||
import co.electriccoin.zcash.ui.BuildConfig
|
import co.electriccoin.zcash.ui.BuildConfig
|
||||||
import co.electriccoin.zcash.ui.MainActivity
|
import co.electriccoin.zcash.ui.MainActivity
|
||||||
|
import co.electriccoin.zcash.ui.configuration.ConfigurationEntries
|
||||||
|
import co.electriccoin.zcash.ui.configuration.RemoteConfig
|
||||||
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
||||||
|
import co.electriccoin.zcash.ui.screen.onboarding.model.OnboardingStage
|
||||||
|
import co.electriccoin.zcash.ui.screen.onboarding.state.OnboardingState
|
||||||
import co.electriccoin.zcash.ui.screen.onboarding.view.Onboarding
|
import co.electriccoin.zcash.ui.screen.onboarding.view.Onboarding
|
||||||
import co.electriccoin.zcash.ui.screen.onboarding.viewmodel.OnboardingViewModel
|
import co.electriccoin.zcash.ui.screen.onboarding.viewmodel.OnboardingViewModel
|
||||||
import co.electriccoin.zcash.ui.screen.restore.view.RestoreWallet
|
import co.electriccoin.zcash.ui.screen.restore.view.RestoreWallet
|
||||||
|
@ -47,8 +51,18 @@ internal fun WrapOnboarding(
|
||||||
|
|
||||||
// TODO [#383]: https://github.com/zcash/secant-android-wallet/issues/383
|
// TODO [#383]: https://github.com/zcash/secant-android-wallet/issues/383
|
||||||
if (!onboardingViewModel.isImporting.collectAsStateWithLifecycle().value) {
|
if (!onboardingViewModel.isImporting.collectAsStateWithLifecycle().value) {
|
||||||
|
val isFullOnboardingEnabled = ConfigurationEntries.IS_FULL_ONBOARDING_ENABLED.getValue(RemoteConfig.current)
|
||||||
|
val onboardingState = if (isFullOnboardingEnabled) {
|
||||||
|
onboardingViewModel.onboardingState
|
||||||
|
} else {
|
||||||
|
// Force to the last screen, which is the "create wallet" screen.
|
||||||
|
// This simplifies the implementation inside the Onboarding composable.
|
||||||
|
OnboardingState(OnboardingStage.values().last())
|
||||||
|
}
|
||||||
|
|
||||||
Onboarding(
|
Onboarding(
|
||||||
onboardingState = onboardingViewModel.onboardingState,
|
isFullOnboardingEnabled = isFullOnboardingEnabled,
|
||||||
|
onboardingState = onboardingState,
|
||||||
isDebugMenuEnabled = isDebugMenuEnabled,
|
isDebugMenuEnabled = isDebugMenuEnabled,
|
||||||
onImportWallet = {
|
onImportWallet = {
|
||||||
// In the case of the app currently being messed with by the robo test runner on
|
// In the case of the app currently being messed with by the robo test runner on
|
||||||
|
@ -64,7 +78,7 @@ internal fun WrapOnboarding(
|
||||||
return@Onboarding
|
return@Onboarding
|
||||||
}
|
}
|
||||||
|
|
||||||
onboardingViewModel.isImporting.value = true
|
onboardingViewModel.setIsImporting(true)
|
||||||
},
|
},
|
||||||
onCreateWallet = {
|
onCreateWallet = {
|
||||||
if (FirebaseTestLabUtil.isFirebaseTestLab(applicationContext)) {
|
if (FirebaseTestLabUtil.isFirebaseTestLab(applicationContext)) {
|
||||||
|
@ -115,7 +129,7 @@ private fun WrapRestore(activity: ComponentActivity) {
|
||||||
RestoreWallet(
|
RestoreWallet(
|
||||||
completeWordList.list,
|
completeWordList.list,
|
||||||
restoreViewModel.userWordList,
|
restoreViewModel.userWordList,
|
||||||
onBack = { onboardingViewModel.isImporting.value = false },
|
onBack = { onboardingViewModel.setIsImporting(false) },
|
||||||
paste = {
|
paste = {
|
||||||
val clipboardManager = applicationContext.getSystemService(ClipboardManager::class.java)
|
val clipboardManager = applicationContext.getSystemService(ClipboardManager::class.java)
|
||||||
return@RestoreWallet clipboardManager?.primaryClip?.toString()
|
return@RestoreWallet clipboardManager?.primaryClip?.toString()
|
||||||
|
|
|
@ -55,8 +55,9 @@ fun ComposablePreview() {
|
||||||
ZcashTheme(darkTheme = true) {
|
ZcashTheme(darkTheme = true) {
|
||||||
GradientSurface {
|
GradientSurface {
|
||||||
Onboarding(
|
Onboarding(
|
||||||
|
isFullOnboardingEnabled = true,
|
||||||
OnboardingState(OnboardingStage.Wallet),
|
OnboardingState(OnboardingStage.Wallet),
|
||||||
false,
|
isDebugMenuEnabled = false,
|
||||||
onImportWallet = {},
|
onImportWallet = {},
|
||||||
onCreateWallet = {},
|
onCreateWallet = {},
|
||||||
onFixtureWallet = {}
|
onFixtureWallet = {}
|
||||||
|
@ -66,12 +67,16 @@ fun ComposablePreview() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param isFullOnboardingEnabled Feature toggle to control whether the full onboarding flow is enabled. If disabled, then an abbreviated flow is shown
|
||||||
|
* and the onboarding state is treated effectively as if it is [OnboardingStage.Wallet].
|
||||||
* @param onImportWallet Callback when the user decides to import an existing wallet.
|
* @param onImportWallet Callback when the user decides to import an existing wallet.
|
||||||
* @param onCreateWallet Callback when the user decides to create a new wallet.
|
* @param onCreateWallet Callback when the user decides to create a new wallet.
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Suppress("LongParameterList")
|
||||||
fun Onboarding(
|
fun Onboarding(
|
||||||
|
isFullOnboardingEnabled: Boolean,
|
||||||
onboardingState: OnboardingState,
|
onboardingState: OnboardingState,
|
||||||
isDebugMenuEnabled: Boolean,
|
isDebugMenuEnabled: Boolean,
|
||||||
onImportWallet: () -> Unit,
|
onImportWallet: () -> Unit,
|
||||||
|
@ -81,7 +86,7 @@ fun Onboarding(
|
||||||
val currentStage = onboardingState.current.collectAsStateWithLifecycle().value
|
val currentStage = onboardingState.current.collectAsStateWithLifecycle().value
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
OnboardingTopAppBar(onboardingState, isDebugMenuEnabled, onFixtureWallet)
|
OnboardingTopAppBar(isFullOnboardingEnabled, onboardingState, isDebugMenuEnabled, onFixtureWallet)
|
||||||
},
|
},
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
BottomNav(currentStage, onboardingState::goNext, onCreateWallet, onImportWallet)
|
BottomNav(currentStage, onboardingState::goNext, onCreateWallet, onImportWallet)
|
||||||
|
@ -97,6 +102,7 @@ fun Onboarding(
|
||||||
@Composable
|
@Composable
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
private fun OnboardingTopAppBar(
|
private fun OnboardingTopAppBar(
|
||||||
|
isFullOnboardingEnabled: Boolean,
|
||||||
onboardingState: OnboardingState,
|
onboardingState: OnboardingState,
|
||||||
isDebugMenuEnabled: Boolean,
|
isDebugMenuEnabled: Boolean,
|
||||||
onFixtureWallet: () -> Unit
|
onFixtureWallet: () -> Unit
|
||||||
|
@ -106,6 +112,7 @@ private fun OnboardingTopAppBar(
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text(text = stringResource(id = R.string.app_name)) },
|
title = { Text(text = stringResource(id = R.string.app_name)) },
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
|
if (isFullOnboardingEnabled) {
|
||||||
if (currentStage.hasPrevious()) {
|
if (currentStage.hasPrevious()) {
|
||||||
IconButton(onboardingState::goPrevious) {
|
IconButton(onboardingState::goPrevious) {
|
||||||
Icon(
|
Icon(
|
||||||
|
@ -114,6 +121,7 @@ private fun OnboardingTopAppBar(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
if (currentStage.hasNext()) {
|
if (currentStage.hasNext()) {
|
||||||
|
@ -145,11 +153,6 @@ private fun DebugMenu(onFixtureWallet: () -> Unit) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param onImportWallet Callback when the user decides to import an existing wallet.
|
|
||||||
* @param onCreateWallet Callback when the user decides to create a new wallet.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun OnboardingMainContent(
|
fun OnboardingMainContent(
|
||||||
paddingValues: PaddingValues,
|
paddingValues: PaddingValues,
|
||||||
|
|
|
@ -7,7 +7,6 @@ import androidx.lifecycle.viewModelScope
|
||||||
import cash.z.ecc.android.sdk.ext.collectWith
|
import cash.z.ecc.android.sdk.ext.collectWith
|
||||||
import co.electriccoin.zcash.ui.screen.onboarding.model.OnboardingStage
|
import co.electriccoin.zcash.ui.screen.onboarding.model.OnboardingStage
|
||||||
import co.electriccoin.zcash.ui.screen.onboarding.state.OnboardingState
|
import co.electriccoin.zcash.ui.screen.onboarding.state.OnboardingState
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Android-specific ViewModel. This is used to save and restore state across Activity recreations
|
* Android-specific ViewModel. This is used to save and restore state across Activity recreations
|
||||||
|
@ -15,7 +14,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
*/
|
*/
|
||||||
class OnboardingViewModel(
|
class OnboardingViewModel(
|
||||||
application: Application,
|
application: Application,
|
||||||
savedStateHandle: SavedStateHandle
|
private val savedStateHandle: SavedStateHandle
|
||||||
) : AndroidViewModel(application) {
|
) : AndroidViewModel(application) {
|
||||||
|
|
||||||
val onboardingState: OnboardingState = run {
|
val onboardingState: OnboardingState = run {
|
||||||
|
@ -35,21 +34,17 @@ class OnboardingViewModel(
|
||||||
// This is a bit weird being placed here, but onboarding currently is considered complete when
|
// This is a bit weird being placed here, but onboarding currently is considered complete when
|
||||||
// the user has a persisted wallet. Also import allows the user to go back to onboarding, while
|
// the user has a persisted wallet. Also import allows the user to go back to onboarding, while
|
||||||
// creating a new wallet does not.
|
// creating a new wallet does not.
|
||||||
val isImporting = run {
|
val isImporting = savedStateHandle.getStateFlow(KEY_IS_IMPORTING, false)
|
||||||
val initialValue = savedStateHandle.get<Boolean?>(KEY_IS_IMPORTING) ?: false
|
|
||||||
|
|
||||||
MutableStateFlow(initialValue)
|
fun setIsImporting(isImporting: Boolean) {
|
||||||
|
savedStateHandle[KEY_IS_IMPORTING] = isImporting
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// viewModelScope is constructed with Dispatchers.Main.immediate, so this will
|
// viewModelScope is constructed with Dispatchers.Main.immediate, so this will
|
||||||
// update the save state as soon as a change occurs.
|
// update the save state as soon as a change occurs.
|
||||||
onboardingState.current.collectWith(viewModelScope) {
|
onboardingState.current.collectWith(viewModelScope) {
|
||||||
savedStateHandle.set(KEY_STAGE, it)
|
savedStateHandle[KEY_STAGE] = it
|
||||||
}
|
|
||||||
|
|
||||||
isImporting.collectWith(viewModelScope) {
|
|
||||||
savedStateHandle.set(KEY_IS_IMPORTING, it)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,8 +79,6 @@ class ScreenshotTest : UiTestPrerequisites() {
|
||||||
.captureToBitmap()
|
.captureToBitmap()
|
||||||
.writeToTestStorage("$screenshotName - $tag")
|
.writeToTestStorage("$screenshotName - $tag")
|
||||||
}
|
}
|
||||||
|
|
||||||
private val emptyConfiguration = StringConfiguration(persistentMapOf(), null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
|
@ -146,6 +144,7 @@ class ScreenshotTest : UiTestPrerequisites() {
|
||||||
|
|
||||||
composeTestRule.waitUntil(DEFAULT_TIMEOUT_MILLISECONDS) { composeTestRule.activity.walletViewModel.secretState.value is SecretState.None }
|
composeTestRule.waitUntil(DEFAULT_TIMEOUT_MILLISECONDS) { composeTestRule.activity.walletViewModel.secretState.value is SecretState.None }
|
||||||
|
|
||||||
|
if (ConfigurationEntries.IS_FULL_ONBOARDING_ENABLED.getValue(emptyConfiguration)) {
|
||||||
composeTestRule.onNodeWithText(resContext.getString(R.string.onboarding_1_header)).also {
|
composeTestRule.onNodeWithText(resContext.getString(R.string.onboarding_1_header)).also {
|
||||||
it.assertExists()
|
it.assertExists()
|
||||||
}
|
}
|
||||||
|
@ -154,6 +153,7 @@ class ScreenshotTest : UiTestPrerequisites() {
|
||||||
it.assertExists()
|
it.assertExists()
|
||||||
it.performClick()
|
it.performClick()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
composeTestRule.onNodeWithText(resContext.getString(R.string.onboarding_4_import_existing_wallet)).also {
|
composeTestRule.onNodeWithText(resContext.getString(R.string.onboarding_4_import_existing_wallet)).also {
|
||||||
it.assertExists()
|
it.assertExists()
|
||||||
|
@ -333,9 +333,11 @@ class ScreenshotTest : UiTestPrerequisites() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val emptyConfiguration = StringConfiguration(persistentMapOf(), null)
|
||||||
|
|
||||||
private fun onboardingScreenshots(resContext: Context, tag: String, composeTestRule: AndroidComposeTestRule<ActivityScenarioRule<MainActivity>, MainActivity>) {
|
private fun onboardingScreenshots(resContext: Context, tag: String, composeTestRule: AndroidComposeTestRule<ActivityScenarioRule<MainActivity>, MainActivity>) {
|
||||||
composeTestRule.waitUntil(DEFAULT_TIMEOUT_MILLISECONDS) { composeTestRule.activity.walletViewModel.secretState.value is SecretState.None }
|
composeTestRule.waitUntil(DEFAULT_TIMEOUT_MILLISECONDS) { composeTestRule.activity.walletViewModel.secretState.value is SecretState.None }
|
||||||
|
if (ConfigurationEntries.IS_FULL_ONBOARDING_ENABLED.getValue(emptyConfiguration)) {
|
||||||
composeTestRule.onNodeWithText(resContext.getString(R.string.onboarding_1_header)).also {
|
composeTestRule.onNodeWithText(resContext.getString(R.string.onboarding_1_header)).also {
|
||||||
it.assertExists()
|
it.assertExists()
|
||||||
}
|
}
|
||||||
|
@ -360,6 +362,7 @@ private fun onboardingScreenshots(resContext: Context, tag: String, composeTestR
|
||||||
composeTestRule.onNodeWithText(resContext.getString(R.string.onboarding_next)).also {
|
composeTestRule.onNodeWithText(resContext.getString(R.string.onboarding_next)).also {
|
||||||
it.performClick()
|
it.performClick()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
composeTestRule.onNodeWithText(resContext.getString(R.string.onboarding_4_header)).also {
|
composeTestRule.onNodeWithText(resContext.getString(R.string.onboarding_4_header)).also {
|
||||||
it.assertExists()
|
it.assertExists()
|
||||||
|
|
Loading…
Reference in New Issue