mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 07:05:35 +03:00
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
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:
parent
551f948644
commit
e3371b7620
2 changed files with 99 additions and 44 deletions
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue