Add navigation chevron for Import Items button (#6020)

This commit is contained in:
David Perez 2025-10-13 15:48:41 -05:00 committed by GitHub
parent 912eba14d6
commit 318307c377
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 75 additions and 7 deletions

View File

@ -8,9 +8,11 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
@ -24,6 +26,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bitwarden.ui.platform.base.util.EventsEffect
import com.bitwarden.ui.platform.base.util.mirrorIfRtl
import com.bitwarden.ui.platform.base.util.standardHorizontalMargin
import com.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
import com.bitwarden.ui.platform.components.badge.NotificationBadge
@ -37,6 +40,7 @@ import com.bitwarden.ui.platform.components.snackbar.model.rememberBitwardenSnac
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
/**
* Displays the vault settings screen.
@ -160,7 +164,18 @@ fun VaultSettingsScreen(
.testTag("ImportItemsLinkItemView")
.standardHorizontalMargin()
.fillMaxWidth(),
)
) {
if (state.showImportItemsChevron) {
Icon(
painter = rememberVectorPainter(id = BitwardenDrawable.ic_chevron_right),
contentDescription = null,
tint = BitwardenTheme.colorScheme.icon.primary,
modifier = Modifier
.mirrorIfRtl()
.size(size = 16.dp),
)
}
}
Spacer(modifier = Modifier.height(height = 16.dp))
Spacer(modifier = Modifier.navigationBarsPadding())
}

View File

@ -30,6 +30,9 @@ class VaultSettingsViewModel @Inject constructor(
val firstTimeState = firstTimeActionManager.currentOrDefaultUserFirstTimeState
VaultSettingsState(
showImportActionCard = firstTimeState.showImportLoginsCardInSettings,
showImportItemsChevron = featureFlagManager.getFeatureFlag(
key = FlagKey.CredentialExchangeProtocolImport,
),
)
},
) {
@ -49,6 +52,12 @@ class VaultSettingsViewModel @Inject constructor(
.map { VaultSettingsAction.Internal.SnackbarDataReceived(it) }
.onEach(::sendAction)
.launchIn(viewModelScope)
featureFlagManager
.getFeatureFlagFlow(key = FlagKey.CredentialExchangeProtocolImport)
.map { VaultSettingsAction.Internal.ImportFeatureUpdated(it) }
.onEach(::sendAction)
.launchIn(viewModelScope)
}
override fun handleAction(action: VaultSettingsAction): Unit = when (action) {
@ -70,6 +79,10 @@ class VaultSettingsViewModel @Inject constructor(
is VaultSettingsAction.Internal.SnackbarDataReceived -> {
handleSnackbarDataReceived(action)
}
is VaultSettingsAction.Internal.ImportFeatureUpdated -> {
handleImportFeatureUpdated(action)
}
}
}
@ -79,6 +92,12 @@ class VaultSettingsViewModel @Inject constructor(
sendEvent(VaultSettingsEvent.ShowSnackbar(action.data))
}
private fun handleImportFeatureUpdated(
action: VaultSettingsAction.Internal.ImportFeatureUpdated,
) {
mutableStateFlow.update { it.copy(showImportItemsChevron = action.isEnabled) }
}
private fun handleImportLoginsCardDismissClicked() {
if (!state.showImportActionCard) return
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = false)
@ -122,6 +141,7 @@ class VaultSettingsViewModel @Inject constructor(
*/
data class VaultSettingsState(
val showImportActionCard: Boolean,
val showImportItemsChevron: Boolean,
)
/**
@ -197,6 +217,13 @@ sealed class VaultSettingsAction {
* Internal actions not performed by user interation
*/
sealed class Internal : VaultSettingsAction() {
/**
* Indicates that the import feature flag has been updated.
*/
data class ImportFeatureUpdated(
val isEnabled: Boolean,
) : Internal()
/**
* Indicates user first time state has changed.
*/

View File

@ -29,11 +29,7 @@ class VaultSettingsScreenTest : BitwardenComposeTest() {
private var onNavigateToExportVaultCalled = false
private var onNavigateToFoldersCalled = false
private val mutableEventFlow = bufferedMutableSharedFlow<VaultSettingsEvent>()
private val mutableStateFlow = MutableStateFlow(
VaultSettingsState(
showImportActionCard = false,
),
)
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
val viewModel = mockk<VaultSettingsViewModel>(relaxed = true) {
every { eventFlow } returns mutableEventFlow
@ -158,3 +154,8 @@ class VaultSettingsScreenTest : BitwardenComposeTest() {
.assertIsNotDisplayed()
}
}
private val DEFAULT_STATE: VaultSettingsState = VaultSettingsState(
showImportActionCard = false,
showImportItemsChevron = true,
)

View File

@ -31,7 +31,13 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
every { firstTimeStateFlow } returns mutableFirstTimeStateFlow
every { storeShowImportLoginsSettingsBadge(any()) } just runs
}
private val featureFlagManager = mockk<FeatureFlagManager>()
private val mutableFeatureFlagFlow = bufferedMutableSharedFlow<Boolean>()
private val featureFlagManager = mockk<FeatureFlagManager> {
every { getFeatureFlag(FlagKey.CredentialExchangeProtocolImport) } returns true
every {
getFeatureFlagFlow(FlagKey.CredentialExchangeProtocolImport)
} returns mutableFeatureFlagFlow
}
private val mutableSnackbarSharedFlow = bufferedMutableSharedFlow<BitwardenSnackbarData>()
private val snackbarRelayManager = mockk<SnackbarRelayManager> {
@ -153,6 +159,25 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
}
}
@Test
fun `CredentialExchangeProtocolImport flag changes should update state accordingly`() {
val viewModel = createViewModel()
assertEquals(
viewModel.stateFlow.value,
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = true),
)
mutableFeatureFlagFlow.tryEmit(false)
assertEquals(
viewModel.stateFlow.value,
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = false),
)
mutableFeatureFlagFlow.tryEmit(true)
assertEquals(
viewModel.stateFlow.value,
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = true),
)
}
private fun createViewModel(): VaultSettingsViewModel = VaultSettingsViewModel(
firstTimeActionManager = firstTimeActionManager,
snackbarRelayManager = snackbarRelayManager,