mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 07:05:35 +03:00
[PM-11741] shortcut navigation pt2 electric boogaloo (#3904)
This commit is contained in:
parent
537c501b9b
commit
f544ccc3ef
8 changed files with 72 additions and 125 deletions
|
@ -47,8 +47,6 @@ import com.x8bit.bitwarden.ui.platform.feature.vaultunlocked.vaultUnlockedGraph
|
|||
import com.x8bit.bitwarden.ui.platform.theme.NonNullEnterTransitionProvider
|
||||
import com.x8bit.bitwarden.ui.platform.theme.NonNullExitTransitionProvider
|
||||
import com.x8bit.bitwarden.ui.platform.theme.RootTransitionProviders
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.navigateToGeneratorModal
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.model.AddSendType
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.navigateToAddSend
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.navigateToVaultAddEdit
|
||||
|
@ -115,7 +113,6 @@ fun RootNavScreen(
|
|||
is RootNavState.VaultUnlockedForFido2Save,
|
||||
is RootNavState.VaultUnlockedForFido2Assertion,
|
||||
is RootNavState.VaultUnlockedForFido2GetCredentials,
|
||||
is RootNavState.GeneratorShortcut,
|
||||
-> VAULT_UNLOCKED_GRAPH_ROUTE
|
||||
}
|
||||
val currentRoute = navController.currentDestination?.rootLevelRoute()
|
||||
|
@ -220,11 +217,6 @@ fun RootNavScreen(
|
|||
navOptions = rootNavOptions,
|
||||
)
|
||||
}
|
||||
|
||||
RootNavState.GeneratorShortcut -> {
|
||||
navController.navigateToVaultUnlockedGraph(rootNavOptions)
|
||||
navController.navigateToGeneratorModal(mode = GeneratorMode.Modal.Password)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,9 +131,7 @@ class RootNavViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
SpecialCircumstance.GeneratorShortcut -> {
|
||||
RootNavState.GeneratorShortcut
|
||||
}
|
||||
SpecialCircumstance.GeneratorShortcut,
|
||||
SpecialCircumstance.VaultShortcut,
|
||||
null,
|
||||
-> RootNavState.VaultUnlocked(activeUserId = userState.activeAccount.userId)
|
||||
|
@ -322,12 +320,6 @@ sealed class RootNavState : Parcelable {
|
|||
*/
|
||||
@Parcelize
|
||||
data object ExpiredRegistrationLink : RootNavState()
|
||||
|
||||
/**
|
||||
* App should show the password generator modal.
|
||||
*/
|
||||
@Parcelize
|
||||
data object GeneratorShortcut : RootNavState()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -88,7 +88,9 @@ fun VaultUnlockedNavBarScreen(
|
|||
navController.apply {
|
||||
val navOptions = vaultUnlockedNavBarScreenNavOptions(tabToNavigateTo = event.tab)
|
||||
when (event) {
|
||||
is VaultUnlockedNavBarEvent.NavigateToVaultScreen -> {
|
||||
is VaultUnlockedNavBarEvent.Shortcut.NavigateToVaultScreen,
|
||||
is VaultUnlockedNavBarEvent.NavigateToVaultScreen,
|
||||
-> {
|
||||
navigateToVaultGraph(navOptions)
|
||||
}
|
||||
|
||||
|
@ -96,7 +98,9 @@ fun VaultUnlockedNavBarScreen(
|
|||
navigateToSendGraph(navOptions)
|
||||
}
|
||||
|
||||
VaultUnlockedNavBarEvent.NavigateToGeneratorScreen -> {
|
||||
VaultUnlockedNavBarEvent.Shortcut.NavigateToGeneratorScreen,
|
||||
VaultUnlockedNavBarEvent.NavigateToGeneratorScreen,
|
||||
-> {
|
||||
navigateToGeneratorGraph(navOptions)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
|||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.BackgroundEvent
|
||||
import com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar.model.VaultUnlockedNavBarTab
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
|
@ -38,13 +39,13 @@ class VaultUnlockedNavBarViewModel @Inject constructor(
|
|||
|
||||
when (specialCircumstancesManager.specialCircumstance) {
|
||||
SpecialCircumstance.GeneratorShortcut -> {
|
||||
sendEvent(VaultUnlockedNavBarEvent.NavigateToGeneratorScreen)
|
||||
sendEvent(VaultUnlockedNavBarEvent.Shortcut.NavigateToGeneratorScreen)
|
||||
specialCircumstancesManager.specialCircumstance = null
|
||||
}
|
||||
|
||||
SpecialCircumstance.VaultShortcut -> {
|
||||
sendEvent(
|
||||
VaultUnlockedNavBarEvent.NavigateToVaultScreen(
|
||||
VaultUnlockedNavBarEvent.Shortcut.NavigateToVaultScreen(
|
||||
labelRes = state.vaultNavBarLabelRes,
|
||||
contentDescRes = state.vaultNavBarContentDescriptionRes,
|
||||
),
|
||||
|
@ -218,4 +219,30 @@ sealed class VaultUnlockedNavBarEvent {
|
|||
data object NavigateToSettingsScreen : VaultUnlockedNavBarEvent() {
|
||||
override val tab: VaultUnlockedNavBarTab = VaultUnlockedNavBarTab.Settings
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut events should to be considered [BackgroundEvent] as they are fired
|
||||
* outside of normal lifecycle aware events and should not be ignored by filter.
|
||||
*/
|
||||
sealed class Shortcut : VaultUnlockedNavBarEvent(), BackgroundEvent {
|
||||
/**
|
||||
* Navigate to the Generator screen via a shortcut.
|
||||
*/
|
||||
data object NavigateToGeneratorScreen : Shortcut() {
|
||||
override val tab: VaultUnlockedNavBarTab = VaultUnlockedNavBarTab.Generator
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the Vault screen via a shortcut.
|
||||
*/
|
||||
data class NavigateToVaultScreen(
|
||||
val labelRes: Int,
|
||||
val contentDescRes: Int,
|
||||
) : Shortcut() {
|
||||
override val tab: VaultUnlockedNavBarTab = VaultUnlockedNavBarTab.Vault(
|
||||
labelRes = labelRes,
|
||||
contentDescriptionRes = contentDescRes,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -225,15 +225,6 @@ class RootNavScreenTest : BaseComposeTest() {
|
|||
navOptions = expectedNavOptions,
|
||||
)
|
||||
}
|
||||
|
||||
// Make sure navigating to the generator shortcut works as expected:
|
||||
rootNavStateFlow.value = RootNavState.GeneratorShortcut
|
||||
composeTestRule
|
||||
.runOnIdle {
|
||||
fakeNavHostController.assertLastNavigation(
|
||||
route = "generator_modal/password_generator",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -906,97 +906,6 @@ class RootNavViewModelTest : BaseViewModelTest() {
|
|||
assertEquals(RootNavState.VaultLocked, viewModel.stateFlow.value)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `when there are no accounts but there is a GeneratorShortcut special circumstance the nav state should be Auth`() {
|
||||
every { authRepository.hasPendingAccountAddition } returns false
|
||||
|
||||
specialCircumstanceManager.specialCircumstance =
|
||||
SpecialCircumstance.GeneratorShortcut
|
||||
mutableUserStateFlow.tryEmit(null)
|
||||
val viewModel = createViewModel()
|
||||
assertEquals(
|
||||
RootNavState.Auth,
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `when the active user has an unlocked vault and there is a GeneratorShortcut special circumstance the nav state should be GeneratorShortcut`() {
|
||||
every { authRepository.hasPendingAccountAddition } returns true
|
||||
|
||||
specialCircumstanceManager.specialCircumstance =
|
||||
SpecialCircumstance.GeneratorShortcut
|
||||
mutableUserStateFlow.tryEmit(
|
||||
UserState(
|
||||
activeUserId = "activeUserId",
|
||||
accounts = listOf(
|
||||
UserState.Account(
|
||||
userId = "activeUserId",
|
||||
name = "name",
|
||||
email = "email",
|
||||
avatarColorHex = "avatarHexColor",
|
||||
environment = Environment.Us,
|
||||
isPremium = true,
|
||||
isLoggedIn = true,
|
||||
isVaultUnlocked = true,
|
||||
needsPasswordReset = false,
|
||||
isBiometricsEnabled = false,
|
||||
organizations = emptyList(),
|
||||
needsMasterPassword = false,
|
||||
trustedDevice = null,
|
||||
hasMasterPassword = true,
|
||||
isUsingKeyConnector = false,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
val viewModel = createViewModel()
|
||||
assertEquals(
|
||||
RootNavState.GeneratorShortcut,
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `when the active user has a locked vault and there is a GeneratorShortcut special circumstance the nav state should be VaultLocked`() {
|
||||
every { authRepository.hasPendingAccountAddition } returns true
|
||||
|
||||
specialCircumstanceManager.specialCircumstance =
|
||||
SpecialCircumstance.GeneratorShortcut
|
||||
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 = false,
|
||||
needsPasswordReset = false,
|
||||
isBiometricsEnabled = false,
|
||||
organizations = emptyList(),
|
||||
needsMasterPassword = false,
|
||||
trustedDevice = null,
|
||||
hasMasterPassword = true,
|
||||
isUsingKeyConnector = false,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
val viewModel = createViewModel()
|
||||
assertEquals(
|
||||
RootNavState.VaultLocked,
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createViewModel(): RootNavViewModel =
|
||||
RootNavViewModel(
|
||||
authRepository = authRepository,
|
||||
|
|
|
@ -81,6 +81,24 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `NavigateToVaultScreen shortcut event should navigate to VaultScreen`() {
|
||||
mutableEventFlow.tryEmit(VaultUnlockedNavBarEvent.NavigateToSendScreen)
|
||||
composeTestRule.runOnIdle { fakeNavHostController.assertCurrentRoute("send_graph") }
|
||||
mutableEventFlow.tryEmit(
|
||||
VaultUnlockedNavBarEvent.Shortcut.NavigateToVaultScreen(
|
||||
labelRes = R.string.my_vault,
|
||||
contentDescRes = R.string.my_vault,
|
||||
),
|
||||
)
|
||||
composeTestRule.runOnIdle {
|
||||
fakeNavHostController.assertLastNavigation(
|
||||
route = "vault_graph",
|
||||
navOptions = expectedNavOptions,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `send tab click should send SendTabClick action`() {
|
||||
composeTestRule.onNodeWithText("Send").performClick()
|
||||
|
@ -121,6 +139,20 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `NavigateToGeneratorScreen shortcut event should navigate to GeneratorScreen`() {
|
||||
composeTestRule.apply {
|
||||
runOnIdle { fakeNavHostController.assertCurrentRoute("vault_graph") }
|
||||
mutableEventFlow.tryEmit(VaultUnlockedNavBarEvent.Shortcut.NavigateToGeneratorScreen)
|
||||
runOnIdle {
|
||||
fakeNavHostController.assertLastNavigation(
|
||||
route = "generator_graph",
|
||||
navOptions = expectedNavOptions,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `settings tab click should send SendTabClick action`() {
|
||||
composeTestRule.onNodeWithText("Settings").performClick()
|
||||
|
|
|
@ -29,7 +29,7 @@ class VaultUnlockedNavBarViewModelTest : BaseViewModelTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `on init with GeneratorShortcut special circumstance should navigate to the generator screen`() =
|
||||
fun `on init with GeneratorShortcut special circumstance should navigate to the generator screen with shortcut event`() =
|
||||
runTest {
|
||||
every {
|
||||
specialCircumstancesManager.specialCircumstance
|
||||
|
@ -38,7 +38,7 @@ class VaultUnlockedNavBarViewModelTest : BaseViewModelTest() {
|
|||
val viewModel = createViewModel()
|
||||
|
||||
viewModel.eventFlow.test {
|
||||
assertEquals(VaultUnlockedNavBarEvent.NavigateToGeneratorScreen, awaitItem())
|
||||
assertEquals(VaultUnlockedNavBarEvent.Shortcut.NavigateToGeneratorScreen, awaitItem())
|
||||
}
|
||||
verify(exactly = 1) {
|
||||
specialCircumstancesManager.specialCircumstance
|
||||
|
@ -48,7 +48,7 @@ class VaultUnlockedNavBarViewModelTest : BaseViewModelTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `on init with VaultShortcut special circumstance should navigate to the generator screen`() =
|
||||
fun `on init with VaultShortcut special circumstance should navigate to the vault screen with shortcut event`() =
|
||||
runTest {
|
||||
every {
|
||||
specialCircumstancesManager.specialCircumstance
|
||||
|
@ -58,7 +58,7 @@ class VaultUnlockedNavBarViewModelTest : BaseViewModelTest() {
|
|||
|
||||
viewModel.eventFlow.test {
|
||||
assertEquals(
|
||||
VaultUnlockedNavBarEvent.NavigateToVaultScreen(
|
||||
VaultUnlockedNavBarEvent.Shortcut.NavigateToVaultScreen(
|
||||
labelRes = R.string.my_vault,
|
||||
contentDescRes = R.string.my_vault,
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue