PM-8522: Fix vault tab nav bar title when logging in (#3710)
Some checks failed
Crowdin Push / Crowdin Push (push) Waiting to run
Scan / Check PR run (push) Failing after 0s
Scan / SAST scan (push) Has been skipped
Scan / Quality scan (push) Has been skipped
Test / Check PR run (push) Failing after 0s
Test / Test (push) Has been skipped

This commit is contained in:
Shannon Draeker 2024-08-13 12:55:51 -04:00 committed by GitHub
parent 551f948644
commit e3371b7620
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 99 additions and 44 deletions

View file

@ -1,12 +1,17 @@
package com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar
import androidx.annotation.StringRes
import androidx.lifecycle.viewModelScope
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.model.UserState
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
import javax.inject.Inject
/**
@ -14,25 +19,22 @@ import javax.inject.Inject
*/
@HiltViewModel
class VaultUnlockedNavBarViewModel @Inject constructor(
private val authRepository: AuthRepository,
authRepository: AuthRepository,
specialCircumstancesManager: SpecialCircumstanceManager,
) : BaseViewModel<VaultUnlockedNavBarState, VaultUnlockedNavBarEvent, VaultUnlockedNavBarAction>(
initialState = run {
val hasOrganization = authRepository
.userStateFlow
.value
?.activeAccount
?.organizations
?.isNotEmpty()
?: false
val vaultRes = if (hasOrganization) R.string.vaults else R.string.my_vault
VaultUnlockedNavBarState(
vaultNavBarLabelRes = vaultRes,
vaultNavBarContentDescriptionRes = vaultRes,
)
},
initialState = VaultUnlockedNavBarState(
vaultNavBarLabelRes = R.string.my_vault,
vaultNavBarContentDescriptionRes = R.string.my_vault,
),
) {
init {
authRepository
.userStateFlow
.onEach {
sendAction(VaultUnlockedNavBarAction.Internal.UserStateUpdateReceive(it))
}
.launchIn(viewModelScope)
when (specialCircumstancesManager.specialCircumstance) {
SpecialCircumstance.GeneratorShortcut -> {
sendEvent(VaultUnlockedNavBarEvent.NavigateToGeneratorScreen)
@ -54,6 +56,15 @@ class VaultUnlockedNavBarViewModel @Inject constructor(
VaultUnlockedNavBarAction.SendTabClick -> handleSendTabClicked()
VaultUnlockedNavBarAction.SettingsTabClick -> handleSettingsTabClicked()
VaultUnlockedNavBarAction.VaultTabClick -> handleVaultTabClicked()
is VaultUnlockedNavBarAction.Internal -> handleInternalAction(action)
}
}
private fun handleInternalAction(action: VaultUnlockedNavBarAction.Internal) {
when (action) {
is VaultUnlockedNavBarAction.Internal.UserStateUpdateReceive -> {
handleUserStateUpdateReceive(action)
}
}
}
// #region BottomTabViewModel Action Handlers
@ -84,6 +95,27 @@ class VaultUnlockedNavBarViewModel @Inject constructor(
private fun handleSettingsTabClicked() {
sendEvent(VaultUnlockedNavBarEvent.NavigateToSettingsScreen)
}
/**
* Updates the nav bar title according to whether the user is part of any organizations or not.
*/
private fun handleUserStateUpdateReceive(
action: VaultUnlockedNavBarAction.Internal.UserStateUpdateReceive,
) {
val hasOrganizations = action
.userState
?.activeAccount
?.organizations
?.isNotEmpty()
?: false
val vaultRes = if (hasOrganizations) R.string.vaults else R.string.my_vault
mutableStateFlow.update {
it.copy(
vaultNavBarLabelRes = vaultRes,
vaultNavBarContentDescriptionRes = vaultRes,
)
}
}
// #endregion BottomTabViewModel Action Handlers
}
@ -100,24 +132,36 @@ data class VaultUnlockedNavBarState(
*/
sealed class VaultUnlockedNavBarAction {
/**
* click Generator tab.
* Click Generator tab.
*/
data object GeneratorTabClick : VaultUnlockedNavBarAction()
/**
* click Send tab.
* Click Send tab.
*/
data object SendTabClick : VaultUnlockedNavBarAction()
/**
* click Vault tab.
* Click Vault tab.
*/
data object VaultTabClick : VaultUnlockedNavBarAction()
/**
* click Settings tab.
* Click Settings tab.
*/
data object SettingsTabClick : VaultUnlockedNavBarAction()
/**
* Models actions that the [VaultUnlockedNavBarViewModel] itself might send.
*/
sealed class Internal : VaultUnlockedNavBarAction() {
/**
* Indicates a change in user state has been received.
*/
data class UserStateUpdateReceive(
val userState: UserState?,
) : Internal()
}
}
/**

View file

@ -83,41 +83,52 @@ class VaultUnlockedNavBarViewModelTest : BaseViewModelTest() {
}
@Test
fun `on init with no organizations should set correct vault label res`() = runTest {
val viewModel = createViewModel()
val expected = VaultUnlockedNavBarState(
fun `new user state should update vault nav bar title`() = runTest {
val activeUserId = "activeUserId"
val accountWithOrganizations: UserState.Account = mockk {
every { userId } returns activeUserId
every { organizations } returns listOf(mockk())
}
val expectedWithOrganizations = VaultUnlockedNavBarState(
vaultNavBarLabelRes = R.string.vaults,
vaultNavBarContentDescriptionRes = R.string.vaults,
)
val accountWithoutOrganizations: UserState.Account = mockk {
every { userId } returns activeUserId
every { organizations } returns emptyList()
}
val expectedWithoutOrganizations = VaultUnlockedNavBarState(
vaultNavBarLabelRes = R.string.my_vault,
vaultNavBarContentDescriptionRes = R.string.my_vault,
)
val viewModel = createViewModel()
viewModel.stateFlow.test {
assertEquals(
expected,
expectedWithoutOrganizations,
awaitItem(),
)
}
}
@Test
fun `on init with organizations should set correct vault label res`() = runTest {
val activeUserId = "activeUserId"
val account: UserState.Account = mockk {
every { userId } returns activeUserId
every { organizations } returns listOf(mockk())
}
mutableUserStateFlow.value = UserState(
activeUserId = activeUserId,
accounts = listOf(account),
)
val viewModel = createViewModel()
val expected = VaultUnlockedNavBarState(
vaultNavBarLabelRes = R.string.vaults,
vaultNavBarContentDescriptionRes = R.string.vaults,
)
viewModel.stateFlow.test {
mutableUserStateFlow.tryEmit(
UserState(
activeUserId = activeUserId,
accounts = listOf(accountWithOrganizations),
),
)
assertEquals(
expected,
expectedWithOrganizations,
awaitItem(),
)
mutableUserStateFlow.tryEmit(
UserState(
activeUserId = activeUserId,
accounts = listOf(accountWithoutOrganizations),
),
)
assertEquals(
expectedWithoutOrganizations,
awaitItem(),
)
}