mirror of
https://github.com/bitwarden/android.git
synced 2026-03-12 05:04:17 -05:00
[PM-15116] Add common vault item content to SSH keys (#4365)
This commit is contained in:
@@ -404,6 +404,7 @@ private fun VaultItemContent(
|
||||
VaultItemSshKeyContent(
|
||||
commonState = viewState.common,
|
||||
sshKeyItemState = viewState.type,
|
||||
vaultCommonItemTypeHandlers = vaultCommonItemTypeHandlers,
|
||||
vaultSshKeyItemTypeHandlers = vaultSshKeyItemTypeHandlers,
|
||||
modifier = modifier,
|
||||
)
|
||||
|
||||
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
@@ -18,6 +19,7 @@ import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextFieldWithActions
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCommonItemTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultSshKeyItemTypeHandlers
|
||||
|
||||
/**
|
||||
@@ -29,6 +31,7 @@ fun VaultItemSshKeyContent(
|
||||
commonState: VaultItemState.ViewState.Content.Common,
|
||||
sshKeyItemState: VaultItemState.ViewState.Content.ItemType.SshKey,
|
||||
vaultSshKeyItemTypeHandlers: VaultSshKeyItemTypeHandlers,
|
||||
vaultCommonItemTypeHandlers: VaultCommonItemTypeHandlers,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyColumn(modifier = modifier) {
|
||||
@@ -120,6 +123,85 @@ fun VaultItemSshKeyContent(
|
||||
)
|
||||
}
|
||||
|
||||
commonState.notes?.let { notes ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.notes),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextFieldWithActions(
|
||||
label = stringResource(id = R.string.notes),
|
||||
value = notes,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_notes),
|
||||
onClick = vaultCommonItemTypeHandlers.onCopyNotesClick,
|
||||
modifier = Modifier.testTag(tag = "CipherNotesCopyButton"),
|
||||
)
|
||||
},
|
||||
textFieldTestTag = "CipherNotesLabel",
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
commonState.customFields.takeUnless { it.isEmpty() }?.let { customFields ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.custom_fields),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
items(customFields) { customField ->
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
CustomField(
|
||||
customField = customField,
|
||||
onCopyCustomHiddenField = vaultCommonItemTypeHandlers.onCopyCustomHiddenField,
|
||||
onCopyCustomTextField = vaultCommonItemTypeHandlers.onCopyCustomTextField,
|
||||
onShowHiddenFieldClick = vaultCommonItemTypeHandlers.onShowHiddenFieldClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
commonState.attachments.takeUnless { it?.isEmpty() == true }?.let { attachments ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.attachments),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
items(attachments) { attachmentItem ->
|
||||
AttachmentItemContent(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp),
|
||||
attachmentItem = attachmentItem,
|
||||
onAttachmentDownloadClick =
|
||||
vaultCommonItemTypeHandlers.onAttachmentDownloadClick,
|
||||
)
|
||||
}
|
||||
item { Spacer(modifier = Modifier.height(8.dp)) }
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
VaultItemUpdateText(
|
||||
|
||||
@@ -1366,6 +1366,42 @@ class VaultItemScreenTest : BaseComposeTest() {
|
||||
viewModel.trySendAction(VaultItemAction.Common.CopyNotesClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on ssh key copy notes field click should send CopyNotesClick`() {
|
||||
// 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(
|
||||
name = "text",
|
||||
value = "value",
|
||||
isCopyable = true,
|
||||
)
|
||||
|
||||
EMPTY_VIEW_STATES
|
||||
.forEach { typeState ->
|
||||
mutableStateFlow.update { currentState ->
|
||||
currentState.copy(
|
||||
viewState = typeState.copy(
|
||||
type = DEFAULT_SSH_KEY,
|
||||
common = EMPTY_COMMON.copy(
|
||||
notes = "this is a note",
|
||||
customFields = listOf(textField),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithTextAfterScroll(textField.name)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTag("CipherNotesCopyButton")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.Common.CopyNotesClick)
|
||||
}
|
||||
}
|
||||
//endregion common
|
||||
|
||||
//region login
|
||||
@@ -2602,29 +2638,6 @@ private fun updateCardType(
|
||||
return currentState.copy(viewState = updatedType)
|
||||
}
|
||||
|
||||
private fun updateSshKeyType(
|
||||
currentState: VaultItemState,
|
||||
transform: VaultItemState.ViewState.Content.ItemType.SshKey.() ->
|
||||
VaultItemState.ViewState.Content.ItemType.SshKey,
|
||||
): VaultItemState {
|
||||
val updatedType = when (val viewState = currentState.viewState) {
|
||||
is VaultItemState.ViewState.Content -> {
|
||||
when (val type = viewState.type) {
|
||||
is VaultItemState.ViewState.Content.ItemType.SshKey -> {
|
||||
viewState.copy(
|
||||
type = type.transform(),
|
||||
)
|
||||
}
|
||||
|
||||
else -> viewState
|
||||
}
|
||||
}
|
||||
|
||||
else -> viewState
|
||||
}
|
||||
return currentState.copy(viewState = updatedType)
|
||||
}
|
||||
|
||||
private fun updateCommonContent(
|
||||
currentState: VaultItemState,
|
||||
transform: VaultItemState.ViewState.Content.Common.()
|
||||
@@ -2811,6 +2824,15 @@ private val EMPTY_CARD_TYPE: VaultItemState.ViewState.Content.ItemType.Card =
|
||||
),
|
||||
)
|
||||
|
||||
private val EMPTY_SSH_KEY_TYPE: VaultItemState.ViewState.Content.ItemType.SshKey =
|
||||
VaultItemState.ViewState.Content.ItemType.SshKey(
|
||||
name = "",
|
||||
publicKey = "",
|
||||
privateKey = "",
|
||||
fingerprint = "",
|
||||
showPrivateKey = false,
|
||||
)
|
||||
|
||||
private val EMPTY_LOGIN_VIEW_STATE: VaultItemState.ViewState.Content =
|
||||
VaultItemState.ViewState.Content(
|
||||
common = EMPTY_COMMON,
|
||||
@@ -2835,6 +2857,12 @@ private val EMPTY_SECURE_NOTE_VIEW_STATE =
|
||||
type = VaultItemState.ViewState.Content.ItemType.SecureNote,
|
||||
)
|
||||
|
||||
private val EMPTY_SSH_KEY_VIEW_STATE =
|
||||
VaultItemState.ViewState.Content(
|
||||
common = EMPTY_COMMON,
|
||||
type = EMPTY_SSH_KEY_TYPE,
|
||||
)
|
||||
|
||||
private val DEFAULT_LOGIN_VIEW_STATE: VaultItemState.ViewState.Content =
|
||||
VaultItemState.ViewState.Content(
|
||||
type = DEFAULT_LOGIN,
|
||||
@@ -2869,10 +2897,12 @@ private val EMPTY_VIEW_STATES = listOf(
|
||||
EMPTY_LOGIN_VIEW_STATE,
|
||||
EMPTY_IDENTITY_VIEW_STATE,
|
||||
EMPTY_SECURE_NOTE_VIEW_STATE,
|
||||
EMPTY_SSH_KEY_VIEW_STATE,
|
||||
)
|
||||
|
||||
private val DEFAULT_VIEW_STATES = listOf(
|
||||
DEFAULT_LOGIN_VIEW_STATE,
|
||||
DEFAULT_IDENTITY_VIEW_STATE,
|
||||
DEFAULT_SECURE_NOTE_VIEW_STATE,
|
||||
DEFAULT_SSH_KEY_VIEW_STATE,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user