mirror of
https://github.com/bitwarden/android.git
synced 2024-11-28 05:13:50 +03:00
PM-14353 : Clean up consumed snackbar on quick resubmission due to state based nav. (#4235)
This commit is contained in:
parent
29384596d4
commit
e397c036e4
5 changed files with 69 additions and 16 deletions
|
@ -19,4 +19,9 @@ interface SnackbarRelayManager {
|
||||||
* the [relay] to receive the data from.
|
* the [relay] to receive the data from.
|
||||||
*/
|
*/
|
||||||
fun getSnackbarDataFlow(relay: SnackbarRelay): Flow<BitwardenSnackbarData>
|
fun getSnackbarDataFlow(relay: SnackbarRelay): Flow<BitwardenSnackbarData>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the buffer for the given [relay].
|
||||||
|
*/
|
||||||
|
fun clearRelayBuffer(relay: SnackbarRelay)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.x8bit.bitwarden.ui.platform.manager.snackbar
|
||||||
|
|
||||||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||||
import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarData
|
import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarData
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
|
@ -26,6 +27,11 @@ class SnackbarRelayManagerImpl : SnackbarRelayManager {
|
||||||
}
|
}
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
override fun clearRelayBuffer(relay: SnackbarRelay) {
|
||||||
|
getSnackbarDataFlowInternal(relay).resetReplayCache()
|
||||||
|
}
|
||||||
|
|
||||||
private fun getSnackbarDataFlowInternal(
|
private fun getSnackbarDataFlowInternal(
|
||||||
relay: SnackbarRelay,
|
relay: SnackbarRelay,
|
||||||
): MutableSharedFlow<BitwardenSnackbarData?> =
|
): MutableSharedFlow<BitwardenSnackbarData?> =
|
||||||
|
|
|
@ -73,8 +73,8 @@ class VaultViewModel @Inject constructor(
|
||||||
private val settingsRepository: SettingsRepository,
|
private val settingsRepository: SettingsRepository,
|
||||||
private val vaultRepository: VaultRepository,
|
private val vaultRepository: VaultRepository,
|
||||||
private val firstTimeActionManager: FirstTimeActionManager,
|
private val firstTimeActionManager: FirstTimeActionManager,
|
||||||
|
private val snackbarRelayManager: SnackbarRelayManager,
|
||||||
featureFlagManager: FeatureFlagManager,
|
featureFlagManager: FeatureFlagManager,
|
||||||
snackbarRelayManager: SnackbarRelayManager,
|
|
||||||
) : BaseViewModel<VaultState, VaultEvent, VaultAction>(
|
) : BaseViewModel<VaultState, VaultEvent, VaultAction>(
|
||||||
initialState = run {
|
initialState = run {
|
||||||
val userState = requireNotNull(authRepository.userStateFlow.value)
|
val userState = requireNotNull(authRepository.userStateFlow.value)
|
||||||
|
@ -283,6 +283,9 @@ class VaultViewModel @Inject constructor(
|
||||||
SwitchAccountResult.AccountSwitched -> true
|
SwitchAccountResult.AccountSwitched -> true
|
||||||
SwitchAccountResult.NoChange -> false
|
SwitchAccountResult.NoChange -> false
|
||||||
}
|
}
|
||||||
|
if (isSwitchingAccounts) {
|
||||||
|
snackbarRelayManager.clearRelayBuffer(SnackbarRelay.MY_VAULT_RELAY)
|
||||||
|
}
|
||||||
mutableStateFlow.update {
|
mutableStateFlow.update {
|
||||||
it.copy(isSwitchingAccounts = isSwitchingAccounts)
|
it.copy(isSwitchingAccounts = isSwitchingAccounts)
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,16 +66,16 @@ class SnackbarRelayManagerTest {
|
||||||
fun `When multiple consumers are registered to the same relay, send data to all consumers`() =
|
fun `When multiple consumers are registered to the same relay, send data to all consumers`() =
|
||||||
runTest {
|
runTest {
|
||||||
val relayManager = SnackbarRelayManagerImpl()
|
val relayManager = SnackbarRelayManagerImpl()
|
||||||
val relay1 = SnackbarRelay.MY_VAULT_RELAY
|
val relay = SnackbarRelay.MY_VAULT_RELAY
|
||||||
val expectedData = BitwardenSnackbarData(message = "Test message".asText())
|
val expectedData = BitwardenSnackbarData(message = "Test message".asText())
|
||||||
turbineScope {
|
turbineScope {
|
||||||
val consumer1 = relayManager.getSnackbarDataFlow(relay1).testIn(backgroundScope)
|
val consumer1 = relayManager.getSnackbarDataFlow(relay).testIn(backgroundScope)
|
||||||
relayManager.sendSnackbarData(data = expectedData, relay = relay1)
|
relayManager.sendSnackbarData(data = expectedData, relay = relay)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
expectedData,
|
expectedData,
|
||||||
consumer1.awaitItem(),
|
consumer1.awaitItem(),
|
||||||
)
|
)
|
||||||
val consumer2 = relayManager.getSnackbarDataFlow(relay1).testIn(backgroundScope)
|
val consumer2 = relayManager.getSnackbarDataFlow(relay).testIn(backgroundScope)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
expectedData,
|
expectedData,
|
||||||
consumer2.awaitItem(),
|
consumer2.awaitItem(),
|
||||||
|
@ -85,20 +85,40 @@ class SnackbarRelayManagerTest {
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
@Suppress("MaxLineLength")
|
||||||
@Test
|
@Test
|
||||||
fun `When multiple consumers are register to the same relay, and one is completed before the other the second consumer registers should not receive any emissions`() =
|
fun `When multiple consumers are registered to the same relay, and one is completed before the other the second consumer registers should not receive any emissions`() =
|
||||||
runTest {
|
runTest {
|
||||||
val relayManager = SnackbarRelayManagerImpl()
|
val relayManager = SnackbarRelayManagerImpl()
|
||||||
val relay1 = SnackbarRelay.MY_VAULT_RELAY
|
val relay = SnackbarRelay.MY_VAULT_RELAY
|
||||||
val expectedData = BitwardenSnackbarData(message = "Test message".asText())
|
val expectedData = BitwardenSnackbarData(message = "Test message".asText())
|
||||||
turbineScope {
|
turbineScope {
|
||||||
val consumer1 = relayManager.getSnackbarDataFlow(relay1).testIn(backgroundScope)
|
val consumer1 = relayManager.getSnackbarDataFlow(relay).testIn(backgroundScope)
|
||||||
relayManager.sendSnackbarData(data = expectedData, relay = relay1)
|
relayManager.sendSnackbarData(data = expectedData, relay = relay)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
expectedData,
|
expectedData,
|
||||||
consumer1.awaitItem(),
|
consumer1.awaitItem(),
|
||||||
)
|
)
|
||||||
consumer1.cancel()
|
consumer1.cancel()
|
||||||
val consumer2 = relayManager.getSnackbarDataFlow(relay1).testIn(backgroundScope)
|
val consumer2 = relayManager.getSnackbarDataFlow(relay).testIn(backgroundScope)
|
||||||
|
consumer2.expectNoEvents()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `When multiple consumers register to the same relay, and clearRelayBuffer is called, the second consumer should not receive any emissions`() =
|
||||||
|
runTest {
|
||||||
|
val relayManager = SnackbarRelayManagerImpl()
|
||||||
|
val relay = SnackbarRelay.MY_VAULT_RELAY
|
||||||
|
val expectedData = BitwardenSnackbarData(message = "Test message".asText())
|
||||||
|
turbineScope {
|
||||||
|
val consumer1 = relayManager.getSnackbarDataFlow(relay).testIn(backgroundScope)
|
||||||
|
relayManager.sendSnackbarData(data = expectedData, relay = relay)
|
||||||
|
assertEquals(
|
||||||
|
expectedData,
|
||||||
|
consumer1.awaitItem(),
|
||||||
|
)
|
||||||
|
relayManager.clearRelayBuffer(relay)
|
||||||
|
val consumer2 = relayManager.getSnackbarDataFlow(relay).testIn(backgroundScope)
|
||||||
consumer2.expectNoEvents()
|
consumer2.expectNoEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||||
import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
|
import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
|
||||||
import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarData
|
import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarData
|
||||||
import com.x8bit.bitwarden.ui.platform.manager.snackbar.SnackbarRelay
|
import com.x8bit.bitwarden.ui.platform.manager.snackbar.SnackbarRelay
|
||||||
import com.x8bit.bitwarden.ui.platform.manager.snackbar.SnackbarRelayManagerImpl
|
import com.x8bit.bitwarden.ui.platform.manager.snackbar.SnackbarRelayManager
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.model.ListingItemOverflowAction
|
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.model.ListingItemOverflowAction
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterData
|
import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterData
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType
|
import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType
|
||||||
|
@ -49,6 +49,7 @@ import io.mockk.mockk
|
||||||
import io.mockk.runs
|
import io.mockk.runs
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
@ -66,7 +67,12 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||||
ZoneOffset.UTC,
|
ZoneOffset.UTC,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val snackbarRelayManager = SnackbarRelayManagerImpl()
|
private val mutableSnackbarDataFlow = MutableStateFlow<BitwardenSnackbarData?>(null)
|
||||||
|
private val snackbarRelayManager: SnackbarRelayManager = mockk {
|
||||||
|
every { getSnackbarDataFlow(SnackbarRelay.MY_VAULT_RELAY) } returns mutableSnackbarDataFlow
|
||||||
|
.filterNotNull()
|
||||||
|
every { clearRelayBuffer(SnackbarRelay.MY_VAULT_RELAY) } just runs
|
||||||
|
}
|
||||||
|
|
||||||
private val clipboardManager: BitwardenClipboardManager = mockk {
|
private val clipboardManager: BitwardenClipboardManager = mockk {
|
||||||
every { setText(any<String>()) } just runs
|
every { setText(any<String>()) } just runs
|
||||||
|
@ -1802,15 +1808,28 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||||
fun `when SnackbarRelay flow updates, snackbar is shown`() = runTest {
|
fun `when SnackbarRelay flow updates, snackbar is shown`() = runTest {
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
val expectedSnackbarData = BitwardenSnackbarData(message = "test message".asText())
|
val expectedSnackbarData = BitwardenSnackbarData(message = "test message".asText())
|
||||||
|
mutableSnackbarDataFlow.update { expectedSnackbarData }
|
||||||
viewModel.eventFlow.test {
|
viewModel.eventFlow.test {
|
||||||
snackbarRelayManager.sendSnackbarData(
|
|
||||||
data = expectedSnackbarData,
|
|
||||||
relay = SnackbarRelay.MY_VAULT_RELAY,
|
|
||||||
)
|
|
||||||
assertEquals(VaultEvent.ShowSnackbar(expectedSnackbarData), awaitItem())
|
assertEquals(VaultEvent.ShowSnackbar(expectedSnackbarData), awaitItem())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when account switch action is handled, clear snackbar relay buffer should be called`() =
|
||||||
|
runTest {
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
switchAccountResult = SwitchAccountResult.AccountSwitched
|
||||||
|
viewModel.trySendAction(
|
||||||
|
VaultAction.SwitchAccountClick(
|
||||||
|
accountSummary = mockk() {
|
||||||
|
every { userId } returns "updatedUserId"
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
verify(exactly = 1) {
|
||||||
|
snackbarRelayManager.clearRelayBuffer(SnackbarRelay.MY_VAULT_RELAY)
|
||||||
|
}
|
||||||
|
}
|
||||||
private fun createViewModel(): VaultViewModel =
|
private fun createViewModel(): VaultViewModel =
|
||||||
VaultViewModel(
|
VaultViewModel(
|
||||||
authRepository = authRepository,
|
authRepository = authRepository,
|
||||||
|
|
Loading…
Reference in a new issue