mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
Add linked fields for identity (#536)
This commit is contained in:
parent
e318a316e7
commit
36d49a62a6
7 changed files with 149 additions and 25 deletions
|
@ -61,14 +61,16 @@ fun VaultAddEditCustomField(
|
||||||
}
|
}
|
||||||
|
|
||||||
is VaultAddEditState.Custom.LinkedField -> {
|
is VaultAddEditState.Custom.LinkedField -> {
|
||||||
CustomFieldLinkedField(
|
customField.vaultLinkedFieldType?.let { fieldType ->
|
||||||
selectedOption = customField.vaultLinkedFieldType,
|
CustomFieldLinkedField(
|
||||||
supportedLinkedTypes = supportedLinkedTypes,
|
selectedOption = fieldType,
|
||||||
onValueChanged = {
|
supportedLinkedTypes = supportedLinkedTypes,
|
||||||
onCustomFieldValueChange(customField.copy(vaultLinkedFieldType = it))
|
onValueChanged = {
|
||||||
},
|
onCustomFieldValueChange(customField.copy(vaultLinkedFieldType = it))
|
||||||
modifier = modifier,
|
},
|
||||||
)
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is VaultAddEditState.Custom.TextField -> {
|
is VaultAddEditState.Custom.TextField -> {
|
||||||
|
|
|
@ -339,14 +339,31 @@ fun LazyListScope.vaultAddEditIdentityItems(
|
||||||
|
|
||||||
items(commonState.customFieldData) { customItem ->
|
items(commonState.customFieldData) { customItem ->
|
||||||
VaultAddEditCustomField(
|
VaultAddEditCustomField(
|
||||||
customItem,
|
customField = customItem,
|
||||||
onCustomFieldValueChange = commonTypeHandlers.onCustomFieldValueChange,
|
onCustomFieldValueChange = commonTypeHandlers.onCustomFieldValueChange,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp),
|
.padding(horizontal = 16.dp),
|
||||||
supportedLinkedTypes = persistentListOf(
|
supportedLinkedTypes = persistentListOf(
|
||||||
VaultLinkedFieldType.PASSWORD,
|
VaultLinkedFieldType.TITLE,
|
||||||
VaultLinkedFieldType.USERNAME,
|
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,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,6 +147,7 @@ class VaultAddEditViewModel @Inject constructor(
|
||||||
private fun handleSwitchToAddLoginItem() {
|
private fun handleSwitchToAddLoginItem() {
|
||||||
updateContent { currentContent ->
|
updateContent { currentContent ->
|
||||||
currentContent.copy(
|
currentContent.copy(
|
||||||
|
common = currentContent.common.clearNonSharedData(),
|
||||||
type = VaultAddEditState.ViewState.Content.ItemType.Login(),
|
type = VaultAddEditState.ViewState.Content.ItemType.Login(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -155,6 +156,7 @@ class VaultAddEditViewModel @Inject constructor(
|
||||||
private fun handleSwitchToAddSecureNotesItem() {
|
private fun handleSwitchToAddSecureNotesItem() {
|
||||||
updateContent { currentContent ->
|
updateContent { currentContent ->
|
||||||
currentContent.copy(
|
currentContent.copy(
|
||||||
|
common = currentContent.common.clearNonSharedData(),
|
||||||
type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
|
type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -163,6 +165,7 @@ class VaultAddEditViewModel @Inject constructor(
|
||||||
private fun handleSwitchToAddCardItem() {
|
private fun handleSwitchToAddCardItem() {
|
||||||
updateContent { currentContent ->
|
updateContent { currentContent ->
|
||||||
currentContent.copy(
|
currentContent.copy(
|
||||||
|
common = currentContent.common.clearNonSharedData(),
|
||||||
type = VaultAddEditState.ViewState.Content.ItemType.Card(),
|
type = VaultAddEditState.ViewState.Content.ItemType.Card(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -171,6 +174,7 @@ class VaultAddEditViewModel @Inject constructor(
|
||||||
private fun handleSwitchToAddIdentityItem() {
|
private fun handleSwitchToAddIdentityItem() {
|
||||||
updateContent { currentContent ->
|
updateContent { currentContent ->
|
||||||
currentContent.copy(
|
currentContent.copy(
|
||||||
|
common = currentContent.common.clearNonSharedData(),
|
||||||
type = VaultAddEditState.ViewState.Content.ItemType.Identity(),
|
type = VaultAddEditState.ViewState.Content.ItemType.Identity(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -230,13 +234,17 @@ class VaultAddEditViewModel @Inject constructor(
|
||||||
private fun handleAddNewCustomFieldClick(
|
private fun handleAddNewCustomFieldClick(
|
||||||
action: VaultAddEditAction.Common.AddNewCustomFieldClick,
|
action: VaultAddEditAction.Common.AddNewCustomFieldClick,
|
||||||
) {
|
) {
|
||||||
|
updateContent {
|
||||||
updateCommonContent { common ->
|
|
||||||
|
|
||||||
val newCustomData: VaultAddEditState.Custom =
|
val newCustomData: VaultAddEditState.Custom =
|
||||||
action.customFieldType.toCustomField(action.name)
|
action.customFieldType.toCustomField(
|
||||||
|
name = action.name,
|
||||||
common.copy(customFieldData = common.customFieldData + newCustomData)
|
itemType = it.type,
|
||||||
|
)
|
||||||
|
it.copy(
|
||||||
|
common = it
|
||||||
|
.common
|
||||||
|
.copy(customFieldData = it.common.customFieldData + newCustomData),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,6 +465,7 @@ class VaultAddEditViewModel @Inject constructor(
|
||||||
//endregion Add Login Item Type Handlers
|
//endregion Add Login Item Type Handlers
|
||||||
|
|
||||||
//region Identity Type Handlers
|
//region Identity Type Handlers
|
||||||
|
@Suppress("LongMethod")
|
||||||
private fun handleIdentityTypeActions(action: VaultAddEditAction.ItemType.IdentityType) {
|
private fun handleIdentityTypeActions(action: VaultAddEditAction.ItemType.IdentityType) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is VaultAddEditAction.ItemType.IdentityType.FirstNameTextChange -> {
|
is VaultAddEditAction.ItemType.IdentityType.FirstNameTextChange -> {
|
||||||
|
@ -909,6 +918,13 @@ class VaultAddEditViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun VaultAddEditState.ViewState.Content.Common.clearNonSharedData():
|
||||||
|
VaultAddEditState.ViewState.Content.Common =
|
||||||
|
copy(
|
||||||
|
customFieldData = customFieldData
|
||||||
|
.filterNot { it is VaultAddEditState.Custom.LinkedField },
|
||||||
|
)
|
||||||
|
|
||||||
//endregion Utility Functions
|
//endregion Utility Functions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1185,7 +1201,7 @@ data class VaultAddEditState(
|
||||||
data class LinkedField(
|
data class LinkedField(
|
||||||
override val itemId: String,
|
override val itemId: String,
|
||||||
val name: String,
|
val name: String,
|
||||||
val vaultLinkedFieldType: VaultLinkedFieldType,
|
val vaultLinkedFieldType: VaultLinkedFieldType?,
|
||||||
) : Custom()
|
) : Custom()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ enum class CustomFieldType(val typeText: Text) {
|
||||||
*/
|
*/
|
||||||
fun CustomFieldType.toCustomField(
|
fun CustomFieldType.toCustomField(
|
||||||
name: String,
|
name: String,
|
||||||
|
itemType: VaultAddEditState.ViewState.Content.ItemType,
|
||||||
): VaultAddEditState.Custom {
|
): VaultAddEditState.Custom {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
CustomFieldType.BOOLEAN -> {
|
CustomFieldType.BOOLEAN -> {
|
||||||
|
@ -36,7 +37,7 @@ fun CustomFieldType.toCustomField(
|
||||||
VaultAddEditState.Custom.LinkedField(
|
VaultAddEditState.Custom.LinkedField(
|
||||||
itemId = UUID.randomUUID().toString(),
|
itemId = UUID.randomUUID().toString(),
|
||||||
name = name,
|
name = name,
|
||||||
vaultLinkedFieldType = VaultLinkedFieldType.USERNAME,
|
vaultLinkedFieldType = itemType.defaultLinkedFieldTypeOrNull,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,3 +58,12 @@ fun CustomFieldType.toCustomField(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
private val VaultAddEditState.ViewState.Content.ItemType.defaultLinkedFieldTypeOrNull: VaultLinkedFieldType?
|
||||||
|
get() = when (this) {
|
||||||
|
is VaultAddEditState.ViewState.Content.ItemType.Card -> VaultLinkedFieldType.CARDHOLDER_NAME
|
||||||
|
is VaultAddEditState.ViewState.Content.ItemType.Identity -> VaultLinkedFieldType.TITLE
|
||||||
|
is VaultAddEditState.ViewState.Content.ItemType.Login -> VaultLinkedFieldType.USERNAME
|
||||||
|
is VaultAddEditState.ViewState.Content.ItemType.SecureNotes -> null
|
||||||
|
}
|
||||||
|
|
|
@ -177,7 +177,7 @@ private fun VaultAddEditState.Custom.toFieldView(): FieldView =
|
||||||
name = item.name,
|
name = item.name,
|
||||||
value = null,
|
value = null,
|
||||||
type = FieldType.LINKED,
|
type = FieldType.LINKED,
|
||||||
linkedId = item.vaultLinkedFieldType.id,
|
linkedId = item.vaultLinkedFieldType?.id,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,26 @@ enum class VaultLinkedFieldType(
|
||||||
SECURITY_CODE(id = 303.toUInt(), label = R.string.security_code.asText()),
|
SECURITY_CODE(id = 303.toUInt(), label = R.string.security_code.asText()),
|
||||||
BRAND(id = 304.toUInt(), label = R.string.brand.asText()),
|
BRAND(id = 304.toUInt(), label = R.string.brand.asText()),
|
||||||
NUMBER(id = 305.toUInt(), label = R.string.number.asText()),
|
NUMBER(id = 305.toUInt(), label = R.string.number.asText()),
|
||||||
|
|
||||||
|
TITLE(id = 400.toUInt(), label = R.string.title.asText()),
|
||||||
|
MIDDLE_NAME(id = 401.toUInt(), label = R.string.middle_name.asText()),
|
||||||
|
ADDRESS_1(id = 402.toUInt(), label = R.string.address1.asText()),
|
||||||
|
ADDRESS_2(id = 403.toUInt(), label = R.string.address2.asText()),
|
||||||
|
ADDRESS_3(id = 404.toUInt(), label = R.string.address3.asText()),
|
||||||
|
CITY(id = 405.toUInt(), label = R.string.city_town.asText()),
|
||||||
|
STATE(id = 406.toUInt(), label = R.string.state_province.asText()),
|
||||||
|
POSTAL_CODE(id = 407.toUInt(), label = R.string.zip_postal_code.asText()),
|
||||||
|
COUNTRY(id = 408.toUInt(), label = R.string.country.asText()),
|
||||||
|
COMPANY(id = 409.toUInt(), label = R.string.company.asText()),
|
||||||
|
EMAIL(id = 410.toUInt(), label = R.string.email.asText()),
|
||||||
|
PHONE(id = 411.toUInt(), label = R.string.phone.asText()),
|
||||||
|
SSN(id = 412.toUInt(), label = R.string.ssn.asText()),
|
||||||
|
IDENTITY_USERNAME(id = 413.toUInt(), label = R.string.username.asText()),
|
||||||
|
PASSPORT_NUMBER(id = 414.toUInt(), label = R.string.passport_number.asText()),
|
||||||
|
LICENSE_NUMBER(id = 415.toUInt(), label = R.string.license_number.asText()),
|
||||||
|
FIRST_NAME(id = 416.toUInt(), label = R.string.first_name.asText()),
|
||||||
|
LAST_NAME(id = 417.toUInt(), label = R.string.last_name.asText()),
|
||||||
|
FULL_NAME(id = 418.toUInt(), label = R.string.full_name.asText()),
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -31,19 +31,72 @@ class CustomFieldTypeTests {
|
||||||
val type = CustomFieldType.BOOLEAN
|
val type = CustomFieldType.BOOLEAN
|
||||||
|
|
||||||
val expected = VaultAddEditState.Custom.BooleanField(TEST_ID, "test", false)
|
val expected = VaultAddEditState.Custom.BooleanField(TEST_ID, "test", false)
|
||||||
val actual = type.toCustomField(name)
|
val actual = type.toCustomField(
|
||||||
|
name = name,
|
||||||
|
itemType = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
|
||||||
|
)
|
||||||
|
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `toCustomField should return a custom linked type when we pass in required linked type`() {
|
fun `toCustomField should return a custom linked type when we pass in Login type`() {
|
||||||
val name = "test"
|
val name = "test"
|
||||||
val type = CustomFieldType.LINKED
|
val type = CustomFieldType.LINKED
|
||||||
|
|
||||||
val expected =
|
val expected =
|
||||||
VaultAddEditState.Custom.LinkedField(TEST_ID, "test", VaultLinkedFieldType.USERNAME)
|
VaultAddEditState.Custom.LinkedField(TEST_ID, "test", VaultLinkedFieldType.USERNAME)
|
||||||
val actual = type.toCustomField(name)
|
val actual = type.toCustomField(
|
||||||
|
name = name,
|
||||||
|
itemType = VaultAddEditState.ViewState.Content.ItemType.Login(),
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `toCustomField should return a custom linked type when we pass in Identity type`() {
|
||||||
|
val name = "test"
|
||||||
|
val type = CustomFieldType.LINKED
|
||||||
|
|
||||||
|
val expected =
|
||||||
|
VaultAddEditState.Custom.LinkedField(TEST_ID, "test", VaultLinkedFieldType.TITLE)
|
||||||
|
val actual = type.toCustomField(
|
||||||
|
name = name,
|
||||||
|
itemType = VaultAddEditState.ViewState.Content.ItemType.Identity(),
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `toCustomField should return a custom linked type when we pass in Card type`() {
|
||||||
|
val name = "test"
|
||||||
|
val type = CustomFieldType.LINKED
|
||||||
|
|
||||||
|
val expected =
|
||||||
|
VaultAddEditState.Custom.LinkedField(TEST_ID, "test", VaultLinkedFieldType.CARDHOLDER_NAME)
|
||||||
|
val actual = type.toCustomField(
|
||||||
|
name = name,
|
||||||
|
itemType = VaultAddEditState.ViewState.Content.ItemType.Card(),
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `toCustomField should return a null custom linked type when we pass in Secure Note type`() {
|
||||||
|
val name = "test"
|
||||||
|
val type = CustomFieldType.LINKED
|
||||||
|
|
||||||
|
val expected =
|
||||||
|
VaultAddEditState.Custom.LinkedField(TEST_ID, "test", null)
|
||||||
|
val actual = type.toCustomField(
|
||||||
|
name = name,
|
||||||
|
itemType = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
|
||||||
|
)
|
||||||
|
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
@ -54,7 +107,10 @@ class CustomFieldTypeTests {
|
||||||
val type = CustomFieldType.TEXT
|
val type = CustomFieldType.TEXT
|
||||||
|
|
||||||
val expected = VaultAddEditState.Custom.TextField(TEST_ID, "test", "")
|
val expected = VaultAddEditState.Custom.TextField(TEST_ID, "test", "")
|
||||||
val actual = type.toCustomField(name)
|
val actual = type.toCustomField(
|
||||||
|
name = name,
|
||||||
|
itemType = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
|
||||||
|
)
|
||||||
|
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
@ -65,7 +121,10 @@ class CustomFieldTypeTests {
|
||||||
val type = CustomFieldType.HIDDEN
|
val type = CustomFieldType.HIDDEN
|
||||||
|
|
||||||
val expected = VaultAddEditState.Custom.HiddenField(TEST_ID, "test", "")
|
val expected = VaultAddEditState.Custom.HiddenField(TEST_ID, "test", "")
|
||||||
val actual = type.toCustomField(name)
|
val actual = type.toCustomField(
|
||||||
|
name = name,
|
||||||
|
itemType = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
|
||||||
|
)
|
||||||
|
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue