mirror of
https://github.com/bitwarden/android.git
synced 2026-06-08 08:06:32 -05:00
BIT-1575: Collection Screen (#878)
This commit is contained in:
committed by
Álison Fernandes
parent
96401aba79
commit
034284fad4
@@ -108,8 +108,11 @@ fun NavGraphBuilder.vaultUnlockedGraph(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
onNavigateToGeneratorModal = { navController.navigateToGeneratorModal(mode = it) },
|
||||
onNavigateToAttachments = { navController.navigateToAttachment(it) },
|
||||
onNavigateToMoveToOrganization = {
|
||||
navController.navigateToVaultMoveToOrganization(it)
|
||||
onNavigateToMoveToOrganization = { vaultItemId, showOnlyCollections ->
|
||||
navController.navigateToVaultMoveToOrganization(
|
||||
vaultItemId = vaultItemId,
|
||||
showOnlyCollections = showOnlyCollections,
|
||||
)
|
||||
},
|
||||
)
|
||||
vaultMoveToOrganizationDestination(
|
||||
@@ -126,8 +129,11 @@ fun NavGraphBuilder.vaultUnlockedGraph(
|
||||
},
|
||||
)
|
||||
},
|
||||
onNavigateToMoveToOrganization = {
|
||||
navController.navigateToVaultMoveToOrganization(it)
|
||||
onNavigateToMoveToOrganization = { vaultItemId, showOnlyCollections ->
|
||||
navController.navigateToVaultMoveToOrganization(
|
||||
vaultItemId = vaultItemId,
|
||||
showOnlyCollections = showOnlyCollections,
|
||||
)
|
||||
},
|
||||
onNavigateToAttachments = { navController.navigateToAttachment(it) },
|
||||
)
|
||||
|
||||
@@ -24,15 +24,19 @@ import com.x8bit.bitwarden.ui.vault.model.VaultCollection
|
||||
fun LazyListScope.collectionItemsSelector(
|
||||
collectionList: List<VaultCollection>?,
|
||||
onCollectionSelect: (VaultCollection) -> Unit,
|
||||
isCollectionsTitleVisible: Boolean = true,
|
||||
) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.collections),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
||||
if (isCollectionsTitleVisible) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.collections),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (collectionList?.isNotEmpty() == true) {
|
||||
@@ -54,14 +58,14 @@ fun LazyListScope.collectionItemsSelector(
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.no_collections_to_list),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ fun NavGraphBuilder.vaultAddEditDestination(
|
||||
onNavigateToQrCodeScanScreen: () -> Unit,
|
||||
onNavigateToGeneratorModal: (GeneratorMode.Modal) -> Unit,
|
||||
onNavigateToAttachments: (cipherId: String) -> Unit,
|
||||
onNavigateToMoveToOrganization: (cipherId: String) -> Unit,
|
||||
onNavigateToMoveToOrganization: (cipherId: String, showOnlyCollections: Boolean) -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(
|
||||
route = ADD_EDIT_ITEM_ROUTE,
|
||||
|
||||
@@ -57,7 +57,7 @@ fun VaultAddEditScreen(
|
||||
onNavigateToManualCodeEntryScreen: () -> Unit,
|
||||
onNavigateToGeneratorModal: (GeneratorMode.Modal) -> Unit,
|
||||
onNavigateToAttachments: (cipherId: String) -> Unit,
|
||||
onNavigateToMoveToOrganization: (cipherId: String) -> Unit,
|
||||
onNavigateToMoveToOrganization: (cipherId: String, showOnlyCollections: Boolean) -> Unit,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
val context = LocalContext.current
|
||||
@@ -83,12 +83,11 @@ fun VaultAddEditScreen(
|
||||
|
||||
is VaultAddEditEvent.NavigateToAttachments -> onNavigateToAttachments(event.cipherId)
|
||||
is VaultAddEditEvent.NavigateToMoveToOrganization -> {
|
||||
onNavigateToMoveToOrganization(event.cipherId)
|
||||
onNavigateToMoveToOrganization(event.cipherId, false)
|
||||
}
|
||||
|
||||
is VaultAddEditEvent.NavigateToCollections -> {
|
||||
// TODO implement Collections in BIT-1575
|
||||
Toast.makeText(context, "Not yet implemented.", Toast.LENGTH_SHORT).show()
|
||||
onNavigateToMoveToOrganization(event.cipherId, true)
|
||||
}
|
||||
|
||||
VaultAddEditEvent.NavigateBack -> onNavigateBack.invoke()
|
||||
|
||||
@@ -29,7 +29,7 @@ data class VaultItemArgs(val vaultItemId: String) {
|
||||
fun NavGraphBuilder.vaultItemDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
onNavigateToVaultEditItem: (vaultItemId: String, isClone: Boolean) -> Unit,
|
||||
onNavigateToMoveToOrganization: (vaultItemId: String) -> Unit,
|
||||
onNavigateToMoveToOrganization: (vaultItemId: String, showOnlyCollections: Boolean) -> Unit,
|
||||
onNavigateToAttachments: (vaultItemId: String) -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(
|
||||
|
||||
@@ -61,7 +61,7 @@ fun VaultItemScreen(
|
||||
intentManager: IntentManager = LocalIntentManager.current,
|
||||
onNavigateBack: () -> Unit,
|
||||
onNavigateToVaultAddEditItem: (vaultItemId: String, isClone: Boolean) -> Unit,
|
||||
onNavigateToMoveToOrganization: (vaultItemId: String) -> Unit,
|
||||
onNavigateToMoveToOrganization: (vaultItemId: String, showOnlyCollections: Boolean) -> Unit,
|
||||
onNavigateToAttachments: (vaultItemId: String) -> Unit,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
@@ -94,12 +94,11 @@ fun VaultItemScreen(
|
||||
is VaultItemEvent.NavigateToAttachments -> onNavigateToAttachments(event.itemId)
|
||||
|
||||
is VaultItemEvent.NavigateToMoveToOrganization -> {
|
||||
onNavigateToMoveToOrganization(event.itemId)
|
||||
onNavigateToMoveToOrganization(event.itemId, false)
|
||||
}
|
||||
|
||||
is VaultItemEvent.NavigateToCollections -> {
|
||||
// TODO implement Collections in BIT-1575
|
||||
Toast.makeText(context, "Not yet implemented.", Toast.LENGTH_SHORT).show()
|
||||
onNavigateToMoveToOrganization(event.itemId, true)
|
||||
}
|
||||
|
||||
is VaultItemEvent.ShowToast -> {
|
||||
|
||||
@@ -27,6 +27,7 @@ import kotlinx.collections.immutable.toImmutableList
|
||||
@Composable
|
||||
fun VaultMoveToOrganizationContent(
|
||||
state: VaultMoveToOrganizationState.ViewState.Content,
|
||||
showOnlyCollections: Boolean,
|
||||
organizationSelect: (VaultMoveToOrganizationState.ViewState.Content.Organization) -> Unit,
|
||||
collectionSelect: (VaultCollection) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -34,44 +35,47 @@ fun VaultMoveToOrganizationContent(
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.organization),
|
||||
options = state
|
||||
.organizations
|
||||
.map { it.name }
|
||||
.toImmutableList(),
|
||||
selectedOption = state.selectedOrganization.name,
|
||||
onOptionSelected = { selectedString ->
|
||||
organizationSelect(
|
||||
state
|
||||
.organizations
|
||||
.first { it.name == selectedString },
|
||||
)
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.move_to_org_desc),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
fontSize = 12.sp,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 32.dp),
|
||||
if (!showOnlyCollections) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.organization),
|
||||
options = state
|
||||
.organizations
|
||||
.map { it.name }
|
||||
.toImmutableList(),
|
||||
selectedOption = state.selectedOrganization.name,
|
||||
onOptionSelected = { selectedString ->
|
||||
organizationSelect(
|
||||
state
|
||||
.organizations
|
||||
.first { it.name == selectedString },
|
||||
)
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.move_to_org_desc),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
fontSize = 12.sp,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 32.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collectionItemsSelector(
|
||||
collectionList = state.selectedOrganization.collections,
|
||||
onCollectionSelect = collectionSelect,
|
||||
isCollectionsTitleVisible = !showOnlyCollections,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,16 +11,26 @@ import com.x8bit.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
||||
|
||||
private const val VAULT_MOVE_TO_ORGANIZATION_PREFIX = "vault_move_to_organization"
|
||||
private const val VAULT_MOVE_TO_ORGANIZATION_ID = "vault_move_to_organization_id"
|
||||
private const val VAULT_MOVE_TO_ORGANIZATION_ONLY_COLLECTIONS =
|
||||
"vault_move_to_organization_only_collections"
|
||||
private const val VAULT_MOVE_TO_ORGANIZATION_ROUTE =
|
||||
"$VAULT_MOVE_TO_ORGANIZATION_PREFIX/{$VAULT_MOVE_TO_ORGANIZATION_ID}"
|
||||
VAULT_MOVE_TO_ORGANIZATION_PREFIX +
|
||||
"/{$VAULT_MOVE_TO_ORGANIZATION_ID}" +
|
||||
"/{$VAULT_MOVE_TO_ORGANIZATION_ONLY_COLLECTIONS}"
|
||||
|
||||
/**
|
||||
* Class to retrieve vault move to organization arguments from the [SavedStateHandle].
|
||||
*/
|
||||
@OmitFromCoverage
|
||||
data class VaultMoveToOrganizationArgs(val vaultItemId: String) {
|
||||
data class VaultMoveToOrganizationArgs(
|
||||
val vaultItemId: String,
|
||||
val showOnlyCollections: Boolean,
|
||||
) {
|
||||
constructor(savedStateHandle: SavedStateHandle) : this(
|
||||
checkNotNull(savedStateHandle[VAULT_MOVE_TO_ORGANIZATION_ID]) as String,
|
||||
vaultItemId = checkNotNull(savedStateHandle[VAULT_MOVE_TO_ORGANIZATION_ID]) as String,
|
||||
showOnlyCollections =
|
||||
(checkNotNull(savedStateHandle[VAULT_MOVE_TO_ORGANIZATION_ONLY_COLLECTIONS]) as String)
|
||||
.toBoolean(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -34,6 +44,9 @@ fun NavGraphBuilder.vaultMoveToOrganizationDestination(
|
||||
route = VAULT_MOVE_TO_ORGANIZATION_ROUTE,
|
||||
arguments = listOf(
|
||||
navArgument(VAULT_MOVE_TO_ORGANIZATION_ID) { type = NavType.StringType },
|
||||
navArgument(VAULT_MOVE_TO_ORGANIZATION_ONLY_COLLECTIONS) {
|
||||
type = NavType.StringType
|
||||
},
|
||||
),
|
||||
) {
|
||||
VaultMoveToOrganizationScreen(
|
||||
@@ -47,10 +60,11 @@ fun NavGraphBuilder.vaultMoveToOrganizationDestination(
|
||||
*/
|
||||
fun NavController.navigateToVaultMoveToOrganization(
|
||||
vaultItemId: String,
|
||||
showOnlyCollections: Boolean,
|
||||
navOptions: NavOptions? = null,
|
||||
) {
|
||||
navigate(
|
||||
route = "$VAULT_MOVE_TO_ORGANIZATION_PREFIX/$vaultItemId",
|
||||
route = "$VAULT_MOVE_TO_ORGANIZATION_PREFIX/$vaultItemId/$showOnlyCollections",
|
||||
navOptions = navOptions,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -109,14 +109,14 @@ private fun VaultMoveToOrganizationScaffold(
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
BitwardenTopAppBar(
|
||||
title = stringResource(id = R.string.move_to_organization),
|
||||
title = state.appBarText(),
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigationIcon = painterResource(id = R.drawable.ic_close),
|
||||
navigationIconContentDescription = stringResource(id = R.string.close),
|
||||
onNavigationIconClick = closeClick,
|
||||
actions = {
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = R.string.move),
|
||||
label = state.appBarButtonText(),
|
||||
onClick = moveClick,
|
||||
isEnabled = state.viewState is
|
||||
VaultMoveToOrganizationState.ViewState.Content,
|
||||
@@ -134,6 +134,7 @@ private fun VaultMoveToOrganizationScaffold(
|
||||
is VaultMoveToOrganizationState.ViewState.Content -> {
|
||||
VaultMoveToOrganizationContent(
|
||||
state = state.viewState,
|
||||
showOnlyCollections = state.onlyShowCollections,
|
||||
organizationSelect = organizationSelect,
|
||||
collectionSelect = collectionSelect,
|
||||
modifier = modifier,
|
||||
|
||||
@@ -45,6 +45,7 @@ class VaultMoveToOrganizationViewModel @Inject constructor(
|
||||
?: run {
|
||||
VaultMoveToOrganizationState(
|
||||
vaultItemId = VaultMoveToOrganizationArgs(savedStateHandle).vaultItemId,
|
||||
onlyShowCollections = VaultMoveToOrganizationArgs(savedStateHandle).showOnlyCollections,
|
||||
viewState = VaultMoveToOrganizationState.ViewState.Loading,
|
||||
dialogState = null,
|
||||
)
|
||||
@@ -306,10 +307,25 @@ class VaultMoveToOrganizationViewModel @Inject constructor(
|
||||
@Parcelize
|
||||
data class VaultMoveToOrganizationState(
|
||||
val vaultItemId: String,
|
||||
val onlyShowCollections: Boolean,
|
||||
val viewState: ViewState,
|
||||
val dialogState: DialogState?,
|
||||
) : Parcelable {
|
||||
|
||||
val appBarText: Text
|
||||
get() = if (onlyShowCollections) {
|
||||
R.string.collections.asText()
|
||||
} else {
|
||||
R.string.move_to_organization.asText()
|
||||
}
|
||||
|
||||
val appBarButtonText: Text
|
||||
get() = if (onlyShowCollections) {
|
||||
R.string.save.asText()
|
||||
} else {
|
||||
R.string.move.asText()
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the current state of any dialogs on the screen.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user