mirror of
https://github.com/bitwarden/android.git
synced 2026-05-21 03:21:29 -05:00
PM-21445: Update the Send delete buttons (#5195)
This commit is contained in:
@@ -2,10 +2,10 @@ package com.x8bit.bitwarden.ui.tools.feature.send.addsend
|
||||
|
||||
import android.Manifest
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
@@ -38,7 +38,9 @@ import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenOutlinedErrorButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.card.BitwardenInfoCalloutCard
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
@@ -46,6 +48,7 @@ import com.x8bit.bitwarden.ui.platform.components.header.BitwardenExpandingHeade
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.stepper.BitwardenStepper
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.handlers.AddSendHandlers
|
||||
@@ -185,11 +188,49 @@ fun AddSendContent(
|
||||
isAddMode = isAddMode,
|
||||
addSendHandlers = addSendHandlers,
|
||||
)
|
||||
|
||||
if (!isAddMode) {
|
||||
DeleteButton(
|
||||
onDeleteClick = addSendHandlers.onDeleteClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DeleteButton(
|
||||
onDeleteClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var shouldShowDeleteConfirmationDialog by rememberSaveable { mutableStateOf(value = false) }
|
||||
if (shouldShowDeleteConfirmationDialog) {
|
||||
BitwardenTwoButtonDialog(
|
||||
title = stringResource(id = R.string.delete),
|
||||
message = stringResource(id = R.string.are_you_sure_delete_send),
|
||||
confirmButtonText = stringResource(id = R.string.yes),
|
||||
dismissButtonText = stringResource(id = R.string.cancel),
|
||||
onConfirmClick = {
|
||||
onDeleteClick()
|
||||
shouldShowDeleteConfirmationDialog = false
|
||||
},
|
||||
onDismissClick = { shouldShowDeleteConfirmationDialog = false },
|
||||
onDismissRequest = { shouldShowDeleteConfirmationDialog = false },
|
||||
)
|
||||
}
|
||||
BitwardenOutlinedErrorButton(
|
||||
label = stringResource(id = R.string.delete_send),
|
||||
onClick = { shouldShowDeleteConfirmationDialog = true },
|
||||
icon = rememberVectorPainter(id = R.drawable.ic_trash_small),
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.TextTypeContent(
|
||||
textType: AddSendState.ViewState.Content.SendType.Text,
|
||||
@@ -357,11 +398,10 @@ private fun AddSendOptions(
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
// Hide all content if not expanded:
|
||||
AnimatedVisibility(
|
||||
visible = isExpanded,
|
||||
enter = fadeIn() + slideInVertically(),
|
||||
exit = fadeOut() + slideOutVertically(),
|
||||
enter = fadeIn() + expandVertically(expandFrom = Alignment.Top),
|
||||
exit = fadeOut() + shrinkVertically(shrinkTowards = Alignment.Top),
|
||||
modifier = Modifier.clipToBounds(),
|
||||
) {
|
||||
Column {
|
||||
@@ -434,6 +474,7 @@ private fun AddSendOptions(
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,7 @@ import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -32,7 +29,6 @@ import com.x8bit.bitwarden.ui.platform.components.content.BitwardenErrorContent
|
||||
import com.x8bit.bitwarden.ui.platform.components.content.BitwardenLoadingContent
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.TopAppBarDividerStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.segment.BitwardenSegmentedButton
|
||||
@@ -109,24 +105,6 @@ fun AddSendScreen(
|
||||
{ viewModel.trySendAction(AddSendAction.DismissDialogClick) }
|
||||
},
|
||||
)
|
||||
var shouldShowDeleteConfirmationDialog by rememberSaveable { mutableStateOf(false) }
|
||||
if (shouldShowDeleteConfirmationDialog) {
|
||||
BitwardenTwoButtonDialog(
|
||||
title = stringResource(id = R.string.delete),
|
||||
message = stringResource(id = R.string.are_you_sure_delete_send),
|
||||
confirmButtonText = stringResource(id = R.string.yes),
|
||||
dismissButtonText = stringResource(id = R.string.cancel),
|
||||
onConfirmClick = remember(viewModel) {
|
||||
{
|
||||
viewModel.trySendAction(AddSendAction.DeleteClick)
|
||||
shouldShowDeleteConfirmationDialog = false
|
||||
}
|
||||
},
|
||||
onDismissClick = { shouldShowDeleteConfirmationDialog = false },
|
||||
onDismissRequest = { shouldShowDeleteConfirmationDialog = false },
|
||||
)
|
||||
}
|
||||
|
||||
BitwardenScaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -181,10 +159,6 @@ fun AddSendScreen(
|
||||
},
|
||||
)
|
||||
.takeIf { !state.policyDisablesSend },
|
||||
OverflowMenuItemData(
|
||||
text = stringResource(id = R.string.delete),
|
||||
onClick = { shouldShowDeleteConfirmationDialog = true },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ data class AddSendHandlers(
|
||||
val onHideEmailToggle: (Boolean) -> Unit,
|
||||
val onDeactivateSendToggle: (Boolean) -> Unit,
|
||||
val onDeletionDateChange: (ZonedDateTime) -> Unit,
|
||||
val onDeleteClick: () -> Unit,
|
||||
) {
|
||||
@Suppress("UndocumentedPublicClass")
|
||||
companion object {
|
||||
@@ -57,6 +58,7 @@ data class AddSendHandlers(
|
||||
onDeletionDateChange = {
|
||||
viewModel.trySendAction(AddSendAction.DeletionDateChange(it))
|
||||
},
|
||||
onDeleteClick = { viewModel.trySendAction(AddSendAction.DeleteClick) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ import com.x8bit.bitwarden.ui.platform.components.content.BitwardenErrorContent
|
||||
import com.x8bit.bitwarden.ui.platform.components.content.BitwardenLoadingContent
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.fab.BitwardenFloatingActionButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenExpandingHeader
|
||||
@@ -306,10 +307,8 @@ private fun ViewStateContent(
|
||||
|
||||
AdditionalOptions(state = state)
|
||||
|
||||
BitwardenOutlinedErrorButton(
|
||||
label = stringResource(id = R.string.delete_send),
|
||||
onClick = onDeleteClick,
|
||||
icon = rememberVectorPainter(id = R.drawable.ic_trash_small),
|
||||
DeleteButton(
|
||||
onDeleteClick = onDeleteClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
@@ -319,6 +318,34 @@ private fun ViewStateContent(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DeleteButton(
|
||||
onDeleteClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var shouldShowDeleteConfirmationDialog by rememberSaveable { mutableStateOf(value = false) }
|
||||
if (shouldShowDeleteConfirmationDialog) {
|
||||
BitwardenTwoButtonDialog(
|
||||
title = stringResource(id = R.string.delete),
|
||||
message = stringResource(id = R.string.are_you_sure_delete_send),
|
||||
confirmButtonText = stringResource(id = R.string.yes),
|
||||
dismissButtonText = stringResource(id = R.string.cancel),
|
||||
onConfirmClick = {
|
||||
onDeleteClick()
|
||||
shouldShowDeleteConfirmationDialog = false
|
||||
},
|
||||
onDismissClick = { shouldShowDeleteConfirmationDialog = false },
|
||||
onDismissRequest = { shouldShowDeleteConfirmationDialog = false },
|
||||
)
|
||||
}
|
||||
BitwardenOutlinedErrorButton(
|
||||
label = stringResource(id = R.string.delete_send),
|
||||
onClick = { shouldShowDeleteConfirmationDialog = true },
|
||||
icon = rememberVectorPainter(id = R.drawable.ic_trash_small),
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A default content block which displays a header with an optional subtitle and an icon.
|
||||
* Implemented to match design component.
|
||||
|
||||
@@ -162,14 +162,10 @@ class AddSendScreenTest : BaseComposeTest() {
|
||||
.onNodeWithText("Share link")
|
||||
.assert(hasAnyAncestor(isPopup()))
|
||||
.isDisplayed()
|
||||
composeTestRule
|
||||
.onNodeWithText("Delete")
|
||||
.assert(hasAnyAncestor(isPopup()))
|
||||
.isDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on overflow button click should only display delete when policy disables send`() {
|
||||
fun `on overflow button should not be present when policy disables send`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE.copy(
|
||||
addSendType = AddSendType.EditItem(sendItemId = "sendId"),
|
||||
policyDisablesSend = true,
|
||||
@@ -177,21 +173,7 @@ class AddSendScreenTest : BaseComposeTest() {
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription("More")
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Remove password")
|
||||
.assertDoesNotExist()
|
||||
composeTestRule
|
||||
.onNodeWithText("Copy link")
|
||||
.assertDoesNotExist()
|
||||
composeTestRule
|
||||
.onNodeWithText("Share link")
|
||||
.assertDoesNotExist()
|
||||
composeTestRule
|
||||
.onNodeWithText("Delete")
|
||||
.assert(hasAnyAncestor(isPopup()))
|
||||
.isDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -250,50 +232,6 @@ class AddSendScreenTest : BaseComposeTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on overflow Delete button click should Display delete confirmation dialog`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE.copy(
|
||||
addSendType = AddSendType.EditItem(sendItemId = "sendId"),
|
||||
)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription("More")
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Delete")
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Are you sure you want to delete this Send?")
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on delete confirmation dialog yes click should send DeleteClick`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE.copy(
|
||||
addSendType = AddSendType.EditItem(sendItemId = "sendId"),
|
||||
)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription("More")
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Delete")
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Yes")
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
|
||||
verify(exactly = 1) {
|
||||
viewModel.trySendAction(AddSendAction.DeleteClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on overflow remove Copy link button click should send CopyLinkClick`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE.copy(
|
||||
@@ -313,6 +251,44 @@ class AddSendScreenTest : BaseComposeTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on Delete button click should Display delete confirmation dialog`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE.copy(
|
||||
addSendType = AddSendType.EditItem(sendItemId = "sendId"),
|
||||
)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Delete send")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Are you sure you want to delete this Send?")
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on delete confirmation dialog yes click should send DeleteClick`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE.copy(
|
||||
addSendType = AddSendType.EditItem(sendItemId = "sendId"),
|
||||
)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Delete send")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Yes")
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
|
||||
verify(exactly = 1) {
|
||||
viewModel.trySendAction(AddSendAction.DeleteClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `policy warning should update according to state`() {
|
||||
val policyText = "Due to an enterprise policy, you are only " +
|
||||
|
||||
@@ -125,11 +125,30 @@ class ViewSendScreenTest : BaseComposeTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on delete click should send DeleteClick`() {
|
||||
fun `on Delete button click should Display delete confirmation dialog`() {
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Delete send")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Are you sure you want to delete this Send?")
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `oon delete confirmation dialog yes click should send DeleteClick`() {
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Delete send")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Yes")
|
||||
.assert(hasAnyAncestor(isDialog()))
|
||||
.performClick()
|
||||
|
||||
verify(exactly = 1) {
|
||||
viewModel.trySendAction(ViewSendAction.DeleteClick)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user