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 package com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.lifecycle.viewModelScope
import com.x8bit.bitwarden.R import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.auth.repository.AuthRepository 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.SpecialCircumstanceManager
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel 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 import javax.inject.Inject
/** /**
@ -14,25 +19,22 @@ import javax.inject.Inject
*/ */
@HiltViewModel @HiltViewModel
class VaultUnlockedNavBarViewModel @Inject constructor( class VaultUnlockedNavBarViewModel @Inject constructor(
private val authRepository: AuthRepository, authRepository: AuthRepository,
specialCircumstancesManager: SpecialCircumstanceManager, specialCircumstancesManager: SpecialCircumstanceManager,
) : BaseViewModel<VaultUnlockedNavBarState, VaultUnlockedNavBarEvent, VaultUnlockedNavBarAction>( ) : BaseViewModel<VaultUnlockedNavBarState, VaultUnlockedNavBarEvent, VaultUnlockedNavBarAction>(
initialState = run { initialState = VaultUnlockedNavBarState(
val hasOrganization = authRepository vaultNavBarLabelRes = R.string.my_vault,
.userStateFlow vaultNavBarContentDescriptionRes = R.string.my_vault,
.value ),
?.activeAccount
?.organizations
?.isNotEmpty()
?: false
val vaultRes = if (hasOrganization) R.string.vaults else R.string.my_vault
VaultUnlockedNavBarState(
vaultNavBarLabelRes = vaultRes,
vaultNavBarContentDescriptionRes = vaultRes,
)
},
) { ) {
init { init {
authRepository
.userStateFlow
.onEach {
sendAction(VaultUnlockedNavBarAction.Internal.UserStateUpdateReceive(it))
}
.launchIn(viewModelScope)
when (specialCircumstancesManager.specialCircumstance) { when (specialCircumstancesManager.specialCircumstance) {
SpecialCircumstance.GeneratorShortcut -> { SpecialCircumstance.GeneratorShortcut -> {
sendEvent(VaultUnlockedNavBarEvent.NavigateToGeneratorScreen) sendEvent(VaultUnlockedNavBarEvent.NavigateToGeneratorScreen)
@ -54,6 +56,15 @@ class VaultUnlockedNavBarViewModel @Inject constructor(
VaultUnlockedNavBarAction.SendTabClick -> handleSendTabClicked() VaultUnlockedNavBarAction.SendTabClick -> handleSendTabClicked()
VaultUnlockedNavBarAction.SettingsTabClick -> handleSettingsTabClicked() VaultUnlockedNavBarAction.SettingsTabClick -> handleSettingsTabClicked()
VaultUnlockedNavBarAction.VaultTabClick -> handleVaultTabClicked() 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 // #region BottomTabViewModel Action Handlers
@ -84,6 +95,27 @@ class VaultUnlockedNavBarViewModel @Inject constructor(
private fun handleSettingsTabClicked() { private fun handleSettingsTabClicked() {
sendEvent(VaultUnlockedNavBarEvent.NavigateToSettingsScreen) 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 // #endregion BottomTabViewModel Action Handlers
} }
@ -100,24 +132,36 @@ data class VaultUnlockedNavBarState(
*/ */
sealed class VaultUnlockedNavBarAction { sealed class VaultUnlockedNavBarAction {
/** /**
* click Generator tab. * Click Generator tab.
*/ */
data object GeneratorTabClick : VaultUnlockedNavBarAction() data object GeneratorTabClick : VaultUnlockedNavBarAction()
/** /**
* click Send tab. * Click Send tab.
*/ */
data object SendTabClick : VaultUnlockedNavBarAction() data object SendTabClick : VaultUnlockedNavBarAction()
/** /**
* click Vault tab. * Click Vault tab.
*/ */
data object VaultTabClick : VaultUnlockedNavBarAction() data object VaultTabClick : VaultUnlockedNavBarAction()
/** /**
* click Settings tab. * Click Settings tab.
*/ */
data object SettingsTabClick : VaultUnlockedNavBarAction() 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 @Test
fun `on init with no organizations should set correct vault label res`() = runTest { fun `new user state should update vault nav bar title`() = runTest {
val viewModel = createViewModel() val activeUserId = "activeUserId"
val expected = VaultUnlockedNavBarState( 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, vaultNavBarLabelRes = R.string.my_vault,
vaultNavBarContentDescriptionRes = R.string.my_vault, vaultNavBarContentDescriptionRes = R.string.my_vault,
) )
val viewModel = createViewModel()
viewModel.stateFlow.test { viewModel.stateFlow.test {
assertEquals( assertEquals(
expected, expectedWithoutOrganizations,
awaitItem(), awaitItem(),
) )
}
}
@Test mutableUserStateFlow.tryEmit(
fun `on init with organizations should set correct vault label res`() = runTest { UserState(
val activeUserId = "activeUserId" activeUserId = activeUserId,
val account: UserState.Account = mockk { accounts = listOf(accountWithOrganizations),
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 {
assertEquals( assertEquals(
expected, expectedWithOrganizations,
awaitItem(),
)
mutableUserStateFlow.tryEmit(
UserState(
activeUserId = activeUserId,
accounts = listOf(accountWithoutOrganizations),
),
)
assertEquals(
expectedWithoutOrganizations,
awaitItem(), awaitItem(),
) )
} }