mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 07:05:35 +03:00
PM-12076 remaining state based navigation linkup for onboarding (#3923)
This commit is contained in:
parent
503c966177
commit
37f1da1ec2
9 changed files with 166 additions and 8 deletions
|
@ -1,6 +1,8 @@
|
|||
package com.x8bit.bitwarden.ui.auth.feature.accountsetup
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
@ -16,9 +18,13 @@ import javax.inject.Inject
|
|||
@HiltViewModel
|
||||
class SetupAutoFillViewModel @Inject constructor(
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val authRepository: AuthRepository,
|
||||
) :
|
||||
BaseViewModel<SetupAutoFillState, SetupAutoFillEvent, SetupAutoFillAction>(
|
||||
initialState = SetupAutoFillState(dialogState = null, autofillEnabled = false),
|
||||
initialState = run {
|
||||
val userId = requireNotNull(authRepository.userStateFlow.value).activeUserId
|
||||
SetupAutoFillState(userId = userId, dialogState = null, autofillEnabled = false)
|
||||
},
|
||||
) {
|
||||
|
||||
init {
|
||||
|
@ -73,11 +79,11 @@ class SetupAutoFillViewModel @Inject constructor(
|
|||
|
||||
private fun handleTurnOnLaterConfirmClick() {
|
||||
// TODO PM-10631 record user chose to turn on later for settings badging.
|
||||
// TODO PM-10632 update status to complete setup step.
|
||||
updateOnboardingStatusToNextStep()
|
||||
}
|
||||
|
||||
private fun handleContinueClick() {
|
||||
// TODO PM-10632 update status to complete setup step.
|
||||
updateOnboardingStatusToNextStep()
|
||||
}
|
||||
|
||||
private fun handleAutofillServiceChanged(action: SetupAutoFillAction.AutofillServiceChanged) {
|
||||
|
@ -87,12 +93,20 @@ class SetupAutoFillViewModel @Inject constructor(
|
|||
settingsRepository.disableAutofill()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateOnboardingStatusToNextStep() =
|
||||
authRepository
|
||||
.setOnboardingStatus(
|
||||
userId = state.userId,
|
||||
status = OnboardingStatus.FINAL_STEP,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* UI State for the Auto-fill setup screen.
|
||||
*/
|
||||
data class SetupAutoFillState(
|
||||
val userId: String,
|
||||
val dialogState: SetupAutoFillDialogState?,
|
||||
val autofillEnabled: Boolean,
|
||||
)
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package com.x8bit.bitwarden.ui.auth.feature.accountsetup
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.composableWithPushTransitions
|
||||
|
||||
/**
|
||||
* Route name for [SetupCompleteScreen].
|
||||
*/
|
||||
const val SETUP_COMPLETE_ROUTE = "setup_complete"
|
||||
|
||||
/**
|
||||
* Navigate to the setup complete screen.
|
||||
*/
|
||||
fun NavController.navigateToSetupCompleteScreen(navOptions: NavOptions? = null) {
|
||||
this.navigate(SETUP_COMPLETE_ROUTE, navOptions)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the setup complete screen to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.setupCompleteDestination() {
|
||||
composableWithPushTransitions(
|
||||
route = SETUP_COMPLETE_ROUTE,
|
||||
) {
|
||||
SetupCompleteScreen()
|
||||
}
|
||||
}
|
|
@ -16,10 +16,13 @@ import androidx.navigation.compose.NavHost
|
|||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navOptions
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.SETUP_AUTO_FILL_ROUTE
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.SETUP_COMPLETE_ROUTE
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.SETUP_UNLOCK_ROUTE
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.navigateToSetupAutoFillScreen
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.navigateToSetupCompleteScreen
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.navigateToSetupUnlockScreen
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.setupAutoFillDestination
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.setupCompleteDestination
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.setupUnlockDestination
|
||||
import com.x8bit.bitwarden.ui.auth.feature.auth.AUTH_GRAPH_ROUTE
|
||||
import com.x8bit.bitwarden.ui.auth.feature.auth.authGraph
|
||||
|
@ -98,6 +101,7 @@ fun RootNavScreen(
|
|||
setupDebugMenuDestination(onNavigateBack = { navController.popBackStack() })
|
||||
setupUnlockDestination()
|
||||
setupAutoFillDestination()
|
||||
setupCompleteDestination()
|
||||
}
|
||||
|
||||
val targetRoute = when (state) {
|
||||
|
@ -125,6 +129,7 @@ fun RootNavScreen(
|
|||
|
||||
RootNavState.OnboardingAccountLockSetup -> SETUP_UNLOCK_ROUTE
|
||||
RootNavState.OnboardingAutoFillSetup -> SETUP_AUTO_FILL_ROUTE
|
||||
RootNavState.OnboardingStepsComplete -> SETUP_COMPLETE_ROUTE
|
||||
}
|
||||
val currentRoute = navController.currentDestination?.rootLevelRoute()
|
||||
|
||||
|
@ -236,6 +241,10 @@ fun RootNavScreen(
|
|||
RootNavState.OnboardingAutoFillSetup -> {
|
||||
navController.navigateToSetupAutoFillScreen(rootNavOptions)
|
||||
}
|
||||
|
||||
RootNavState.OnboardingStepsComplete -> {
|
||||
navController.navigateToSetupCompleteScreen(rootNavOptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,8 +97,8 @@ class RootNavViewModel @Inject constructor(
|
|||
OnboardingStatus.ACCOUNT_LOCK_SETUP,
|
||||
-> RootNavState.OnboardingAccountLockSetup
|
||||
OnboardingStatus.AUTOFILL_SETUP -> RootNavState.OnboardingAutoFillSetup
|
||||
OnboardingStatus.FINAL_STEP -> RootNavState.OnboardingStepsComplete
|
||||
OnboardingStatus.COMPLETE -> throw IllegalStateException("Should not have entered here.")
|
||||
OnboardingStatus.FINAL_STEP -> TODO("PM-12076 complete navigation wiring")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -345,6 +345,12 @@ sealed class RootNavState : Parcelable {
|
|||
*/
|
||||
@Parcelize
|
||||
data object OnboardingAutoFillSetup : RootNavState()
|
||||
|
||||
/**
|
||||
* App should show the onboarding steps complete screen.
|
||||
*/
|
||||
@Parcelize
|
||||
data object OnboardingStepsComplete : RootNavState()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package com.x8bit.bitwarden.ui.auth.feature.accountsetup
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import io.mockk.every
|
||||
|
@ -24,6 +27,15 @@ class SetupAutoFillViewModelTest : BaseViewModelTest() {
|
|||
every { disableAutofill() } just runs
|
||||
}
|
||||
|
||||
private val mockUserState = mockk<UserState> {
|
||||
every { activeUserId } returns DEFAULT_USER_ID
|
||||
}
|
||||
private val mutableUserStateFlow = MutableStateFlow<UserState?>(mockUserState)
|
||||
private val authRepository: AuthRepository = mockk {
|
||||
every { userStateFlow } returns mutableUserStateFlow
|
||||
every { setOnboardingStatus(any(), any()) } just runs
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handleAutofillEnabledUpdateReceive updates autofillEnabled state`() {
|
||||
val viewModel = createViewModel()
|
||||
|
@ -92,5 +104,31 @@ class SetupAutoFillViewModelTest : BaseViewModelTest() {
|
|||
)
|
||||
}
|
||||
|
||||
private fun createViewModel() = SetupAutoFillViewModel(settingsRepository)
|
||||
@Test
|
||||
fun `handleTurnOnLaterConfirmClick sets onboarding status to FINAL_STEP`() {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.trySendAction(SetupAutoFillAction.TurnOnLaterConfirmClick)
|
||||
verify {
|
||||
authRepository.setOnboardingStatus(
|
||||
DEFAULT_USER_ID,
|
||||
OnboardingStatus.FINAL_STEP,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handleContinueClick sets onboarding status to FINAL_STEP`() {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.trySendAction(SetupAutoFillAction.ContinueClick)
|
||||
verify {
|
||||
authRepository.setOnboardingStatus(
|
||||
DEFAULT_USER_ID,
|
||||
OnboardingStatus.FINAL_STEP,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createViewModel() = SetupAutoFillViewModel(settingsRepository, authRepository)
|
||||
}
|
||||
|
||||
private const val DEFAULT_USER_ID = "userId"
|
||||
|
|
|
@ -208,4 +208,8 @@ class SetupAutofillScreenTest : BaseComposeTest() {
|
|||
}
|
||||
}
|
||||
|
||||
private val DEFAULT_STATE = SetupAutoFillState(dialogState = null, autofillEnabled = false)
|
||||
private val DEFAULT_STATE = SetupAutoFillState(
|
||||
userId = "userId",
|
||||
dialogState = null,
|
||||
autofillEnabled = false,
|
||||
)
|
||||
|
|
|
@ -5,7 +5,9 @@ import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
|||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
|
@ -18,8 +20,9 @@ class SetupCompleteViewModelTest : BaseViewModelTest() {
|
|||
every { activeUserId } returns DEFAULT_USER_ID
|
||||
}
|
||||
private val mutableUserStateFlow = MutableStateFlow<UserState?>(mockUserState)
|
||||
private val authRepository: AuthRepository = mockk(relaxed = true) {
|
||||
private val authRepository: AuthRepository = mockk {
|
||||
every { userStateFlow } returns mutableUserStateFlow
|
||||
every { setOnboardingStatus(any(), any()) } just runs
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -226,7 +226,7 @@ class RootNavScreenTest : BaseComposeTest() {
|
|||
)
|
||||
}
|
||||
|
||||
// Make sure navigating to onboarding graph works as expected:
|
||||
// Make sure navigating to account lock setup works as expected:
|
||||
rootNavStateFlow.value =
|
||||
RootNavState.OnboardingAccountLockSetup
|
||||
composeTestRule.runOnIdle {
|
||||
|
@ -235,6 +235,26 @@ class RootNavScreenTest : BaseComposeTest() {
|
|||
navOptions = expectedNavOptions,
|
||||
)
|
||||
}
|
||||
|
||||
// Make sure navigating to account autofill setup works as expected:
|
||||
rootNavStateFlow.value =
|
||||
RootNavState.OnboardingAutoFillSetup
|
||||
composeTestRule.runOnIdle {
|
||||
fakeNavHostController.assertLastNavigation(
|
||||
route = "setup_auto_fill",
|
||||
navOptions = expectedNavOptions,
|
||||
)
|
||||
}
|
||||
|
||||
// Make sure navigating to account setup complete works as expected:
|
||||
rootNavStateFlow.value =
|
||||
RootNavState.OnboardingStepsComplete
|
||||
composeTestRule.runOnIdle {
|
||||
fakeNavHostController.assertLastNavigation(
|
||||
route = "setup_complete",
|
||||
navOptions = expectedNavOptions,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1032,6 +1032,41 @@ class RootNavViewModelTest : BaseViewModelTest() {
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `when the active user has an unlocked vault and they have a OnboardingStatus of FINAL_STEP the nav state should be OnboardingAutoFillSetup`() {
|
||||
mutableUserStateFlow.tryEmit(
|
||||
UserState(
|
||||
activeUserId = "activeUserId",
|
||||
accounts = listOf(
|
||||
UserState.Account(
|
||||
userId = "activeUserId",
|
||||
name = "name",
|
||||
email = "email",
|
||||
avatarColorHex = "avatarColorHex",
|
||||
environment = Environment.Us,
|
||||
isPremium = true,
|
||||
isLoggedIn = true,
|
||||
isVaultUnlocked = true,
|
||||
needsPasswordReset = false,
|
||||
isBiometricsEnabled = false,
|
||||
organizations = emptyList(),
|
||||
needsMasterPassword = false,
|
||||
trustedDevice = null,
|
||||
hasMasterPassword = true,
|
||||
isUsingKeyConnector = false,
|
||||
onboardingStatus = OnboardingStatus.FINAL_STEP,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
val viewModel = createViewModel()
|
||||
assertEquals(
|
||||
RootNavState.OnboardingStepsComplete,
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `when the active user has an locked vault and they have a OnboardingStatus of NOT_STARTED the nav state should be VaultLocked`() {
|
||||
|
|
Loading…
Reference in a new issue