Update the compose BOM to 2024.09.00 (#3874)

This commit is contained in:
David Perez 2024-09-06 12:45:12 -05:00 committed by GitHub
parent e468ec695b
commit 3f78ad6d6d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
51 changed files with 355 additions and 373 deletions

View file

@ -20,13 +20,13 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -308,7 +308,7 @@ private fun TermsAndPrivacySwitch(
}
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = { onCheckedChange.invoke(!isChecked) },
)
.padding(start = 16.dp)

View file

@ -20,7 +20,6 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.ClickableText
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@ -28,6 +27,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
@ -448,7 +448,7 @@ private fun ReceiveMarketingEmailsSwitch(
}
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = { onCheckedChange.invoke(!isChecked) },
)
.fillMaxWidth(),

View file

@ -298,7 +298,6 @@ private fun TwoFactorLoginScreenContent(
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@Preview(showBackground = true)
private fun TwoFactorLoginScreenContentPreview() {

View file

@ -1,7 +1,6 @@
package com.x8bit.bitwarden.ui.auth.feature.welcome
import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@ -23,7 +22,6 @@ import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -57,7 +55,6 @@ private val LANDSCAPE_HORIZONTAL_MARGIN: Dp = 128.dp
/**
* Top level composable for the welcome screen.
*/
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable
fun WelcomeScreen(
onNavigateToCreateAccount: () -> Unit,
@ -103,7 +100,6 @@ fun WelcomeScreen(
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun WelcomeScreenContent(
state: WelcomeState,

View file

@ -2,8 +2,8 @@ package com.x8bit.bitwarden.ui.platform.base.util
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LocalLifecycleOwner
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn

View file

@ -7,6 +7,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.CombinedModifier
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithCache
@ -42,14 +43,17 @@ fun Modifier.scrolledContainerBackground(
): Modifier {
val expandedColor = MaterialTheme.colorScheme.surface
val collapsedColor = MaterialTheme.colorScheme.surfaceContainer
return this then drawBehind {
drawRect(
color = topAppBarScrollBehavior.toScrolledContainerColor(
expandedColor = expandedColor,
collapsedColor = collapsedColor,
),
)
}
return CombinedModifier(
outer = this,
inner = drawBehind {
drawRect(
color = topAppBarScrollBehavior.toScrolledContainerColor(
expandedColor = expandedColor,
collapsedColor = collapsedColor,
),
)
},
)
}
/**

View file

@ -21,7 +21,6 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
@ -30,6 +29,7 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -276,7 +276,7 @@ private fun AccountSummaryItem(
.testTag("AccountCell")
.combinedClickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = { onSwitchAccountClick(accountSummary) },
onLongClick = { onSwitchAccountLongClick(accountSummary) },
)
@ -398,7 +398,7 @@ private fun AddAccountItem(
modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = onClick,
)
.padding(vertical = 8.dp)

View file

@ -4,9 +4,9 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@ -33,7 +33,7 @@ fun BitwardenBasicDialogRow(
modifier = modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = onClick,
)
.padding(

View file

@ -6,10 +6,10 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -57,7 +57,7 @@ fun EnvironmentSelector(
modifier = Modifier
.clip(RoundedCornerShape(28.dp))
.clickable(
indication = rememberRipple(
indication = ripple(
bounded = true,
color = MaterialTheme.colorScheme.primary,
),

View file

@ -6,10 +6,10 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@ -51,7 +51,7 @@ fun BitwardenGroupItem(
modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = onClick,
)
.bottomDivider(

View file

@ -10,11 +10,11 @@ import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -83,7 +83,7 @@ fun BitwardenListItem(
modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = onClick,
)
.defaultMinSize(minHeight = 72.dp)

View file

@ -9,10 +9,10 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@ -50,7 +50,7 @@ fun BitwardenTextRow(
.clickable(
enabled = isEnabled,
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = onClick,
)
.semantics(mergeDescendants = true) { },

View file

@ -13,14 +13,15 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScaffoldDefaults
import androidx.compose.material3.contentColorFor
import androidx.compose.material3.pulltorefresh.PullToRefreshContainer
import androidx.compose.material3.pulltorefresh.PullToRefreshState
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
import androidx.compose.material3.pulltorefresh.pullToRefresh
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId
@ -37,7 +38,7 @@ fun BitwardenScaffold(
snackbarHost: @Composable () -> Unit = { },
floatingActionButton: @Composable () -> Unit = { },
floatingActionButtonPosition: FabPosition = FabPosition.End,
pullToRefreshState: PullToRefreshState? = null,
pullToRefreshState: BitwardenPullToRefreshState = rememberBitwardenPullToRefreshState(),
containerColor: Color = MaterialTheme.colorScheme.surface,
contentColor: Color = contentColorFor(containerColor),
contentWindowInsets: WindowInsets = ScaffoldDefaults
@ -48,7 +49,6 @@ fun BitwardenScaffold(
Scaffold(
modifier = Modifier
.semantics { testTagsAsResourceId = true }
.run { pullToRefreshState?.let { nestedScroll(it.nestedScrollConnection) } ?: this }
.then(modifier),
topBar = topBar,
bottomBar = bottomBar,
@ -63,18 +63,50 @@ fun BitwardenScaffold(
contentColor = contentColor,
contentWindowInsets = contentWindowInsets,
content = { paddingValues ->
Box {
val internalPullToRefreshState = rememberPullToRefreshState()
Box(
modifier = Modifier.pullToRefresh(
state = internalPullToRefreshState,
isRefreshing = pullToRefreshState.isRefreshing,
onRefresh = pullToRefreshState.onRefresh,
enabled = pullToRefreshState.isEnabled,
),
) {
content(paddingValues)
pullToRefreshState?.let {
PullToRefreshContainer(
state = it,
modifier = Modifier
.padding(paddingValues)
.align(Alignment.TopCenter),
)
}
PullToRefreshDefaults.Indicator(
modifier = Modifier
.padding(paddingValues)
.align(Alignment.TopCenter),
isRefreshing = pullToRefreshState.isRefreshing,
state = internalPullToRefreshState,
)
}
},
)
}
/**
* The state of the pull-to-refresh.
*/
data class BitwardenPullToRefreshState(
val isEnabled: Boolean,
val isRefreshing: Boolean,
val onRefresh: () -> Unit,
)
/**
* Create and remember the default [BitwardenPullToRefreshState].
*/
@Composable
fun rememberBitwardenPullToRefreshState(
isEnabled: Boolean = false,
isRefreshing: Boolean = false,
onRefresh: () -> Unit = { },
): BitwardenPullToRefreshState = remember(isEnabled, isRefreshing, onRefresh) {
BitwardenPullToRefreshState(
isEnabled = isEnabled,
isRefreshing = isRefreshing,
onRefresh = onRefresh,
)
}

View file

@ -1,6 +1,5 @@
package com.x8bit.bitwarden.ui.platform.components.segment
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MultiChoiceSegmentedButtonRow
import androidx.compose.material3.SegmentedButton
import androidx.compose.material3.SegmentedButtonDefaults
@ -16,7 +15,6 @@ import kotlinx.collections.immutable.ImmutableList
* @param options List of options to display.
* @param modifier Modifier.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BitwardenSegmentedButton(
modifier: Modifier = Modifier,

View file

@ -5,9 +5,9 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@ -41,7 +41,7 @@ fun BitwardenClickableText(
modifier = modifier
.clip(RoundedCornerShape(cornerSize))
.clickable(
indication = rememberRipple(
indication = ripple(
bounded = true,
color = MaterialTheme.colorScheme.primary,
),

View file

@ -10,10 +10,10 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@ -49,7 +49,7 @@ fun BitwardenSwitch(
if (onCheckedChange != null) {
this.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = { onCheckedChange.invoke(!isChecked) },
)
} else {

View file

@ -5,11 +5,11 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@ -49,7 +49,7 @@ fun BitwardenSwitchWithActions(
modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = { onCheckedChange?.invoke(!isChecked) },
)
.semantics(mergeDescendants = true) {

View file

@ -10,10 +10,10 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@ -57,7 +57,7 @@ fun BitwardenWideSwitch(
.wrapContentHeight()
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = { onCheckedChange?.invoke(!isChecked) },
enabled = !readOnly && enabled,
)

View file

@ -12,13 +12,13 @@ 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.material.ripple.rememberRipple
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@ -108,7 +108,7 @@ private fun SettingsRow(
modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = onClick,
)
.bottomDivider(paddingStart = 16.dp)

View file

@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
@ -22,6 +21,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
@ -249,7 +249,7 @@ private fun CopyRow(
modifier = modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = onClick,
)
.semantics(mergeDescendants = true) {

View file

@ -17,19 +17,16 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -52,6 +49,7 @@ import com.x8bit.bitwarden.ui.platform.components.content.BitwardenErrorContent
import com.x8bit.bitwarden.ui.platform.components.content.BitwardenLoadingContent
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
import com.x8bit.bitwarden.ui.platform.components.scaffold.rememberBitwardenPullToRefreshState
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
import com.x8bit.bitwarden.ui.platform.theme.LocalNonMaterialColors
import com.x8bit.bitwarden.ui.platform.theme.LocalNonMaterialTypography
@ -70,17 +68,15 @@ fun PendingRequestsScreen(
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
val context = LocalContext.current
val resources = context.resources
val pullToRefreshState by rememberUpdatedState(
newValue = rememberPullToRefreshState().takeIf { state.isPullToRefreshEnabled },
val pullToRefreshState = rememberBitwardenPullToRefreshState(
isEnabled = state.isPullToRefreshEnabled,
isRefreshing = state.isRefreshing,
onRefresh = remember(viewModel) {
{ viewModel.trySendAction(PendingRequestsAction.RefreshPull) }
},
)
LaunchedEffect(key1 = pullToRefreshState?.isRefreshing) {
if (pullToRefreshState?.isRefreshing == true) {
viewModel.trySendAction(PendingRequestsAction.RefreshPull)
}
}
EventsEffect(viewModel = viewModel) { event ->
when (event) {
PendingRequestsEvent.DismissPullToRefresh -> pullToRefreshState?.endRefresh()
PendingRequestsEvent.NavigateBack -> onNavigateBack()
is PendingRequestsEvent.NavigateToLoginApproval -> {
onNavigateToLoginApproval(event.fingerprint)
@ -244,7 +240,7 @@ private fun PendingRequestItem(
modifier = modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = { onNavigateToLoginApproval(fingerprintPhrase) },
),
horizontalAlignment = Alignment.Start,

View file

@ -38,6 +38,7 @@ class PendingRequestsViewModel @Inject constructor(
authRequests = emptyList(),
viewState = PendingRequestsState.ViewState.Loading,
isPullToRefreshSettingEnabled = settingsRepository.getPullToRefreshEnabledFlow().value,
isRefreshing = false,
),
) {
private var authJob: Job = Job().apply { complete() }
@ -93,6 +94,7 @@ class PendingRequestsViewModel @Inject constructor(
}
private fun handleRefreshPull() {
mutableStateFlow.update { it.copy(isRefreshing = true) }
updateAuthRequestList()
}
@ -169,7 +171,7 @@ class PendingRequestsViewModel @Inject constructor(
}
}
}
sendEvent(PendingRequestsEvent.DismissPullToRefresh)
mutableStateFlow.update { it.copy(isRefreshing = false) }
}
private fun updateAuthRequestList() {
@ -190,6 +192,7 @@ data class PendingRequestsState(
val authRequests: List<AuthRequest>,
val viewState: ViewState,
private val isPullToRefreshSettingEnabled: Boolean,
val isRefreshing: Boolean,
) : Parcelable {
/**
* Indicates that the pull-to-refresh should be enabled in the UI.
@ -259,11 +262,6 @@ data class PendingRequestsState(
* Models events for the delete account screen.
*/
sealed class PendingRequestsEvent {
/**
* Dismisses the pull-to-refresh indicator.
*/
data object DismissPullToRefresh : PendingRequestsEvent()
/**
* Navigates back.
*/

View file

@ -18,7 +18,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
@ -28,6 +27,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
@ -280,7 +280,7 @@ private fun BlockAutoFillListItem(
modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = onClick,
)
.bottomDivider(paddingStart = 16.dp)

View file

@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
@ -20,6 +19,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@ -168,7 +168,7 @@ private fun FoldersContent(
.testTag("FolderCell")
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = { onItemClick(it.id) },
)
.bottomDivider(paddingStart = 16.dp)

View file

@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBars
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBarItem
@ -142,7 +141,6 @@ fun VaultUnlockedNavBarScreen(
* Scaffold that contains the bottom nav bar for the [VaultUnlockedNavBarScreen]
*/
@Composable
@OptIn(ExperimentalMaterial3Api::class)
@Suppress("LongMethod")
private fun VaultUnlockedNavBarScaffold(
state: VaultUnlockedNavBarState,

View file

@ -12,10 +12,8 @@ import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@ -39,6 +37,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
import com.x8bit.bitwarden.ui.platform.components.dialog.LoadingDialogState
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
import com.x8bit.bitwarden.ui.platform.components.scaffold.rememberBitwardenPullToRefreshState
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
import com.x8bit.bitwarden.ui.platform.feature.search.model.SearchType
@ -63,18 +62,16 @@ fun SendScreen(
) {
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
val context = LocalContext.current
val pullToRefreshState = rememberPullToRefreshState()
.takeIf { state.isPullToRefreshEnabled }
LaunchedEffect(key1 = pullToRefreshState?.isRefreshing) {
if (pullToRefreshState?.isRefreshing == true) {
viewModel.trySendAction(SendAction.RefreshPull)
}
}
val pullToRefreshState = rememberBitwardenPullToRefreshState(
isEnabled = state.isPullToRefreshEnabled,
isRefreshing = state.isRefreshing,
onRefresh = remember(viewModel) {
{ viewModel.trySendAction(SendAction.RefreshPull) }
},
)
EventsEffect(viewModel = viewModel) { event ->
when (event) {
is SendEvent.DismissPullToRefresh -> pullToRefreshState?.endRefresh()
is SendEvent.NavigateToSearch -> onNavigateToSearchSend(SearchType.Sends.All)
is SendEvent.NavigateNewSend -> onNavigateToAddSend()

View file

@ -56,6 +56,7 @@ class SendViewModel @Inject constructor(
policyDisablesSend = policyManager
.getActivePolicies(type = PolicyTypeJson.DISABLE_SEND)
.any(),
isRefreshing = false,
),
) {
@ -174,9 +175,9 @@ class SendViewModel @Inject constructor(
message = R.string.generic_error_message.asText(),
),
dialogState = null,
isRefreshing = false,
)
}
sendEvent(SendEvent.DismissPullToRefresh)
}
is DataState.Loaded -> {
@ -189,9 +190,9 @@ class SendViewModel @Inject constructor(
.baseWebSendUrl,
),
dialogState = null,
isRefreshing = false,
)
}
sendEvent(SendEvent.DismissPullToRefresh)
}
DataState.Loading -> {
@ -212,9 +213,9 @@ class SendViewModel @Inject constructor(
),
),
dialogState = null,
isRefreshing = false,
)
}
sendEvent(SendEvent.DismissPullToRefresh)
}
is DataState.Pending -> {
@ -317,6 +318,7 @@ class SendViewModel @Inject constructor(
}
private fun handleRefreshPull() {
mutableStateFlow.update { it.copy(isRefreshing = true) }
// The Pull-To-Refresh composable is already in the refreshing state.
// We will reset that state when sendDataStateFlow emits later on.
vaultRepo.sync()
@ -332,6 +334,7 @@ data class SendState(
val dialogState: DialogState?,
private val isPullToRefreshSettingEnabled: Boolean,
val policyDisablesSend: Boolean,
val isRefreshing: Boolean,
) : Parcelable {
/**
@ -573,11 +576,6 @@ sealed class SendAction {
* Models events for the send screen.
*/
sealed class SendEvent {
/**
* Dismisses the pull-to-refresh indicator.
*/
data object DismissPullToRefresh : SendEvent()
/**
* Navigate to the new send screen.
*/

View file

@ -8,11 +8,8 @@ import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.pulltorefresh.PullToRefreshState
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -45,7 +42,9 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenMasterPassword
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenOverwritePasskeyConfirmationDialog
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenPinDialog
import com.x8bit.bitwarden.ui.platform.components.dialog.LoadingDialogState
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenPullToRefreshState
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
import com.x8bit.bitwarden.ui.platform.components.scaffold.rememberBitwardenPullToRefreshState
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
import com.x8bit.bitwarden.ui.platform.composition.LocalBiometricsManager
import com.x8bit.bitwarden.ui.platform.composition.LocalFido2CompletionManager
@ -65,7 +64,6 @@ import kotlinx.collections.immutable.toImmutableList
/**
* Displays the vault item listing screen.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@Suppress("LongMethod", "CyclomaticComplexMethod")
fun VaultItemListingScreen(
@ -89,18 +87,17 @@ fun VaultItemListingScreen(
VaultItemListingUserVerificationHandlers.create(viewModel = viewModel)
}
val pullToRefreshState = rememberPullToRefreshState().takeIf { state.isPullToRefreshEnabled }
LaunchedEffect(key1 = pullToRefreshState?.isRefreshing) {
if (pullToRefreshState?.isRefreshing == true) {
viewModel.trySendAction(VaultItemListingsAction.RefreshPull)
}
}
val pullToRefreshState = rememberBitwardenPullToRefreshState(
isEnabled = state.isPullToRefreshEnabled,
isRefreshing = state.isRefreshing,
onRefresh = remember(viewModel) {
{ viewModel.trySendAction(VaultItemListingsAction.RefreshPull) }
},
)
EventsEffect(viewModel = viewModel) { event ->
when (event) {
is VaultItemListingEvent.NavigateBack -> onNavigateBack()
is VaultItemListingEvent.DismissPullToRefresh -> pullToRefreshState?.endRefresh()
is VaultItemListingEvent.NavigateToVaultItem -> {
onNavigateToVaultItem(event.id)
}
@ -388,7 +385,7 @@ private fun VaultItemListingDialogs(
@Composable
private fun VaultItemListingScaffold(
state: VaultItemListingState,
pullToRefreshState: PullToRefreshState?,
pullToRefreshState: BitwardenPullToRefreshState,
vaultItemListingHandlers: VaultItemListingHandlers,
) {
var isAccountMenuVisible by rememberSaveable { mutableStateOf(false) }

View file

@ -133,6 +133,7 @@ class VaultItemListingViewModel @Inject constructor(
fido2CredentialAssertionRequest = fido2AssertionData?.fido2AssertionRequest,
fido2GetCredentialsRequest = fido2GetCredentialsData?.fido2GetCredentialsRequest,
isPremium = userState.activeAccount.isPremium,
isRefreshing = false,
)
},
) {
@ -298,6 +299,7 @@ class VaultItemListingViewModel @Inject constructor(
}
private fun handleRefreshPull() {
mutableStateFlow.update { it.copy(isRefreshing = true) }
// The Pull-To-Refresh composable is already in the refreshing state.
// We will reset that state when sendDataStateFlow emits later on.
vaultRepository.sync()
@ -1232,7 +1234,7 @@ class VaultItemListingViewModel @Inject constructor(
)
}
}
sendEvent(VaultItemListingEvent.DismissPullToRefresh)
mutableStateFlow.update { it.copy(isRefreshing = false) }
}
private fun vaultLoadedReceive(vaultData: DataState.Loaded<VaultData>) {
@ -1261,7 +1263,7 @@ class VaultItemListingViewModel @Inject constructor(
),
)
}
?: sendEvent(VaultItemListingEvent.DismissPullToRefresh)
?: mutableStateFlow.update { it.copy(isRefreshing = false) }
}
private fun vaultLoadingReceive() {
@ -1286,7 +1288,7 @@ class VaultItemListingViewModel @Inject constructor(
)
}
}
sendEvent(VaultItemListingEvent.DismissPullToRefresh)
mutableStateFlow.update { it.copy(isRefreshing = false) }
}
private fun vaultPendingReceive(vaultData: DataState.Pending<VaultData>) {
@ -1640,6 +1642,7 @@ data class VaultItemListingState(
val fido2GetCredentialsRequest: Fido2GetCredentialsRequest? = null,
val hasMasterPassword: Boolean,
val isPremium: Boolean,
val isRefreshing: Boolean,
) {
/**
* Whether or not this represents a listing screen for autofill.
@ -2027,11 +2030,6 @@ data class VaultItemListingState(
* Models events for the [VaultItemListingScreen].
*/
sealed class VaultItemListingEvent {
/**
* Dismisses the pull-to-refresh indicator.
*/
data object DismissPullToRefresh : VaultItemListingEvent()
/**
* Navigates to the Create Account screen.
*/

View file

@ -16,8 +16,6 @@ import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.pulltorefresh.PullToRefreshState
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@ -52,7 +50,9 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenMasterPasswordDialog
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
import com.x8bit.bitwarden.ui.platform.components.dialog.LoadingDialogState
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenPullToRefreshState
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
import com.x8bit.bitwarden.ui.platform.components.scaffold.rememberBitwardenPullToRefreshState
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
import com.x8bit.bitwarden.ui.platform.composition.LocalExitManager
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
@ -70,7 +70,6 @@ import kotlinx.collections.immutable.toImmutableList
/**
* The vault screen for the application.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Suppress("LongMethod")
@Composable
fun VaultScreen(
@ -88,16 +87,15 @@ fun VaultScreen(
) {
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
val context = LocalContext.current
val pullToRefreshState = rememberPullToRefreshState().takeIf { state.isPullToRefreshEnabled }
LaunchedEffect(key1 = pullToRefreshState?.isRefreshing) {
if (pullToRefreshState?.isRefreshing == true) {
viewModel.trySendAction(VaultAction.RefreshPull)
}
}
val pullToRefreshState = rememberBitwardenPullToRefreshState(
isEnabled = state.isPullToRefreshEnabled,
isRefreshing = state.isRefreshing,
onRefresh = remember(viewModel) {
{ viewModel.trySendAction(VaultAction.RefreshPull) }
},
)
EventsEffect(viewModel = viewModel) { event ->
when (event) {
VaultEvent.DismissPullToRefresh -> pullToRefreshState?.endRefresh()
VaultEvent.NavigateToAddItemScreen -> onNavigateToVaultAddItemScreen()
VaultEvent.NavigateToVaultSearchScreen -> onNavigateToSearchVault(SearchType.Vault.All)
@ -167,7 +165,7 @@ private fun VaultScreenPushNotifications(
@Composable
private fun VaultScreenScaffold(
state: VaultState,
pullToRefreshState: PullToRefreshState?,
pullToRefreshState: BitwardenPullToRefreshState,
vaultHandlers: VaultHandlers,
onDimBottomNavBarRequest: (shouldDim: Boolean) -> Unit,
) {

View file

@ -91,6 +91,7 @@ class VaultViewModel @Inject constructor(
baseIconUrl = userState.activeAccount.environment.environmentUrlData.baseIconUrl,
hasMasterPassword = userState.activeAccount.hasMasterPassword,
hideNotificationsDialog = isBuildVersionBelow(Build.VERSION_CODES.TIRAMISU) || isFdroid,
isRefreshing = false,
)
},
) {
@ -298,6 +299,7 @@ class VaultViewModel @Inject constructor(
}
private fun handleRefreshPull() {
mutableStateFlow.update { it.copy(isRefreshing = true) }
// The Pull-To-Refresh composable is already in the refreshing state.
// We will reset that state when sendDataStateFlow emits later on.
vaultRepository.sync()
@ -510,8 +512,8 @@ class VaultViewModel @Inject constructor(
hasMasterPassword = state.hasMasterPassword,
errorTitle = R.string.an_error_has_occurred.asText(),
errorMessage = R.string.generic_error_message.asText(),
isRefreshing = false,
)
sendEvent(VaultEvent.DismissPullToRefresh)
}
private fun vaultLoadedReceive(vaultData: DataState.Loaded<VaultData>) {
@ -532,9 +534,9 @@ class VaultViewModel @Inject constructor(
vaultFilterType = vaultFilterTypeOrDefault,
),
dialog = null,
isRefreshing = false,
)
}
sendEvent(VaultEvent.DismissPullToRefresh)
}
private fun vaultLoadingReceive() {
@ -551,8 +553,8 @@ class VaultViewModel @Inject constructor(
isIconLoadingDisabled = state.isIconLoadingDisabled,
hasMasterPassword = state.hasMasterPassword,
errorMessage = R.string.internet_connection_required_message.asText(),
isRefreshing = false,
)
sendEvent(VaultEvent.DismissPullToRefresh)
}
private fun vaultPendingReceive(vaultData: DataState.Pending<VaultData>) {
@ -633,6 +635,7 @@ data class VaultState(
val baseIconUrl: String,
val isIconLoadingDisabled: Boolean,
val hideNotificationsDialog: Boolean,
val isRefreshing: Boolean,
) : Parcelable {
/**
@ -921,11 +924,6 @@ data class VaultState(
* Models effects for the [VaultScreen].
*/
sealed class VaultEvent {
/**
* Dismisses the pull-to-refresh indicator.
*/
data object DismissPullToRefresh : VaultEvent()
/**
* Navigate to the Vault Search screen.
*/
@ -1186,6 +1184,7 @@ private fun MutableStateFlow<VaultState>.updateToErrorStateOrDialog(
hasMasterPassword: Boolean,
errorTitle: Text,
errorMessage: Text,
isRefreshing: Boolean,
) {
this.update {
if (vaultData != null) {
@ -1201,6 +1200,7 @@ private fun MutableStateFlow<VaultState>.updateToErrorStateOrDialog(
title = errorTitle,
message = errorMessage,
),
isRefreshing = isRefreshing,
)
} else {
it.copy(
@ -1208,6 +1208,7 @@ private fun MutableStateFlow<VaultState>.updateToErrorStateOrDialog(
message = errorMessage,
),
dialog = null,
isRefreshing = isRefreshing,
)
}
}

View file

@ -8,11 +8,11 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@ -60,7 +60,7 @@ fun VaultVerificationCodeItem(
modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
indication = ripple(color = MaterialTheme.colorScheme.primary),
onClick = onItemClick,
)
.defaultMinSize(minHeight = 72.dp)

View file

@ -7,10 +7,8 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@ -31,6 +29,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
import com.x8bit.bitwarden.ui.platform.components.dialog.LoadingDialogState
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderTextWithSupportLabel
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
import com.x8bit.bitwarden.ui.platform.components.scaffold.rememberBitwardenPullToRefreshState
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
import com.x8bit.bitwarden.ui.vault.feature.verificationcode.handlers.VerificationCodeHandlers
import kotlinx.collections.immutable.ImmutableList
@ -54,16 +53,16 @@ fun VerificationCodeScreen(
VerificationCodeHandlers.create(viewModel)
}
val pullToRefreshState = rememberPullToRefreshState().takeIf { state.isPullToRefreshEnabled }
LaunchedEffect(key1 = pullToRefreshState?.isRefreshing) {
if (pullToRefreshState?.isRefreshing == true) {
viewModel.trySendAction(VerificationCodeAction.RefreshPull)
}
}
val pullToRefreshState = rememberBitwardenPullToRefreshState(
isEnabled = state.isPullToRefreshEnabled,
isRefreshing = state.isRefreshing,
onRefresh = remember(viewModel) {
{ viewModel.trySendAction(VerificationCodeAction.RefreshPull) }
},
)
EventsEffect(viewModel = viewModel) { event ->
when (event) {
is VerificationCodeEvent.DismissPullToRefresh -> pullToRefreshState?.endRefresh()
is VerificationCodeEvent.NavigateBack -> onNavigateBack()
is VerificationCodeEvent.NavigateToVaultItem -> onNavigateToVaultItemScreen(event.id)
is VerificationCodeEvent.NavigateToVaultSearchScreen -> {
@ -74,7 +73,6 @@ fun VerificationCodeScreen(
VerificationCodeDialogs(dialogState = state.dialogState)
@OptIn(ExperimentalMaterial3Api::class)
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
BitwardenScaffold(
modifier = Modifier

View file

@ -49,6 +49,7 @@ class VerificationCodeViewModel @Inject constructor(
vaultFilterType = vaultRepository.vaultFilterType,
viewState = VerificationCodeState.ViewState.Loading,
dialogState = null,
isRefreshing = false,
)
},
) {
@ -123,6 +124,7 @@ class VerificationCodeViewModel @Inject constructor(
}
private fun handleRefreshPull() {
mutableStateFlow.update { it.copy(isRefreshing = true) }
// The Pull-To-Refresh composable is already in the refreshing state.
// We will reset that state when sendDataStateFlow emits later on.
vaultRepository.sync()
@ -228,7 +230,7 @@ class VerificationCodeViewModel @Inject constructor(
)
}
}
sendEvent(VerificationCodeEvent.DismissPullToRefresh)
mutableStateFlow.update { it.copy(isRefreshing = false) }
}
private fun vaultPendingReceive(
@ -248,7 +250,7 @@ class VerificationCodeViewModel @Inject constructor(
verificationCodeData = verificationCodeData.data,
clearDialogState = true,
)
sendEvent(VerificationCodeEvent.DismissPullToRefresh)
mutableStateFlow.update { it.copy(isRefreshing = false) }
}
private fun vaultLoadingReceive() {
@ -271,7 +273,7 @@ class VerificationCodeViewModel @Inject constructor(
)
}
}
sendEvent(VerificationCodeEvent.DismissPullToRefresh)
mutableStateFlow.update { it.copy(isRefreshing = false) }
}
private fun updateStateWithVerificationCodeData(
@ -339,6 +341,7 @@ data class VerificationCodeState(
val baseIconUrl: String,
val dialogState: DialogState?,
val isPullToRefreshSettingEnabled: Boolean,
val isRefreshing: Boolean,
) : Parcelable {
/**
@ -421,12 +424,6 @@ data class VerificationCodeDisplayItem(
* Models events for the [VerificationCodeScreen].
*/
sealed class VerificationCodeEvent {
/**
* Dismisses the pull-to-refresh indicator.
*/
data object DismissPullToRefresh : VerificationCodeEvent()
/**
* Navigate back.
*/

View file

@ -2,6 +2,7 @@ package com.x8bit.bitwarden.ui.auth.feature.loginwithdevice
import android.net.Uri
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.hasAnyAncestor
import androidx.compose.ui.test.isDialog
@ -124,12 +125,14 @@ class LoginWithDeviceScreenTest : BaseComposeTest() {
mutableStateFlow.update {
it.copy(viewState = LoginWithDeviceState.ViewState.Loading)
}
composeTestRule.onNode(isProgressBar).assertIsDisplayed()
// There are 2 because of the pull-to-refresh
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(2)
mutableStateFlow.update {
it.copy(viewState = DEFAULT_STATE.viewState)
}
composeTestRule.onNode(isProgressBar).assertDoesNotExist()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
}
@Test

View file

@ -1,6 +1,7 @@
package com.x8bit.bitwarden.ui.platform.feature.search
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.filterToOne
@ -125,13 +126,15 @@ class SearchScreenTest : BaseComposeTest() {
@Test
fun `progressbar should be displayed according to state`() {
mutableStateFlow.update { DEFAULT_STATE }
composeTestRule.onNode(isProgressBar).assertIsDisplayed()
// There are 2 because of the pull-to-refresh
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(2)
mutableStateFlow.update {
it.copy(viewState = SearchState.ViewState.Empty(message = null))
}
composeTestRule.onNode(isProgressBar).assertDoesNotExist()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
}
@Test

View file

@ -119,6 +119,7 @@ class PendingRequestsScreenTest : BaseComposeTest() {
authRequests = emptyList(),
viewState = PendingRequestsState.ViewState.Loading,
isPullToRefreshSettingEnabled = false,
isRefreshing = false,
)
}
}

View file

@ -376,6 +376,7 @@ class PendingRequestsViewModelTest : BaseViewModelTest() {
authRequests = emptyList(),
viewState = PendingRequestsState.ViewState.Empty,
isPullToRefreshSettingEnabled = false,
isRefreshing = false,
)
}
}

View file

@ -1,6 +1,7 @@
package com.x8bit.bitwarden.ui.tools.feature.send
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.filterToOne
@ -263,22 +264,26 @@ class SendScreenTest : BaseComposeTest() {
mutableStateFlow.update {
it.copy(viewState = SendState.ViewState.Loading)
}
composeTestRule.onNode(isProgressBar).assertIsDisplayed()
// There are 2 because of the pull-to-refresh
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(2)
mutableStateFlow.update {
it.copy(viewState = SendState.ViewState.Empty)
}
composeTestRule.onNode(isProgressBar).assertDoesNotExist()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
mutableStateFlow.update {
it.copy(viewState = SendState.ViewState.Error("Fail".asText()))
}
composeTestRule.onNode(isProgressBar).assertDoesNotExist()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
mutableStateFlow.update {
it.copy(viewState = DEFAULT_CONTENT_VIEW_STATE)
}
composeTestRule.onNode(isProgressBar).assertDoesNotExist()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
}
@Test
@ -709,6 +714,7 @@ private val DEFAULT_STATE: SendState = SendState(
dialogState = null,
isPullToRefreshSettingEnabled = false,
policyDisablesSend = false,
isRefreshing = false,
)
private val DEFAULT_SEND_ITEM: SendState.ViewState.Content.SendItem =

View file

@ -313,52 +313,52 @@ class SendViewModelTest : BaseViewModelTest() {
assertEquals(initialState.copy(dialogState = null), viewModel.stateFlow.value)
}
@Suppress("MaxLineLength")
@Test
fun `VaultRepository SendData Error should update view state to Error and emit DismissPullToRefresh`() =
runTest {
val dialogState = SendState.DialogState.Loading(R.string.syncing.asText())
val viewModel = createViewModel(state = DEFAULT_STATE.copy(dialogState = dialogState))
fun `VaultRepository SendData Error should update view state to Error`() = runTest {
val dialogState = SendState.DialogState.Loading(R.string.syncing.asText())
val viewModel = createViewModel(state = DEFAULT_STATE.copy(dialogState = dialogState))
viewModel.eventFlow.test {
mutableSendDataFlow.value = DataState.Error(Throwable("Fail"))
assertEquals(SendEvent.DismissPullToRefresh, awaitItem())
}
viewModel.eventFlow.test {
mutableSendDataFlow.value = DataState.Error(Throwable("Fail"))
}
assertEquals(
DEFAULT_STATE.copy(
viewState = SendState.ViewState.Error(
message = R.string.generic_error_message.asText(),
),
dialogState = null,
assertEquals(
DEFAULT_STATE.copy(
viewState = SendState.ViewState.Error(
message = R.string.generic_error_message.asText(),
),
viewModel.stateFlow.value,
)
}
dialogState = null,
isRefreshing = false,
),
viewModel.stateFlow.value,
)
}
@Test
fun `VaultRepository SendData Loaded should update view state and emit DismissPullToRefresh`() =
runTest {
val dialogState = SendState.DialogState.Loading(R.string.syncing.asText())
val viewModel = createViewModel(state = DEFAULT_STATE.copy(dialogState = dialogState))
val viewState = mockk<SendState.ViewState.Content>()
val sendData = mockk<SendData> {
every {
toViewState(Environment.Us.environmentUrlData.baseWebSendUrl)
} returns viewState
}
viewModel.eventFlow.test {
mutableSendDataFlow.value = DataState.Loaded(sendData)
assertEquals(SendEvent.DismissPullToRefresh, awaitItem())
}
assertEquals(
DEFAULT_STATE.copy(viewState = viewState, dialogState = null),
viewModel.stateFlow.value,
)
fun `VaultRepository SendData Loaded should update view state`() = runTest {
val dialogState = SendState.DialogState.Loading(R.string.syncing.asText())
val viewModel = createViewModel(state = DEFAULT_STATE.copy(dialogState = dialogState))
val viewState = mockk<SendState.ViewState.Content>()
val sendData = mockk<SendData> {
every {
toViewState(Environment.Us.environmentUrlData.baseWebSendUrl)
} returns viewState
}
viewModel.eventFlow.test {
mutableSendDataFlow.value = DataState.Loaded(sendData)
}
assertEquals(
DEFAULT_STATE.copy(
viewState = viewState,
dialogState = null,
isRefreshing = false,
),
viewModel.stateFlow.value,
)
}
@Test
fun `VaultRepository SendData Loading should update view state to Loading`() {
val dialogState = SendState.DialogState.Loading(R.string.syncing.asText())
@ -372,34 +372,32 @@ class SendViewModelTest : BaseViewModelTest() {
)
}
@Suppress("MaxLineLength")
@Test
fun `VaultRepository SendData NoNetwork should update view state to Error and emit DismissPullToRefresh`() =
runTest {
val dialogState = SendState.DialogState.Loading(R.string.syncing.asText())
val viewModel = createViewModel(state = DEFAULT_STATE.copy(dialogState = dialogState))
fun `VaultRepository SendData NoNetwork should update view state to Error`() = runTest {
val dialogState = SendState.DialogState.Loading(R.string.syncing.asText())
val viewModel = createViewModel(state = DEFAULT_STATE.copy(dialogState = dialogState))
viewModel.eventFlow.test {
mutableSendDataFlow.value = DataState.NoNetwork()
assertEquals(SendEvent.DismissPullToRefresh, awaitItem())
}
assertEquals(
DEFAULT_STATE.copy(
viewState = SendState.ViewState.Error(
message = R.string.internet_connection_required_title
.asText()
.concat(
" ".asText(),
R.string.internet_connection_required_message.asText(),
),
),
dialogState = null,
),
viewModel.stateFlow.value,
)
viewModel.eventFlow.test {
mutableSendDataFlow.value = DataState.NoNetwork()
}
assertEquals(
DEFAULT_STATE.copy(
viewState = SendState.ViewState.Error(
message = R.string.internet_connection_required_title
.asText()
.concat(
" ".asText(),
R.string.internet_connection_required_message.asText(),
),
),
dialogState = null,
isRefreshing = false,
),
viewModel.stateFlow.value,
)
}
@Test
fun `VaultRepository SendData Pending should update view state`() {
val dialogState = SendState.DialogState.Loading(R.string.syncing.asText())
@ -470,4 +468,5 @@ private val DEFAULT_STATE: SendState = SendState(
dialogState = null,
isPullToRefreshSettingEnabled = false,
policyDisablesSend = false,
isRefreshing = false,
)

View file

@ -1,6 +1,8 @@
package com.x8bit.bitwarden.ui.tools.feature.send.addsend
import androidx.compose.ui.semantics.SemanticsActions
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
@ -19,6 +21,7 @@ import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.compose.ui.test.performSemanticsAction
import androidx.compose.ui.test.performTextInput
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
@ -409,7 +412,9 @@ class AddSendScreenTest : BaseComposeTest() {
fun `File segmented button click should send FileTypeClick`() {
composeTestRule
.onNodeWithText("File")
.performClick()
// A bug prevents performClick from working here so we
// have to perform the semantic action instead.
.performSemanticsAction(SemanticsActions.OnClick)
verify { viewModel.trySendAction(AddSendAction.FileTypeClick) }
}
@ -417,7 +422,9 @@ class AddSendScreenTest : BaseComposeTest() {
fun `Text segmented button click should send TextTypeClick`() {
composeTestRule
.onAllNodesWithText("Text")[0]
.performClick()
// A bug prevents performClick from working here so we
// have to perform the semantic action instead.
.performSemanticsAction(SemanticsActions.OnClick)
verify { viewModel.trySendAction(AddSendAction.TextTypeClick) }
}
@ -912,17 +919,20 @@ class AddSendScreenTest : BaseComposeTest() {
mutableStateFlow.update {
it.copy(viewState = AddSendState.ViewState.Loading)
}
composeTestRule.onNode(isProgressBar).assertIsDisplayed()
// There are 2 because of the pull-to-refresh
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(2)
mutableStateFlow.update {
it.copy(viewState = AddSendState.ViewState.Error("Fail".asText()))
}
composeTestRule.onNode(isProgressBar).assertDoesNotExist()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
mutableStateFlow.update {
it.copy(viewState = DEFAULT_VIEW_STATE)
}
composeTestRule.onNode(isProgressBar).assertDoesNotExist()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
}
@Test

View file

@ -2,6 +2,7 @@ package com.x8bit.bitwarden.ui.vault.feature.addedit
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
@ -580,12 +581,14 @@ class VaultAddEditScreenTest : BaseComposeTest() {
mutableStateFlow.update {
it.copy(viewState = VaultAddEditState.ViewState.Loading)
}
composeTestRule.onNode(isProgressBar).assertIsDisplayed()
// There are 2 because of the pull-to-refresh
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(2)
mutableStateFlow.update {
it.copy(viewState = VaultAddEditState.ViewState.Error("Fail".asText()))
}
composeTestRule.onNode(isProgressBar).assertDoesNotExist()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
mutableStateFlow.update {
it.copy(
@ -596,7 +599,8 @@ class VaultAddEditScreenTest : BaseComposeTest() {
),
)
}
composeTestRule.onNode(isProgressBar).assertDoesNotExist()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
}
@Test

View file

@ -1,6 +1,7 @@
package com.x8bit.bitwarden.ui.vault.feature.attachments
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.filterToOne
import androidx.compose.ui.test.hasAnyAncestor
@ -89,17 +90,20 @@ class AttachmentsScreenTest : BaseComposeTest() {
@Test
fun `progressbar should be displayed according to state`() {
mutableStateFlow.update { it.copy(viewState = AttachmentsState.ViewState.Loading) }
composeTestRule.onNode(isProgressBar).assertIsDisplayed()
// There are 2 because of the pull-to-refresh
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(2)
mutableStateFlow.update {
it.copy(viewState = AttachmentsState.ViewState.Error("Fail".asText()))
}
composeTestRule.onNode(isProgressBar).assertDoesNotExist()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
mutableStateFlow.update {
it.copy(viewState = DEFAULT_CONTENT_WITHOUT_ATTACHMENTS)
}
composeTestRule.onNode(isProgressBar).assertDoesNotExist()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
}
@Test

View file

@ -22,7 +22,6 @@ import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onSiblings
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.compose.ui.test.performTextInput
import androidx.core.net.toUri
import com.x8bit.bitwarden.R
@ -1364,9 +1363,8 @@ class VaultItemScreenTest : BaseComposeTest() {
)
}
composeTestRule
.onNode(isProgressBar)
.assertDoesNotExist()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
composeTestRule
.onNodeWithText("Passkey")
@ -1411,9 +1409,8 @@ class VaultItemScreenTest : BaseComposeTest() {
)
}
composeTestRule
.onNode(isProgressBar)
.assertDoesNotExist()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
composeTestRule
.onNodeWithContentDescription("Copy TOTP")
@ -1434,10 +1431,8 @@ class VaultItemScreenTest : BaseComposeTest() {
)
}
composeTestRule
.onNode(isProgressBar)
.performScrollTo()
.assertIsDisplayed()
// There are 2 because of the pull-to-refresh
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(2)
composeTestRule
.onNodeWithContentDescription("Copy TOTP")
@ -1452,10 +1447,8 @@ class VaultItemScreenTest : BaseComposeTest() {
)
}
composeTestRule
.onNode(isProgressBar)
.performScrollTo()
.assertIsDisplayed()
// There are 2 because of the pull-to-refresh
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(2)
composeTestRule
.onNodeWithContentDescription("Copy TOTP")
@ -1471,9 +1464,8 @@ class VaultItemScreenTest : BaseComposeTest() {
)
}
composeTestRule
.onNode(isProgressBar)
.assertIsNotDisplayed()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
composeTestRule
.onNodeWithContentDescription("Copy TOTP")
@ -1680,19 +1672,22 @@ class VaultItemScreenTest : BaseComposeTest() {
mutableStateFlow.update {
it.copy(viewState = VaultItemState.ViewState.Loading)
}
composeTestRule.onNode(isProgressBar).assertIsDisplayed()
// There are 2 because of the pull-to-refresh
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(2)
mutableStateFlow.update {
it.copy(viewState = VaultItemState.ViewState.Error("Fail".asText()))
}
composeTestRule.onNode(isProgressBar).assertDoesNotExist()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
mutableStateFlow.update { currentState ->
updateLoginType(currentState) {
copy(totpCodeItemData = null)
}
}
composeTestRule.onNode(isProgressBar).assertDoesNotExist()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
}
@Test

View file

@ -1,6 +1,7 @@
package com.x8bit.bitwarden.ui.vault.feature.itemlisting
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertTextEquals
@ -490,9 +491,8 @@ class VaultItemListingScreenTest : BaseComposeTest() {
fun `progressbar should be displayed according to state`() {
mutableStateFlow.update { DEFAULT_STATE }
composeTestRule
.onNode(isProgressBar)
.assertIsDisplayed()
// There are 2 because of the pull-to-refresh
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(2)
mutableStateFlow.update {
it.copy(
@ -504,9 +504,8 @@ class VaultItemListingScreenTest : BaseComposeTest() {
)
}
composeTestRule
.onNode(isProgressBar)
.assertDoesNotExist()
// Only pull-to-refresh remains
composeTestRule.onAllNodes(isProgressBar).assertCountEquals(1)
}
@Test
@ -2059,6 +2058,7 @@ private val DEFAULT_STATE = VaultItemListingState(
policyDisablesSend = false,
hasMasterPassword = true,
isPremium = false,
isRefreshing = false,
)
private val STATE_FOR_AUTOFILL = DEFAULT_STATE.copy(

View file

@ -498,10 +498,6 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
VaultItemListingState.DialogState.Loading(R.string.saving.asText()),
viewModel.stateFlow.value.dialogState,
)
assertEquals(
VaultItemListingEvent.DismissPullToRefresh,
awaitItem(),
)
assertEquals(
VaultItemListingEvent.Fido2UserVerification(
isRequired = true,
@ -1195,40 +1191,36 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
}
@Test
fun `vaultDataStateFlow Loaded with items should update ViewState to Content`() =
runTest {
setupMockUri()
fun `vaultDataStateFlow Loaded with items should update ViewState to Content`() = runTest {
setupMockUri()
val dataState = DataState.Loaded(
data = VaultData(
cipherViewList = listOf(createMockCipherView(number = 1, isDeleted = false)),
folderViewList = listOf(createMockFolderView(number = 1)),
collectionViewList = listOf(createMockCollectionView(number = 1)),
sendViewList = listOf(createMockSendView(number = 1)),
),
)
val dataState = DataState.Loaded(
data = VaultData(
cipherViewList = listOf(createMockCipherView(number = 1, isDeleted = false)),
folderViewList = listOf(createMockFolderView(number = 1)),
collectionViewList = listOf(createMockCollectionView(number = 1)),
sendViewList = listOf(createMockSendView(number = 1)),
),
)
val viewModel = createVaultItemListingViewModel()
val viewModel = createVaultItemListingViewModel()
viewModel.eventFlow.test {
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem())
}
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(
createVaultItemListingState(
viewState = VaultItemListingState.ViewState.Content(
displayCollectionList = emptyList(),
displayItemList = listOf(
createMockDisplayItemForCipher(number = 1)
.copy(secondSubtitleTestTag = "PasskeySite"),
),
displayFolderList = emptyList(),
assertEquals(
createVaultItemListingState(
viewState = VaultItemListingState.ViewState.Content(
displayCollectionList = emptyList(),
displayItemList = listOf(
createMockDisplayItemForCipher(number = 1)
.copy(secondSubtitleTestTag = "PasskeySite"),
),
displayFolderList = emptyList(),
),
viewModel.stateFlow.value,
)
}
),
viewModel.stateFlow.value,
)
}
@Suppress("MaxLineLength")
@Test
@ -1475,10 +1467,9 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
),
)
val viewModel = createVaultItemListingViewModel()
viewModel.eventFlow.test {
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem())
}
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(
createVaultItemListingState(
viewState = VaultItemListingState.ViewState.NoItems(
@ -1504,10 +1495,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
)
val viewModel = createVaultItemListingViewModel()
viewModel.eventFlow.test {
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem())
}
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(
createVaultItemListingState(
viewState = VaultItemListingState.ViewState.NoItems(
@ -1626,10 +1615,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
val viewModel = createVaultItemListingViewModel()
viewModel.eventFlow.test {
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem())
}
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(
createVaultItemListingState(
viewState = VaultItemListingState.ViewState.Error(
@ -1656,10 +1643,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
val viewModel = createVaultItemListingViewModel()
viewModel.eventFlow.test {
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem())
}
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(
createVaultItemListingState(
viewState = VaultItemListingState.ViewState.Content(
@ -1691,10 +1676,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
val viewModel = createVaultItemListingViewModel()
viewModel.eventFlow.test {
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem())
}
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(
createVaultItemListingState(
viewState = VaultItemListingState.ViewState.NoItems(
@ -1721,10 +1704,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
val viewModel = createVaultItemListingViewModel()
viewModel.eventFlow.test {
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem())
}
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(
createVaultItemListingState(
viewState = VaultItemListingState.ViewState.NoItems(
@ -1743,10 +1724,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
val viewModel = createVaultItemListingViewModel()
viewModel.eventFlow.test {
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem())
}
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(
createVaultItemListingState(
viewState = VaultItemListingState.ViewState.Error(
@ -1777,10 +1756,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
val viewModel = createVaultItemListingViewModel()
viewModel.eventFlow.test {
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem())
}
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(
createVaultItemListingState(
viewState = VaultItemListingState.ViewState.Content(
@ -1811,10 +1788,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
val viewModel = createVaultItemListingViewModel()
viewModel.eventFlow.test {
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem())
}
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(
createVaultItemListingState(
viewState = VaultItemListingState.ViewState.NoItems(
@ -1840,10 +1815,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
val viewModel = createVaultItemListingViewModel()
viewModel.eventFlow.test {
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(VaultItemListingEvent.DismissPullToRefresh, awaitItem())
}
mutableVaultDataStateFlow.tryEmit(value = dataState)
assertEquals(
createVaultItemListingState(
viewState = VaultItemListingState.ViewState.NoItems(
@ -3763,6 +3736,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
fido2CredentialRequest = null,
isPremium = true,
isRefreshing = false,
)
}

View file

@ -1190,6 +1190,7 @@ private val DEFAULT_STATE: VaultState = VaultState(
isIconLoadingDisabled = false,
hasMasterPassword = true,
hideNotificationsDialog = true,
isRefreshing = false,
)
private val DEFAULT_CONTENT_VIEW_STATE: VaultState.ViewState.Content = VaultState.ViewState.Content(

View file

@ -612,7 +612,6 @@ class VaultViewModelTest : BaseViewModelTest() {
VaultEvent.ShowToast(R.string.syncing_complete.asText()),
awaitItem(),
)
assertEquals(VaultEvent.DismissPullToRefresh, awaitItem())
}
}
@ -660,7 +659,6 @@ class VaultViewModelTest : BaseViewModelTest() {
VaultEvent.ShowToast(R.string.syncing_complete.asText()),
awaitItem(),
)
assertEquals(VaultEvent.DismissPullToRefresh, awaitItem())
}
}
@ -1591,4 +1589,5 @@ private fun createMockVaultState(
isIconLoadingDisabled = false,
hasMasterPassword = true,
hideNotificationsDialog = true,
isRefreshing = false,
)

View file

@ -407,4 +407,5 @@ private val DEFAULT_STATE = VerificationCodeState(
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
isPullToRefreshSettingEnabled = false,
dialogState = null,
isRefreshing = false,
)

View file

@ -171,7 +171,6 @@ class VerificationCodeViewModelTest : BaseViewModelTest() {
}
}
@Suppress("MaxLineLength")
@Test
fun `AuthCodeFlow Pending with data should update state to Content`() {
setupMockUri()
@ -197,7 +196,6 @@ class VerificationCodeViewModelTest : BaseViewModelTest() {
)
}
@Suppress("MaxLineLength")
@Test
fun `AuthCodeFlow Pending with no data should call NavigateBack to go to the vault screen`() =
runTest {
@ -216,7 +214,6 @@ class VerificationCodeViewModelTest : BaseViewModelTest() {
}
}
@Suppress("MaxLineLength")
@Test
fun `AuthCodeFlow Error with data should update state to Content`() = runTest {
setupMockUri()
@ -233,10 +230,6 @@ class VerificationCodeViewModelTest : BaseViewModelTest() {
),
)
viewModel.eventFlow.test {
assertEquals(VerificationCodeEvent.DismissPullToRefresh, awaitItem())
}
assertEquals(
createVerificationCodeState(
viewState = VerificationCodeState.ViewState.Content(
@ -247,7 +240,6 @@ class VerificationCodeViewModelTest : BaseViewModelTest() {
)
}
@Suppress("MaxLineLength")
@Test
fun `AuthCodeFlow Error with no data should call NavigateBack to go to the vault screen`() =
runTest {
@ -264,11 +256,9 @@ class VerificationCodeViewModelTest : BaseViewModelTest() {
viewModel.eventFlow.test {
assertEquals(VerificationCodeEvent.NavigateBack, awaitItem())
assertEquals(VerificationCodeEvent.DismissPullToRefresh, awaitItem())
}
}
@Suppress("MaxLineLength")
@Test
fun `AuthCodeFlow Error with null data should show error screen`() = runTest {
setupMockUri()
@ -282,10 +272,6 @@ class VerificationCodeViewModelTest : BaseViewModelTest() {
),
)
viewModel.eventFlow.test {
assertEquals(VerificationCodeEvent.DismissPullToRefresh, awaitItem())
}
assertEquals(
createVerificationCodeState(
viewState = VerificationCodeState.ViewState.Error(
@ -308,7 +294,6 @@ class VerificationCodeViewModelTest : BaseViewModelTest() {
viewModel.eventFlow.test {
assertEquals(VerificationCodeEvent.NavigateBack, awaitItem())
assertEquals(VerificationCodeEvent.DismissPullToRefresh, awaitItem())
}
}
@ -350,10 +335,6 @@ class VerificationCodeViewModelTest : BaseViewModelTest() {
),
)
viewModel.eventFlow.test {
assertEquals(VerificationCodeEvent.DismissPullToRefresh, awaitItem())
}
assertEquals(
createVerificationCodeState(
viewState = VerificationCodeState.ViewState.Content(
@ -375,7 +356,6 @@ class VerificationCodeViewModelTest : BaseViewModelTest() {
viewModel.eventFlow.test {
assertEquals(VerificationCodeEvent.NavigateBack, awaitItem())
assertEquals(VerificationCodeEvent.DismissPullToRefresh, awaitItem())
}
}
@ -394,10 +374,6 @@ class VerificationCodeViewModelTest : BaseViewModelTest() {
),
)
viewModel.eventFlow.test {
assertEquals(VerificationCodeEvent.DismissPullToRefresh, awaitItem())
}
assertEquals(
createVerificationCodeState(
viewState = VerificationCodeState.ViewState.Content(
@ -426,7 +402,6 @@ class VerificationCodeViewModelTest : BaseViewModelTest() {
viewModel.eventFlow.test {
assertEquals(VerificationCodeEvent.NavigateBack, awaitItem())
assertEquals(VerificationCodeEvent.DismissPullToRefresh, awaitItem())
}
}
@ -527,6 +502,7 @@ class VerificationCodeViewModelTest : BaseViewModelTest() {
baseIconUrl = environmentRepository.environment.environmentUrlData.baseIconUrl,
dialogState = null,
isPullToRefreshSettingEnabled = settingsRepository.getPullToRefreshEnabledFlow().value,
isRefreshing = false,
)
private fun createDisplayItemList() = listOf(

View file

@ -12,7 +12,7 @@ androidxActivity = "1.9.1"
androidXBiometrics = "1.2.0-alpha05"
androidxBrowser = "1.8.0"
androidxCamera = "1.3.4"
androidxComposeBom = "2024.08.00"
androidxComposeBom = "2024.09.00"
androidxCore = "1.13.1"
androidxCredentials = "1.2.2"
androidxHiltNavigationCompose = "1.2.0"