mirror of
https://github.com/bitwarden/android.git
synced 2026-03-22 06:11:38 -05:00
[PM-15873] Fix PTR on item listing page (#4778)
This commit is contained in:
@@ -32,6 +32,7 @@ import com.x8bit.bitwarden.data.platform.manager.ciphermatching.CipherMatchingMa
|
||||
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.OrganizationEvent
|
||||
import com.x8bit.bitwarden.data.platform.manager.network.NetworkConnectionManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSelectionDataOrNull
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.toFido2AssertionRequestOrNull
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.toFido2CreateRequestOrNull
|
||||
@@ -83,6 +84,7 @@ import com.x8bit.bitwarden.ui.vault.model.VaultItemCipherType
|
||||
import com.x8bit.bitwarden.ui.vault.util.toVaultItemCipherType
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@@ -114,6 +116,7 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
private val fido2OriginManager: Fido2OriginManager,
|
||||
private val fido2CredentialManager: Fido2CredentialManager,
|
||||
private val organizationEventManager: OrganizationEventManager,
|
||||
private val networkConnectionManager: NetworkConnectionManager,
|
||||
) : BaseViewModel<VaultItemListingState, VaultItemListingEvent, VaultItemListingsAction>(
|
||||
initialState = run {
|
||||
val userState = requireNotNull(authRepository.userStateFlow.value)
|
||||
@@ -306,11 +309,17 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
vaultRepository.sync(forced = true)
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
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(forced = false)
|
||||
viewModelScope.launch {
|
||||
delay(250)
|
||||
if (networkConnectionManager.isNetworkConnected) {
|
||||
vaultRepository.sync(forced = false)
|
||||
} else {
|
||||
sendAction(VaultItemListingsAction.Internal.InternetConnectionErrorReceived)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleConfirmOverwriteExistingPasskeyClick(
|
||||
@@ -1018,14 +1027,25 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun handleSyncClick() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialogState = VaultItemListingState.DialogState.Loading(
|
||||
message = R.string.syncing.asText(),
|
||||
),
|
||||
)
|
||||
if (networkConnectionManager.isNetworkConnected) {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialogState = VaultItemListingState.DialogState.Loading(
|
||||
message = R.string.syncing.asText(),
|
||||
),
|
||||
)
|
||||
}
|
||||
vaultRepository.sync(forced = true)
|
||||
} else {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialogState = VaultItemListingState.DialogState.Error(
|
||||
R.string.internet_connection_required_title.asText(),
|
||||
R.string.internet_connection_required_message.asText(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
vaultRepository.sync(forced = true)
|
||||
}
|
||||
|
||||
private fun handleSearchIconClick() {
|
||||
@@ -1150,6 +1170,22 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
is VaultItemListingsAction.Internal.Fido2AssertionResultReceive -> {
|
||||
handleFido2AssertionResultReceive(action)
|
||||
}
|
||||
|
||||
VaultItemListingsAction.Internal.InternetConnectionErrorReceived -> {
|
||||
handleInternetConnectionErrorReceived()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleInternetConnectionErrorReceived() {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
isRefreshing = false,
|
||||
dialogState = VaultItemListingState.DialogState.Error(
|
||||
R.string.internet_connection_required_title.asText(),
|
||||
R.string.internet_connection_required_message.asText(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2768,6 +2804,11 @@ sealed class VaultItemListingsAction {
|
||||
data class Fido2AssertionResultReceive(
|
||||
val result: Fido2CredentialAssertionResult,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates that the there is not internet connection.
|
||||
*/
|
||||
data object InternetConnectionErrorReceived : Internal()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.OrganizationEvent
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
||||
import com.x8bit.bitwarden.data.platform.manager.network.NetworkConnectionManager
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||
@@ -89,8 +90,10 @@ import io.mockk.runs
|
||||
import io.mockk.unmockkStatic
|
||||
import io.mockk.verify
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.test.advanceTimeBy
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
@@ -185,6 +188,10 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
every { trackEvent(event = any()) } just runs
|
||||
}
|
||||
|
||||
private val networkConnectionManager: NetworkConnectionManager = mockk {
|
||||
every { isNetworkConnected } returns true
|
||||
}
|
||||
|
||||
private val initialState = createVaultItemListingState()
|
||||
private val initialSavedStateHandle = createSavedStateHandleWithVaultItemListingType(
|
||||
vaultItemListingType = VaultItemListingType.Login,
|
||||
@@ -363,6 +370,27 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on SyncClick should show the no network dialog if no connection is available`() {
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
every {
|
||||
networkConnectionManager.isNetworkConnected
|
||||
} returns false
|
||||
viewModel.trySendAction(VaultItemListingsAction.SyncClick)
|
||||
assertEquals(
|
||||
initialState.copy(
|
||||
dialogState = VaultItemListingState.DialogState.Error(
|
||||
R.string.internet_connection_required_title.asText(),
|
||||
R.string.internet_connection_required_message.asText(),
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
verify(exactly = 0) {
|
||||
vaultRepository.sync(forced = true)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `ItemClick for vault item when accessibility autofill should post to the accessibilitySelectionManager`() =
|
||||
@@ -2451,17 +2479,43 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
assertTrue(viewModel.stateFlow.value.isIconLoadingDisabled)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun `RefreshPull should call vault repository sync`() {
|
||||
fun `RefreshPull should call vault repository sync`() = runTest {
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
|
||||
viewModel.trySendAction(VaultItemListingsAction.RefreshPull)
|
||||
|
||||
advanceTimeBy(300)
|
||||
verify(exactly = 1) {
|
||||
vaultRepository.sync(forced = false)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun `RefreshPull should show network error if no internet connection`() = runTest {
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
every {
|
||||
networkConnectionManager.isNetworkConnected
|
||||
} returns false
|
||||
|
||||
viewModel.trySendAction(VaultItemListingsAction.RefreshPull)
|
||||
advanceTimeBy(300)
|
||||
assertEquals(
|
||||
initialState.copy(
|
||||
isRefreshing = false,
|
||||
dialogState = VaultItemListingState.DialogState.Error(
|
||||
R.string.internet_connection_required_title.asText(),
|
||||
R.string.internet_connection_required_message.asText(),
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
verify(exactly = 0) {
|
||||
vaultRepository.sync(forced = false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `PullToRefreshEnableReceive should update isPullToRefreshEnabled`() = runTest {
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
@@ -4461,6 +4515,25 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `InternetConnectionErrorReceived should show network error if no internet connection`() =
|
||||
runTest {
|
||||
val viewModel = createVaultItemListingViewModel()
|
||||
viewModel.trySendAction(
|
||||
VaultItemListingsAction.Internal.InternetConnectionErrorReceived,
|
||||
)
|
||||
assertEquals(
|
||||
initialState.copy(
|
||||
isRefreshing = false,
|
||||
dialogState = VaultItemListingState.DialogState.Error(
|
||||
R.string.internet_connection_required_title.asText(),
|
||||
R.string.internet_connection_required_message.asText(),
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
private fun createSavedStateHandleWithVaultItemListingType(
|
||||
vaultItemListingType: VaultItemListingType,
|
||||
@@ -4523,6 +4596,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
fido2CredentialManager = fido2CredentialManager,
|
||||
organizationEventManager = organizationEventManager,
|
||||
fido2OriginManager = fido2OriginManager,
|
||||
networkConnectionManager = networkConnectionManager,
|
||||
)
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
|
||||
Reference in New Issue
Block a user