mirror of
https://github.com/bitwarden/android.git
synced 2025-03-16 03:08:50 +03:00
BIT-540 Adding Custom Field Edit Actions (#723)
This commit is contained in:
parent
9cc8763642
commit
17eb3e2e0b
10 changed files with 524 additions and 36 deletions
|
@ -232,6 +232,7 @@ fun LazyListScope.vaultAddEditCardItems(
|
|||
VaultAddEditCustomField(
|
||||
customItem,
|
||||
onCustomFieldValueChange = commonHandlers.onCustomFieldValueChange,
|
||||
onCustomFieldAction = commonHandlers.onCustomFieldActionSelect,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
|
|
|
@ -3,21 +3,27 @@ package com.x8bit.bitwarden.ui.vault.feature.addedit
|
|||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.showNotYetImplementedToast
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenBasicDialogRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenIconButtonWithResource
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenMultiSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenPasswordFieldWithActions
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenRowOfActions
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenSelectionDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextEntryDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextFieldWithActions
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenWideSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.IconResource
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultLinkedFieldType
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
@ -28,6 +34,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
*
|
||||
* @param customField The field that is to be displayed.
|
||||
* @param onCustomFieldValueChange Invoked when the user changes the value.
|
||||
* @param onCustomFieldAction Invoked when the user chooses an action.
|
||||
* @param modifier Modifier for the UI elements.
|
||||
* @param supportedLinkedTypes The supported linked types for the vault item.
|
||||
*/
|
||||
|
@ -36,15 +43,46 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
fun VaultAddEditCustomField(
|
||||
customField: VaultAddEditState.Custom,
|
||||
onCustomFieldValueChange: (VaultAddEditState.Custom) -> Unit,
|
||||
onCustomFieldAction: (CustomFieldAction, VaultAddEditState.Custom) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
supportedLinkedTypes: ImmutableList<VaultLinkedFieldType> = persistentListOf(),
|
||||
) {
|
||||
var shouldShowChooserDialog by remember { mutableStateOf(false) }
|
||||
var shouldShowEditDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (shouldShowChooserDialog) {
|
||||
CustomFieldActionDialog(
|
||||
onCustomFieldAction = { action ->
|
||||
shouldShowChooserDialog = false
|
||||
onCustomFieldAction(action, customField)
|
||||
},
|
||||
onDismissRequest = { shouldShowChooserDialog = false },
|
||||
onEditAction = {
|
||||
shouldShowEditDialog = true
|
||||
shouldShowChooserDialog = false
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (shouldShowEditDialog) {
|
||||
BitwardenTextEntryDialog(
|
||||
title = stringResource(id = R.string.custom_field_name),
|
||||
textFieldLabel = stringResource(id = R.string.name),
|
||||
onDismissRequest = { shouldShowEditDialog = false },
|
||||
onConfirmClick = { name ->
|
||||
onCustomFieldValueChange(customField.updateName(name))
|
||||
shouldShowEditDialog = false
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
when (customField) {
|
||||
is VaultAddEditState.Custom.BooleanField -> {
|
||||
CustomFieldBoolean(
|
||||
label = customField.name,
|
||||
value = customField.value,
|
||||
onValueChanged = { onCustomFieldValueChange(customField.copy(value = it)) },
|
||||
onEditValue = { shouldShowChooserDialog = true },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
@ -56,6 +94,7 @@ fun VaultAddEditCustomField(
|
|||
onValueChanged = {
|
||||
onCustomFieldValueChange(customField.copy(value = it))
|
||||
},
|
||||
onEditValue = { shouldShowChooserDialog = true },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
@ -68,6 +107,7 @@ fun VaultAddEditCustomField(
|
|||
onValueChanged = {
|
||||
onCustomFieldValueChange(customField.copy(vaultLinkedFieldType = it))
|
||||
},
|
||||
onEditValue = { shouldShowChooserDialog = true },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
@ -78,6 +118,7 @@ fun VaultAddEditCustomField(
|
|||
label = customField.name,
|
||||
value = customField.value,
|
||||
onValueChanged = { onCustomFieldValueChange(customField.copy(value = it)) },
|
||||
onEditValue = { shouldShowChooserDialog = true },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
@ -92,10 +133,9 @@ private fun CustomFieldBoolean(
|
|||
label: String,
|
||||
value: Boolean,
|
||||
onValueChanged: (Boolean) -> Unit,
|
||||
onEditValue: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.semantics(mergeDescendants = true) {}
|
||||
|
@ -116,10 +156,7 @@ private fun CustomFieldBoolean(
|
|||
iconPainter = painterResource(id = R.drawable.ic_settings),
|
||||
contentDescription = stringResource(id = R.string.edit),
|
||||
),
|
||||
onClick = {
|
||||
// TODO add support for custom field actions (BIT-540)
|
||||
showNotYetImplementedToast(context = context)
|
||||
},
|
||||
onClick = onEditValue,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
@ -134,10 +171,9 @@ private fun CustomFieldHiddenField(
|
|||
label: String,
|
||||
value: String,
|
||||
onValueChanged: (String) -> Unit,
|
||||
onEditValue: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
BitwardenPasswordFieldWithActions(
|
||||
label = label,
|
||||
value = value,
|
||||
|
@ -150,10 +186,7 @@ private fun CustomFieldHiddenField(
|
|||
iconPainter = painterResource(id = R.drawable.ic_settings),
|
||||
contentDescription = stringResource(id = R.string.edit),
|
||||
),
|
||||
onClick = {
|
||||
// TODO Add support for custom field actions (BIT-540)
|
||||
showNotYetImplementedToast(context = context)
|
||||
},
|
||||
onClick = onEditValue,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
@ -167,10 +200,9 @@ private fun CustomFieldTextField(
|
|||
label: String,
|
||||
value: String,
|
||||
onValueChanged: (String) -> Unit,
|
||||
onEditValue: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
BitwardenTextFieldWithActions(
|
||||
label = label,
|
||||
value = value,
|
||||
|
@ -183,10 +215,7 @@ private fun CustomFieldTextField(
|
|||
iconPainter = painterResource(id = R.drawable.ic_settings),
|
||||
contentDescription = stringResource(id = R.string.edit),
|
||||
),
|
||||
onClick = {
|
||||
// TODO add support for custom field actions (BIT-540)
|
||||
showNotYetImplementedToast(context = context)
|
||||
},
|
||||
onClick = onEditValue,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
@ -199,11 +228,11 @@ private fun CustomFieldTextField(
|
|||
private fun CustomFieldLinkedField(
|
||||
selectedOption: VaultLinkedFieldType,
|
||||
onValueChanged: (VaultLinkedFieldType) -> Unit,
|
||||
onEditValue: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
label: String = "",
|
||||
label: String = stringResource(id = R.string.options),
|
||||
supportedLinkedTypes: ImmutableList<VaultLinkedFieldType> = persistentListOf(),
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val possibleTypesWithStrings = supportedLinkedTypes.associateWith { it.label.invoke() }
|
||||
|
||||
Row(
|
||||
|
@ -233,12 +262,50 @@ private fun CustomFieldLinkedField(
|
|||
iconPainter = painterResource(id = R.drawable.ic_settings),
|
||||
contentDescription = stringResource(id = R.string.edit),
|
||||
),
|
||||
onClick = {
|
||||
// TODO add support for custom field actions (BIT-540)
|
||||
showNotYetImplementedToast(context = context)
|
||||
},
|
||||
onClick = onEditValue,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A dialog for editing a custom field item.
|
||||
*/
|
||||
@Composable
|
||||
private fun CustomFieldActionDialog(
|
||||
onCustomFieldAction: (CustomFieldAction) -> Unit,
|
||||
onEditAction: () -> Unit,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
BitwardenSelectionDialog(
|
||||
title = stringResource(id = R.string.options),
|
||||
onDismissRequest = onDismissRequest,
|
||||
) {
|
||||
CustomFieldAction
|
||||
.entries
|
||||
.forEach { action ->
|
||||
BitwardenBasicDialogRow(
|
||||
text = action.actionText.invoke(),
|
||||
onClick = {
|
||||
if (action == CustomFieldAction.EDIT) {
|
||||
onEditAction()
|
||||
} else {
|
||||
onCustomFieldAction(action)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method that will copy over the new name to a custom field.
|
||||
*/
|
||||
private fun VaultAddEditState.Custom.updateName(name: String): VaultAddEditState.Custom =
|
||||
when (this) {
|
||||
is VaultAddEditState.Custom.BooleanField -> this.copy(name = name)
|
||||
is VaultAddEditState.Custom.HiddenField -> this.copy(name = name)
|
||||
is VaultAddEditState.Custom.LinkedField -> this.copy(name = name)
|
||||
is VaultAddEditState.Custom.TextField -> this.copy(name = name)
|
||||
}
|
||||
|
|
|
@ -341,6 +341,7 @@ fun LazyListScope.vaultAddEditIdentityItems(
|
|||
VaultAddEditCustomField(
|
||||
customField = customItem,
|
||||
onCustomFieldValueChange = commonTypeHandlers.onCustomFieldValueChange,
|
||||
onCustomFieldAction = commonTypeHandlers.onCustomFieldActionSelect,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
|
|
|
@ -312,6 +312,7 @@ fun LazyListScope.vaultAddEditLoginItems(
|
|||
VaultAddEditCustomField(
|
||||
customItem,
|
||||
onCustomFieldValueChange = commonActionHandler.onCustomFieldValueChange,
|
||||
onCustomFieldAction = commonActionHandler.onCustomFieldActionSelect,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
|
|
|
@ -138,6 +138,7 @@ fun LazyListScope.vaultAddEditSecureNotesItems(
|
|||
VaultAddEditCustomField(
|
||||
customItem,
|
||||
onCustomFieldValueChange = commonTypeHandlers.onCustomFieldValueChange,
|
||||
onCustomFieldAction = commonTypeHandlers.onCustomFieldActionSelect,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
|
|
|
@ -20,6 +20,7 @@ import com.x8bit.bitwarden.ui.platform.base.util.asText
|
|||
import com.x8bit.bitwarden.ui.platform.base.util.concat
|
||||
import com.x8bit.bitwarden.ui.platform.manager.resource.ResourceManager
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.toCustomField
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toViewState
|
||||
|
@ -37,6 +38,7 @@ import kotlinx.coroutines.flow.update
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.util.Collections
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val KEY_STATE = "state"
|
||||
|
@ -148,6 +150,9 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
)
|
||||
|
||||
is VaultAddEditAction.Common.TooltipClick -> handleTooltipClick()
|
||||
is VaultAddEditAction.Common.CustomFieldActionSelect -> handleCustomFieldActionSelected(
|
||||
action,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -297,6 +302,67 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleCustomFieldActionSelected(
|
||||
action: VaultAddEditAction.Common.CustomFieldActionSelect,
|
||||
) {
|
||||
when (action.customFieldAction) {
|
||||
CustomFieldAction.MOVE_UP -> {
|
||||
val items =
|
||||
(mutableStateFlow.value.viewState as VaultAddEditState.ViewState.Content)
|
||||
.common
|
||||
.customFieldData
|
||||
.toMutableList()
|
||||
|
||||
val index = items.lastIndexOf(action.customField)
|
||||
if (index == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
Collections.swap(items, index, index - 1)
|
||||
|
||||
updateCommonContent { commonContent ->
|
||||
commonContent.copy(
|
||||
customFieldData = items,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
CustomFieldAction.MOVE_DOWN -> {
|
||||
val items =
|
||||
(mutableStateFlow.value.viewState as VaultAddEditState.ViewState.Content)
|
||||
.common
|
||||
.customFieldData
|
||||
.toMutableList()
|
||||
|
||||
val index = items.indexOf(action.customField)
|
||||
if (index == items.lastIndex) {
|
||||
return
|
||||
}
|
||||
|
||||
Collections.swap(items, index, index + 1)
|
||||
|
||||
updateCommonContent { commonContent ->
|
||||
commonContent.copy(
|
||||
customFieldData = items,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
CustomFieldAction.DELETE -> {
|
||||
updateCommonContent { commonContent ->
|
||||
commonContent.copy(
|
||||
customFieldData = commonContent.customFieldData.filter {
|
||||
it != action.customField
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing is done here since we handle this with a CustomFieldValueChange action
|
||||
CustomFieldAction.EDIT -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleFolderTextInputChange(
|
||||
action: VaultAddEditAction.Common.FolderChange,
|
||||
) {
|
||||
|
@ -1277,13 +1343,18 @@ data class VaultAddEditState(
|
|||
*/
|
||||
abstract val itemId: String
|
||||
|
||||
/**
|
||||
* The name of the custom field.
|
||||
*/
|
||||
abstract val name: String
|
||||
|
||||
/**
|
||||
* Represents the data for displaying a custom text field.
|
||||
*/
|
||||
@Parcelize
|
||||
data class TextField(
|
||||
override val itemId: String,
|
||||
val name: String,
|
||||
override val name: String,
|
||||
val value: String,
|
||||
) : Custom()
|
||||
|
||||
|
@ -1293,7 +1364,7 @@ data class VaultAddEditState(
|
|||
@Parcelize
|
||||
data class HiddenField(
|
||||
override val itemId: String,
|
||||
val name: String,
|
||||
override val name: String,
|
||||
val value: String,
|
||||
) : Custom()
|
||||
|
||||
|
@ -1303,7 +1374,7 @@ data class VaultAddEditState(
|
|||
@Parcelize
|
||||
data class BooleanField(
|
||||
override val itemId: String,
|
||||
val name: String,
|
||||
override val name: String,
|
||||
val value: Boolean,
|
||||
) : Custom()
|
||||
|
||||
|
@ -1313,7 +1384,7 @@ data class VaultAddEditState(
|
|||
@Parcelize
|
||||
data class LinkedField(
|
||||
override val itemId: String,
|
||||
val name: String,
|
||||
override val name: String,
|
||||
val vaultLinkedFieldType: VaultLinkedFieldType?,
|
||||
) : Custom()
|
||||
}
|
||||
|
@ -1462,6 +1533,14 @@ sealed class VaultAddEditAction {
|
|||
*/
|
||||
data class CustomFieldValueChange(val customField: VaultAddEditState.Custom) : Common()
|
||||
|
||||
/**
|
||||
* Fired when the custom field data is changed.
|
||||
*/
|
||||
data class CustomFieldActionSelect(
|
||||
val customFieldAction: CustomFieldAction,
|
||||
val customField: VaultAddEditState.Custom,
|
||||
) : Common()
|
||||
|
||||
/**
|
||||
* Represents the action to open tooltip
|
||||
*/
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.x8bit.bitwarden.ui.platform.base.util.asText
|
|||
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditAction
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditState
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditViewModel
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
|
||||
|
||||
/**
|
||||
|
@ -33,6 +34,7 @@ data class VaultAddEditCommonHandlers(
|
|||
val onTooltipClick: () -> Unit,
|
||||
val onAddNewCustomFieldClick: (CustomFieldType, String) -> Unit,
|
||||
val onCustomFieldValueChange: (VaultAddEditState.Custom) -> Unit,
|
||||
val onCustomFieldActionSelect: (CustomFieldAction, VaultAddEditState.Custom) -> Unit,
|
||||
) {
|
||||
companion object {
|
||||
|
||||
|
@ -97,6 +99,14 @@ data class VaultAddEditCommonHandlers(
|
|||
),
|
||||
)
|
||||
},
|
||||
onCustomFieldActionSelect = { customFieldAction, field ->
|
||||
viewModel.trySendAction(
|
||||
VaultAddEditAction.Common.CustomFieldActionSelect(
|
||||
customFieldAction,
|
||||
field,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package com.x8bit.bitwarden.ui.vault.feature.addedit.model
|
||||
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
|
||||
/**
|
||||
* Represents the different actions that can be taken in a custom
|
||||
* item edit menu.
|
||||
*/
|
||||
enum class CustomFieldAction(val actionText: Text) {
|
||||
EDIT(R.string.edit.asText()),
|
||||
MOVE_UP(R.string.move_up.asText()),
|
||||
MOVE_DOWN(R.string.move_down.asText()),
|
||||
DELETE(R.string.delete.asText()),
|
||||
}
|
|
@ -37,6 +37,7 @@ import com.x8bit.bitwarden.ui.util.onAllNodesWithContentDescriptionAfterScroll
|
|||
import com.x8bit.bitwarden.ui.util.onAllNodesWithTextAfterScroll
|
||||
import com.x8bit.bitwarden.ui.util.onNodeWithContentDescriptionAfterScroll
|
||||
import com.x8bit.bitwarden.ui.util.onNodeWithTextAfterScroll
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultCardBrand
|
||||
|
@ -1939,7 +1940,7 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
|||
verify {
|
||||
viewModel.trySendAction(
|
||||
VaultAddEditAction.Common.CustomFieldValueChange(
|
||||
VaultAddEditState.Custom.TextField("Test ID", "TestText", ""),
|
||||
VaultAddEditState.Custom.TextField("Test ID 2", "TestText", ""),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -1957,7 +1958,7 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
|||
verify {
|
||||
viewModel.trySendAction(
|
||||
VaultAddEditAction.Common.CustomFieldValueChange(
|
||||
VaultAddEditState.Custom.HiddenField("Test ID", "TestHidden", ""),
|
||||
VaultAddEditState.Custom.HiddenField("Test ID 3", "TestHidden", ""),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -1975,7 +1976,122 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
|||
verify {
|
||||
viewModel.trySendAction(
|
||||
VaultAddEditAction.Common.CustomFieldValueChange(
|
||||
VaultAddEditState.Custom.BooleanField("Test ID", "TestBoolean", true),
|
||||
VaultAddEditState.Custom.BooleanField("Test ID 1", "TestBoolean", true),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `clicking custom field edit icon and Edit option sends a CustomFieldValueChange action`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithContentDescriptionAfterScroll("Edit")
|
||||
.onFirst()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Edit")
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Name")
|
||||
.performTextInput("NewTestBooleanName")
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Ok")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
VaultAddEditAction.Common.CustomFieldValueChange(
|
||||
VaultAddEditState.Custom.BooleanField("Test ID 1", "NewTestBooleanName", false),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `clicking custom field edit icon and Delete option sends a CustomFieldActionSelect delete action`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithContentDescriptionAfterScroll("Edit")
|
||||
.onFirst()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Delete")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
VaultAddEditAction.Common.CustomFieldActionSelect(
|
||||
customFieldAction = CustomFieldAction.DELETE,
|
||||
customField = VaultAddEditState.Custom.BooleanField(
|
||||
itemId = "Test ID 1",
|
||||
name = "TestBoolean",
|
||||
value = false,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `clicking custom field edit icon and Move down option sends a CustomFieldActionSelect move down action`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithContentDescriptionAfterScroll("Edit")
|
||||
.onFirst()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Move down")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
VaultAddEditAction.Common.CustomFieldActionSelect(
|
||||
customFieldAction = CustomFieldAction.MOVE_DOWN,
|
||||
customField = VaultAddEditState.Custom.BooleanField(
|
||||
itemId = "Test ID 1",
|
||||
name = "TestBoolean",
|
||||
value = false,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `clicking custom field edit icon and Move Up options sends a CustomFieldActionSelect move up action`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES_CUSTOM_FIELDS
|
||||
|
||||
composeTestRule
|
||||
.onAllNodesWithContentDescriptionAfterScroll("Edit")
|
||||
.onFirst()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Move Up")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
VaultAddEditAction.Common.CustomFieldActionSelect(
|
||||
customFieldAction = CustomFieldAction.MOVE_UP,
|
||||
customField = VaultAddEditState.Custom.BooleanField(
|
||||
itemId = "Test ID 1",
|
||||
name = "TestBoolean",
|
||||
value = false,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -2111,10 +2227,10 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
|||
viewState = VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(
|
||||
customFieldData = listOf(
|
||||
VaultAddEditState.Custom.BooleanField("Test ID", "TestBoolean", false),
|
||||
VaultAddEditState.Custom.TextField("Test ID", "TestText", "TestTextVal"),
|
||||
VaultAddEditState.Custom.BooleanField("Test ID 1", "TestBoolean", false),
|
||||
VaultAddEditState.Custom.TextField("Test ID 2", "TestText", "TestTextVal"),
|
||||
VaultAddEditState.Custom.HiddenField(
|
||||
"Test ID",
|
||||
"Test ID 3",
|
||||
"TestHidden",
|
||||
"TestHiddenVal",
|
||||
),
|
||||
|
|
|
@ -18,6 +18,7 @@ import com.x8bit.bitwarden.ui.platform.base.util.Text
|
|||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.manager.resource.ResourceManager
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.toCustomField
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toViewState
|
||||
|
@ -1444,6 +1445,201 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `CustomFieldValueChange should update name field`() = runTest {
|
||||
val initState = createVaultAddItemState(
|
||||
typeContentViewState = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
|
||||
commonContentViewState = VaultAddEditState.ViewState.Content.Common(
|
||||
customFieldData = listOf(
|
||||
VaultAddEditState.Custom.BooleanField(
|
||||
"TestId 3",
|
||||
"Boolean Field",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertCustomFieldValueChange(
|
||||
initState,
|
||||
CustomFieldType.BOOLEAN,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `CustomFieldActionSelect with delete action should delete the item`() = runTest {
|
||||
val customFieldData = VaultAddEditState.Custom.BooleanField(
|
||||
"TestId 3",
|
||||
"Boolean Field",
|
||||
true,
|
||||
)
|
||||
|
||||
val initState = createVaultAddItemState(
|
||||
typeContentViewState = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
|
||||
commonContentViewState = VaultAddEditState.ViewState.Content.Common(
|
||||
customFieldData = listOf(customFieldData),
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createAddVaultItemViewModel(
|
||||
savedStateHandle = createSavedStateHandleWithState(
|
||||
state = initState,
|
||||
vaultAddEditType = VaultAddEditType.AddItem,
|
||||
),
|
||||
)
|
||||
val currentContentState =
|
||||
(viewModel.stateFlow.value.viewState as VaultAddEditState.ViewState.Content)
|
||||
val expectedState = currentContentState
|
||||
.copy(
|
||||
common = currentContentState.common.copy(
|
||||
customFieldData = listOf(),
|
||||
),
|
||||
)
|
||||
|
||||
viewModel.actionChannel.trySend(
|
||||
VaultAddEditAction.Common.CustomFieldActionSelect(
|
||||
CustomFieldAction.DELETE,
|
||||
customFieldData,
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expectedState,
|
||||
viewModel.stateFlow.value.viewState,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `CustomFieldActionSelect with move up action should move the item up`() = runTest {
|
||||
val customFieldData =
|
||||
VaultAddEditState.Custom.BooleanField(
|
||||
"TestId 3",
|
||||
"Boolean Field",
|
||||
true,
|
||||
)
|
||||
|
||||
val initState = createVaultAddItemState(
|
||||
typeContentViewState = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
|
||||
commonContentViewState = VaultAddEditState.ViewState.Content.Common(
|
||||
customFieldData = listOf(
|
||||
VaultAddEditState.Custom.BooleanField(
|
||||
"TestId 1",
|
||||
"Boolean Field",
|
||||
true,
|
||||
), VaultAddEditState.Custom.BooleanField(
|
||||
"TestId 3",
|
||||
"Boolean Field",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createAddVaultItemViewModel(
|
||||
savedStateHandle = createSavedStateHandleWithState(
|
||||
state = initState,
|
||||
vaultAddEditType = VaultAddEditType.AddItem,
|
||||
),
|
||||
)
|
||||
val currentContentState =
|
||||
(viewModel.stateFlow.value.viewState as VaultAddEditState.ViewState.Content)
|
||||
val expectedState = currentContentState
|
||||
.copy(
|
||||
common = currentContentState.common.copy(
|
||||
customFieldData = listOf(
|
||||
VaultAddEditState.Custom.BooleanField(
|
||||
"TestId 3",
|
||||
"Boolean Field",
|
||||
true,
|
||||
),
|
||||
VaultAddEditState.Custom.BooleanField(
|
||||
"TestId 1",
|
||||
"Boolean Field",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
viewModel.actionChannel.trySend(
|
||||
VaultAddEditAction.Common.CustomFieldActionSelect(
|
||||
CustomFieldAction.MOVE_UP,
|
||||
customFieldData,
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expectedState,
|
||||
viewModel.stateFlow.value.viewState,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `CustomFieldActionSelect with move down action should move the item down`() = runTest {
|
||||
val customFieldData =
|
||||
VaultAddEditState.Custom.BooleanField(
|
||||
"TestId 1",
|
||||
"Boolean Field",
|
||||
true,
|
||||
)
|
||||
|
||||
val initState = createVaultAddItemState(
|
||||
typeContentViewState = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
|
||||
commonContentViewState = VaultAddEditState.ViewState.Content.Common(
|
||||
customFieldData = listOf(
|
||||
VaultAddEditState.Custom.BooleanField(
|
||||
"TestId 1",
|
||||
"Boolean Field",
|
||||
true,
|
||||
),
|
||||
VaultAddEditState.Custom.BooleanField(
|
||||
"TestId 3",
|
||||
"Boolean Field",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createAddVaultItemViewModel(
|
||||
savedStateHandle = createSavedStateHandleWithState(
|
||||
state = initState,
|
||||
vaultAddEditType = VaultAddEditType.AddItem,
|
||||
),
|
||||
)
|
||||
val currentContentState =
|
||||
(viewModel.stateFlow.value.viewState as VaultAddEditState.ViewState.Content)
|
||||
val expectedState = currentContentState
|
||||
.copy(
|
||||
common = currentContentState.common.copy(
|
||||
customFieldData = listOf(
|
||||
VaultAddEditState.Custom.BooleanField(
|
||||
"TestId 3",
|
||||
"Boolean Field",
|
||||
true,
|
||||
),
|
||||
VaultAddEditState.Custom.BooleanField(
|
||||
"TestId 1",
|
||||
"Boolean Field",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
viewModel.actionChannel.trySend(
|
||||
VaultAddEditAction.Common.CustomFieldActionSelect(
|
||||
CustomFieldAction.MOVE_DOWN,
|
||||
customFieldData,
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expectedState,
|
||||
viewModel.stateFlow.value.viewState,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `TooltipClick should emit ShowToast with 'Tooltip' message`() = runTest {
|
||||
viewModel.eventFlow.test {
|
||||
|
|
Loading…
Add table
Reference in a new issue