mirror of
https://github.com/bitwarden/android.git
synced 2026-10-22 02:53:19 -05:00
PM-39821: feat: Vault empty state can display action cards (#7126)
This commit is contained in:
1 parent
0d2fd2d184
commit
18036bb620
7 files changed
+245
-161
No files matched your search
@@ -0,0 +1,103 @@
|
||||
package com.x8bit.bitwarden.ui.vault.feature.vault
|
||||
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.bitwarden.ui.platform.components.card.BitwardenActionCard
|
||||
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.vault.feature.vault.handlers.VaultHandlers
|
||||
|
||||
/**
|
||||
* The action card for the vault screen.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun VaultActionCard(
|
||||
actionCardState: VaultState.ActionCardState,
|
||||
vaultHandlers: VaultHandlers,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (actionCardState) {
|
||||
VaultState.ActionCardState.UpgradedToPremium -> {
|
||||
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 = { vaultHandlers.actionCardClick(actionCardState) },
|
||||
onDismissClick = { vaultHandlers.dismissActionCardClick(actionCardState) },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
VaultState.ActionCardState.UpgradePremium -> {
|
||||
BitwardenActionCard(
|
||||
cardTitle = stringResource(
|
||||
id = BitwardenString.unlock_advanced_security_features,
|
||||
),
|
||||
cardSubtitle = stringResource(
|
||||
id = BitwardenString
|
||||
.a_premium_plan_gives_you_more_tools_to_stay_secure_and_in_control,
|
||||
),
|
||||
actionText = stringResource(id = BitwardenString.learn_more),
|
||||
onActionClick = { vaultHandlers.actionCardClick(actionCardState) },
|
||||
onDismissClick = { vaultHandlers.dismissActionCardClick(actionCardState) },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
VaultState.ActionCardState.PremiumNeedsAttention -> {
|
||||
BitwardenActionCard(
|
||||
cardTitle = stringResource(id = BitwardenString.your_subscription_needs_attention),
|
||||
cardSubtitle = stringResource(id = BitwardenString.check_your_plan_for_details),
|
||||
actionText = stringResource(id = BitwardenString.view_plan),
|
||||
onActionClick = { vaultHandlers.actionCardClick(actionCardState) },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
VaultState.ActionCardState.IntroducingArchive -> {
|
||||
BitwardenActionCard(
|
||||
cardTitle = stringResource(id = BitwardenString.introducing_archive),
|
||||
cardSubtitle = stringResource(
|
||||
id = BitwardenString.keep_items_you_dont_need_right_now_safe_but_out_sight,
|
||||
),
|
||||
actionText = stringResource(id = BitwardenString.go_to_archive),
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(id = BitwardenDrawable.ic_archive),
|
||||
contentDescription = null,
|
||||
tint = BitwardenTheme.colorScheme.icon.secondary,
|
||||
)
|
||||
},
|
||||
onActionClick = { vaultHandlers.actionCardClick(actionCardState) },
|
||||
onDismissClick = { vaultHandlers.dismissActionCardClick(actionCardState) },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
VaultState.ActionCardState.ImportItems -> {
|
||||
BitwardenActionCard(
|
||||
cardTitle = stringResource(id = BitwardenString.import_saved_logins),
|
||||
cardSubtitle = stringResource(id = BitwardenString.use_a_computer_to_import_logins),
|
||||
actionText = stringResource(id = BitwardenString.get_started),
|
||||
onActionClick = { vaultHandlers.actionCardClick(actionCardState) },
|
||||
onDismissClick = { vaultHandlers.dismissActionCardClick(actionCardState) },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -19,15 +18,12 @@ 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.dialog.BitwardenTwoButtonDialog
|
||||
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.dialog.BitwardenMasterPasswordDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.listitem.BitwardenGroupItem
|
||||
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.model.ListingItemOverflowAction
|
||||
@@ -107,7 +103,7 @@ fun VaultContent(
|
||||
|
||||
actionCardState?.let {
|
||||
item(key = "action_card") {
|
||||
ActionCard(
|
||||
VaultActionCard(
|
||||
actionCardState = it,
|
||||
vaultHandlers = vaultHandlers,
|
||||
modifier = Modifier
|
||||
@@ -561,80 +557,3 @@ fun VaultContent(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun ActionCard(
|
||||
actionCardState: VaultState.ActionCardState,
|
||||
vaultHandlers: VaultHandlers,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (actionCardState) {
|
||||
VaultState.ActionCardState.UpgradedToPremium -> {
|
||||
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 = { vaultHandlers.actionCardClick(actionCardState) },
|
||||
onDismissClick = { vaultHandlers.dismissActionCardClick(actionCardState) },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
VaultState.ActionCardState.UpgradePremium -> {
|
||||
BitwardenActionCard(
|
||||
cardTitle = stringResource(
|
||||
id = BitwardenString.unlock_advanced_security_features,
|
||||
),
|
||||
cardSubtitle = stringResource(
|
||||
id = BitwardenString
|
||||
.a_premium_plan_gives_you_more_tools_to_stay_secure_and_in_control,
|
||||
),
|
||||
actionText = stringResource(id = BitwardenString.learn_more),
|
||||
onActionClick = { vaultHandlers.actionCardClick(actionCardState) },
|
||||
onDismissClick = { vaultHandlers.dismissActionCardClick(actionCardState) },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
VaultState.ActionCardState.PremiumNeedsAttention -> {
|
||||
BitwardenActionCard(
|
||||
cardTitle = stringResource(id = BitwardenString.your_subscription_needs_attention),
|
||||
cardSubtitle = stringResource(id = BitwardenString.check_your_plan_for_details),
|
||||
actionText = stringResource(id = BitwardenString.view_plan),
|
||||
onActionClick = { vaultHandlers.actionCardClick(actionCardState) },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
VaultState.ActionCardState.IntroducingArchive -> {
|
||||
BitwardenActionCard(
|
||||
cardTitle = stringResource(id = BitwardenString.introducing_archive),
|
||||
cardSubtitle = stringResource(
|
||||
id = BitwardenString.keep_items_you_dont_need_right_now_safe_but_out_sight,
|
||||
),
|
||||
actionText = stringResource(id = BitwardenString.go_to_archive),
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(id = BitwardenDrawable.ic_archive),
|
||||
contentDescription = null,
|
||||
tint = BitwardenTheme.colorScheme.icon.secondary,
|
||||
)
|
||||
},
|
||||
onActionClick = { vaultHandlers.actionCardClick(actionCardState) },
|
||||
onDismissClick = { vaultHandlers.dismissActionCardClick(actionCardState) },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,6 @@ import com.bitwarden.ui.platform.components.appbar.action.BitwardenSearchActionI
|
||||
import com.bitwarden.ui.platform.components.appbar.model.OverflowMenuItemData
|
||||
import com.bitwarden.ui.platform.components.appbar.model.TopAppBarDividerStyle
|
||||
import com.bitwarden.ui.platform.components.button.model.BitwardenButtonData
|
||||
import com.bitwarden.ui.platform.components.card.BitwardenActionCard
|
||||
import com.bitwarden.ui.platform.components.card.actionCardExitAnimation
|
||||
import com.bitwarden.ui.platform.components.content.BitwardenErrorContent
|
||||
import com.bitwarden.ui.platform.components.content.BitwardenLoadingContent
|
||||
@@ -340,19 +339,14 @@ private fun VaultScreenScaffold(
|
||||
)
|
||||
|
||||
is VaultState.ViewState.NoItems -> {
|
||||
AnimatedVisibility(
|
||||
visible = state.showImportActionCard,
|
||||
AnimateNullableContentVisibility(
|
||||
targetState = state.actionCard,
|
||||
exit = actionCardExitAnimation(),
|
||||
label = "VaultNoItemsActionCard",
|
||||
) {
|
||||
BitwardenActionCard(
|
||||
cardTitle = stringResource(BitwardenString.import_saved_logins),
|
||||
cardSubtitle = stringResource(
|
||||
BitwardenString.use_a_computer_to_import_logins,
|
||||
),
|
||||
actionText = stringResource(BitwardenString.get_started),
|
||||
onActionClick = vaultHandlers.importActionCardClick,
|
||||
onDismissClick = vaultHandlers.dismissImportActionCard,
|
||||
) { actionCard ->
|
||||
VaultActionCard(
|
||||
actionCardState = actionCard,
|
||||
vaultHandlers = vaultHandlers,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
|
||||
@@ -358,8 +358,6 @@ class VaultViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
is VaultAction.Internal -> handleInternalAction(action)
|
||||
VaultAction.DismissImportActionCard -> handleDismissImportActionCard()
|
||||
VaultAction.ImportActionCardClick -> handleImportActionCardClick()
|
||||
VaultAction.LifecycleResumed -> handleLifecycleResumed()
|
||||
VaultAction.SelectAddItemType -> handleSelectAddItemType()
|
||||
VaultAction.DismissFlightRecorderSnackbar -> handleDismissFlightRecorderSnackbar()
|
||||
@@ -457,6 +455,12 @@ class VaultViewModel @Inject constructor(
|
||||
VaultState.ActionCardState.IntroducingArchive -> {
|
||||
settingsRepository.dismissIntroducingArchiveActionCard()
|
||||
}
|
||||
|
||||
VaultState.ActionCardState.ImportItems -> {
|
||||
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = true)
|
||||
if (!state.showImportActionCard) return
|
||||
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,9 +481,11 @@ class VaultViewModel @Inject constructor(
|
||||
|
||||
VaultState.ActionCardState.IntroducingArchive -> {
|
||||
settingsRepository.dismissIntroducingArchiveActionCard()
|
||||
sendEvent(
|
||||
VaultEvent.NavigateToItemListing(VaultItemListingType.Archive),
|
||||
)
|
||||
sendEvent(VaultEvent.NavigateToItemListing(VaultItemListingType.Archive))
|
||||
}
|
||||
|
||||
VaultState.ActionCardState.ImportItems -> {
|
||||
sendEvent(VaultEvent.NavigateToImportLogins)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -526,16 +532,6 @@ class VaultViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleImportActionCardClick() {
|
||||
sendEvent(VaultEvent.NavigateToImportLogins)
|
||||
}
|
||||
|
||||
private fun handleDismissImportActionCard() {
|
||||
firstTimeActionManager.storeShowImportLoginsSettingsBadge(true)
|
||||
if (!state.showImportActionCard) return
|
||||
firstTimeActionManager.storeShowImportLogins(false)
|
||||
}
|
||||
|
||||
private fun handleIconLoadingSettingReceive(
|
||||
action: VaultAction.Internal.IconLoadingSettingReceive,
|
||||
) {
|
||||
@@ -1789,16 +1785,30 @@ data class VaultState(
|
||||
* Indicates what action card to display.
|
||||
*/
|
||||
val actionCard: ActionCardState?
|
||||
get() = (viewState as? ViewState.Content)?.let {
|
||||
ActionCardState.UpgradedToPremium
|
||||
.takeIf { isUpgradedToPremiumCardEligible }
|
||||
?: ActionCardState.UpgradePremium.takeIf { premiumCard == PremiumCard.UPGRADE }
|
||||
?: ActionCardState.PremiumNeedsAttention.takeIf {
|
||||
premiumCard == PremiumCard.NEEDS_ATTENTION
|
||||
}
|
||||
?: ActionCardState.IntroducingArchive.takeIf {
|
||||
isPremium && !isIntroducingArchiveActionCardDismissed
|
||||
}
|
||||
get() = when (viewState) {
|
||||
is ViewState.Content -> {
|
||||
ActionCardState.UpgradedToPremium
|
||||
.takeIf { isUpgradedToPremiumCardEligible }
|
||||
?: ActionCardState.UpgradePremium.takeIf { premiumCard == PremiumCard.UPGRADE }
|
||||
?: ActionCardState.PremiumNeedsAttention.takeIf {
|
||||
premiumCard == PremiumCard.NEEDS_ATTENTION
|
||||
}
|
||||
?: ActionCardState.IntroducingArchive.takeIf {
|
||||
isPremium && !isIntroducingArchiveActionCardDismissed
|
||||
}
|
||||
}
|
||||
|
||||
ViewState.NoItems -> {
|
||||
ActionCardState.UpgradePremium.takeIf { premiumCard == PremiumCard.UPGRADE }
|
||||
?: ActionCardState.PremiumNeedsAttention.takeIf {
|
||||
premiumCard == PremiumCard.NEEDS_ATTENTION
|
||||
}
|
||||
?: ActionCardState.ImportItems.takeIf { showImportActionCard }
|
||||
}
|
||||
|
||||
is ViewState.Error,
|
||||
ViewState.Loading,
|
||||
-> null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2211,6 +2221,11 @@ data class VaultState(
|
||||
* Indicates that the archive feature is ready for use.
|
||||
*/
|
||||
data object IntroducingArchive : ActionCardState()
|
||||
|
||||
/**
|
||||
* Indicates that the import items card should be displayed.
|
||||
*/
|
||||
data object ImportItems : ActionCardState()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2599,16 +2614,6 @@ sealed class VaultAction {
|
||||
*/
|
||||
data object TryAgainClick : VaultAction()
|
||||
|
||||
/**
|
||||
* The user has dismissed the import action card.
|
||||
*/
|
||||
data object DismissImportActionCard : VaultAction()
|
||||
|
||||
/**
|
||||
* The user has clicked the import action card.
|
||||
*/
|
||||
data object ImportActionCardClick : VaultAction()
|
||||
|
||||
/**
|
||||
* User clicked an overflow action.
|
||||
*/
|
||||
|
||||
-8
@@ -44,8 +44,6 @@ data class VaultHandlers(
|
||||
String,
|
||||
) -> Unit,
|
||||
val masterPasswordRepromptSubmit: (VaultState.ViewState.VaultItem, String) -> Unit,
|
||||
val dismissImportActionCard: () -> Unit,
|
||||
val importActionCardClick: () -> Unit,
|
||||
val flightRecorderGoToSettingsClick: () -> Unit,
|
||||
val dismissFlightRecorderSnackbar: () -> Unit,
|
||||
val onShareCipherDecryptionErrorClick: (selectedCipherId: String) -> Unit,
|
||||
@@ -131,12 +129,6 @@ data class VaultHandlers(
|
||||
),
|
||||
)
|
||||
},
|
||||
dismissImportActionCard = {
|
||||
viewModel.trySendAction(VaultAction.DismissImportActionCard)
|
||||
},
|
||||
importActionCardClick = {
|
||||
viewModel.trySendAction(VaultAction.ImportActionCardClick)
|
||||
},
|
||||
flightRecorderGoToSettingsClick = {
|
||||
viewModel.trySendAction(VaultAction.FlightRecorderGoToSettingsClick)
|
||||
},
|
||||
|
||||
@@ -2498,7 +2498,7 @@ class VaultScreenTest : BitwardenComposeTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when import action card is showing, clicking it should send ImportLoginsClick action`() {
|
||||
fun `when import action card is showing, clicking it should send ActionCardClick action`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = VaultState.ViewState.NoItems,
|
||||
@@ -2509,12 +2509,16 @@ class VaultScreenTest : BitwardenComposeTest() {
|
||||
.onNodeWithText("Get started")
|
||||
.performClick()
|
||||
|
||||
verify { viewModel.trySendAction(VaultAction.ImportActionCardClick) }
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
VaultAction.ActionCardClick(VaultState.ActionCardState.ImportItems),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `when import action card is showing, dismissing it should send DismissImportActionCard action`() {
|
||||
fun `when import action card is showing, dismissing it should send DismissActionCardClick action`() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = VaultState.ViewState.NoItems,
|
||||
@@ -2524,7 +2528,11 @@ class VaultScreenTest : BitwardenComposeTest() {
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription("Close")
|
||||
.performClick()
|
||||
verify { viewModel.trySendAction(VaultAction.DismissImportActionCard) }
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
VaultAction.DismissActionCardClick(VaultState.ActionCardState.ImportItems),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+83
-20
@@ -3950,9 +3950,11 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `when DismissImportActionCard is sent, repository called to showImportLogins to false and storeShowImportLoginsBadge to true`() {
|
||||
fun `DismissActionCardClick with ImportItems should call storeShowImportLogins false and storeShowImportLoginsSettingsBadge true`() {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.trySendAction(VaultAction.DismissImportActionCard)
|
||||
viewModel.trySendAction(
|
||||
VaultAction.DismissActionCardClick(VaultState.ActionCardState.ImportItems),
|
||||
)
|
||||
verify(exactly = 1) {
|
||||
firstTimeActionManager.storeShowImportLogins(false)
|
||||
firstTimeActionManager.storeShowImportLoginsSettingsBadge(true)
|
||||
@@ -3961,7 +3963,7 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `when DismissImportActionCard is sent, repository is not called if value is already false`() {
|
||||
fun `DismissActionCardClick with ImportItems should not call storeShowImportLogins if value is already false`() {
|
||||
mutableUserStateFlow.value = DEFAULT_USER_STATE.copy(
|
||||
accounts = DEFAULT_USER_STATE.accounts.map {
|
||||
it.copy(
|
||||
@@ -3972,18 +3974,25 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
},
|
||||
)
|
||||
val viewModel = createViewModel()
|
||||
viewModel.trySendAction(VaultAction.DismissImportActionCard)
|
||||
viewModel.trySendAction(
|
||||
VaultAction.DismissActionCardClick(VaultState.ActionCardState.ImportItems),
|
||||
)
|
||||
verify(exactly = 1) {
|
||||
firstTimeActionManager.storeShowImportLoginsSettingsBadge(true)
|
||||
}
|
||||
verify(exactly = 0) {
|
||||
firstTimeActionManager.storeShowImportLogins(false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when ImportActionCardClick is sent, NavigateToImportLogins event is sent`() =
|
||||
fun `ActionCardClick with ImportItems should emit NavigateToImportLogins`() =
|
||||
runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(VaultAction.ImportActionCardClick)
|
||||
viewModel.trySendAction(
|
||||
VaultAction.ActionCardClick(VaultState.ActionCardState.ImportItems),
|
||||
)
|
||||
assertEquals(VaultEvent.NavigateToImportLogins, awaitItem())
|
||||
}
|
||||
verify(exactly = 0) {
|
||||
@@ -3991,22 +4000,76 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `when ImportActionCardClick is sent, repository is not called if value is already false`() {
|
||||
mutableUserStateFlow.value = DEFAULT_USER_STATE.copy(
|
||||
accounts = DEFAULT_USER_STATE.accounts.map {
|
||||
it.copy(
|
||||
firstTimeState = DEFAULT_FIRST_TIME_STATE.copy(
|
||||
showImportLoginsCard = false,
|
||||
),
|
||||
)
|
||||
},
|
||||
fun `actionCard should return ImportItems when NoItems is showing and showImportActionCard is true`() {
|
||||
val state = createMockVaultState(viewState = VaultState.ViewState.NoItems).copy(
|
||||
premiumCard = PremiumCard.NONE,
|
||||
showImportActionCard = true,
|
||||
)
|
||||
val viewModel = createViewModel()
|
||||
viewModel.trySendAction(VaultAction.ImportActionCardClick)
|
||||
verify(exactly = 0) {
|
||||
firstTimeActionManager.storeShowImportLogins(false)
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
VaultState.ActionCardState.ImportItems,
|
||||
state.actionCard,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `actionCard should return UpgradePremium when NoItems is showing and eligible`() {
|
||||
val state = createMockVaultState(viewState = VaultState.ViewState.NoItems).copy(
|
||||
premiumCard = PremiumCard.UPGRADE,
|
||||
showImportActionCard = true,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
VaultState.ActionCardState.UpgradePremium,
|
||||
state.actionCard,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `actionCard should return PremiumNeedsAttention when NoItems is showing and eligible`() {
|
||||
val state = createMockVaultState(viewState = VaultState.ViewState.NoItems).copy(
|
||||
premiumCard = PremiumCard.NEEDS_ATTENTION,
|
||||
showImportActionCard = true,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
VaultState.ActionCardState.PremiumNeedsAttention,
|
||||
state.actionCard,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `actionCard should return null when NoItems is showing and not eligible for any card`() {
|
||||
val state = createMockVaultState(viewState = VaultState.ViewState.NoItems).copy(
|
||||
premiumCard = PremiumCard.NONE,
|
||||
showImportActionCard = false,
|
||||
)
|
||||
|
||||
assertNull(state.actionCard)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `actionCard should return null when Loading is showing`() {
|
||||
val state = createMockVaultState(viewState = VaultState.ViewState.Loading).copy(
|
||||
premiumCard = PremiumCard.UPGRADE,
|
||||
showImportActionCard = true,
|
||||
)
|
||||
|
||||
assertNull(state.actionCard)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `actionCard should return null when Error is showing`() {
|
||||
val state = createMockVaultState(
|
||||
viewState = VaultState.ViewState.Error(message = "error".asText()),
|
||||
).copy(
|
||||
premiumCard = PremiumCard.UPGRADE,
|
||||
showImportActionCard = true,
|
||||
)
|
||||
|
||||
assertNull(state.actionCard)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in new issue
Block a user