mirror of
https://github.com/bitwarden/android.git
synced 2026-08-01 03:05:47 -05:00
BIT-457: Add Vault Settings and Folders screen UI (#457)
This commit is contained in:
committed by
Álison Fernandes
parent
78461394f3
commit
c94b303abc
+6
-2
@@ -27,6 +27,7 @@ private const val SETTINGS_ROUTE: String = "settings"
|
||||
fun NavGraphBuilder.settingsGraph(
|
||||
navController: NavController,
|
||||
onNavigateToDeleteAccount: () -> Unit,
|
||||
onNavigateToFolders: () -> Unit,
|
||||
) {
|
||||
navigation(
|
||||
startDestination = SETTINGS_ROUTE,
|
||||
@@ -52,12 +53,15 @@ fun NavGraphBuilder.settingsGraph(
|
||||
appearanceDestination(onNavigateBack = { navController.popBackStack() })
|
||||
autoFillDestination(onNavigateBack = { navController.popBackStack() })
|
||||
otherDestination(onNavigateBack = { navController.popBackStack() })
|
||||
vaultSettingsDestination(onNavigateBack = { navController.popBackStack() })
|
||||
vaultSettingsDestination(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
onNavigateToFolders = onNavigateToFolders,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the settings screen screen.
|
||||
* Navigate to the settings screen.
|
||||
*/
|
||||
fun NavController.navigateToSettingsGraph(navOptions: NavOptions? = null) {
|
||||
navigate(SETTINGS_GRAPH_ROUTE, navOptions)
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.folders
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
||||
|
||||
private const val FOLDERS_ROUTE = "settings_folders"
|
||||
|
||||
/**
|
||||
* Add folders destinations to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.foldersDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(
|
||||
route = FOLDERS_ROUTE,
|
||||
) {
|
||||
FoldersScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the folders screen.
|
||||
*/
|
||||
fun NavController.navigateToFolders(navOptions: NavOptions? = null) {
|
||||
navigate(FOLDERS_ROUTE, navOptions)
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.folders
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||
|
||||
/**
|
||||
* Displays the folders screen.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun FoldersScreen(
|
||||
onNavigateBack: () -> Unit,
|
||||
viewModel: FoldersViewModel = hiltViewModel(),
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
EventsEffect(viewModel = viewModel) { event ->
|
||||
when (event) {
|
||||
FoldersEvent.NavigateBack -> onNavigateBack()
|
||||
is FoldersEvent.ShowToast -> {
|
||||
Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
BitwardenScaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
BitwardenTopAppBar(
|
||||
title = stringResource(id = R.string.folders),
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigationIcon = painterResource(id = R.drawable.ic_close),
|
||||
navigationIconContentDescription = stringResource(id = R.string.close),
|
||||
onNavigationIconClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(FoldersAction.CloseButtonClick) }
|
||||
},
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(FoldersAction.AddFolderButtonClick) }
|
||||
},
|
||||
modifier = Modifier.navigationBarsPadding(),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_plus),
|
||||
contentDescription = stringResource(id = R.string.add_item),
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
)
|
||||
}
|
||||
},
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
// TODO BIT-460 populate Folders screen
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.no_folders_to_list),
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
vertical = 4.dp,
|
||||
horizontal = 16.dp,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.folders
|
||||
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* View model for the folders screen.
|
||||
*/
|
||||
@HiltViewModel
|
||||
class FoldersViewModel @Inject constructor() :
|
||||
BaseViewModel<Unit, FoldersEvent, FoldersAction>(
|
||||
initialState = Unit,
|
||||
) {
|
||||
override fun handleAction(action: FoldersAction): Unit = when (action) {
|
||||
FoldersAction.AddFolderButtonClick -> handleAddFolderButtonClicked()
|
||||
FoldersAction.CloseButtonClick -> handleCloseButtonClicked()
|
||||
}
|
||||
|
||||
private fun handleAddFolderButtonClicked() {
|
||||
// TODO BIT-458 implement add folders
|
||||
sendEvent(FoldersEvent.ShowToast("Not yet implemented."))
|
||||
}
|
||||
|
||||
private fun handleCloseButtonClicked() {
|
||||
sendEvent(FoldersEvent.NavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Models events for the folders screen.
|
||||
*/
|
||||
sealed class FoldersEvent {
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
data object NavigateBack : FoldersEvent()
|
||||
|
||||
/**
|
||||
* Shows a toast with the given [message].
|
||||
*/
|
||||
data class ShowToast(
|
||||
val message: String,
|
||||
) : FoldersEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
* Models actions for the folders screen.
|
||||
*/
|
||||
sealed class FoldersAction {
|
||||
/**
|
||||
* Indicates that the user clicked the add folder button.
|
||||
*/
|
||||
data object AddFolderButtonClick : FoldersAction()
|
||||
|
||||
/**
|
||||
* Indicates that the user clicked the close button.
|
||||
*/
|
||||
data object CloseButtonClick : FoldersAction()
|
||||
}
|
||||
+5
-1
@@ -12,11 +12,15 @@ private const val VAULT_SETTINGS_ROUTE = "vault_settings"
|
||||
*/
|
||||
fun NavGraphBuilder.vaultSettingsDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
onNavigateToFolders: () -> Unit,
|
||||
) {
|
||||
composableWithPushTransitions(
|
||||
route = VAULT_SETTINGS_ROUTE,
|
||||
) {
|
||||
VaultSettingsScreen(onNavigateBack = onNavigateBack)
|
||||
VaultSettingsScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
onNavigateToFolders = onNavigateToFolders,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+42
-3
@@ -1,7 +1,9 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
@@ -12,26 +14,36 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenExternalLinkRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||
|
||||
/**
|
||||
* Displays the vault screen.
|
||||
* Displays the vault settings screen.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun VaultSettingsScreen(
|
||||
onNavigateBack: () -> Unit,
|
||||
onNavigateToFolders: () -> Unit,
|
||||
viewModel: VaultSettingsViewModel = hiltViewModel(),
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
EventsEffect(viewModel = viewModel) { event ->
|
||||
when (event) {
|
||||
VaultSettingsEvent.NavigateBack -> onNavigateBack.invoke()
|
||||
VaultSettingsEvent.NavigateBack -> onNavigateBack()
|
||||
VaultSettingsEvent.NavigateToFolders -> onNavigateToFolders()
|
||||
is VaultSettingsEvent.ShowToast -> {
|
||||
Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +70,34 @@ fun VaultSettingsScreen(
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
// TODO: BIT-928 Display Vault UI
|
||||
BitwardenTextRow(
|
||||
text = stringResource(R.string.folders),
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(VaultSettingsAction.FoldersButtonClick) }
|
||||
},
|
||||
withDivider = true,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
|
||||
BitwardenTextRow(
|
||||
text = stringResource(R.string.export_vault),
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(VaultSettingsAction.ExportVaultClick) }
|
||||
},
|
||||
withDivider = true,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
|
||||
BitwardenExternalLinkRow(
|
||||
text = stringResource(R.string.import_items),
|
||||
onConfirmClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(VaultSettingsAction.ImportItemsClick) }
|
||||
},
|
||||
withDivider = true,
|
||||
dialogTitle = stringResource(id = R.string.import_items_confirmation),
|
||||
dialogMessage = stringResource(id = R.string.import_items_description),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+49
-1
@@ -13,7 +13,28 @@ class VaultSettingsViewModel @Inject constructor() :
|
||||
initialState = Unit,
|
||||
) {
|
||||
override fun handleAction(action: VaultSettingsAction): Unit = when (action) {
|
||||
VaultSettingsAction.BackClick -> sendEvent(VaultSettingsEvent.NavigateBack)
|
||||
VaultSettingsAction.BackClick -> handleBackClicked()
|
||||
VaultSettingsAction.ExportVaultClick -> handleExportVaultClicked()
|
||||
VaultSettingsAction.FoldersButtonClick -> handleFoldersButtonClicked()
|
||||
VaultSettingsAction.ImportItemsClick -> handleImportItemsClicked()
|
||||
}
|
||||
|
||||
private fun handleBackClicked() {
|
||||
sendEvent(VaultSettingsEvent.NavigateBack)
|
||||
}
|
||||
|
||||
private fun handleExportVaultClicked() {
|
||||
// TODO BIT-1272 go to vault export screen
|
||||
sendEvent(VaultSettingsEvent.ShowToast("Not yet implemented."))
|
||||
}
|
||||
|
||||
private fun handleFoldersButtonClicked() {
|
||||
sendEvent(VaultSettingsEvent.NavigateToFolders)
|
||||
}
|
||||
|
||||
private fun handleImportItemsClicked() {
|
||||
// TODO BIT-972 implement import items functionality
|
||||
sendEvent(VaultSettingsEvent.ShowToast("Not yet implemented."))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +46,18 @@ sealed class VaultSettingsEvent {
|
||||
* Navigate back.
|
||||
*/
|
||||
data object NavigateBack : VaultSettingsEvent()
|
||||
|
||||
/**
|
||||
* Navigate to the Folders screen.
|
||||
*/
|
||||
data object NavigateToFolders : VaultSettingsEvent()
|
||||
|
||||
/**
|
||||
* Shows a toast with the given [message].
|
||||
*/
|
||||
data class ShowToast(
|
||||
val message: String,
|
||||
) : VaultSettingsEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,4 +68,19 @@ sealed class VaultSettingsAction {
|
||||
* User clicked back button.
|
||||
*/
|
||||
data object BackClick : VaultSettingsAction()
|
||||
|
||||
/**
|
||||
* Indicates that the user clicked the Export Vault button.
|
||||
*/
|
||||
data object ExportVaultClick : VaultSettingsAction()
|
||||
|
||||
/**
|
||||
* Indicates that the user clicked the Folders button.
|
||||
*/
|
||||
data object FoldersButtonClick : VaultSettingsAction()
|
||||
|
||||
/**
|
||||
* Indicates that the user clicked the Import Items button.
|
||||
*/
|
||||
data object ImportItemsClick : VaultSettingsAction()
|
||||
}
|
||||
|
||||
+4
@@ -6,6 +6,8 @@ import androidx.navigation.NavOptions
|
||||
import androidx.navigation.navigation
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.deleteaccount.deleteAccountDestination
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.deleteaccount.navigateToDeleteAccount
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.folders.foldersDestination
|
||||
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.passwordhistory.navigateToPasswordHistory
|
||||
@@ -38,6 +40,7 @@ fun NavGraphBuilder.vaultUnlockedGraph(
|
||||
route = VAULT_UNLOCKED_GRAPH_ROUTE,
|
||||
) {
|
||||
vaultUnlockedNavBarDestination(
|
||||
onNavigateToFolders = { navController.navigateToFolders() },
|
||||
onNavigateToVaultAddItem = {
|
||||
navController.navigateToVaultAddEditItem(VaultAddEditType.AddItem)
|
||||
},
|
||||
@@ -59,5 +62,6 @@ fun NavGraphBuilder.vaultUnlockedGraph(
|
||||
)
|
||||
newSendDestination(onNavigateBack = { navController.popBackStack() })
|
||||
passwordHistoryDestination(onNavigateBack = { navController.popBackStack() })
|
||||
foldersDestination(onNavigateBack = { navController.popBackStack() })
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -27,6 +27,7 @@ fun NavGraphBuilder.vaultUnlockedNavBarDestination(
|
||||
onNavigateToVaultEditItem: (vaultItemId: String) -> Unit,
|
||||
onNavigateToNewSend: () -> Unit,
|
||||
onNavigateToDeleteAccount: () -> Unit,
|
||||
onNavigateToFolders: () -> Unit,
|
||||
onNavigateToPasswordHistory: () -> Unit,
|
||||
) {
|
||||
composableWithStayTransitions(
|
||||
@@ -38,6 +39,7 @@ fun NavGraphBuilder.vaultUnlockedNavBarDestination(
|
||||
onNavigateToVaultEditItem = onNavigateToVaultEditItem,
|
||||
onNavigateToNewSend = onNavigateToNewSend,
|
||||
onNavigateToDeleteAccount = onNavigateToDeleteAccount,
|
||||
onNavigateToFolders = onNavigateToFolders,
|
||||
onNavigateToPasswordHistory = onNavigateToPasswordHistory,
|
||||
)
|
||||
}
|
||||
|
||||
+5
@@ -62,6 +62,7 @@ import kotlinx.parcelize.Parcelize
|
||||
/**
|
||||
* Top level composable for the Vault Unlocked Screen.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
fun VaultUnlockedNavBarScreen(
|
||||
viewModel: VaultUnlockedNavBarViewModel = hiltViewModel(),
|
||||
@@ -71,6 +72,7 @@ fun VaultUnlockedNavBarScreen(
|
||||
onNavigateToVaultEditItem: (vaultItemId: String) -> Unit,
|
||||
onNavigateToNewSend: () -> Unit,
|
||||
onNavigateToDeleteAccount: () -> Unit,
|
||||
onNavigateToFolders: () -> Unit,
|
||||
onNavigateToPasswordHistory: () -> Unit,
|
||||
) {
|
||||
EventsEffect(viewModel = viewModel) { event ->
|
||||
@@ -102,6 +104,7 @@ fun VaultUnlockedNavBarScreen(
|
||||
navigateToVaultAddItem = onNavigateToVaultAddItem,
|
||||
navigateToNewSend = onNavigateToNewSend,
|
||||
navigateToDeleteAccount = onNavigateToDeleteAccount,
|
||||
navigateToFolders = onNavigateToFolders,
|
||||
navigateToPasswordHistory = onNavigateToPasswordHistory,
|
||||
generatorTabClickedAction = {
|
||||
viewModel.trySendAction(VaultUnlockedNavBarAction.GeneratorTabClick)
|
||||
@@ -134,6 +137,7 @@ private fun VaultUnlockedNavBarScaffold(
|
||||
onNavigateToVaultEditItem: (vaultItemId: String) -> Unit,
|
||||
navigateToNewSend: () -> Unit,
|
||||
navigateToDeleteAccount: () -> Unit,
|
||||
navigateToFolders: () -> Unit,
|
||||
navigateToPasswordHistory: () -> Unit,
|
||||
) {
|
||||
var shouldDimNavBar by remember { mutableStateOf(false) }
|
||||
@@ -200,6 +204,7 @@ private fun VaultUnlockedNavBarScaffold(
|
||||
settingsGraph(
|
||||
navController = navController,
|
||||
onNavigateToDeleteAccount = navigateToDeleteAccount,
|
||||
onNavigateToFolders = navigateToFolders,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user