mirror of
https://github.com/bitwarden/android.git
synced 2024-11-25 02:46:00 +03:00
Add edit cipher org events (#3352)
This commit is contained in:
parent
949768ac95
commit
d1e8ed63a4
11 changed files with 383 additions and 17 deletions
|
@ -9,6 +9,10 @@ import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
|
@ -70,10 +74,16 @@ fun LazyListScope.vaultAddEditCardItems(
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
var showNumber by rememberSaveable { mutableStateOf(value = false) }
|
||||||
BitwardenPasswordField(
|
BitwardenPasswordField(
|
||||||
label = stringResource(id = R.string.number),
|
label = stringResource(id = R.string.number),
|
||||||
value = cardState.number,
|
value = cardState.number,
|
||||||
onValueChange = cardHandlers.onNumberTextChange,
|
onValueChange = cardHandlers.onNumberTextChange,
|
||||||
|
showPassword = showNumber,
|
||||||
|
showPasswordChange = {
|
||||||
|
showNumber = !showNumber
|
||||||
|
cardHandlers.onNumberVisibilityChange(showNumber)
|
||||||
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.testTag("CardNumberEntry")
|
.testTag("CardNumberEntry")
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
@ -140,10 +150,16 @@ fun LazyListScope.vaultAddEditCardItems(
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
var showSecurityCode by rememberSaveable { mutableStateOf(value = false) }
|
||||||
BitwardenPasswordField(
|
BitwardenPasswordField(
|
||||||
label = stringResource(id = R.string.security_code),
|
label = stringResource(id = R.string.security_code),
|
||||||
value = cardState.securityCode,
|
value = cardState.securityCode,
|
||||||
onValueChange = cardHandlers.onSecurityCodeTextChange,
|
onValueChange = cardHandlers.onSecurityCodeTextChange,
|
||||||
|
showPassword = showSecurityCode,
|
||||||
|
showPasswordChange = {
|
||||||
|
showSecurityCode = !showSecurityCode
|
||||||
|
cardHandlers.onSecurityCodeVisibilityChange(showSecurityCode)
|
||||||
|
},
|
||||||
keyboardType = KeyboardType.NumberPassword,
|
keyboardType = KeyboardType.NumberPassword,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.testTag("CardSecurityCodeEntry")
|
.testTag("CardSecurityCodeEntry")
|
||||||
|
@ -259,7 +275,7 @@ fun LazyListScope.vaultAddEditCardItems(
|
||||||
|
|
||||||
items(commonState.customFieldData) { customItem ->
|
items(commonState.customFieldData) { customItem ->
|
||||||
VaultAddEditCustomField(
|
VaultAddEditCustomField(
|
||||||
customItem,
|
customField = customItem,
|
||||||
onCustomFieldValueChange = commonHandlers.onCustomFieldValueChange,
|
onCustomFieldValueChange = commonHandlers.onCustomFieldValueChange,
|
||||||
onCustomFieldAction = commonHandlers.onCustomFieldActionSelect,
|
onCustomFieldAction = commonHandlers.onCustomFieldActionSelect,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -273,6 +289,7 @@ fun LazyListScope.vaultAddEditCardItems(
|
||||||
VaultLinkedFieldType.BRAND,
|
VaultLinkedFieldType.BRAND,
|
||||||
VaultLinkedFieldType.NUMBER,
|
VaultLinkedFieldType.NUMBER,
|
||||||
),
|
),
|
||||||
|
onHiddenVisibilityChanged = commonHandlers.onHiddenFieldVisibilityChange,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ import kotlinx.collections.immutable.toImmutableList
|
||||||
* @param onCustomFieldAction Invoked when the user chooses an action.
|
* @param onCustomFieldAction Invoked when the user chooses an action.
|
||||||
* @param modifier Modifier for the UI elements.
|
* @param modifier Modifier for the UI elements.
|
||||||
* @param supportedLinkedTypes The supported linked types for the vault item.
|
* @param supportedLinkedTypes The supported linked types for the vault item.
|
||||||
|
* @param onHiddenVisibilityChanged Emits when the visibility of a hidden custom field changes.
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
|
@ -46,6 +47,7 @@ fun VaultAddEditCustomField(
|
||||||
onCustomFieldAction: (CustomFieldAction, VaultAddEditState.Custom) -> Unit,
|
onCustomFieldAction: (CustomFieldAction, VaultAddEditState.Custom) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
supportedLinkedTypes: ImmutableList<VaultLinkedFieldType> = persistentListOf(),
|
supportedLinkedTypes: ImmutableList<VaultLinkedFieldType> = persistentListOf(),
|
||||||
|
onHiddenVisibilityChanged: (Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
var shouldShowChooserDialog by remember { mutableStateOf(false) }
|
var shouldShowChooserDialog by remember { mutableStateOf(false) }
|
||||||
var shouldShowEditDialog by remember { mutableStateOf(false) }
|
var shouldShowEditDialog by remember { mutableStateOf(false) }
|
||||||
|
@ -91,11 +93,12 @@ fun VaultAddEditCustomField(
|
||||||
|
|
||||||
is VaultAddEditState.Custom.HiddenField -> {
|
is VaultAddEditState.Custom.HiddenField -> {
|
||||||
CustomFieldHiddenField(
|
CustomFieldHiddenField(
|
||||||
customField.name,
|
label = customField.name,
|
||||||
customField.value,
|
value = customField.value,
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
onCustomFieldValueChange(customField.copy(value = it))
|
onCustomFieldValueChange(customField.copy(value = it))
|
||||||
},
|
},
|
||||||
|
onVisibilityChanged = onHiddenVisibilityChanged,
|
||||||
onEditValue = { shouldShowChooserDialog = true },
|
onEditValue = { shouldShowChooserDialog = true },
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
@ -175,12 +178,19 @@ private fun CustomFieldHiddenField(
|
||||||
value: String,
|
value: String,
|
||||||
onValueChanged: (String) -> Unit,
|
onValueChanged: (String) -> Unit,
|
||||||
onEditValue: () -> Unit,
|
onEditValue: () -> Unit,
|
||||||
|
onVisibilityChanged: (Boolean) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
var shouldShowPassword by remember { mutableStateOf(value = false) }
|
||||||
BitwardenPasswordFieldWithActions(
|
BitwardenPasswordFieldWithActions(
|
||||||
label = label,
|
label = label,
|
||||||
value = value,
|
value = value,
|
||||||
onValueChange = onValueChanged,
|
onValueChange = onValueChanged,
|
||||||
|
showPassword = shouldShowPassword,
|
||||||
|
showPasswordChange = {
|
||||||
|
shouldShowPassword = !shouldShowPassword
|
||||||
|
onVisibilityChanged(shouldShowPassword)
|
||||||
|
},
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
actions = {
|
actions = {
|
||||||
|
|
|
@ -403,6 +403,7 @@ fun LazyListScope.vaultAddEditIdentityItems(
|
||||||
VaultLinkedFieldType.LAST_NAME,
|
VaultLinkedFieldType.LAST_NAME,
|
||||||
VaultLinkedFieldType.FULL_NAME,
|
VaultLinkedFieldType.FULL_NAME,
|
||||||
),
|
),
|
||||||
|
onHiddenVisibilityChanged = commonTypeHandlers.onHiddenFieldVisibilityChange,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
@ -301,7 +302,7 @@ fun LazyListScope.vaultAddEditLoginItems(
|
||||||
|
|
||||||
items(commonState.customFieldData) { customItem ->
|
items(commonState.customFieldData) { customItem ->
|
||||||
VaultAddEditCustomField(
|
VaultAddEditCustomField(
|
||||||
customItem,
|
customField = customItem,
|
||||||
onCustomFieldValueChange = commonActionHandler.onCustomFieldValueChange,
|
onCustomFieldValueChange = commonActionHandler.onCustomFieldValueChange,
|
||||||
onCustomFieldAction = commonActionHandler.onCustomFieldActionSelect,
|
onCustomFieldAction = commonActionHandler.onCustomFieldActionSelect,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -311,6 +312,7 @@ fun LazyListScope.vaultAddEditLoginItems(
|
||||||
VaultLinkedFieldType.PASSWORD,
|
VaultLinkedFieldType.PASSWORD,
|
||||||
VaultLinkedFieldType.USERNAME,
|
VaultLinkedFieldType.USERNAME,
|
||||||
),
|
),
|
||||||
|
onHiddenVisibilityChanged = commonActionHandler.onHiddenFieldVisibilityChange,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,10 +438,16 @@ private fun PasswordRow(
|
||||||
var shouldShowDialog by rememberSaveable { mutableStateOf(false) }
|
var shouldShowDialog by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
if (canViewPassword) {
|
if (canViewPassword) {
|
||||||
|
var shouldShowPassword by remember { mutableStateOf(false) }
|
||||||
BitwardenPasswordFieldWithActions(
|
BitwardenPasswordFieldWithActions(
|
||||||
label = stringResource(id = R.string.password),
|
label = stringResource(id = R.string.password),
|
||||||
value = password,
|
value = password,
|
||||||
onValueChange = loginItemTypeHandlers.onPasswordTextChange,
|
onValueChange = loginItemTypeHandlers.onPasswordTextChange,
|
||||||
|
showPassword = shouldShowPassword,
|
||||||
|
showPasswordChange = {
|
||||||
|
shouldShowPassword = !shouldShowPassword
|
||||||
|
loginItemTypeHandlers.onPasswordVisibilityChange(shouldShowPassword)
|
||||||
|
},
|
||||||
showPasswordTestTag = "ViewPasswordButton",
|
showPasswordTestTag = "ViewPasswordButton",
|
||||||
passwordFieldTestTag = "LoginPasswordEntry",
|
passwordFieldTestTag = "LoginPasswordEntry",
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
|
@ -154,12 +154,13 @@ fun LazyListScope.vaultAddEditSecureNotesItems(
|
||||||
}
|
}
|
||||||
items(commonState.customFieldData) { customItem ->
|
items(commonState.customFieldData) { customItem ->
|
||||||
VaultAddEditCustomField(
|
VaultAddEditCustomField(
|
||||||
customItem,
|
customField = customItem,
|
||||||
onCustomFieldValueChange = commonTypeHandlers.onCustomFieldValueChange,
|
onCustomFieldValueChange = commonTypeHandlers.onCustomFieldValueChange,
|
||||||
onCustomFieldAction = commonTypeHandlers.onCustomFieldActionSelect,
|
onCustomFieldAction = commonTypeHandlers.onCustomFieldActionSelect,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp),
|
.padding(horizontal = 16.dp),
|
||||||
|
onHiddenVisibilityChanged = commonTypeHandlers.onHiddenFieldVisibilityChange,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -214,9 +214,9 @@ class VaultAddEditViewModel @Inject constructor(
|
||||||
|
|
||||||
private fun handleCommonActions(action: VaultAddEditAction.Common) {
|
private fun handleCommonActions(action: VaultAddEditAction.Common) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is VaultAddEditAction.Common.CustomFieldValueChange -> handleCustomFieldValueChange(
|
is VaultAddEditAction.Common.CustomFieldValueChange -> {
|
||||||
action,
|
handleCustomFieldValueChange(action)
|
||||||
)
|
}
|
||||||
|
|
||||||
is VaultAddEditAction.Common.FolderChange -> handleFolderTextInputChange(action)
|
is VaultAddEditAction.Common.FolderChange -> handleFolderTextInputChange(action)
|
||||||
is VaultAddEditAction.Common.NameTextChange -> handleNameTextInputChange(action)
|
is VaultAddEditAction.Common.NameTextChange -> handleNameTextInputChange(action)
|
||||||
|
@ -235,19 +235,23 @@ class VaultAddEditViewModel @Inject constructor(
|
||||||
is VaultAddEditAction.Common.DismissDialog -> handleDismissDialog()
|
is VaultAddEditAction.Common.DismissDialog -> handleDismissDialog()
|
||||||
is VaultAddEditAction.Common.SaveClick -> handleSaveClick()
|
is VaultAddEditAction.Common.SaveClick -> handleSaveClick()
|
||||||
is VaultAddEditAction.Common.TypeOptionSelect -> handleTypeOptionSelect(action)
|
is VaultAddEditAction.Common.TypeOptionSelect -> handleTypeOptionSelect(action)
|
||||||
is VaultAddEditAction.Common.AddNewCustomFieldClick -> handleAddNewCustomFieldClick(
|
is VaultAddEditAction.Common.AddNewCustomFieldClick -> {
|
||||||
action,
|
handleAddNewCustomFieldClick(action)
|
||||||
)
|
}
|
||||||
|
|
||||||
is VaultAddEditAction.Common.TooltipClick -> handleTooltipClick()
|
is VaultAddEditAction.Common.TooltipClick -> handleTooltipClick()
|
||||||
is VaultAddEditAction.Common.CustomFieldActionSelect -> handleCustomFieldActionSelected(
|
is VaultAddEditAction.Common.CustomFieldActionSelect -> {
|
||||||
action,
|
handleCustomFieldActionSelected(action)
|
||||||
)
|
}
|
||||||
|
|
||||||
is VaultAddEditAction.Common.CollectionSelect -> handleCollectionSelect(action)
|
is VaultAddEditAction.Common.CollectionSelect -> handleCollectionSelect(action)
|
||||||
is VaultAddEditAction.Common.InitialAutofillDialogDismissed -> {
|
is VaultAddEditAction.Common.InitialAutofillDialogDismissed -> {
|
||||||
handleInitialAutofillDialogDismissed()
|
handleInitialAutofillDialogDismissed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is VaultAddEditAction.Common.HiddenFieldVisibilityChange -> {
|
||||||
|
handleHiddenFieldVisibilityChange(action)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,6 +426,20 @@ class VaultAddEditViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleHiddenFieldVisibilityChange(
|
||||||
|
action: VaultAddEditAction.Common.HiddenFieldVisibilityChange,
|
||||||
|
) {
|
||||||
|
onEdit {
|
||||||
|
if (action.isVisible) {
|
||||||
|
organizationEventManager.trackEvent(
|
||||||
|
event = OrganizationEvent.CipherClientToggledHiddenFieldVisible(
|
||||||
|
cipherId = it.vaultItemId,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleAddNewCustomFieldClick(
|
private fun handleAddNewCustomFieldClick(
|
||||||
action: VaultAddEditAction.Common.AddNewCustomFieldClick,
|
action: VaultAddEditAction.Common.AddNewCustomFieldClick,
|
||||||
) {
|
) {
|
||||||
|
@ -636,6 +654,10 @@ class VaultAddEditViewModel @Inject constructor(
|
||||||
is VaultAddEditAction.ItemType.LoginType.ClearTotpKeyClick -> {
|
is VaultAddEditAction.ItemType.LoginType.ClearTotpKeyClick -> {
|
||||||
handleLoginClearTotpKey()
|
handleLoginClearTotpKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is VaultAddEditAction.ItemType.LoginType.PasswordVisibilityChange -> {
|
||||||
|
handlePasswordVisibilityChange(action)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -736,6 +758,20 @@ class VaultAddEditViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handlePasswordVisibilityChange(
|
||||||
|
action: VaultAddEditAction.ItemType.LoginType.PasswordVisibilityChange,
|
||||||
|
) {
|
||||||
|
onEdit {
|
||||||
|
if (action.isVisible) {
|
||||||
|
organizationEventManager.trackEvent(
|
||||||
|
event = OrganizationEvent.CipherClientToggledPasswordVisible(
|
||||||
|
cipherId = it.vaultItemId,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleLoginAddNewUriClick() {
|
private fun handleLoginAddNewUriClick() {
|
||||||
updateLoginContent { loginType ->
|
updateLoginContent { loginType ->
|
||||||
loginType.copy(
|
loginType.copy(
|
||||||
|
@ -961,9 +997,17 @@ class VaultAddEditViewModel @Inject constructor(
|
||||||
handleCardNumberTextChange(action)
|
handleCardNumberTextChange(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is VaultAddEditAction.ItemType.CardType.NumberVisibilityChange -> {
|
||||||
|
handleNumberVisibilityChange(action)
|
||||||
|
}
|
||||||
|
|
||||||
is VaultAddEditAction.ItemType.CardType.SecurityCodeTextChange -> {
|
is VaultAddEditAction.ItemType.CardType.SecurityCodeTextChange -> {
|
||||||
handleCardSecurityCodeTextChange(action)
|
handleCardSecurityCodeTextChange(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is VaultAddEditAction.ItemType.CardType.SecurityCodeVisibilityChange -> {
|
||||||
|
handleSecurityCodeVisibilityChange(action)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -997,12 +1041,40 @@ class VaultAddEditViewModel @Inject constructor(
|
||||||
updateCardContent { it.copy(number = action.number) }
|
updateCardContent { it.copy(number = action.number) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleNumberVisibilityChange(
|
||||||
|
action: VaultAddEditAction.ItemType.CardType.NumberVisibilityChange,
|
||||||
|
) {
|
||||||
|
onEdit {
|
||||||
|
if (action.isVisible) {
|
||||||
|
organizationEventManager.trackEvent(
|
||||||
|
event = OrganizationEvent.CipherClientToggledCardNumberVisible(
|
||||||
|
cipherId = it.vaultItemId,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleCardSecurityCodeTextChange(
|
private fun handleCardSecurityCodeTextChange(
|
||||||
action: VaultAddEditAction.ItemType.CardType.SecurityCodeTextChange,
|
action: VaultAddEditAction.ItemType.CardType.SecurityCodeTextChange,
|
||||||
) {
|
) {
|
||||||
updateCardContent { it.copy(securityCode = action.securityCode) }
|
updateCardContent { it.copy(securityCode = action.securityCode) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleSecurityCodeVisibilityChange(
|
||||||
|
action: VaultAddEditAction.ItemType.CardType.SecurityCodeVisibilityChange,
|
||||||
|
) {
|
||||||
|
onEdit {
|
||||||
|
if (action.isVisible) {
|
||||||
|
organizationEventManager.trackEvent(
|
||||||
|
event = OrganizationEvent.CipherClientToggledCardCodeVisible(
|
||||||
|
cipherId = it.vaultItemId,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//endregion Card Type Handlers
|
//endregion Card Type Handlers
|
||||||
|
|
||||||
//region Internal Type Handlers
|
//region Internal Type Handlers
|
||||||
|
@ -1024,8 +1096,9 @@ class VaultAddEditViewModel @Inject constructor(
|
||||||
handleGeneratorResultReceive(action)
|
handleGeneratorResultReceive(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
is VaultAddEditAction.Internal.PasswordBreachReceive ->
|
is VaultAddEditAction.Internal.PasswordBreachReceive -> {
|
||||||
handlePasswordBreachReceive(action)
|
handlePasswordBreachReceive(action)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2009,6 +2082,13 @@ sealed class VaultAddEditAction {
|
||||||
data class CollectionSelect(
|
data class CollectionSelect(
|
||||||
val collection: VaultCollection,
|
val collection: VaultCollection,
|
||||||
) : Common()
|
) : Common()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user has changed the visibility state of a hidden field.
|
||||||
|
*
|
||||||
|
* @property isVisible the new visibility state of the hidden field.
|
||||||
|
*/
|
||||||
|
data class HiddenFieldVisibilityChange(val isVisible: Boolean) : Common()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2085,6 +2165,13 @@ sealed class VaultAddEditAction {
|
||||||
* Represents the action to add a new URI field.
|
* Represents the action to add a new URI field.
|
||||||
*/
|
*/
|
||||||
data object AddNewUriClick : LoginType()
|
data object AddNewUriClick : LoginType()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when the password's visibility has changed.
|
||||||
|
*
|
||||||
|
* @property isVisible The new password visibility state.
|
||||||
|
*/
|
||||||
|
data class PasswordVisibilityChange(val isVisible: Boolean) : LoginType()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2240,6 +2327,13 @@ sealed class VaultAddEditAction {
|
||||||
*/
|
*/
|
||||||
data class NumberTextChange(val number: String) : CardType()
|
data class NumberTextChange(val number: String) : CardType()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when the number's visibility has changed.
|
||||||
|
*
|
||||||
|
* @property isVisible The new number visibility state.
|
||||||
|
*/
|
||||||
|
data class NumberVisibilityChange(val isVisible: Boolean) : CardType()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fired when the brand input is selected.
|
* Fired when the brand input is selected.
|
||||||
*
|
*
|
||||||
|
@ -2272,6 +2366,13 @@ sealed class VaultAddEditAction {
|
||||||
* @property securityCode The new security code text.
|
* @property securityCode The new security code text.
|
||||||
*/
|
*/
|
||||||
data class SecurityCodeTextChange(val securityCode: String) : CardType()
|
data class SecurityCodeTextChange(val securityCode: String) : CardType()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when the security code's visibility has changed.
|
||||||
|
*
|
||||||
|
* @property isVisible The new code visibility state.
|
||||||
|
*/
|
||||||
|
data class SecurityCodeVisibilityChange(val isVisible: Boolean) : CardType()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,9 @@ import com.x8bit.bitwarden.ui.vault.model.VaultCardExpirationMonth
|
||||||
* @property onExpirationMonthSelected Handles the action when an expiration month is selected.
|
* @property onExpirationMonthSelected Handles the action when an expiration month is selected.
|
||||||
* @property onExpirationYearTextChange Handles the action when the expiration year text is changed.
|
* @property onExpirationYearTextChange Handles the action when the expiration year text is changed.
|
||||||
* @property onSecurityCodeTextChange Handles the action when the expiration year text is changed.
|
* @property onSecurityCodeTextChange Handles the action when the expiration year text is changed.
|
||||||
|
* @property onSecurityCodeVisibilityChange Handles the action when the security code visibility
|
||||||
|
* changes.
|
||||||
|
* @property onNumberVisibilityChange Handles the action when the number visibility changes.
|
||||||
*/
|
*/
|
||||||
@Suppress("MaxLineLength")
|
@Suppress("MaxLineLength")
|
||||||
data class VaultAddEditCardTypeHandlers(
|
data class VaultAddEditCardTypeHandlers(
|
||||||
|
@ -24,8 +27,9 @@ data class VaultAddEditCardTypeHandlers(
|
||||||
val onExpirationMonthSelected: (VaultCardExpirationMonth) -> Unit,
|
val onExpirationMonthSelected: (VaultCardExpirationMonth) -> Unit,
|
||||||
val onExpirationYearTextChange: (String) -> Unit,
|
val onExpirationYearTextChange: (String) -> Unit,
|
||||||
val onSecurityCodeTextChange: (String) -> Unit,
|
val onSecurityCodeTextChange: (String) -> Unit,
|
||||||
|
val onSecurityCodeVisibilityChange: (Boolean) -> Unit,
|
||||||
) {
|
val onNumberVisibilityChange: (Boolean) -> Unit,
|
||||||
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -76,6 +80,18 @@ data class VaultAddEditCardTypeHandlers(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
onSecurityCodeVisibilityChange = {
|
||||||
|
viewModel.trySendAction(
|
||||||
|
VaultAddEditAction.ItemType.CardType.SecurityCodeVisibilityChange(
|
||||||
|
isVisible = it,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onNumberVisibilityChange = {
|
||||||
|
viewModel.trySendAction(
|
||||||
|
VaultAddEditAction.ItemType.CardType.NumberVisibilityChange(isVisible = it),
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ data class VaultAddEditCommonHandlers(
|
||||||
val onCustomFieldValueChange: (VaultAddEditState.Custom) -> Unit,
|
val onCustomFieldValueChange: (VaultAddEditState.Custom) -> Unit,
|
||||||
val onCustomFieldActionSelect: (CustomFieldAction, VaultAddEditState.Custom) -> Unit,
|
val onCustomFieldActionSelect: (CustomFieldAction, VaultAddEditState.Custom) -> Unit,
|
||||||
val onCollectionSelect: (VaultCollection) -> Unit,
|
val onCollectionSelect: (VaultCollection) -> Unit,
|
||||||
|
val onHiddenFieldVisibilityChange: (Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
@ -116,6 +117,11 @@ data class VaultAddEditCommonHandlers(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
onHiddenFieldVisibilityChange = {
|
||||||
|
viewModel.trySendAction(
|
||||||
|
VaultAddEditAction.Common.HiddenFieldVisibilityChange(isVisible = it),
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriItem
|
||||||
* @property onCopyTotpKeyClick Handles the action when the copy TOTP text button is clicked.
|
* @property onCopyTotpKeyClick Handles the action when the copy TOTP text button is clicked.
|
||||||
* @property onClearTotpKeyClick Handles the action when the clear TOTP text button is clicked.
|
* @property onClearTotpKeyClick Handles the action when the clear TOTP text button is clicked.
|
||||||
* @property onAddNewUriClick Handles the action when the add new URI button is clicked.
|
* @property onAddNewUriClick Handles the action when the add new URI button is clicked.
|
||||||
|
* @property onPasswordVisibilityChange Handles the action when the password visibility button is
|
||||||
|
* clicked.
|
||||||
*/
|
*/
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
data class VaultAddEditLoginTypeHandlers(
|
data class VaultAddEditLoginTypeHandlers(
|
||||||
|
@ -36,6 +38,7 @@ data class VaultAddEditLoginTypeHandlers(
|
||||||
val onCopyTotpKeyClick: (String) -> Unit,
|
val onCopyTotpKeyClick: (String) -> Unit,
|
||||||
val onClearTotpKeyClick: () -> Unit,
|
val onClearTotpKeyClick: () -> Unit,
|
||||||
val onAddNewUriClick: () -> Unit,
|
val onAddNewUriClick: () -> Unit,
|
||||||
|
val onPasswordVisibilityChange: (Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
@ -106,6 +109,11 @@ data class VaultAddEditLoginTypeHandlers(
|
||||||
VaultAddEditAction.ItemType.LoginType.ClearTotpKeyClick,
|
VaultAddEditAction.ItemType.LoginType.ClearTotpKeyClick,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
onPasswordVisibilityChange = {
|
||||||
|
viewModel.trySendAction(
|
||||||
|
VaultAddEditAction.ItemType.LoginType.PasswordVisibilityChange(it),
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import androidx.compose.ui.test.isDialog
|
||||||
import androidx.compose.ui.test.isPopup
|
import androidx.compose.ui.test.isPopup
|
||||||
import androidx.compose.ui.test.onAllNodesWithContentDescription
|
import androidx.compose.ui.test.onAllNodesWithContentDescription
|
||||||
import androidx.compose.ui.test.onAllNodesWithText
|
import androidx.compose.ui.test.onAllNodesWithText
|
||||||
|
import androidx.compose.ui.test.onChildren
|
||||||
import androidx.compose.ui.test.onFirst
|
import androidx.compose.ui.test.onFirst
|
||||||
import androidx.compose.ui.test.onLast
|
import androidx.compose.ui.test.onLast
|
||||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||||
|
@ -466,6 +467,24 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
||||||
.assertDoesNotExist()
|
.assertDoesNotExist()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `in ItemType_Login state changing password visibility state should send PasswordVisibilityChange`() {
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithTextAfterScroll(text = "Password")
|
||||||
|
.assertExists()
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithContentDescriptionAfterScroll(label = "Show")
|
||||||
|
.assertExists()
|
||||||
|
.performClick()
|
||||||
|
|
||||||
|
verify(exactly = 1) {
|
||||||
|
viewModel.trySendAction(
|
||||||
|
VaultAddEditAction.ItemType.LoginType.PasswordVisibilityChange(isVisible = true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `in ItemType_Login state changing Username text field should trigger UsernameTextChange`() {
|
fun `in ItemType_Login state changing Username text field should trigger UsernameTextChange`() {
|
||||||
composeTestRule
|
composeTestRule
|
||||||
|
@ -1671,6 +1690,30 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `in ItemType_Card changing number visibility should trigger NumberVisibilityChange`() {
|
||||||
|
mutableStateFlow.value = DEFAULT_STATE_CARD.copy(
|
||||||
|
viewState = VaultAddEditState.ViewState.Content(
|
||||||
|
common = VaultAddEditState.ViewState.Content.Common(),
|
||||||
|
type = VaultAddEditState.ViewState.Content.ItemType.Card(number = "12345"),
|
||||||
|
isIndividualVaultDisabled = false,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithTextAfterScroll(text = "Number")
|
||||||
|
.assertExists()
|
||||||
|
.onChildren()
|
||||||
|
.filterToOne(hasContentDescription(value = "Show"))
|
||||||
|
.assertExists()
|
||||||
|
.performClick()
|
||||||
|
|
||||||
|
verify(exactly = 1) {
|
||||||
|
viewModel.trySendAction(
|
||||||
|
VaultAddEditAction.ItemType.CardType.NumberVisibilityChange(isVisible = true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `in ItemType_Card the number text field should display the text provided by the state`() {
|
fun `in ItemType_Card the number text field should display the text provided by the state`() {
|
||||||
mutableStateFlow.value = DEFAULT_STATE_CARD
|
mutableStateFlow.value = DEFAULT_STATE_CARD
|
||||||
|
@ -1831,6 +1874,30 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `in ItemType_Card changing code visibility should trigger SecurityCodeVisibilityChange`() {
|
||||||
|
mutableStateFlow.value = DEFAULT_STATE_CARD.copy(
|
||||||
|
viewState = VaultAddEditState.ViewState.Content(
|
||||||
|
common = VaultAddEditState.ViewState.Content.Common(),
|
||||||
|
type = VaultAddEditState.ViewState.Content.ItemType.Card(number = "12345"),
|
||||||
|
isIndividualVaultDisabled = false,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithTextAfterScroll(text = "Security code")
|
||||||
|
.assertExists()
|
||||||
|
.onChildren()
|
||||||
|
.filterToOne(hasContentDescription(value = "Show"))
|
||||||
|
.assertExists()
|
||||||
|
.performClick()
|
||||||
|
|
||||||
|
verify(exactly = 1) {
|
||||||
|
viewModel.trySendAction(
|
||||||
|
VaultAddEditAction.ItemType.CardType.SecurityCodeVisibilityChange(isVisible = true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
@Suppress("MaxLineLength")
|
||||||
@Test
|
@Test
|
||||||
fun `in ItemType_Card the security code text field should display the text provided by the state`() {
|
fun `in ItemType_Card the security code text field should display the text provided by the state`() {
|
||||||
|
@ -2373,6 +2440,40 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `changing hidden field visibility state should send HiddenFieldVisibilityChange`() {
|
||||||
|
mutableStateFlow.update {
|
||||||
|
it.copy(
|
||||||
|
viewState = VaultAddEditState.ViewState.Content(
|
||||||
|
common = VaultAddEditState.ViewState.Content.Common(
|
||||||
|
customFieldData = listOf(
|
||||||
|
VaultAddEditState.Custom.HiddenField(
|
||||||
|
itemId = "itemId",
|
||||||
|
name = "Hidden item",
|
||||||
|
value = "I am hiding",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
type = VaultAddEditState.ViewState.Content.ItemType.Login(),
|
||||||
|
isIndividualVaultDisabled = false,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithTextAfterScroll(text = "Hidden item")
|
||||||
|
.assertExists()
|
||||||
|
composeTestRule
|
||||||
|
.onAllNodesWithContentDescriptionAfterScroll(label = "Show")
|
||||||
|
.onLast()
|
||||||
|
.performClick()
|
||||||
|
|
||||||
|
verify(exactly = 1) {
|
||||||
|
viewModel.trySendAction(
|
||||||
|
VaultAddEditAction.Common.HiddenFieldVisibilityChange(isVisible = true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `clicking and changing the custom text field will send a CustomFieldValueChange event`() {
|
fun `clicking and changing the custom text field will send a CustomFieldValueChange event`() {
|
||||||
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
|
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
|
||||||
|
|
|
@ -1205,6 +1205,32 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
||||||
assertEquals(expectedState, viewModel.stateFlow.value)
|
assertEquals(expectedState, viewModel.stateFlow.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `PasswordVisibilityChange should log an event when in edit mode and password is visible`() =
|
||||||
|
runTest {
|
||||||
|
val vaultAddEditType = VaultAddEditType.EditItem(vaultItemId = "vault_item_id")
|
||||||
|
val viewModel = createAddVaultItemViewModel(
|
||||||
|
savedStateHandle = loginInitialSavedStateHandle.apply {
|
||||||
|
set("state", loginInitialState.copy(vaultAddEditType = vaultAddEditType))
|
||||||
|
set("vault_add_edit_type", vaultAddEditType)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
viewModel.trySendAction(
|
||||||
|
VaultAddEditAction.ItemType.LoginType.PasswordVisibilityChange(
|
||||||
|
isVisible = true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
verify(exactly = 1) {
|
||||||
|
organizationEventManager.trackEvent(
|
||||||
|
event = OrganizationEvent.CipherClientToggledPasswordVisible(
|
||||||
|
cipherId = "vault_item_id",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
@Suppress("MaxLineLength")
|
||||||
@Test
|
@Test
|
||||||
fun `OpenUsernameGeneratorClick should emit NavigateToGeneratorModal with username GeneratorMode`() =
|
fun `OpenUsernameGeneratorClick should emit NavigateToGeneratorModal with username GeneratorMode`() =
|
||||||
|
@ -1920,6 +1946,53 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `NumberVisibilityChange should log an event when in edit mode and password is visible`() =
|
||||||
|
runTest {
|
||||||
|
val vaultAddEditType = VaultAddEditType.EditItem(vaultItemId = "vault_item_id")
|
||||||
|
val viewModel = createAddVaultItemViewModel(
|
||||||
|
savedStateHandle = loginInitialSavedStateHandle.apply {
|
||||||
|
set("state", loginInitialState.copy(vaultAddEditType = vaultAddEditType))
|
||||||
|
set("vault_add_edit_type", vaultAddEditType)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
viewModel.trySendAction(
|
||||||
|
VaultAddEditAction.ItemType.CardType.NumberVisibilityChange(isVisible = true),
|
||||||
|
)
|
||||||
|
|
||||||
|
verify(exactly = 1) {
|
||||||
|
organizationEventManager.trackEvent(
|
||||||
|
event = OrganizationEvent.CipherClientToggledCardNumberVisible(
|
||||||
|
cipherId = "vault_item_id",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `SecurityCodeVisibilityChange should log an event when in edit mode and password is visible`() =
|
||||||
|
runTest {
|
||||||
|
val vaultAddEditType = VaultAddEditType.EditItem(vaultItemId = "vault_item_id")
|
||||||
|
val viewModel = createAddVaultItemViewModel(
|
||||||
|
savedStateHandle = loginInitialSavedStateHandle.apply {
|
||||||
|
set("state", loginInitialState.copy(vaultAddEditType = vaultAddEditType))
|
||||||
|
set("vault_add_edit_type", vaultAddEditType)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
viewModel.trySendAction(
|
||||||
|
VaultAddEditAction.ItemType.CardType.SecurityCodeVisibilityChange(isVisible = true),
|
||||||
|
)
|
||||||
|
|
||||||
|
verify(exactly = 1) {
|
||||||
|
organizationEventManager.trackEvent(
|
||||||
|
event = OrganizationEvent.CipherClientToggledCardCodeVisible(
|
||||||
|
cipherId = "vault_item_id",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
inner class VaultAddEditCommonActions {
|
inner class VaultAddEditCommonActions {
|
||||||
private lateinit var viewModel: VaultAddEditViewModel
|
private lateinit var viewModel: VaultAddEditViewModel
|
||||||
|
@ -2371,6 +2444,30 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `HiddenFieldVisibilityChange should log an event when in edit mode and password is visible`() =
|
||||||
|
runTest {
|
||||||
|
val vaultAddEditType = VaultAddEditType.EditItem(vaultItemId = "vault_item_id")
|
||||||
|
val viewModel = createAddVaultItemViewModel(
|
||||||
|
savedStateHandle = loginInitialSavedStateHandle.apply {
|
||||||
|
set("state", loginInitialState.copy(vaultAddEditType = vaultAddEditType))
|
||||||
|
set("vault_add_edit_type", vaultAddEditType)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
viewModel.trySendAction(
|
||||||
|
VaultAddEditAction.Common.HiddenFieldVisibilityChange(isVisible = true),
|
||||||
|
)
|
||||||
|
|
||||||
|
verify(exactly = 1) {
|
||||||
|
organizationEventManager.trackEvent(
|
||||||
|
event = OrganizationEvent.CipherClientToggledHiddenFieldVisible(
|
||||||
|
cipherId = "vault_item_id",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `TooltipClick should emit NavigateToToolTipUri`() = runTest {
|
fun `TooltipClick should emit NavigateToToolTipUri`() = runTest {
|
||||||
viewModel.eventFlow.test {
|
viewModel.eventFlow.test {
|
||||||
|
|
Loading…
Reference in a new issue