PM-17841: Hide additional options behind expandable section (#4687)

This commit is contained in:
David Perez
2025-02-04 16:43:27 -06:00
committed by GitHub
parent 1710b563c0
commit c3506c1c25
12 changed files with 384 additions and 583 deletions

View File

@@ -0,0 +1,64 @@
package com.x8bit.bitwarden.ui.platform.components.header
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
/**
* Reusable header element that is clickable for expanding or collapsing content.
*/
@Composable
fun BitwardenExpandingHeader(
isExpanded: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
title: String = stringResource(id = R.string.additional_options),
) {
Row(
modifier = modifier
.clip(shape = BitwardenTheme.shapes.content)
.clickable(
onClickLabel = stringResource(
id = if (isExpanded) R.string.options_expanded else R.string.options_collapsed,
),
onClick = onClick,
)
.minimumInteractiveComponentSize()
.padding(top = 16.dp, bottom = 8.dp)
.padding(horizontal = 16.dp)
.semantics(mergeDescendants = true) {},
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = title,
color = BitwardenTheme.colorScheme.text.interaction,
style = BitwardenTheme.typography.labelLarge,
modifier = Modifier.padding(end = 8.dp),
)
val iconRotationDegrees = animateFloatAsState(
targetValue = if (isExpanded) 0f else 180f,
label = "expanderIconRotationAnimation",
)
Icon(
painter = rememberVectorPainter(id = R.drawable.ic_chevron_up_small),
contentDescription = null,
tint = BitwardenTheme.colorScheme.icon.secondary,
modifier = Modifier.rotate(degrees = iconRotationDegrees.value),
)
}
}

View File

@@ -6,7 +6,6 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -18,9 +17,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -31,7 +28,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.base.util.asText
@@ -43,11 +39,11 @@ import com.x8bit.bitwarden.ui.platform.components.card.BitwardenInfoCalloutCard
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenExpandingHeader
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
import com.x8bit.bitwarden.ui.platform.components.stepper.BitwardenStepper
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.handlers.AddSendHandlers
@@ -324,43 +320,14 @@ private fun AddSendOptions(
addSendHandlers: AddSendHandlers,
) {
var isExpanded by rememberSaveable { mutableStateOf(false) }
Row(
BitwardenExpandingHeader(
isExpanded = isExpanded,
onClick = { isExpanded = !isExpanded },
modifier = Modifier
.testTag("SendShowHideOptionsButton")
.fillMaxWidth()
.clickable(
onClickLabel = if (isExpanded) {
stringResource(id = R.string.options_expanded)
} else {
stringResource(id = R.string.options_collapsed)
},
onClick = { isExpanded = !isExpanded },
)
.minimumInteractiveComponentSize()
.padding(top = 16.dp, bottom = 8.dp)
.testTag(tag = "SendShowHideOptionsButton")
.standardHorizontalMargin()
.padding(horizontal = 16.dp)
.semantics(mergeDescendants = true) {},
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = stringResource(id = R.string.additional_options),
color = BitwardenTheme.colorScheme.text.interaction,
style = BitwardenTheme.typography.labelLarge,
modifier = Modifier.padding(end = 8.dp),
)
Icon(
painter = rememberVectorPainter(
if (isExpanded) {
R.drawable.ic_chevron_up_small
} else {
R.drawable.ic_chevron_down_small
},
),
contentDescription = null,
tint = BitwardenTheme.colorScheme.icon.secondary,
)
}
.fillMaxWidth(),
)
// Hide all content if not expanded:
AnimatedVisibility(
visible = isExpanded,

View File

@@ -0,0 +1,164 @@
package com.x8bit.bitwarden.ui.vault.feature.addedit
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenExpandingHeader
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
import com.x8bit.bitwarden.ui.platform.util.persistentListOfNotNull
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCommonHandlers
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
/**
* The collapsable UI for additional options when adding or editing a cipher.
*/
@Suppress("LongMethod")
fun LazyListScope.vaultAddEditAdditionalOptions(
itemType: VaultAddEditState.ViewState.Content.ItemType,
commonState: VaultAddEditState.ViewState.Content.Common,
commonTypeHandlers: VaultAddEditCommonHandlers,
isAdditionalOptionsExpanded: Boolean,
onAdditionalOptionsClick: () -> Unit,
) {
item {
BitwardenExpandingHeader(
isExpanded = isAdditionalOptionsExpanded,
onClick = onAdditionalOptionsClick,
modifier = Modifier
.standardHorizontalMargin()
.fillMaxWidth(),
)
}
if (isAdditionalOptionsExpanded) {
val isNotes = itemType is VaultAddEditState.ViewState.Content.ItemType.SecureNotes
if (!isNotes) {
item(key = "optionalNotes") {
BitwardenTextField(
singleLine = false,
label = stringResource(id = R.string.notes),
value = commonState.notes,
onValueChange = commonTypeHandlers.onNotesTextChange,
textFieldTestTag = "ItemNotesEntry",
cardStyle = CardStyle.Full,
modifier = Modifier
.animateItem()
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
}
if (commonState.isUnlockWithPasswordEnabled) {
item(key = "MasterPasswordRepromptToggle") {
Column(
modifier = Modifier
.animateItem()
.fillMaxWidth()
.standardHorizontalMargin(),
) {
if (!isNotes) {
Spacer(modifier = Modifier.height(height = 8.dp))
}
BitwardenSwitch(
label = stringResource(id = R.string.password_prompt),
isChecked = commonState.masterPasswordReprompt,
onCheckedChange = commonTypeHandlers.onToggleMasterPasswordReprompt,
actions = {
BitwardenStandardIconButton(
vectorIconRes = R.drawable.ic_question_circle_small,
contentDescription = stringResource(
id = R.string.master_password_re_prompt_help,
),
onClick = commonTypeHandlers.onTooltipClick,
contentColor = BitwardenTheme.colorScheme.icon.secondary,
)
},
cardStyle = CardStyle.Full,
modifier = Modifier
.testTag(tag = "MasterPasswordRepromptToggle")
.fillMaxWidth(),
)
}
}
}
item(key = "customFieldsHeader") {
Column(
modifier = Modifier
.animateItem()
.fillMaxWidth()
.standardHorizontalMargin(),
) {
Spacer(modifier = Modifier.height(height = 16.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.custom_fields),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
}
items(
items = commonState.customFieldData,
key = { "customField_${it.itemId}" },
) { customItem ->
Column(
modifier = Modifier
.animateItem()
.fillMaxWidth()
.standardHorizontalMargin(),
) {
Spacer(modifier = Modifier.height(height = 8.dp))
VaultAddEditCustomField(
customField = customItem,
onCustomFieldValueChange = commonTypeHandlers.onCustomFieldValueChange,
onCustomFieldAction = commonTypeHandlers.onCustomFieldActionSelect,
onHiddenVisibilityChanged = commonTypeHandlers.onHiddenFieldVisibilityChange,
supportedLinkedTypes = itemType.vaultLinkedFieldTypes,
cardStyle = CardStyle.Full,
modifier = Modifier.fillMaxWidth(),
)
}
}
item(key = "addCustomFieldButton") {
Column(
modifier = Modifier
.animateItem()
.fillMaxWidth()
.standardHorizontalMargin(),
) {
Spacer(modifier = Modifier.height(height = 16.dp))
VaultAddEditCustomFieldsButton(
onFinishNamingClick = commonTypeHandlers.onAddNewCustomFieldClick,
options = persistentListOfNotNull(
CustomFieldType.TEXT,
CustomFieldType.HIDDEN,
CustomFieldType.BOOLEAN,
CustomFieldType.LINKED.takeIf {
itemType.vaultLinkedFieldTypes.isNotEmpty()
},
),
modifier = Modifier.fillMaxWidth(),
)
}
}
}
}

View File

@@ -3,9 +3,7 @@ package com.x8bit.bitwarden.ui.vault.feature.addedit
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
@@ -18,21 +16,14 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
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.model.VaultCardBrand
import com.x8bit.bitwarden.ui.vault.model.VaultCardExpirationMonth
import com.x8bit.bitwarden.ui.vault.model.VaultLinkedFieldType
import com.x8bit.bitwarden.ui.vault.util.longName
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
/**
@@ -40,9 +31,7 @@ import kotlinx.collections.immutable.toImmutableList
*/
@Suppress("LongMethod")
fun LazyListScope.vaultAddEditCardItems(
commonState: VaultAddEditState.ViewState.Content.Common,
cardState: VaultAddEditState.ViewState.Content.ItemType.Card,
commonHandlers: VaultAddEditCommonHandlers,
cardHandlers: VaultAddEditCardTypeHandlers,
) {
item {
@@ -156,97 +145,4 @@ fun LazyListScope.vaultAddEditCardItems(
.standardHorizontalMargin(),
)
}
item {
Spacer(modifier = Modifier.height(height = 16.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.miscellaneous),
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(height = 8.dp))
}
item {
BitwardenTextField(
singleLine = false,
label = stringResource(id = R.string.notes),
value = commonState.notes,
onValueChange = commonHandlers.onNotesTextChange,
textFieldTestTag = "ItemNotesEntry",
cardStyle = CardStyle.Full,
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
if (commonState.isUnlockWithPasswordEnabled) {
item {
Spacer(modifier = Modifier.height(height = 8.dp))
BitwardenSwitch(
label = stringResource(id = R.string.password_prompt),
isChecked = commonState.masterPasswordReprompt,
onCheckedChange = commonHandlers.onToggleMasterPasswordReprompt,
actions = {
BitwardenStandardIconButton(
vectorIconRes = R.drawable.ic_question_circle,
contentDescription = stringResource(
id = R.string.master_password_re_prompt_help,
),
onClick = commonHandlers.onTooltipClick,
contentColor = BitwardenTheme.colorScheme.icon.secondary,
)
},
cardStyle = CardStyle.Full,
modifier = Modifier
.testTag("MasterPasswordRepromptToggle")
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
}
item {
Spacer(modifier = Modifier.height(height = 16.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.custom_fields),
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin()
.padding(horizontal = 16.dp),
)
}
items(commonState.customFieldData) { customItem ->
Spacer(modifier = Modifier.height(height = 8.dp))
VaultAddEditCustomField(
customField = customItem,
onCustomFieldValueChange = commonHandlers.onCustomFieldValueChange,
onCustomFieldAction = commonHandlers.onCustomFieldActionSelect,
supportedLinkedTypes = persistentListOf(
VaultLinkedFieldType.CARDHOLDER_NAME,
VaultLinkedFieldType.EXPIRATION_MONTH,
VaultLinkedFieldType.EXPIRATION_YEAR,
VaultLinkedFieldType.SECURITY_CODE,
VaultLinkedFieldType.BRAND,
VaultLinkedFieldType.NUMBER,
),
onHiddenVisibilityChanged = commonHandlers.onHiddenFieldVisibilityChange,
cardStyle = CardStyle.Full,
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
item {
Spacer(modifier = Modifier.height(16.dp))
VaultAddEditCustomFieldsButton(
onFinishNamingClick = commonHandlers.onAddNewCustomFieldClick,
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
}

View File

@@ -16,7 +16,6 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTextEntryDialo
import com.x8bit.bitwarden.ui.platform.components.dialog.row.BitwardenBasicDialogRow
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
/**
* A UI element that is used by the user to add a custom field item.
@@ -24,17 +23,11 @@ import kotlinx.collections.immutable.persistentListOf
* @param options The types that are to be chosen by the user.
* @param onFinishNamingClick Invoked when the user finishes naming the item.
*/
@Suppress("LongMethod")
@Composable
fun VaultAddEditCustomFieldsButton(
onFinishNamingClick: (CustomFieldType, String) -> Unit,
options: ImmutableList<CustomFieldType>,
modifier: Modifier = Modifier,
options: ImmutableList<CustomFieldType> = persistentListOf(
CustomFieldType.TEXT,
CustomFieldType.HIDDEN,
CustomFieldType.BOOLEAN,
CustomFieldType.LINKED,
),
) {
var shouldShowChooserDialog by remember { mutableStateOf(false) }
var shouldShowNameDialog by remember { mutableStateOf(false) }

View File

@@ -3,9 +3,7 @@ package com.x8bit.bitwarden.ui.vault.feature.addedit
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@@ -14,18 +12,11 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCommonHandlers
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditIdentityTypeHandlers
import com.x8bit.bitwarden.ui.vault.model.VaultIdentityTitle
import com.x8bit.bitwarden.ui.vault.model.VaultLinkedFieldType
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
/**
@@ -33,9 +24,7 @@ import kotlinx.collections.immutable.toImmutableList
*/
@Suppress("LongMethod")
fun LazyListScope.vaultAddEditIdentityItems(
commonState: VaultAddEditState.ViewState.Content.Common,
identityState: VaultAddEditState.ViewState.Content.ItemType.Identity,
commonTypeHandlers: VaultAddEditCommonHandlers,
identityItemTypeHandlers: VaultAddEditIdentityTypeHandlers,
) {
item {
@@ -253,112 +242,6 @@ fun LazyListScope.vaultAddEditIdentityItems(
.standardHorizontalMargin(),
)
}
item {
Spacer(modifier = Modifier.height(height = 16.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.miscellaneous),
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(height = 8.dp))
}
item {
BitwardenTextField(
singleLine = false,
label = stringResource(id = R.string.notes),
value = commonState.notes,
onValueChange = commonTypeHandlers.onNotesTextChange,
textFieldTestTag = "ItemNotesEntry",
cardStyle = CardStyle.Full,
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
if (commonState.isUnlockWithPasswordEnabled) {
item {
Spacer(modifier = Modifier.height(height = 8.dp))
BitwardenSwitch(
label = stringResource(id = R.string.password_prompt),
isChecked = commonState.masterPasswordReprompt,
onCheckedChange = commonTypeHandlers.onToggleMasterPasswordReprompt,
actions = {
BitwardenStandardIconButton(
vectorIconRes = R.drawable.ic_question_circle,
contentDescription = stringResource(
id = R.string.master_password_re_prompt_help,
),
onClick = commonTypeHandlers.onTooltipClick,
contentColor = BitwardenTheme.colorScheme.icon.secondary,
)
},
cardStyle = CardStyle.Full,
modifier = Modifier
.testTag("MasterPasswordRepromptToggle")
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
}
item {
Spacer(modifier = Modifier.height(height = 16.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.custom_fields),
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin()
.padding(horizontal = 16.dp),
)
}
items(commonState.customFieldData) { customItem ->
Spacer(modifier = Modifier.height(height = 8.dp))
VaultAddEditCustomField(
customField = customItem,
onCustomFieldValueChange = commonTypeHandlers.onCustomFieldValueChange,
onCustomFieldAction = commonTypeHandlers.onCustomFieldActionSelect,
supportedLinkedTypes = persistentListOf(
VaultLinkedFieldType.TITLE,
VaultLinkedFieldType.MIDDLE_NAME,
VaultLinkedFieldType.ADDRESS_1,
VaultLinkedFieldType.ADDRESS_2,
VaultLinkedFieldType.ADDRESS_3,
VaultLinkedFieldType.CITY,
VaultLinkedFieldType.STATE,
VaultLinkedFieldType.POSTAL_CODE,
VaultLinkedFieldType.COUNTRY,
VaultLinkedFieldType.COMPANY,
VaultLinkedFieldType.EMAIL,
VaultLinkedFieldType.PHONE,
VaultLinkedFieldType.SSN,
VaultLinkedFieldType.IDENTITY_USERNAME,
VaultLinkedFieldType.PASSPORT_NUMBER,
VaultLinkedFieldType.LICENSE_NUMBER,
VaultLinkedFieldType.FIRST_NAME,
VaultLinkedFieldType.LAST_NAME,
VaultLinkedFieldType.FULL_NAME,
),
onHiddenVisibilityChanged = commonTypeHandlers.onHiddenFieldVisibilityChange,
cardStyle = CardStyle.Full,
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
item {
Spacer(modifier = Modifier.height(16.dp))
VaultAddEditCustomFieldsButton(
onFinishNamingClick = commonTypeHandlers.onAddNewCustomFieldClick,
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
}
@Composable

View File

@@ -8,6 +8,8 @@ import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
@@ -68,6 +70,7 @@ fun CoachMarkScope<AddEditItemCoachMark>.VaultAddEditContent(
},
)
var isAdditionalOptionsExpanded = rememberSaveable { mutableStateOf(value = false) }
LazyColumn(modifier = modifier, state = lazyListState) {
item {
Spacer(modifier = Modifier.height(height = 12.dp))
@@ -203,9 +206,7 @@ fun CoachMarkScope<AddEditItemCoachMark>.VaultAddEditContent(
when (state.type) {
is VaultAddEditState.ViewState.Content.ItemType.Login -> {
vaultAddEditLoginItems(
commonState = state.common,
loginState = state.type,
commonActionHandler = commonTypeHandlers,
loginItemTypeHandlers = loginItemTypeHandlers,
onTotpSetupClick = {
if (permissionsManager.checkPermission(Manifest.permission.CAMERA)) {
@@ -224,18 +225,14 @@ fun CoachMarkScope<AddEditItemCoachMark>.VaultAddEditContent(
is VaultAddEditState.ViewState.Content.ItemType.Card -> {
vaultAddEditCardItems(
commonState = state.common,
cardState = state.type,
commonHandlers = commonTypeHandlers,
cardHandlers = cardItemTypeHandlers,
)
}
is VaultAddEditState.ViewState.Content.ItemType.Identity -> {
vaultAddEditIdentityItems(
commonState = state.common,
identityState = state.type,
commonTypeHandlers = commonTypeHandlers,
identityItemTypeHandlers = identityItemTypeHandlers,
)
}
@@ -249,14 +246,22 @@ fun CoachMarkScope<AddEditItemCoachMark>.VaultAddEditContent(
is VaultAddEditState.ViewState.Content.ItemType.SshKey -> {
vaultAddEditSshKeyItems(
commonState = state.common,
sshKeyState = state.type,
commonTypeHandlers = commonTypeHandlers,
sshKeyTypeHandlers = sshKeyItemTypeHandlers,
)
}
}
vaultAddEditAdditionalOptions(
itemType = state.type,
commonState = state.common,
commonTypeHandlers = commonTypeHandlers,
isAdditionalOptionsExpanded = isAdditionalOptionsExpanded.value,
onAdditionalOptionsClick = {
isAdditionalOptionsExpanded.value = !isAdditionalOptionsExpanded.value
},
)
item {
Spacer(modifier = Modifier.height(height = 16.dp))
Spacer(modifier = Modifier.navigationBarsPadding())

View File

@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -36,13 +35,9 @@ import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
import com.x8bit.bitwarden.ui.platform.components.text.BitwardenClickableText
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCommonHandlers
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditLoginTypeHandlers
import com.x8bit.bitwarden.ui.vault.model.VaultLinkedFieldType
import kotlinx.collections.immutable.persistentListOf
/**
* The UI for adding and editing a login cipher.
@@ -50,9 +45,7 @@ import kotlinx.collections.immutable.persistentListOf
@Suppress("LongMethod", "LongParameterList")
fun LazyListScope.vaultAddEditLoginItems(
coachMarkScope: CoachMarkScope<AddEditItemCoachMark>,
commonState: VaultAddEditState.ViewState.Content.Common,
loginState: VaultAddEditState.ViewState.Content.ItemType.Login,
commonActionHandler: VaultAddEditCommonHandlers,
loginItemTypeHandlers: VaultAddEditLoginTypeHandlers,
onTotpSetupClick: () -> Unit,
onNextCoachMark: () -> Unit,
@@ -201,97 +194,6 @@ fun LazyListScope.vaultAddEditLoginItems(
.fillMaxWidth(),
)
}
item {
Spacer(modifier = Modifier.height(height = 16.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.miscellaneous),
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(height = 8.dp))
}
item {
BitwardenTextField(
singleLine = false,
label = stringResource(id = R.string.notes),
value = commonState.notes,
onValueChange = commonActionHandler.onNotesTextChange,
textFieldTestTag = "ItemNotesEntry",
cardStyle = CardStyle.Full,
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
if (commonState.isUnlockWithPasswordEnabled) {
item {
Spacer(modifier = Modifier.height(height = 8.dp))
BitwardenSwitch(
label = stringResource(id = R.string.password_prompt),
isChecked = commonState.masterPasswordReprompt,
onCheckedChange = commonActionHandler.onToggleMasterPasswordReprompt,
actions = {
BitwardenStandardIconButton(
vectorIconRes = R.drawable.ic_question_circle_small,
contentDescription = stringResource(
id = R.string.master_password_re_prompt_help,
),
onClick = commonActionHandler.onTooltipClick,
contentColor = BitwardenTheme.colorScheme.icon.secondary,
)
},
cardStyle = CardStyle.Full,
modifier = Modifier
.testTag("MasterPasswordRepromptToggle")
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
}
item {
Spacer(modifier = Modifier.height(height = 16.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.custom_fields),
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin()
.padding(horizontal = 16.dp),
)
}
items(commonState.customFieldData) { customItem ->
Spacer(modifier = Modifier.height(height = 8.dp))
VaultAddEditCustomField(
customField = customItem,
onCustomFieldValueChange = commonActionHandler.onCustomFieldValueChange,
onCustomFieldAction = commonActionHandler.onCustomFieldActionSelect,
supportedLinkedTypes = persistentListOf(
VaultLinkedFieldType.PASSWORD,
VaultLinkedFieldType.USERNAME,
),
onHiddenVisibilityChanged = commonActionHandler.onHiddenFieldVisibilityChange,
cardStyle = CardStyle.Full,
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
item {
Spacer(modifier = Modifier.height(16.dp))
VaultAddEditCustomFieldsButton(
onFinishNamingClick = commonActionHandler.onAddNewCustomFieldClick,
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
}
@Composable

View File

@@ -3,29 +3,19 @@ package com.x8bit.bitwarden.ui.vault.feature.addedit
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCommonHandlers
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
import kotlinx.collections.immutable.persistentListOf
/**
* The UI for adding and editing a secure notes cipher.
*/
@Suppress("LongMethod")
fun LazyListScope.vaultAddEditSecureNotesItems(
commonState: VaultAddEditState.ViewState.Content.Common,
commonTypeHandlers: VaultAddEditCommonHandlers,
@@ -44,79 +34,4 @@ fun LazyListScope.vaultAddEditSecureNotesItems(
.standardHorizontalMargin(),
)
}
item {
Spacer(modifier = Modifier.height(height = 16.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.miscellaneous),
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin()
.padding(horizontal = 16.dp),
)
}
if (commonState.isUnlockWithPasswordEnabled) {
item {
Spacer(modifier = Modifier.height(height = 8.dp))
BitwardenSwitch(
label = stringResource(id = R.string.password_prompt),
isChecked = commonState.masterPasswordReprompt,
onCheckedChange = commonTypeHandlers.onToggleMasterPasswordReprompt,
actions = {
BitwardenStandardIconButton(
vectorIconRes = R.drawable.ic_question_circle,
contentDescription = stringResource(
id = R.string.master_password_re_prompt_help,
),
onClick = commonTypeHandlers.onTooltipClick,
contentColor = BitwardenTheme.colorScheme.icon.secondary,
)
},
cardStyle = CardStyle.Full,
modifier = Modifier
.testTag("MasterPasswordRepromptToggle")
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
}
item {
Spacer(modifier = Modifier.height(height = 16.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.custom_fields),
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
items(commonState.customFieldData) { customItem ->
Spacer(modifier = Modifier.height(height = 8.dp))
VaultAddEditCustomField(
customField = customItem,
onCustomFieldValueChange = commonTypeHandlers.onCustomFieldValueChange,
onCustomFieldAction = commonTypeHandlers.onCustomFieldActionSelect,
onHiddenVisibilityChanged = commonTypeHandlers.onHiddenFieldVisibilityChange,
cardStyle = CardStyle.Full,
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
item {
Spacer(modifier = Modifier.height(16.dp))
VaultAddEditCustomFieldsButton(
onFinishNamingClick = commonTypeHandlers.onAddNewCustomFieldClick,
options = persistentListOf(
CustomFieldType.TEXT,
CustomFieldType.HIDDEN,
CustomFieldType.BOOLEAN,
),
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
}

View File

@@ -3,38 +3,26 @@ package com.x8bit.bitwarden.ui.vault.feature.addedit
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCommonHandlers
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditSshKeyTypeHandlers
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
import kotlinx.collections.immutable.persistentListOf
/**
* The UI for adding and editing a SSH key cipher.
*/
@Suppress("LongMethod")
fun LazyListScope.vaultAddEditSshKeyItems(
commonState: VaultAddEditState.ViewState.Content.Common,
sshKeyState: VaultAddEditState.ViewState.Content.ItemType.SshKey,
commonTypeHandlers: VaultAddEditCommonHandlers,
sshKeyTypeHandlers: VaultAddEditSshKeyTypeHandlers,
) {
item {
@@ -82,109 +70,14 @@ fun LazyListScope.vaultAddEditSshKeyItems(
.standardHorizontalMargin(),
)
}
item {
Spacer(modifier = Modifier.height(height = 16.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.miscellaneous),
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(height = 8.dp))
}
item {
BitwardenTextField(
singleLine = false,
label = stringResource(id = R.string.notes),
value = commonState.notes,
onValueChange = commonTypeHandlers.onNotesTextChange,
textFieldTestTag = "ItemNotesEntry",
cardStyle = CardStyle.Full,
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
if (commonState.isUnlockWithPasswordEnabled) {
item {
Spacer(modifier = Modifier.height(height = 8.dp))
BitwardenSwitch(
label = stringResource(id = R.string.password_prompt),
isChecked = commonState.masterPasswordReprompt,
onCheckedChange = commonTypeHandlers.onToggleMasterPasswordReprompt,
actions = {
BitwardenStandardIconButton(
vectorIconRes = R.drawable.ic_question_circle_small,
contentDescription = stringResource(
id = R.string.master_password_re_prompt_help,
),
onClick = commonTypeHandlers.onTooltipClick,
contentColor = BitwardenTheme.colorScheme.icon.secondary,
)
},
cardStyle = CardStyle.Full,
modifier = Modifier
.testTag("MasterPasswordRepromptToggle")
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
}
item {
Spacer(modifier = Modifier.height(height = 16.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.custom_fields),
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin()
.padding(horizontal = 16.dp),
)
}
items(commonState.customFieldData) { customItem ->
Spacer(modifier = Modifier.height(height = 8.dp))
VaultAddEditCustomField(
customField = customItem,
onCustomFieldValueChange = commonTypeHandlers.onCustomFieldValueChange,
onCustomFieldAction = commonTypeHandlers.onCustomFieldActionSelect,
onHiddenVisibilityChanged = commonTypeHandlers.onHiddenFieldVisibilityChange,
cardStyle = CardStyle.Full,
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
item {
Spacer(modifier = Modifier.height(16.dp))
VaultAddEditCustomFieldsButton(
onFinishNamingClick = commonTypeHandlers.onAddNewCustomFieldClick,
options = persistentListOf(
CustomFieldType.TEXT,
CustomFieldType.HIDDEN,
CustomFieldType.BOOLEAN,
),
modifier = Modifier
.fillMaxWidth()
.standardHorizontalMargin(),
)
}
}
@Preview(showBackground = true)
@Preview
@Composable
private fun VaultAddEditSshKeyItems_preview() {
BitwardenTheme {
LazyColumn {
vaultAddEditSshKeyItems(
commonState = VaultAddEditState.ViewState.Content.Common(
name = "SSH Key",
),
sshKeyState = VaultAddEditState.ViewState.Content.ItemType.SshKey(
publicKey = "public key",
privateKey = "private key",
@@ -193,20 +86,6 @@ private fun VaultAddEditSshKeyItems_preview() {
showPrivateKey = false,
showFingerprint = false,
),
commonTypeHandlers = VaultAddEditCommonHandlers(
onNameTextChange = { },
onFolderSelected = { },
onToggleFavorite = { },
onToggleMasterPasswordReprompt = { },
onNotesTextChange = { },
onOwnerSelected = { },
onTooltipClick = { },
onAddNewCustomFieldClick = { _, _ -> },
onCustomFieldValueChange = { },
onCustomFieldActionSelect = { _, _ -> },
onCollectionSelect = { },
onHiddenFieldVisibilityChange = { },
),
sshKeyTypeHandlers = VaultAddEditSshKeyTypeHandlers(
onPrivateKeyVisibilityChange = { },
),

View File

@@ -65,6 +65,8 @@ import com.x8bit.bitwarden.ui.vault.model.VaultCollection
import com.x8bit.bitwarden.ui.vault.model.VaultIdentityTitle
import com.x8bit.bitwarden.ui.vault.model.VaultLinkedFieldType
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
@@ -2208,6 +2210,11 @@ data class VaultAddEditState(
*/
abstract val itemTypeOption: ItemTypeOption
/**
* A list of all the linked field types supported by this [ItemType].
*/
abstract val vaultLinkedFieldTypes: ImmutableList<VaultLinkedFieldType>
/**
* Represents the login item information.
*
@@ -2240,6 +2247,12 @@ data class VaultAddEditState(
) : ItemType() {
override val itemTypeOption: ItemTypeOption get() = ItemTypeOption.LOGIN
override val vaultLinkedFieldTypes: ImmutableList<VaultLinkedFieldType>
get() = persistentListOf(
VaultLinkedFieldType.PASSWORD,
VaultLinkedFieldType.USERNAME,
)
/**
* Indicates whether the passkey can or cannot be removed.
*/
@@ -2266,6 +2279,16 @@ data class VaultAddEditState(
val securityCode: String = "",
) : ItemType() {
override val itemTypeOption: ItemTypeOption get() = ItemTypeOption.CARD
override val vaultLinkedFieldTypes: ImmutableList<VaultLinkedFieldType>
get() = persistentListOf(
VaultLinkedFieldType.CARDHOLDER_NAME,
VaultLinkedFieldType.EXPIRATION_MONTH,
VaultLinkedFieldType.EXPIRATION_YEAR,
VaultLinkedFieldType.SECURITY_CODE,
VaultLinkedFieldType.BRAND,
VaultLinkedFieldType.NUMBER,
)
}
/**
@@ -2311,8 +2334,30 @@ data class VaultAddEditState(
val zip: String = "",
val country: String = "",
) : ItemType() {
override val itemTypeOption: ItemTypeOption get() = ItemTypeOption.IDENTITY
override val vaultLinkedFieldTypes: ImmutableList<VaultLinkedFieldType>
get() = persistentListOf(
VaultLinkedFieldType.TITLE,
VaultLinkedFieldType.MIDDLE_NAME,
VaultLinkedFieldType.ADDRESS_1,
VaultLinkedFieldType.ADDRESS_2,
VaultLinkedFieldType.ADDRESS_3,
VaultLinkedFieldType.CITY,
VaultLinkedFieldType.STATE,
VaultLinkedFieldType.POSTAL_CODE,
VaultLinkedFieldType.COUNTRY,
VaultLinkedFieldType.COMPANY,
VaultLinkedFieldType.EMAIL,
VaultLinkedFieldType.PHONE,
VaultLinkedFieldType.SSN,
VaultLinkedFieldType.IDENTITY_USERNAME,
VaultLinkedFieldType.PASSPORT_NUMBER,
VaultLinkedFieldType.LICENSE_NUMBER,
VaultLinkedFieldType.FIRST_NAME,
VaultLinkedFieldType.LAST_NAME,
VaultLinkedFieldType.FULL_NAME,
)
}
/**
@@ -2321,6 +2366,8 @@ data class VaultAddEditState(
@Parcelize
data object SecureNotes : ItemType() {
override val itemTypeOption: ItemTypeOption get() = ItemTypeOption.SECURE_NOTES
override val vaultLinkedFieldTypes: ImmutableList<VaultLinkedFieldType>
get() = persistentListOf()
}
/**
@@ -2340,6 +2387,8 @@ data class VaultAddEditState(
val showFingerprint: Boolean = false,
) : ItemType() {
override val itemTypeOption: ItemTypeOption get() = ItemTypeOption.SSH_KEYS
override val vaultLinkedFieldTypes: ImmutableList<VaultLinkedFieldType>
get() = persistentListOf()
}
}

View File

@@ -2302,6 +2302,11 @@ class VaultAddEditScreenTest : BaseComposeTest() {
fun `clicking New Custom Field button should allow creation of Linked type`() {
mutableStateFlow.value = DEFAULT_STATE_LOGIN
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onNodeWithTextAfterScroll(text = "New custom field")
.performClick()
@@ -2564,6 +2569,11 @@ class VaultAddEditScreenTest : BaseComposeTest() {
fun `toggling the Master password re-prompt toggle should send ToggleMasterPasswordReprompt action`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onNodeWithTextAfterScroll("Master password re-prompt")
.performTouchInput {
@@ -2583,6 +2593,11 @@ class VaultAddEditScreenTest : BaseComposeTest() {
fun `re-prompt toggle should display according to state`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onNodeWithTextAfterScroll("Master password re-prompt")
.assertIsDisplayed()
@@ -2599,6 +2614,11 @@ class VaultAddEditScreenTest : BaseComposeTest() {
fun `the master password re-prompt toggle should be enabled or disabled according to state`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onNodeWithTextAfterScroll("Master password re-prompt")
.assertIsOff()
@@ -2616,6 +2636,11 @@ class VaultAddEditScreenTest : BaseComposeTest() {
fun `toggling the Master password re-prompt tooltip button should send TooltipClick action`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onNodeWithContentDescriptionAfterScroll(label = "Master password re-prompt help")
.performClick()
@@ -2721,6 +2746,11 @@ class VaultAddEditScreenTest : BaseComposeTest() {
fun `clicking New Custom Field button should allow creation of Text type`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onNodeWithTextAfterScroll(text = "New custom field")
.performClick()
@@ -2757,6 +2787,11 @@ class VaultAddEditScreenTest : BaseComposeTest() {
fun `clicking New Custom Field button should not display linked type`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onNodeWithTextAfterScroll(text = "New custom field")
.performClick()
@@ -2775,6 +2810,11 @@ class VaultAddEditScreenTest : BaseComposeTest() {
fun `clicking New Custom Field button should allow creation of Boolean type`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onNodeWithTextAfterScroll(text = "New custom field")
.performClick()
@@ -2811,6 +2851,11 @@ class VaultAddEditScreenTest : BaseComposeTest() {
fun `clicking New Custom Field button should allow creation of Hidden type`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onNodeWithTextAfterScroll(text = "New custom field")
.performClick()
@@ -2863,6 +2908,10 @@ class VaultAddEditScreenTest : BaseComposeTest() {
),
)
}
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onNodeWithTextAfterScroll(text = "Hidden item")
.assertExists()
@@ -2882,6 +2931,11 @@ class VaultAddEditScreenTest : BaseComposeTest() {
fun `clicking and changing the custom text field will send a CustomFieldValueChange event`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onNodeWithTextAfterScroll("TestText")
.performTextClearance()
@@ -2899,6 +2953,11 @@ class VaultAddEditScreenTest : BaseComposeTest() {
fun `clicking and changing the custom hidden field will send a CustomFieldValueChange event`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onNodeWithTextAfterScroll("TestHidden")
.performTextClearance()
@@ -2917,6 +2976,11 @@ class VaultAddEditScreenTest : BaseComposeTest() {
fun `clicking and changing the custom boolean field will send a CustomFieldValueChange event`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onNodeWithTextAfterScroll("TestBoolean")
.performClick()
@@ -2934,6 +2998,11 @@ class VaultAddEditScreenTest : BaseComposeTest() {
fun `clicking custom field edit icon and Edit option sends a CustomFieldValueChange action`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onAllNodesWithContentDescriptionAfterScroll("Edit")
.onFirst()
@@ -2974,6 +3043,11 @@ class VaultAddEditScreenTest : BaseComposeTest() {
fun `clicking custom field edit icon and Remove option sends a CustomFieldActionSelect remove action`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onAllNodesWithContentDescriptionAfterScroll("Edit")
.onFirst()
@@ -3002,6 +3076,11 @@ class VaultAddEditScreenTest : BaseComposeTest() {
fun `clicking custom field edit icon and Move down option sends a CustomFieldActionSelect move down action`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onAllNodesWithContentDescriptionAfterScroll("Edit")
.onFirst()
@@ -3030,6 +3109,11 @@ class VaultAddEditScreenTest : BaseComposeTest() {
fun `clicking custom field edit icon and Move Up options sends a CustomFieldActionSelect move up action`() {
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
// Expand the additional options UI before interacting with it
composeTestRule
.onNodeWithTextAfterScroll(text = "Additional options")
.performClick()
composeTestRule
.onAllNodesWithContentDescriptionAfterScroll("Edit")
.onFirst()