mirror of
https://github.com/bitwarden/android.git
synced 2026-05-27 06:54:00 -05:00
[PM-37284] fix: Show Upgraded to Premium card across all Send view states (#6947)
This commit is contained in:
@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
@@ -15,17 +14,15 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.bitwarden.ui.platform.components.card.BitwardenActionCard
|
||||
import com.bitwarden.ui.platform.components.card.BitwardenInfoCalloutCard
|
||||
import com.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.bitwarden.ui.platform.components.icon.model.IconData
|
||||
import com.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.bitwarden.ui.platform.resource.BitwardenDrawable
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.platform.components.listitem.BitwardenGroupItem
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.handlers.SendHandlers
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.model.UpgradedToPremiumCardData
|
||||
|
||||
private const val SEND_TYPES_COUNT: Int = 2
|
||||
|
||||
@@ -37,39 +34,24 @@ private const val SEND_TYPES_COUNT: Int = 2
|
||||
fun SendContent(
|
||||
policyDisablesSend: Boolean,
|
||||
state: SendState.ViewState.Content,
|
||||
isUpgradedToPremiumCardEligible: Boolean,
|
||||
upgradedToPremiumCardData: UpgradedToPremiumCardData?,
|
||||
sendHandlers: SendHandlers,
|
||||
onUpgradedToPremiumCardClick: () -> Unit,
|
||||
onUpgradedToPremiumCardDismiss: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyColumn(modifier = modifier) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
}
|
||||
if (isUpgradedToPremiumCardEligible) {
|
||||
upgradedToPremiumCardData?.let {
|
||||
item {
|
||||
BitwardenActionCard(
|
||||
cardTitle = stringResource(id = BitwardenString.upgraded_to_premium),
|
||||
cardSubtitle = stringResource(
|
||||
id = BitwardenString.you_now_have_access_to_all_advanced_security_features,
|
||||
),
|
||||
actionText = stringResource(id = BitwardenString.learn_more),
|
||||
isExternalLink = true,
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(id = BitwardenDrawable.ic_star),
|
||||
contentDescription = null,
|
||||
tint = BitwardenTheme.colorScheme.icon.secondary,
|
||||
)
|
||||
},
|
||||
onActionClick = onUpgradedToPremiumCardClick,
|
||||
onDismissClick = onUpgradedToPremiumCardDismiss,
|
||||
UpgradedToPremiumActionCard(
|
||||
onActionClick = it.onCardClick,
|
||||
onDismissClick = it.onCardDismiss,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
}
|
||||
}
|
||||
if (policyDisablesSend) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.bitwarden.ui.platform.resource.BitwardenDrawable
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.model.UpgradedToPremiumCardData
|
||||
|
||||
/**
|
||||
* Content for the empty state of the [SendScreen].
|
||||
@@ -40,12 +41,23 @@ import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
fun SendEmpty(
|
||||
policyDisablesSend: Boolean,
|
||||
onAddItemClick: () -> Unit,
|
||||
upgradedToPremiumCardData: UpgradedToPremiumCardData?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = modifier.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
upgradedToPremiumCardData?.let {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
UpgradedToPremiumActionCard(
|
||||
onActionClick = it.onCardClick,
|
||||
onDismissClick = it.onCardDismiss,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
if (policyDisablesSend) {
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
BitwardenInfoCalloutCard(
|
||||
@@ -119,6 +131,7 @@ private fun SendEmpty_preview() {
|
||||
SendEmpty(
|
||||
policyDisablesSend = false,
|
||||
onAddItemClick = {},
|
||||
upgradedToPremiumCardData = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -135,6 +148,7 @@ private fun SendEmptyPolicyDisabled_preview() {
|
||||
SendEmpty(
|
||||
policyDisablesSend = true,
|
||||
onAddItemClick = {},
|
||||
upgradedToPremiumCardData = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import androidx.compose.animation.scaleIn
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -23,6 +24,7 @@ import com.bitwarden.ui.platform.components.appbar.action.BitwardenOverflowActio
|
||||
import com.bitwarden.ui.platform.components.appbar.action.BitwardenSearchActionItem
|
||||
import com.bitwarden.ui.platform.components.appbar.model.OverflowMenuItemData
|
||||
import com.bitwarden.ui.platform.components.button.model.BitwardenButtonData
|
||||
import com.bitwarden.ui.platform.components.card.BitwardenActionCard
|
||||
import com.bitwarden.ui.platform.components.content.BitwardenErrorContent
|
||||
import com.bitwarden.ui.platform.components.content.BitwardenLoadingContent
|
||||
import com.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
@@ -39,6 +41,7 @@ import com.bitwarden.ui.platform.composition.LocalIntentManager
|
||||
import com.bitwarden.ui.platform.manager.IntentManager
|
||||
import com.bitwarden.ui.platform.resource.BitwardenDrawable
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.AppResumeScreenData
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.AppResumeStateManager
|
||||
@@ -49,6 +52,7 @@ import com.x8bit.bitwarden.ui.tools.feature.send.addedit.AddEditSendRoute
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addedit.ModeType
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.handlers.SendHandlers
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.model.SendItemType
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.model.UpgradedToPremiumCardData
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.util.selectionText
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.viewsend.ViewSendRoute
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
@@ -181,24 +185,27 @@ fun SendScreen(
|
||||
snackbarHost = { BitwardenSnackbarHost(bitwardenHostState = snackbarHostState) },
|
||||
) {
|
||||
val contentModifier = Modifier.fillMaxSize()
|
||||
val upgradedToPremiumCardData = UpgradedToPremiumCardData(
|
||||
onCardClick = {
|
||||
viewModel.trySendAction(SendAction.UpgradedToPremiumCardClick)
|
||||
},
|
||||
onCardDismiss = {
|
||||
viewModel.trySendAction(SendAction.UpgradedToPremiumCardDismiss)
|
||||
},
|
||||
).takeIf { state.isUpgradedToPremiumCardEligible }
|
||||
when (val viewState = state.viewState) {
|
||||
is SendState.ViewState.Content -> SendContent(
|
||||
policyDisablesSend = state.policyDisablesSend,
|
||||
state = viewState,
|
||||
isUpgradedToPremiumCardEligible = state.isUpgradedToPremiumCardEligible,
|
||||
upgradedToPremiumCardData = upgradedToPremiumCardData,
|
||||
sendHandlers = sendHandlers,
|
||||
onUpgradedToPremiumCardClick = {
|
||||
viewModel.trySendAction(SendAction.UpgradedToPremiumCardClick)
|
||||
},
|
||||
onUpgradedToPremiumCardDismiss = {
|
||||
viewModel.trySendAction(SendAction.UpgradedToPremiumCardDismiss)
|
||||
},
|
||||
modifier = contentModifier,
|
||||
)
|
||||
|
||||
SendState.ViewState.Empty -> SendEmpty(
|
||||
policyDisablesSend = state.policyDisablesSend,
|
||||
onAddItemClick = { viewModel.trySendAction(SendAction.AddSendClick) },
|
||||
upgradedToPremiumCardData = upgradedToPremiumCardData,
|
||||
modifier = contentModifier,
|
||||
)
|
||||
|
||||
@@ -251,3 +258,33 @@ private fun SendDialogs(
|
||||
null -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action card rendered at the top of the Send list when the user has just upgraded to premium.
|
||||
* Owned by the screen so it can be hosted inside each view state's scrollable container.
|
||||
*/
|
||||
@Composable
|
||||
internal fun UpgradedToPremiumActionCard(
|
||||
onActionClick: () -> Unit,
|
||||
onDismissClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BitwardenActionCard(
|
||||
cardTitle = stringResource(id = BitwardenString.upgraded_to_premium),
|
||||
cardSubtitle = stringResource(
|
||||
id = BitwardenString.you_now_have_access_to_all_advanced_security_features,
|
||||
),
|
||||
actionText = stringResource(id = BitwardenString.learn_more),
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(id = BitwardenDrawable.ic_star),
|
||||
contentDescription = null,
|
||||
tint = BitwardenTheme.colorScheme.icon.secondary,
|
||||
)
|
||||
},
|
||||
isExternalLink = true,
|
||||
onActionClick = onActionClick,
|
||||
onDismissClick = onDismissClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.x8bit.bitwarden.ui.tools.feature.send.model
|
||||
|
||||
/**
|
||||
* Handlers for the "Upgraded to Premium" action card shown on Send surfaces.
|
||||
*
|
||||
* Pass `null` to indicate the card should not be displayed.
|
||||
*/
|
||||
data class UpgradedToPremiumCardData(
|
||||
val onCardClick: () -> Unit,
|
||||
val onCardDismiss: () -> Unit,
|
||||
)
|
||||
@@ -1000,6 +1000,51 @@ class SendScreenTest : BitwardenComposeTest() {
|
||||
viewModel.trySendAction(SendAction.UpgradedToPremiumCardDismiss)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `UpgradedToPremium action card should display in Empty viewState when eligible`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = SendState.ViewState.Empty,
|
||||
isUpgradedToPremiumCardEligible = true,
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Upgraded to Premium")
|
||||
.assertIsDisplayed()
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Learn more")
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `UpgradedToPremium action card should not display in Loading viewState`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = SendState.ViewState.Loading,
|
||||
isUpgradedToPremiumCardEligible = true,
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Upgraded to Premium")
|
||||
.assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `UpgradedToPremium action card should not display in Error viewState`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = SendState.ViewState.Error("Fail".asText()),
|
||||
isUpgradedToPremiumCardEligible = true,
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Upgraded to Premium")
|
||||
.assertDoesNotExist()
|
||||
}
|
||||
}
|
||||
|
||||
private val DEFAULT_STATE: SendState = SendState(
|
||||
|
||||
Reference in New Issue
Block a user