mirror of
https://github.com/bitwarden/android.git
synced 2026-03-21 22:00:42 -05:00
PM-19131 - Custom hidden fields not working properly if TOTP also configured (#4916)
This commit is contained in:
@@ -1597,11 +1597,17 @@ data class VaultItemState(
|
||||
* Represents a custom field, TextField, HiddenField, BooleanField, or LinkedField.
|
||||
*/
|
||||
sealed class Custom : Parcelable {
|
||||
/**
|
||||
* The unique ID of the custom field.
|
||||
*/
|
||||
abstract val id: String
|
||||
|
||||
/**
|
||||
* Represents the data for displaying a custom text field.
|
||||
*/
|
||||
@Parcelize
|
||||
data class TextField(
|
||||
override val id: String,
|
||||
val name: String,
|
||||
val value: String,
|
||||
val isCopyable: Boolean,
|
||||
@@ -1612,6 +1618,7 @@ data class VaultItemState(
|
||||
*/
|
||||
@Parcelize
|
||||
data class HiddenField(
|
||||
override val id: String,
|
||||
val name: String,
|
||||
val value: String,
|
||||
val isCopyable: Boolean,
|
||||
@@ -1623,6 +1630,7 @@ data class VaultItemState(
|
||||
*/
|
||||
@Parcelize
|
||||
data class BooleanField(
|
||||
override val id: String,
|
||||
val name: String,
|
||||
val value: Boolean,
|
||||
) : Custom()
|
||||
@@ -1632,6 +1640,7 @@ data class VaultItemState(
|
||||
*/
|
||||
@Parcelize
|
||||
data class LinkedField(
|
||||
override val id: String,
|
||||
val vaultLinkedFieldType: VaultLinkedFieldType,
|
||||
val name: String,
|
||||
) : Custom()
|
||||
|
||||
@@ -57,7 +57,14 @@ fun CipherView.toViewState(
|
||||
name = name,
|
||||
requiresReprompt = (reprompt == CipherRepromptType.PASSWORD && hasMasterPassword) &&
|
||||
previousState?.common?.requiresReprompt != false,
|
||||
customFields = fields.orEmpty().map { it.toCustomField() },
|
||||
customFields = fields.orEmpty().map { fieldView ->
|
||||
fieldView.toCustomField(
|
||||
previousState = previousState
|
||||
?.common
|
||||
?.customFields
|
||||
?.find { it.id == fieldView.hashCode().toString() },
|
||||
)
|
||||
},
|
||||
lastUpdated = revisionDate.toFormattedPattern(
|
||||
pattern = LAST_UPDATED_DATE_TIME_PATTERN,
|
||||
clock = clock,
|
||||
@@ -195,27 +202,39 @@ fun CipherView.toViewState(
|
||||
},
|
||||
)
|
||||
|
||||
private fun FieldView.toCustomField(): VaultItemState.ViewState.Content.Common.Custom =
|
||||
/**
|
||||
* Transforms [FieldView] into [VaultItemState.ViewState.Content.Common.Custom].
|
||||
*/
|
||||
fun FieldView.toCustomField(
|
||||
previousState: VaultItemState.ViewState.Content.Common.Custom?,
|
||||
): VaultItemState.ViewState.Content.Common.Custom =
|
||||
when (type) {
|
||||
FieldType.TEXT -> VaultItemState.ViewState.Content.Common.Custom.TextField(
|
||||
id = this.hashCode().toString(),
|
||||
name = name.orEmpty(),
|
||||
value = value.orZeroWidthSpace(),
|
||||
isCopyable = !value.isNullOrBlank(),
|
||||
)
|
||||
|
||||
FieldType.HIDDEN -> VaultItemState.ViewState.Content.Common.Custom.HiddenField(
|
||||
id = this.hashCode().toString(),
|
||||
name = name.orEmpty(),
|
||||
value = value.orZeroWidthSpace(),
|
||||
isCopyable = !value.isNullOrBlank(),
|
||||
isVisible = false,
|
||||
isVisible = (previousState as?
|
||||
VaultItemState.ViewState.Content.Common.Custom.HiddenField)
|
||||
?.isVisible
|
||||
?: false,
|
||||
)
|
||||
|
||||
FieldType.BOOLEAN -> VaultItemState.ViewState.Content.Common.Custom.BooleanField(
|
||||
id = this.hashCode().toString(),
|
||||
name = name.orEmpty(),
|
||||
value = value?.toBoolean() ?: false,
|
||||
)
|
||||
|
||||
FieldType.LINKED -> VaultItemState.ViewState.Content.Common.Custom.LinkedField(
|
||||
id = this.hashCode().toString(),
|
||||
vaultLinkedFieldType = VaultLinkedFieldType.fromId(requireNotNull(linkedId)),
|
||||
name = name.orEmpty(),
|
||||
)
|
||||
|
||||
@@ -776,6 +776,7 @@ class VaultItemScreenTest : BaseComposeTest() {
|
||||
@Test
|
||||
fun `on show hidden field click should send HiddenFieldVisibilityClicked`() {
|
||||
val textField = VaultItemState.ViewState.Content.Common.Custom.HiddenField(
|
||||
id = "12345",
|
||||
name = "hidden",
|
||||
value = "hidden password",
|
||||
isCopyable = true,
|
||||
@@ -814,6 +815,7 @@ class VaultItemScreenTest : BaseComposeTest() {
|
||||
@Test
|
||||
fun `copy hidden field button should be displayed according to state`() {
|
||||
val hiddenField = VaultItemState.ViewState.Content.Common.Custom.HiddenField(
|
||||
id = "12345",
|
||||
name = "hidden",
|
||||
value = "hidden password",
|
||||
isCopyable = true,
|
||||
@@ -855,6 +857,7 @@ class VaultItemScreenTest : BaseComposeTest() {
|
||||
@Test
|
||||
fun `on copy hidden field click should send CopyCustomHiddenFieldClick`() {
|
||||
val hiddenField = VaultItemState.ViewState.Content.Common.Custom.HiddenField(
|
||||
id = "12345",
|
||||
name = "hidden",
|
||||
value = "hidden password",
|
||||
isCopyable = true,
|
||||
@@ -890,6 +893,7 @@ class VaultItemScreenTest : BaseComposeTest() {
|
||||
@Test
|
||||
fun `on copy text field click should send CopyCustomTextFieldClick`() {
|
||||
val textField = VaultItemState.ViewState.Content.Common.Custom.TextField(
|
||||
id = "12345",
|
||||
name = "text",
|
||||
value = "value",
|
||||
isCopyable = true,
|
||||
@@ -924,6 +928,7 @@ class VaultItemScreenTest : BaseComposeTest() {
|
||||
@Test
|
||||
fun `text field copy button should be displayed according to state`() {
|
||||
val textField = VaultItemState.ViewState.Content.Common.Custom.TextField(
|
||||
id = "12345",
|
||||
name = "text",
|
||||
value = "value",
|
||||
isCopyable = true,
|
||||
@@ -1552,6 +1557,7 @@ class VaultItemScreenTest : BaseComposeTest() {
|
||||
// Adding a custom field so that we can scroll to it
|
||||
// So we can see the Copy notes button but not have it covered by the FAB
|
||||
val textField = VaultItemState.ViewState.Content.Common.Custom.TextField(
|
||||
id = "12345",
|
||||
name = "text",
|
||||
value = "value",
|
||||
isCopyable = true,
|
||||
@@ -1587,6 +1593,7 @@ class VaultItemScreenTest : BaseComposeTest() {
|
||||
// Adding a custom field so that we can scroll to it
|
||||
// So we can see the Copy notes button but not have it covered by the FAB
|
||||
val textField = VaultItemState.ViewState.Content.Common.Custom.TextField(
|
||||
id = "12345",
|
||||
name = "text",
|
||||
value = "value",
|
||||
isCopyable = true,
|
||||
@@ -1642,6 +1649,7 @@ class VaultItemScreenTest : BaseComposeTest() {
|
||||
// Adding a custom field so that we can scroll to it
|
||||
// So we can see the Copy notes button but not have it covered by the FAB
|
||||
val textField = VaultItemState.ViewState.Content.Common.Custom.TextField(
|
||||
id = "12345",
|
||||
name = "text",
|
||||
value = "value",
|
||||
isCopyable = true,
|
||||
@@ -1885,11 +1893,13 @@ class VaultItemScreenTest : BaseComposeTest() {
|
||||
fun `in login state, linked custom fields should be displayed according to state`() {
|
||||
val linkedFieldUserName =
|
||||
VaultItemState.ViewState.Content.Common.Custom.LinkedField(
|
||||
id = "12345",
|
||||
name = "linked username",
|
||||
vaultLinkedFieldType = VaultLinkedFieldType.USERNAME,
|
||||
)
|
||||
|
||||
val linkedFieldsPassword = VaultItemState.ViewState.Content.Common.Custom.LinkedField(
|
||||
id = "12345",
|
||||
name = "linked password",
|
||||
vaultLinkedFieldType = VaultLinkedFieldType.PASSWORD,
|
||||
)
|
||||
@@ -3149,17 +3159,20 @@ private val DEFAULT_COMMON: VaultItemState.ViewState.Content.Common =
|
||||
notes = "Lots of notes",
|
||||
customFields = listOf(
|
||||
VaultItemState.ViewState.Content.Common.Custom.TextField(
|
||||
id = "12345",
|
||||
name = "text",
|
||||
value = "value",
|
||||
isCopyable = true,
|
||||
),
|
||||
VaultItemState.ViewState.Content.Common.Custom.HiddenField(
|
||||
id = "12345",
|
||||
name = "hidden",
|
||||
value = "hidden password",
|
||||
isCopyable = true,
|
||||
isVisible = false,
|
||||
),
|
||||
VaultItemState.ViewState.Content.Common.Custom.BooleanField(
|
||||
id = "12345",
|
||||
name = "boolean",
|
||||
value = true,
|
||||
),
|
||||
|
||||
@@ -1070,6 +1070,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
runTest {
|
||||
val loginState = DEFAULT_STATE.copy(viewState = DEFAULT_VIEW_STATE)
|
||||
val field = VaultItemState.ViewState.Content.Common.Custom.HiddenField(
|
||||
id = "12345",
|
||||
name = "hidden",
|
||||
value = "value",
|
||||
isCopyable = true,
|
||||
@@ -1135,6 +1136,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
runTest {
|
||||
val hiddenField =
|
||||
VaultItemState.ViewState.Content.Common.Custom.HiddenField(
|
||||
id = "12345",
|
||||
name = "hidden",
|
||||
value = "value",
|
||||
isCopyable = true,
|
||||
@@ -3702,25 +3704,30 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
notes = "Lots of notes",
|
||||
customFields = listOf(
|
||||
VaultItemState.ViewState.Content.Common.Custom.TextField(
|
||||
id = "12345",
|
||||
name = "text",
|
||||
value = "value",
|
||||
isCopyable = true,
|
||||
),
|
||||
VaultItemState.ViewState.Content.Common.Custom.HiddenField(
|
||||
id = "12345",
|
||||
name = "hidden",
|
||||
value = "value",
|
||||
isCopyable = true,
|
||||
isVisible = false,
|
||||
),
|
||||
VaultItemState.ViewState.Content.Common.Custom.BooleanField(
|
||||
id = "12345",
|
||||
name = "boolean",
|
||||
value = true,
|
||||
),
|
||||
VaultItemState.ViewState.Content.Common.Custom.LinkedField(
|
||||
id = "12345",
|
||||
name = "linked username",
|
||||
vaultLinkedFieldType = VaultLinkedFieldType.USERNAME,
|
||||
),
|
||||
VaultItemState.ViewState.Content.Common.Custom.LinkedField(
|
||||
id = "12345",
|
||||
name = "linked password",
|
||||
vaultLinkedFieldType = VaultLinkedFieldType.PASSWORD,
|
||||
),
|
||||
|
||||
@@ -18,7 +18,6 @@ import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.IconData
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.VaultItemState
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.model.TotpCodeItemData
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultLinkedFieldType
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.time.Instant
|
||||
|
||||
@@ -188,29 +187,41 @@ fun createCommonContent(
|
||||
lastUpdated = "1/1/70 12:16 AM",
|
||||
notes = "Lots of notes",
|
||||
customFields = listOf(
|
||||
VaultItemState.ViewState.Content.Common.Custom.TextField(
|
||||
FieldView(
|
||||
name = "text",
|
||||
value = "value",
|
||||
isCopyable = true,
|
||||
),
|
||||
VaultItemState.ViewState.Content.Common.Custom.HiddenField(
|
||||
type = FieldType.TEXT,
|
||||
linkedId = null,
|
||||
)
|
||||
.toCustomField(null),
|
||||
FieldView(
|
||||
name = "hidden",
|
||||
value = "value",
|
||||
isCopyable = true,
|
||||
isVisible = false,
|
||||
),
|
||||
VaultItemState.ViewState.Content.Common.Custom.BooleanField(
|
||||
type = FieldType.HIDDEN,
|
||||
linkedId = null,
|
||||
)
|
||||
.toCustomField(null),
|
||||
FieldView(
|
||||
name = "boolean",
|
||||
value = true,
|
||||
),
|
||||
VaultItemState.ViewState.Content.Common.Custom.LinkedField(
|
||||
value = "true",
|
||||
type = FieldType.BOOLEAN,
|
||||
linkedId = null,
|
||||
)
|
||||
.toCustomField(null),
|
||||
FieldView(
|
||||
name = "linked username",
|
||||
vaultLinkedFieldType = VaultLinkedFieldType.USERNAME,
|
||||
),
|
||||
VaultItemState.ViewState.Content.Common.Custom.LinkedField(
|
||||
value = null,
|
||||
type = FieldType.LINKED,
|
||||
linkedId = 100U,
|
||||
)
|
||||
.toCustomField(null),
|
||||
FieldView(
|
||||
name = "linked password",
|
||||
vaultLinkedFieldType = VaultLinkedFieldType.PASSWORD,
|
||||
),
|
||||
value = null,
|
||||
type = FieldType.LINKED,
|
||||
linkedId = 101U,
|
||||
)
|
||||
.toCustomField(null),
|
||||
),
|
||||
requiresReprompt = true,
|
||||
requiresCloneConfirmation = true,
|
||||
|
||||
Reference in New Issue
Block a user