PM-27136: Replace FirstTimeSyncSnackbarHost with BitwardenSnackbarHost (#6058)

This commit is contained in:
David Perez 2025-10-20 15:42:47 -05:00 committed by GitHub
parent 31e7e05eda
commit 97bb93c18e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 49 additions and 93 deletions

View File

@ -1,69 +0,0 @@
package com.bitwarden.authenticator.ui.authenticator.feature.itemlisting
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.bitwarden.ui.platform.resource.BitwardenDrawable
import com.bitwarden.ui.platform.resource.BitwardenString
import com.bitwarden.ui.platform.theme.BitwardenTheme
/**
* Show a snackbar that says "Account synced from Bitwarden app" with a close action.
*
* @param state Snackbar state used to show/hide. The message and title from this state are unused.
*/
@Composable
fun FirstTimeSyncSnackbarHost(
state: SnackbarHostState,
) {
SnackbarHost(
hostState = state,
snackbar = {
Row(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth()
.shadow(elevation = 6.dp)
.background(
color = BitwardenTheme.colorScheme.background.alert,
shape = BitwardenTheme.shapes.snackbar,
),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
modifier = Modifier
.padding(16.dp)
.weight(1f, fill = true),
text = stringResource(BitwardenString.account_synced_from_bitwarden_app),
style = BitwardenTheme.typography.bodyLarge,
color = BitwardenTheme.colorScheme.text.reversed,
)
IconButton(
onClick = { state.currentSnackbarData?.dismiss() },
) {
Icon(
painter = painterResource(id = BitwardenDrawable.ic_close),
contentDescription = stringResource(id = BitwardenString.close),
tint = BitwardenTheme.colorScheme.icon.reversed,
modifier = Modifier
.size(24.dp),
)
}
}
},
)
}

View File

@ -17,7 +17,6 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
@ -25,7 +24,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
@ -70,6 +68,8 @@ import com.bitwarden.ui.platform.components.fab.model.ExpandableFabOption
import com.bitwarden.ui.platform.components.header.BitwardenListHeaderText
import com.bitwarden.ui.platform.components.icon.model.IconData
import com.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
import com.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarHost
import com.bitwarden.ui.platform.components.snackbar.model.rememberBitwardenSnackbarHostState
import com.bitwarden.ui.platform.components.util.rememberVectorPainter
import com.bitwarden.ui.platform.composition.LocalIntentManager
import com.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme
@ -79,7 +79,6 @@ import com.bitwarden.ui.platform.resource.BitwardenString
import com.bitwarden.ui.platform.theme.BitwardenTheme
import com.bitwarden.ui.util.asText
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.launch
/**
* Displays the item listing screen.
@ -108,9 +107,7 @@ fun ItemListingScreen(
viewModel.trySendAction(ItemListingAction.EnterSetupKeyClick)
}
}
val snackbarHostState = remember { SnackbarHostState() }
val coroutineScope = rememberCoroutineScope()
val snackbarHostState = rememberBitwardenSnackbarHostState()
EventsEffect(viewModel = viewModel) { event ->
when (event) {
is ItemListingEvent.NavigateBack -> onNavigateBack()
@ -140,11 +137,8 @@ fun ItemListingScreen(
intentManager.startBitwardenAccountSettings()
}
is ItemListingEvent.ShowFirstTimeSyncSnackbar -> {
// Message property is overridden by FirstTimeSyncSnackbarHost:
coroutineScope.launch {
snackbarHostState.showSnackbar("")
}
is ItemListingEvent.ShowSnackbar -> {
snackbarHostState.showSnackbar(snackbarData = event.data)
}
}
}
@ -214,7 +208,7 @@ fun ItemListingScreen(
),
)
},
snackbarHost = { FirstTimeSyncSnackbarHost(state = snackbarHostState) },
snackbarHost = { BitwardenSnackbarHost(bitwardenHostState = snackbarHostState) },
) {
when (val currentState = state.viewState) {
is ItemListingState.ViewState.Content -> {

View File

@ -26,6 +26,7 @@ import com.bitwarden.authenticator.ui.platform.components.listitem.model.Verific
import com.bitwarden.authenticatorbridge.manager.AuthenticatorBridgeManager
import com.bitwarden.core.data.repository.model.DataState
import com.bitwarden.ui.platform.base.BaseViewModel
import com.bitwarden.ui.platform.components.snackbar.model.BitwardenSnackbarData
import com.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme
import com.bitwarden.ui.platform.resource.BitwardenString
import com.bitwarden.ui.util.Text
@ -263,7 +264,12 @@ class ItemListingViewModel @Inject constructor(
}
private fun handleFirstTimeUserSync() {
sendEvent(ItemListingEvent.ShowFirstTimeSyncSnackbar)
sendEvent(
event = ItemListingEvent.ShowSnackbar(
message = BitwardenString.account_synced_from_bitwarden_app.asText(),
withDismissAction = true,
),
)
}
private fun handleAppThemeChangeReceive(appTheme: AppTheme) {
@ -887,9 +893,25 @@ sealed class ItemListingEvent {
) : ItemListingEvent()
/**
* Show a Snackbar letting the user know accounts have synced.
* Show a Snackbar with the given [data].
*/
data object ShowFirstTimeSyncSnackbar : ItemListingEvent()
data class ShowSnackbar(
val data: BitwardenSnackbarData,
) : ItemListingEvent() {
constructor(
message: Text,
messageHeader: Text? = null,
actionLabel: Text? = null,
withDismissAction: Boolean = false,
) : this(
data = BitwardenSnackbarData(
message = message,
messageHeader = messageHeader,
actionLabel = actionLabel,
withDismissAction = withDismissAction,
),
)
}
}
/**

View File

@ -418,8 +418,10 @@ class ItemListingScreenTest : AuthenticatorComposeTest() {
.onNodeWithText("Account synced from Bitwarden app")
.assertIsNotDisplayed()
// Send ShowFirstTimeSyncSnackbar event
mutableEventFlow.tryEmit(ItemListingEvent.ShowFirstTimeSyncSnackbar)
// Send ShowSnackbar event
mutableEventFlow.tryEmit(
ItemListingEvent.ShowSnackbar(message = "Account synced from Bitwarden app".asText()),
)
// Make sure the snackbar is showing:
composeTestRule

View File

@ -442,11 +442,17 @@ class ItemListingViewModelTest : BaseViewModelTest() {
}
@Test
fun `on FirstTimeUserSyncReceive should emit ShowFirstTimeSyncSnackbar`() = runTest {
fun `on FirstTimeUserSyncReceive should emit ShowSnackbar`() = runTest {
val viewModel = createViewModel()
viewModel.eventFlow.test {
firstTimeAccountSyncChannel.send(Unit)
assertEquals(ItemListingEvent.ShowFirstTimeSyncSnackbar, awaitItem())
assertEquals(
ItemListingEvent.ShowSnackbar(
message = BitwardenString.account_synced_from_bitwarden_app.asText(),
withDismissAction = true,
),
awaitItem(),
)
}
}

View File

@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.displayCutout
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.union
@ -73,10 +72,13 @@ fun BitwardenSnackbar(
.clickable(
enabled = !bitwardenSnackbarData.withDismissAction,
onClick = onDismiss,
)
.padding(16.dp),
),
) {
Column(modifier = Modifier.weight(weight = 1f)) {
Column(
modifier = Modifier
.padding(top = 16.dp, bottom = 16.dp, start = 16.dp)
.weight(weight = 1f),
) {
bitwardenSnackbarData.messageHeader?.let {
Text(
text = it(),
@ -111,7 +113,6 @@ fun BitwardenSnackbar(
vectorIconRes = BitwardenDrawable.ic_close,
contentDescription = stringResource(BitwardenString.close),
contentColor = BitwardenTheme.colorScheme.icon.reversed,
modifier = Modifier.offset(x = 12.dp, y = (-12).dp),
)
}
}