mirror of
https://github.com/bitwarden/android.git
synced 2025-02-17 04:19:54 +03:00
Adding navigation for generator modals (#627)
This commit is contained in:
parent
61a162b6de
commit
cd4db46e13
12 changed files with 153 additions and 7 deletions
|
@ -10,6 +10,8 @@ import com.x8bit.bitwarden.ui.platform.feature.settings.folders.foldersDestinati
|
|||
import com.x8bit.bitwarden.ui.platform.feature.settings.folders.navigateToFolders
|
||||
import com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar.VAULT_UNLOCKED_NAV_BAR_ROUTE
|
||||
import com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar.vaultUnlockedNavBarDestination
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.generatorModalDestination
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.navigateToGeneratorModal
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.passwordhistory.navigateToPasswordHistory
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.passwordhistory.passwordHistoryDestination
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.addSendDestination
|
||||
|
@ -70,6 +72,7 @@ fun NavGraphBuilder.vaultUnlockedGraph(
|
|||
navController.navigateToManualCodeEntryScreen()
|
||||
},
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
onNavigateToGeneratorModal = { navController.navigateToGeneratorModal(mode = it) },
|
||||
)
|
||||
vaultMoveToOrganizationDestination(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
|
@ -90,7 +93,6 @@ fun NavGraphBuilder.vaultUnlockedGraph(
|
|||
},
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
)
|
||||
|
||||
vaultManualCodeEntryDestination(
|
||||
onNavigateToQrCodeScreen = {
|
||||
navController.popBackStack()
|
||||
|
@ -102,5 +104,6 @@ fun NavGraphBuilder.vaultUnlockedGraph(
|
|||
addSendDestination(onNavigateBack = { navController.popBackStack() })
|
||||
passwordHistoryDestination(onNavigateBack = { navController.popBackStack() })
|
||||
foldersDestination(onNavigateBack = { navController.popBackStack() })
|
||||
generatorModalDestination(onNavigateBack = { navController.popBackStack() })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ import com.x8bit.bitwarden.ui.platform.feature.settings.SETTINGS_GRAPH_ROUTE
|
|||
import com.x8bit.bitwarden.ui.platform.feature.settings.navigateToSettingsGraph
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.settingsGraph
|
||||
import com.x8bit.bitwarden.ui.platform.theme.RootTransitionProviders
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GENERATOR_ROUTE
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GENERATOR_GRAPH_ROUTE
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.generatorGraph
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.navigateToGeneratorGraph
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.SEND_GRAPH_ROUTE
|
||||
|
@ -345,7 +345,7 @@ private sealed class VaultUnlockedNavBarTab : Parcelable {
|
|||
override val iconRes get() = R.drawable.ic_generator
|
||||
override val labelRes get() = R.string.generator
|
||||
override val contentDescriptionRes get() = R.string.generator
|
||||
override val route get() = GENERATOR_ROUTE
|
||||
override val route get() = GENERATOR_GRAPH_ROUTE
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,7 +5,7 @@ import androidx.navigation.NavGraphBuilder
|
|||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.navigation
|
||||
|
||||
private const val GENERATOR_GRAPH_ROUTE: String = "generator_graph"
|
||||
const val GENERATOR_GRAPH_ROUTE: String = "generator_graph"
|
||||
|
||||
/**
|
||||
* Add generator destination to the root nav graph.
|
||||
|
|
|
@ -1,20 +1,42 @@
|
|||
package com.x8bit.bitwarden.ui.tools.feature.generator
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.navArgument
|
||||
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
|
||||
|
||||
/**
|
||||
* The functions below pertain to entry into the [GeneratorScreen].
|
||||
*/
|
||||
private const val GENERATOR_MODAL_ROUTE_PREFIX: String = "generator_modal"
|
||||
private const val GENERATOR_MODE_TYPE: String = "generator_mode_type"
|
||||
private const val USERNAME_GENERATOR: String = "username_generator"
|
||||
private const val PASSWORD_GENERATOR: String = "password_generator"
|
||||
|
||||
const val GENERATOR_ROUTE: String = "generator"
|
||||
private const val GENERATOR_MODAL_ROUTE: String =
|
||||
"$GENERATOR_MODAL_ROUTE_PREFIX/{$GENERATOR_MODE_TYPE}"
|
||||
|
||||
/**
|
||||
* Navigate to the [GeneratorScreen].
|
||||
* Class to retrieve vault item listing arguments from the [SavedStateHandle].
|
||||
*/
|
||||
fun NavController.navigateToGenerator(navOptions: NavOptions? = null) {
|
||||
navigate(GENERATOR_ROUTE, navOptions)
|
||||
@OmitFromCoverage
|
||||
data class GeneratorArgs(
|
||||
val type: GeneratorMode,
|
||||
) {
|
||||
constructor(savedStateHandle: SavedStateHandle) : this(
|
||||
type = when (savedStateHandle.get<String>(GENERATOR_MODE_TYPE)) {
|
||||
USERNAME_GENERATOR -> GeneratorMode.Modal.Username
|
||||
PASSWORD_GENERATOR -> GeneratorMode.Modal.Password
|
||||
else -> GeneratorMode.Default
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,6 +48,43 @@ fun NavGraphBuilder.generatorDestination(
|
|||
composable(GENERATOR_ROUTE) {
|
||||
GeneratorScreen(
|
||||
onNavigateToPasswordHistory = onNavigateToPasswordHistory,
|
||||
onNavigateBack = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the generator modal destination to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.generatorModalDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(
|
||||
route = GENERATOR_MODAL_ROUTE,
|
||||
arguments = listOf(
|
||||
navArgument(GENERATOR_MODE_TYPE) { type = NavType.StringType },
|
||||
),
|
||||
) {
|
||||
GeneratorScreen(
|
||||
onNavigateToPasswordHistory = {},
|
||||
onNavigateBack = onNavigateBack,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the generator screen in the username generation mode.
|
||||
*/
|
||||
fun NavController.navigateToGeneratorModal(
|
||||
mode: GeneratorMode.Modal,
|
||||
navOptions: NavOptions? = null,
|
||||
) {
|
||||
val generatorModeType = when (mode) {
|
||||
GeneratorMode.Modal.Password -> PASSWORD_GENERATOR
|
||||
GeneratorMode.Modal.Username -> USERNAME_GENERATOR
|
||||
}
|
||||
navigate(
|
||||
route = "$GENERATOR_MODAL_ROUTE_PREFIX/$generatorModeType",
|
||||
navOptions = navOptions,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
fun GeneratorScreen(
|
||||
viewModel: GeneratorViewModel = hiltViewModel(),
|
||||
onNavigateToPasswordHistory: () -> Unit,
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
val context = LocalContext.current
|
||||
|
@ -1059,6 +1060,7 @@ private fun GeneratorPreview() {
|
|||
BitwardenTheme {
|
||||
GeneratorScreen(
|
||||
onNavigateToPasswordHistory = {},
|
||||
onNavigateBack = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Us
|
|||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias.ServiceType.SimpleLogin
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.PlusAddressedEmail
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.RandomWord
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.util.toUsernameGeneratorRequest
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Job
|
||||
|
@ -50,6 +51,7 @@ import kotlinx.parcelize.Parcelize
|
|||
import javax.inject.Inject
|
||||
|
||||
private const val KEY_STATE = "state"
|
||||
private const val KEY_GENERATOR_MODE = "key_generator_mode"
|
||||
|
||||
/**
|
||||
* ViewModel responsible for handling user interactions in the generator screen.
|
||||
|
@ -73,6 +75,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
selectedType = Passcode(
|
||||
selectedType = Password(),
|
||||
),
|
||||
generatorMode = GeneratorArgs(savedStateHandle).type,
|
||||
currentEmailAddress =
|
||||
requireNotNull(authRepository.userStateFlow.value?.activeAccount?.email),
|
||||
),
|
||||
|
@ -1409,6 +1412,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
data class GeneratorState(
|
||||
val generatedText: String,
|
||||
val selectedType: MainType,
|
||||
val generatorMode: GeneratorMode = GeneratorMode.Default,
|
||||
val currentEmailAddress: String,
|
||||
) : Parcelable {
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package com.x8bit.bitwarden.ui.tools.feature.generator.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
/**
|
||||
* A sealed class representing the mode in which the generator displays.
|
||||
*/
|
||||
sealed class GeneratorMode : Parcelable {
|
||||
/**
|
||||
* Represents the main or default generator mode.
|
||||
*/
|
||||
@Parcelize
|
||||
data object Default : GeneratorMode()
|
||||
|
||||
/**
|
||||
* A sealed class representing the types of modals in which the generator displays.
|
||||
*/
|
||||
@Parcelize
|
||||
sealed class Modal : GeneratorMode() {
|
||||
|
||||
/**
|
||||
* Represents the mode for generating passwords.
|
||||
*/
|
||||
@Parcelize
|
||||
data object Password : Modal()
|
||||
|
||||
/**
|
||||
* Represents the mode for generating usernames.
|
||||
*/
|
||||
@Parcelize
|
||||
data object Username : Modal()
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ import androidx.navigation.NavType
|
|||
import androidx.navigation.navArgument
|
||||
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType
|
||||
|
||||
private const val ADD_TYPE: String = "add"
|
||||
|
@ -43,6 +44,7 @@ fun NavGraphBuilder.vaultAddEditDestination(
|
|||
onNavigateBack: () -> Unit,
|
||||
onNavigateToManualCodeEntryScreen: () -> Unit,
|
||||
onNavigateToQrCodeScanScreen: () -> Unit,
|
||||
onNavigateToGeneratorModal: (GeneratorMode.Modal) -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(
|
||||
route = ADD_EDIT_ITEM_ROUTE,
|
||||
|
@ -54,6 +56,7 @@ fun NavGraphBuilder.vaultAddEditDestination(
|
|||
onNavigateBack = onNavigateBack,
|
||||
onNavigateToManualCodeEntryScreen = onNavigateToManualCodeEntryScreen,
|
||||
onNavigateToQrCodeScanScreen = onNavigateToQrCodeScanScreen,
|
||||
onNavigateToGeneratorModal = onNavigateToGeneratorModal,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import com.x8bit.bitwarden.ui.platform.components.BitwardenScaffold
|
|||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.LoadingDialogState
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCardTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCommonHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditIdentityTypeHandlers
|
||||
|
@ -50,6 +51,7 @@ fun VaultAddEditScreen(
|
|||
permissionsManager: PermissionsManager =
|
||||
PermissionsManagerImpl(LocalContext.current as Activity),
|
||||
onNavigateToManualCodeEntryScreen: () -> Unit,
|
||||
onNavigateToGeneratorModal: (GeneratorMode.Modal) -> Unit,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
val context = LocalContext.current
|
||||
|
@ -65,6 +67,10 @@ fun VaultAddEditScreen(
|
|||
onNavigateToManualCodeEntryScreen()
|
||||
}
|
||||
|
||||
is VaultAddEditEvent.NavigateToGeneratorModal -> {
|
||||
onNavigateToGeneratorModal(event.generatorMode)
|
||||
}
|
||||
|
||||
is VaultAddEditEvent.ShowToast -> {
|
||||
Toast.makeText(context, event.message(resources), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
|||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.concat
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.toCustomField
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toViewState
|
||||
|
@ -1316,6 +1317,13 @@ sealed class VaultAddEditEvent {
|
|||
* Navigate to the manual code entry screen.
|
||||
*/
|
||||
data object NavigateToManualCodeEntry : VaultAddEditEvent()
|
||||
|
||||
/**
|
||||
* Navigate to the generator modal.
|
||||
*/
|
||||
data class NavigateToGeneratorModal(
|
||||
val generatorMode: GeneratorMode.Modal,
|
||||
) : VaultAddEditEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -53,6 +53,7 @@ class GeneratorScreenTest : BaseComposeTest() {
|
|||
GeneratorScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateToPasswordHistory = { onNavigateToPasswordHistoryScreenCalled = true },
|
||||
onNavigateBack = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFl
|
|||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.FakePermissionManager
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
|
||||
import com.x8bit.bitwarden.ui.util.isProgressBar
|
||||
import com.x8bit.bitwarden.ui.util.onAllNodesWithContentDescriptionAfterScroll
|
||||
import com.x8bit.bitwarden.ui.util.onAllNodesWithTextAfterScroll
|
||||
|
@ -46,6 +47,7 @@ import io.mockk.mockk
|
|||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
@ -56,6 +58,7 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
|||
private var onNavigateBackCalled = false
|
||||
private var onNavigateQrCodeScanScreenCalled = false
|
||||
private var onNavigateToManualCodeEntryScreenCalled = false
|
||||
private var onNavigateToGeneratorModalType: GeneratorMode.Modal? = null
|
||||
|
||||
private val mutableEventFlow = bufferedMutableSharedFlow<VaultAddEditEvent>()
|
||||
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE_LOGIN)
|
||||
|
@ -78,6 +81,7 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
|||
onNavigateToManualCodeEntryScreen = {
|
||||
onNavigateToManualCodeEntryScreenCalled = true
|
||||
},
|
||||
onNavigateToGeneratorModal = { onNavigateToGeneratorModalType = it },
|
||||
viewModel = viewModel,
|
||||
permissionsManager = fakePermissionManager,
|
||||
)
|
||||
|
@ -104,6 +108,28 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
|||
assertTrue(onNavigateToManualCodeEntryScreenCalled)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `on NavigateToGeneratorModal event in password mode should invoke NavigateToGeneratorModal with Password Generator Mode `() {
|
||||
mutableEventFlow.tryEmit(
|
||||
VaultAddEditEvent.NavigateToGeneratorModal(
|
||||
generatorMode = GeneratorMode.Modal.Password,
|
||||
),
|
||||
)
|
||||
assertEquals(GeneratorMode.Modal.Password, onNavigateToGeneratorModalType)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `on NavigateToGeneratorModal event in username mode should invoke NavigateToGeneratorModal with Username Generator Mode `() {
|
||||
mutableEventFlow.tryEmit(
|
||||
VaultAddEditEvent.NavigateToGeneratorModal(
|
||||
generatorMode = GeneratorMode.Modal.Username,
|
||||
),
|
||||
)
|
||||
assertEquals(GeneratorMode.Modal.Username, onNavigateToGeneratorModalType)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking close button should send CloseClick action`() {
|
||||
composeTestRule
|
||||
|
|
Loading…
Add table
Reference in a new issue