BIT-515, BIT-512 Adding the ability to view and edit secure note items. (#462)

This commit is contained in:
Oleg Semenenko 2024-01-02 16:39:18 -06:00 committed by Álison Fernandes
parent fd9ca8f544
commit 1c8501b69b
5 changed files with 181 additions and 2 deletions

View file

@ -217,7 +217,11 @@ private fun VaultItemContent(
}
is VaultItemState.ViewState.Content.ItemType.SecureNote -> {
// TODO UI for viewing SecureNote BIT-515
VaultItemSecureNoteContent(
commonState = viewState.common,
vaultCommonItemTypeHandlers = vaultCommonItemTypeHandlers,
modifier = modifier,
)
}
}
}

View file

@ -0,0 +1,132 @@
package com.x8bit.bitwarden.ui.vault.feature.item
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
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.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.components.BitwardenListHeaderText
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextField
import com.x8bit.bitwarden.ui.platform.theme.LocalNonMaterialTypography
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCommonItemTypeHandlers
/**
* The top level content UI state for the [VaultItemScreen] when viewing a secure note cipher.
*/
@Suppress("LongMethod")
@Composable
fun VaultItemSecureNoteContent(
commonState: VaultItemState.ViewState.Content.Common,
vaultCommonItemTypeHandlers: VaultCommonItemTypeHandlers,
modifier: Modifier = Modifier,
) {
LazyColumn(modifier = modifier) {
item {
BitwardenListHeaderText(
label = stringResource(id = R.string.item_information),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(8.dp))
BitwardenTextField(
label = stringResource(id = R.string.name),
value = commonState.name,
onValueChange = { },
readOnly = true,
singleLine = false,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
commonState.notes?.let { notes ->
item {
Spacer(modifier = Modifier.height(4.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.notes),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(8.dp))
BitwardenTextField(
label = stringResource(id = R.string.notes),
value = notes,
onValueChange = { },
readOnly = true,
singleLine = false,
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),
)
}
}
item {
Spacer(modifier = Modifier.height(24.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.semantics(mergeDescendants = true) { },
) {
Text(
text = "${stringResource(id = R.string.date_updated)}: ",
style = LocalNonMaterialTypography.current.labelMediumProminent,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Text(
text = commonState.lastUpdated,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}
item {
Spacer(modifier = Modifier.height(88.dp))
Spacer(modifier = Modifier.navigationBarsPadding())
}
}
}

View file

@ -768,7 +768,7 @@ sealed class VaultItemAction {
) : Common()
/**
* The user has clicked to display the a hidden field.
* The user has clicked to display the hidden field.
*/
data class HiddenFieldVisibilityClicked(
val field: VaultItemState.ViewState.Content.Common.Custom.HiddenField,

View file

@ -1164,6 +1164,12 @@ private val EMPTY_IDENTITY_VIEW_STATE: VaultItemState.ViewState.Content =
type = EMPTY_IDENTITY_TYPE,
)
private val EMPTY_SECURE_NOTE_VIEW_STATE =
VaultItemState.ViewState.Content(
common = EMPTY_COMMON,
type = VaultItemState.ViewState.Content.ItemType.SecureNote,
)
private val DEFAULT_LOGIN_VIEW_STATE: VaultItemState.ViewState.Content =
VaultItemState.ViewState.Content(
type = DEFAULT_LOGIN,
@ -1176,12 +1182,20 @@ private val DEFAULT_IDENTITY_VIEW_STATE: VaultItemState.ViewState.Content =
common = DEFAULT_COMMON,
)
private val DEFAULT_SECURE_NOTE_VIEW_STATE: VaultItemState.ViewState.Content =
VaultItemState.ViewState.Content(
common = DEFAULT_COMMON,
type = VaultItemState.ViewState.Content.ItemType.SecureNote,
)
private val EMPTY_VIEW_STATES = listOf(
EMPTY_LOGIN_VIEW_STATE,
EMPTY_IDENTITY_VIEW_STATE,
EMPTY_SECURE_NOTE_VIEW_STATE,
)
private val DEFAULT_VIEW_STATES = listOf(
DEFAULT_LOGIN_VIEW_STATE,
DEFAULT_IDENTITY_VIEW_STATE,
DEFAULT_SECURE_NOTE_VIEW_STATE,
)

View file

@ -170,4 +170,33 @@ class CipherViewExtensionsTest {
result,
)
}
@Suppress("MaxLineLength")
@Test
fun `toViewState should transform full CipherView into ViewState Secure Note Content with premium`() {
val viewState = createCipherView(type = CipherType.SECURE_NOTE, isEmpty = false)
.toViewState(isPremiumUser = true)
assertEquals(
VaultItemState.ViewState.Content(
common = createCommonContent(isEmpty = false),
type = VaultItemState.ViewState.Content.ItemType.SecureNote,
),
viewState,
)
}
@Suppress("MaxLineLength")
@Test
fun `toViewState should transform empty Secure Note CipherView into ViewState Secure Note Content`() {
val viewState = createCipherView(type = CipherType.SECURE_NOTE, isEmpty = true)
.toViewState(isPremiumUser = true)
val expectedState = VaultItemState.ViewState.Content(
common = createCommonContent(isEmpty = true).copy(isPremiumUser = true),
type = VaultItemState.ViewState.Content.ItemType.SecureNote,
)
assertEquals(expectedState, viewState)
}
}