mirror of
https://github.com/bitwarden/android.git
synced 2025-03-08 15:36:31 +03:00
[PM-12592] & [PM-12593] bottom nav notification dot (#3968)
This commit is contained in:
parent
21e1d8b5bc
commit
f349a72f72
6 changed files with 241 additions and 19 deletions
app/src
main/java/com/x8bit/bitwarden/ui/platform
components/badge
feature/vaultunlockednavbar
test/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar
|
@ -0,0 +1,73 @@
|
||||||
|
package com.x8bit.bitwarden.ui.platform.components.badge
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Badge
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.x8bit.bitwarden.ui.platform.theme.LocalNonMaterialColors
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reusable component for displaying a notification badge.
|
||||||
|
*
|
||||||
|
* @param notificationCount numeric value to display in center of the badge.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun NotificationBadge(
|
||||||
|
notificationCount: Int,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
isVisible: Boolean = true,
|
||||||
|
backgroundColor: Color = LocalNonMaterialColors.current.fingerprint,
|
||||||
|
contentColor: Color = MaterialTheme.colorScheme.onSecondary,
|
||||||
|
) {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = isVisible,
|
||||||
|
enter = slideInVertically() + fadeIn(),
|
||||||
|
exit = slideOutVertically() + fadeOut(),
|
||||||
|
) {
|
||||||
|
Badge(
|
||||||
|
content = {
|
||||||
|
Text(
|
||||||
|
text = notificationCount.toString(),
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
modifier = Modifier.padding(horizontal = 5.dp, vertical = 2.dp),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
modifier = modifier,
|
||||||
|
containerColor = backgroundColor,
|
||||||
|
contentColor = contentColor,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun NotificationBadge_preview() {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.background(color = Color.White)
|
||||||
|
.padding(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
) {
|
||||||
|
NotificationBadge(notificationCount = 0, backgroundColor = Color.Red)
|
||||||
|
NotificationBadge(notificationCount = 4, backgroundColor = Color.Red)
|
||||||
|
NotificationBadge(notificationCount = 199, backgroundColor = Color.Green)
|
||||||
|
NotificationBadge(
|
||||||
|
notificationCount = 1999,
|
||||||
|
backgroundColor = Color.Blue,
|
||||||
|
contentColor = Color.Yellow,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
package com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar
|
package com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar
|
||||||
|
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||||
|
@ -10,6 +12,7 @@ import androidx.compose.foundation.layout.ime
|
||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.statusBars
|
import androidx.compose.foundation.layout.statusBars
|
||||||
|
import androidx.compose.material3.BadgedBox
|
||||||
import androidx.compose.material3.BottomAppBar
|
import androidx.compose.material3.BottomAppBar
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
@ -44,6 +47,7 @@ import androidx.navigation.navOptions
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.max
|
import com.x8bit.bitwarden.ui.platform.base.util.max
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.toDp
|
import com.x8bit.bitwarden.ui.platform.base.util.toDp
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.badge.NotificationBadge
|
||||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||||
import com.x8bit.bitwarden.ui.platform.components.scrim.BitwardenAnimatedScrim
|
import com.x8bit.bitwarden.ui.platform.components.scrim.BitwardenAnimatedScrim
|
||||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||||
|
@ -267,7 +271,7 @@ private fun VaultBottomAppBar(
|
||||||
),
|
),
|
||||||
VaultUnlockedNavBarTab.Send,
|
VaultUnlockedNavBarTab.Send,
|
||||||
VaultUnlockedNavBarTab.Generator,
|
VaultUnlockedNavBarTab.Generator,
|
||||||
VaultUnlockedNavBarTab.Settings,
|
VaultUnlockedNavBarTab.Settings(state.notificationState.settingsTabNotificationCount),
|
||||||
)
|
)
|
||||||
// Collecting the back stack entry here as state is crucial to ensuring that the items
|
// Collecting the back stack entry here as state is crucial to ensuring that the items
|
||||||
// below recompose when the navigation state changes to update the selected tab.
|
// below recompose when the navigation state changes to update the selected tab.
|
||||||
|
@ -277,18 +281,27 @@ private fun VaultBottomAppBar(
|
||||||
|
|
||||||
NavigationBarItem(
|
NavigationBarItem(
|
||||||
icon = {
|
icon = {
|
||||||
Icon(
|
BadgedBox(
|
||||||
painter = rememberVectorPainter(
|
badge = {
|
||||||
id = if (isSelected) {
|
NotificationBadge(
|
||||||
destination.iconResSelected
|
notificationCount = destination.notificationCount,
|
||||||
} else {
|
isVisible = destination.notificationCount > 0,
|
||||||
destination.iconRes
|
)
|
||||||
},
|
},
|
||||||
),
|
) {
|
||||||
contentDescription = stringResource(
|
Icon(
|
||||||
id = destination.contentDescriptionRes,
|
painter = rememberVectorPainter(
|
||||||
),
|
id = if (isSelected) {
|
||||||
)
|
destination.iconResSelected
|
||||||
|
} else {
|
||||||
|
destination.iconRes
|
||||||
|
},
|
||||||
|
),
|
||||||
|
contentDescription = stringResource(
|
||||||
|
id = destination.contentDescriptionRes,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
label = {
|
label = {
|
||||||
Text(
|
Text(
|
||||||
|
@ -303,7 +316,7 @@ private fun VaultBottomAppBar(
|
||||||
is VaultUnlockedNavBarTab.Vault -> vaultTabClickedAction()
|
is VaultUnlockedNavBarTab.Vault -> vaultTabClickedAction()
|
||||||
VaultUnlockedNavBarTab.Send -> sendTabClickedAction()
|
VaultUnlockedNavBarTab.Send -> sendTabClickedAction()
|
||||||
VaultUnlockedNavBarTab.Generator -> generatorTabClickedAction()
|
VaultUnlockedNavBarTab.Generator -> generatorTabClickedAction()
|
||||||
VaultUnlockedNavBarTab.Settings -> settingsTabClickedAction()
|
is VaultUnlockedNavBarTab.Settings -> settingsTabClickedAction()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
colors = NavigationBarItemDefaults.colors(
|
colors = NavigationBarItemDefaults.colors(
|
||||||
|
|
|
@ -7,6 +7,7 @@ import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
||||||
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
||||||
|
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.BackgroundEvent
|
import com.x8bit.bitwarden.ui.platform.base.util.BackgroundEvent
|
||||||
import com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar.model.VaultUnlockedNavBarTab
|
import com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar.model.VaultUnlockedNavBarTab
|
||||||
|
@ -23,10 +24,14 @@ import javax.inject.Inject
|
||||||
class VaultUnlockedNavBarViewModel @Inject constructor(
|
class VaultUnlockedNavBarViewModel @Inject constructor(
|
||||||
authRepository: AuthRepository,
|
authRepository: AuthRepository,
|
||||||
specialCircumstancesManager: SpecialCircumstanceManager,
|
specialCircumstancesManager: SpecialCircumstanceManager,
|
||||||
|
settingsRepository: SettingsRepository,
|
||||||
) : BaseViewModel<VaultUnlockedNavBarState, VaultUnlockedNavBarEvent, VaultUnlockedNavBarAction>(
|
) : BaseViewModel<VaultUnlockedNavBarState, VaultUnlockedNavBarEvent, VaultUnlockedNavBarAction>(
|
||||||
initialState = VaultUnlockedNavBarState(
|
initialState = VaultUnlockedNavBarState(
|
||||||
vaultNavBarLabelRes = R.string.my_vault,
|
vaultNavBarLabelRes = R.string.my_vault,
|
||||||
vaultNavBarContentDescriptionRes = R.string.my_vault,
|
vaultNavBarContentDescriptionRes = R.string.my_vault,
|
||||||
|
notificationState = VaultUnlockedNavBarNotificationState(
|
||||||
|
settingsTabNotificationCount = settingsRepository.allSettingsBadgeCountFlow.value,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
|
@ -37,6 +42,13 @@ class VaultUnlockedNavBarViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
.launchIn(viewModelScope)
|
.launchIn(viewModelScope)
|
||||||
|
|
||||||
|
settingsRepository
|
||||||
|
.allSettingsBadgeCountFlow
|
||||||
|
.onEach {
|
||||||
|
sendAction(VaultUnlockedNavBarAction.Internal.SettingsNotificationCountUpdate(it))
|
||||||
|
}
|
||||||
|
.launchIn(viewModelScope)
|
||||||
|
|
||||||
when (specialCircumstancesManager.specialCircumstance) {
|
when (specialCircumstancesManager.specialCircumstance) {
|
||||||
SpecialCircumstance.GeneratorShortcut -> {
|
SpecialCircumstance.GeneratorShortcut -> {
|
||||||
sendEvent(VaultUnlockedNavBarEvent.Shortcut.NavigateToGeneratorScreen)
|
sendEvent(VaultUnlockedNavBarEvent.Shortcut.NavigateToGeneratorScreen)
|
||||||
|
@ -72,6 +84,10 @@ class VaultUnlockedNavBarViewModel @Inject constructor(
|
||||||
is VaultUnlockedNavBarAction.Internal.UserStateUpdateReceive -> {
|
is VaultUnlockedNavBarAction.Internal.UserStateUpdateReceive -> {
|
||||||
handleUserStateUpdateReceive(action)
|
handleUserStateUpdateReceive(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is VaultUnlockedNavBarAction.Internal.SettingsNotificationCountUpdate -> {
|
||||||
|
handleSettingsNotificationCountUpdate(action)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #region BottomTabViewModel Action Handlers
|
// #region BottomTabViewModel Action Handlers
|
||||||
|
@ -128,6 +144,18 @@ class VaultUnlockedNavBarViewModel @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleSettingsNotificationCountUpdate(
|
||||||
|
action: VaultUnlockedNavBarAction.Internal.SettingsNotificationCountUpdate,
|
||||||
|
) {
|
||||||
|
mutableStateFlow.update {
|
||||||
|
it.copy(
|
||||||
|
notificationState = it.notificationState.copy(
|
||||||
|
settingsTabNotificationCount = action.count,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
// #endregion BottomTabViewModel Action Handlers
|
// #endregion BottomTabViewModel Action Handlers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +165,14 @@ class VaultUnlockedNavBarViewModel @Inject constructor(
|
||||||
data class VaultUnlockedNavBarState(
|
data class VaultUnlockedNavBarState(
|
||||||
@StringRes val vaultNavBarLabelRes: Int,
|
@StringRes val vaultNavBarLabelRes: Int,
|
||||||
@StringRes val vaultNavBarContentDescriptionRes: Int,
|
@StringRes val vaultNavBarContentDescriptionRes: Int,
|
||||||
|
val notificationState: VaultUnlockedNavBarNotificationState,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models the notification state for each the tabs in the nav bar which support notification badges.
|
||||||
|
*/
|
||||||
|
data class VaultUnlockedNavBarNotificationState(
|
||||||
|
val settingsTabNotificationCount: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -170,9 +206,12 @@ sealed class VaultUnlockedNavBarAction {
|
||||||
/**
|
/**
|
||||||
* Indicates a change in user state has been received.
|
* Indicates a change in user state has been received.
|
||||||
*/
|
*/
|
||||||
data class UserStateUpdateReceive(
|
data class UserStateUpdateReceive(val userState: UserState?) : Internal()
|
||||||
val userState: UserState?,
|
|
||||||
) : Internal()
|
/**
|
||||||
|
* Indicates a change to the count of settings notifications to show
|
||||||
|
*/
|
||||||
|
data class SettingsNotificationCountUpdate(val count: Int) : Internal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +256,7 @@ sealed class VaultUnlockedNavBarEvent {
|
||||||
* Navigate to the Settings screen.
|
* Navigate to the Settings screen.
|
||||||
*/
|
*/
|
||||||
data object NavigateToSettingsScreen : VaultUnlockedNavBarEvent() {
|
data object NavigateToSettingsScreen : VaultUnlockedNavBarEvent() {
|
||||||
override val tab: VaultUnlockedNavBarTab = VaultUnlockedNavBarTab.Settings
|
override val tab: VaultUnlockedNavBarTab = VaultUnlockedNavBarTab.Settings()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -49,6 +49,11 @@ sealed class VaultUnlockedNavBarTab : Parcelable {
|
||||||
*/
|
*/
|
||||||
abstract val testTag: String
|
abstract val testTag: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount of notifications for items that fall under this tab.
|
||||||
|
*/
|
||||||
|
abstract val notificationCount: Int
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the Generator screen.
|
* Show the Generator screen.
|
||||||
*/
|
*/
|
||||||
|
@ -60,6 +65,7 @@ sealed class VaultUnlockedNavBarTab : Parcelable {
|
||||||
override val contentDescriptionRes get() = R.string.generator
|
override val contentDescriptionRes get() = R.string.generator
|
||||||
override val route get() = GENERATOR_GRAPH_ROUTE
|
override val route get() = GENERATOR_GRAPH_ROUTE
|
||||||
override val testTag get() = "GeneratorTab"
|
override val testTag get() = "GeneratorTab"
|
||||||
|
override val notificationCount get() = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,6 +79,7 @@ sealed class VaultUnlockedNavBarTab : Parcelable {
|
||||||
override val contentDescriptionRes get() = R.string.send
|
override val contentDescriptionRes get() = R.string.send
|
||||||
override val route get() = SEND_GRAPH_ROUTE
|
override val route get() = SEND_GRAPH_ROUTE
|
||||||
override val testTag get() = "SendTab"
|
override val testTag get() = "SendTab"
|
||||||
|
override val notificationCount get() = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,13 +94,16 @@ sealed class VaultUnlockedNavBarTab : Parcelable {
|
||||||
override val iconRes get() = R.drawable.ic_vault
|
override val iconRes get() = R.drawable.ic_vault
|
||||||
override val route get() = VAULT_GRAPH_ROUTE
|
override val route get() = VAULT_GRAPH_ROUTE
|
||||||
override val testTag get() = "VaultTab"
|
override val testTag get() = "VaultTab"
|
||||||
|
override val notificationCount get() = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the Settings screen.
|
* Show the Settings screen.
|
||||||
*/
|
*/
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data object Settings : VaultUnlockedNavBarTab() {
|
data class Settings(
|
||||||
|
override val notificationCount: Int = 0,
|
||||||
|
) : VaultUnlockedNavBarTab() {
|
||||||
override val iconResSelected get() = R.drawable.ic_settings_filled
|
override val iconResSelected get() = R.drawable.ic_settings_filled
|
||||||
override val iconRes get() = R.drawable.ic_settings
|
override val iconRes get() = R.drawable.ic_settings
|
||||||
override val labelRes get() = R.string.settings
|
override val labelRes get() = R.string.settings
|
||||||
|
|
|
@ -11,6 +11,7 @@ import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
|
@ -182,15 +183,47 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
||||||
VaultUnlockedNavBarState(
|
VaultUnlockedNavBarState(
|
||||||
vaultNavBarLabelRes = R.string.vaults,
|
vaultNavBarLabelRes = R.string.vaults,
|
||||||
vaultNavBarContentDescriptionRes = R.string.vaults,
|
vaultNavBarContentDescriptionRes = R.string.vaults,
|
||||||
|
notificationState = VaultUnlockedNavBarNotificationState(
|
||||||
|
settingsTabNotificationCount = 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
composeTestRule.onNodeWithText("My vault").assertDoesNotExist()
|
composeTestRule.onNodeWithText("My vault").assertDoesNotExist()
|
||||||
composeTestRule.onNodeWithText("Vaults").assertExists()
|
composeTestRule.onNodeWithText("Vaults").assertExists()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `settings tab notification count should update according to state and show correct count`() {
|
||||||
|
mutableStateFlow.update {
|
||||||
|
it.copy(
|
||||||
|
notificationState = VaultUnlockedNavBarNotificationState(
|
||||||
|
settingsTabNotificationCount = 1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithText("1", useUnmergedTree = true)
|
||||||
|
.assertExists()
|
||||||
|
|
||||||
|
mutableStateFlow.update {
|
||||||
|
it.copy(
|
||||||
|
notificationState = VaultUnlockedNavBarNotificationState(
|
||||||
|
settingsTabNotificationCount = 0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithText("1", useUnmergedTree = true)
|
||||||
|
.assertDoesNotExist()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val DEFAULT_STATE = VaultUnlockedNavBarState(
|
private val DEFAULT_STATE = VaultUnlockedNavBarState(
|
||||||
vaultNavBarLabelRes = R.string.my_vault,
|
vaultNavBarLabelRes = R.string.my_vault,
|
||||||
vaultNavBarContentDescriptionRes = R.string.my_vault,
|
vaultNavBarContentDescriptionRes = R.string.my_vault,
|
||||||
|
notificationState = VaultUnlockedNavBarNotificationState(
|
||||||
|
settingsTabNotificationCount = 0,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
||||||
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
||||||
|
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.just
|
import io.mockk.just
|
||||||
|
@ -27,6 +28,11 @@ class VaultUnlockedNavBarViewModelTest : BaseViewModelTest() {
|
||||||
every { specialCircumstance } returns null
|
every { specialCircumstance } returns null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val mutableSettingsBadgeCountFlow = MutableStateFlow(0)
|
||||||
|
private val settingsRepository: SettingsRepository = mockk {
|
||||||
|
every { allSettingsBadgeCountFlow } returns mutableSettingsBadgeCountFlow
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
@Suppress("MaxLineLength")
|
||||||
@Test
|
@Test
|
||||||
fun `on init with GeneratorShortcut special circumstance should navigate to the generator screen with shortcut event`() =
|
fun `on init with GeneratorShortcut special circumstance should navigate to the generator screen with shortcut event`() =
|
||||||
|
@ -98,6 +104,7 @@ class VaultUnlockedNavBarViewModelTest : BaseViewModelTest() {
|
||||||
val expectedWithOrganizations = VaultUnlockedNavBarState(
|
val expectedWithOrganizations = VaultUnlockedNavBarState(
|
||||||
vaultNavBarLabelRes = R.string.vaults,
|
vaultNavBarLabelRes = R.string.vaults,
|
||||||
vaultNavBarContentDescriptionRes = R.string.vaults,
|
vaultNavBarContentDescriptionRes = R.string.vaults,
|
||||||
|
notificationState = DEFAULT_NOTIFICATION_STATE,
|
||||||
)
|
)
|
||||||
val accountWithoutOrganizations: UserState.Account = mockk {
|
val accountWithoutOrganizations: UserState.Account = mockk {
|
||||||
every { userId } returns activeUserId
|
every { userId } returns activeUserId
|
||||||
|
@ -106,6 +113,7 @@ class VaultUnlockedNavBarViewModelTest : BaseViewModelTest() {
|
||||||
val expectedWithoutOrganizations = VaultUnlockedNavBarState(
|
val expectedWithoutOrganizations = VaultUnlockedNavBarState(
|
||||||
vaultNavBarLabelRes = R.string.my_vault,
|
vaultNavBarLabelRes = R.string.my_vault,
|
||||||
vaultNavBarContentDescriptionRes = R.string.my_vault,
|
vaultNavBarContentDescriptionRes = R.string.my_vault,
|
||||||
|
notificationState = DEFAULT_NOTIFICATION_STATE,
|
||||||
)
|
)
|
||||||
|
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
|
@ -182,9 +190,55 @@ class VaultUnlockedNavBarViewModelTest : BaseViewModelTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Internal action for SettingsNotificationCountUpdate should update notification state`() {
|
||||||
|
val expectedCount = 3
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
assertEquals(DEFAULT_STATE, viewModel.stateFlow.value)
|
||||||
|
viewModel.trySendAction(
|
||||||
|
VaultUnlockedNavBarAction.Internal.SettingsNotificationCountUpdate(
|
||||||
|
expectedCount,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
DEFAULT_STATE.copy(
|
||||||
|
notificationState = DEFAULT_NOTIFICATION_STATE.copy(
|
||||||
|
settingsTabNotificationCount = expectedCount,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
viewModel.stateFlow.value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `On initialization settings count should be updated from settings repository`() {
|
||||||
|
val expectedCount = 5
|
||||||
|
mutableSettingsBadgeCountFlow.value = expectedCount
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
assertEquals(
|
||||||
|
DEFAULT_STATE.copy(
|
||||||
|
notificationState = DEFAULT_NOTIFICATION_STATE.copy(
|
||||||
|
settingsTabNotificationCount = expectedCount,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
viewModel.stateFlow.value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun createViewModel() =
|
private fun createViewModel() =
|
||||||
VaultUnlockedNavBarViewModel(
|
VaultUnlockedNavBarViewModel(
|
||||||
authRepository = authRepository,
|
authRepository = authRepository,
|
||||||
specialCircumstancesManager = specialCircumstancesManager,
|
specialCircumstancesManager = specialCircumstancesManager,
|
||||||
|
settingsRepository = settingsRepository,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val DEFAULT_NOTIFICATION_STATE = VaultUnlockedNavBarNotificationState(
|
||||||
|
settingsTabNotificationCount = 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
private val DEFAULT_STATE = VaultUnlockedNavBarState(
|
||||||
|
vaultNavBarLabelRes = R.string.my_vault,
|
||||||
|
vaultNavBarContentDescriptionRes = R.string.my_vault,
|
||||||
|
notificationState = DEFAULT_NOTIFICATION_STATE,
|
||||||
|
)
|
||||||
|
|
Loading…
Add table
Reference in a new issue