Merge pull request #7292 from thunderbird/one_time_initialization
Add support for one-time (initialization) events
This commit is contained in:
commit
0b23c20b7e
7 changed files with 76 additions and 5 deletions
|
@ -35,6 +35,8 @@ abstract class BaseViewModel<STATE, EVENT, EFFECT>(
|
||||||
private val _effect = MutableSharedFlow<EFFECT>()
|
private val _effect = MutableSharedFlow<EFFECT>()
|
||||||
override val effect: SharedFlow<EFFECT> = _effect.asSharedFlow()
|
override val effect: SharedFlow<EFFECT> = _effect.asSharedFlow()
|
||||||
|
|
||||||
|
private val handledOneTimeEvents = mutableSetOf<EVENT>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the [STATE] of the ViewModel.
|
* Updates the [STATE] of the ViewModel.
|
||||||
*
|
*
|
||||||
|
@ -54,4 +56,20 @@ abstract class BaseViewModel<STATE, EVENT, EFFECT>(
|
||||||
_effect.emit(effect)
|
_effect.emit(effect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that one-time events are only handled once.
|
||||||
|
*
|
||||||
|
* When you can't ensure that an event is only sent once, but you want the event to only be handled once, call this
|
||||||
|
* method. It will ensure [block] is only executed the first time this function is called. Subsequent calls with an
|
||||||
|
* [event] argument equal to that of a previous invocation will not execute [block].
|
||||||
|
*
|
||||||
|
* Multiple one-time events are supported.
|
||||||
|
*/
|
||||||
|
protected fun handleOneTimeEvent(event: EVENT, block: () -> Unit) {
|
||||||
|
if (event !in handledOneTimeEvents) {
|
||||||
|
handledOneTimeEvents.add(event)
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import app.cash.turbine.test
|
||||||
import app.k9mail.core.ui.compose.testing.MainDispatcherRule
|
import app.k9mail.core.ui.compose.testing.MainDispatcherRule
|
||||||
import assertk.assertThat
|
import assertk.assertThat
|
||||||
import assertk.assertions.isEqualTo
|
import assertk.assertions.isEqualTo
|
||||||
|
import assertk.assertions.isFalse
|
||||||
|
import assertk.assertions.isTrue
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
@ -47,10 +49,61 @@ class BaseViewModelTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `handleOneTimeEvent() should execute block`() = runTest {
|
||||||
|
val viewModel = TestBaseViewModel()
|
||||||
|
var eventHandled = false
|
||||||
|
|
||||||
|
viewModel.callHandleOneTimeEvent(event = "event") {
|
||||||
|
eventHandled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(eventHandled).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `handleOneTimeEvent() should execute block only once`() = runTest {
|
||||||
|
val viewModel = TestBaseViewModel()
|
||||||
|
var eventHandledCount = 0
|
||||||
|
|
||||||
|
repeat(2) {
|
||||||
|
viewModel.callHandleOneTimeEvent(event = "event") {
|
||||||
|
eventHandledCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(eventHandledCount).isEqualTo(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `handleOneTimeEvent() should support multiple one-time events`() = runTest {
|
||||||
|
val viewModel = TestBaseViewModel()
|
||||||
|
var eventOneHandled = false
|
||||||
|
var eventTwoHandled = false
|
||||||
|
|
||||||
|
viewModel.callHandleOneTimeEvent(event = "eventOne") {
|
||||||
|
eventOneHandled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(eventOneHandled).isTrue()
|
||||||
|
assertThat(eventTwoHandled).isFalse()
|
||||||
|
|
||||||
|
viewModel.callHandleOneTimeEvent(event = "eventTwo") {
|
||||||
|
eventTwoHandled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(eventOneHandled).isTrue()
|
||||||
|
assertThat(eventTwoHandled).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
private class TestBaseViewModel : BaseViewModel<String, String, String>("Initial state") {
|
private class TestBaseViewModel : BaseViewModel<String, String, String>("Initial state") {
|
||||||
override fun event(event: String) {
|
override fun event(event: String) {
|
||||||
updateState { event }
|
updateState { event }
|
||||||
emitEffect(event)
|
emitEffect(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun callHandleOneTimeEvent(event: String, block: () -> Unit) {
|
||||||
|
handleOneTimeEvent(event, block)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ abstract class BaseSaveServerSettingsViewModel(
|
||||||
|
|
||||||
override fun event(event: Event) {
|
override fun event(event: Event) {
|
||||||
when (event) {
|
when (event) {
|
||||||
Event.SaveServerSettings -> onSaveServerSettings()
|
Event.SaveServerSettings -> handleOneTimeEvent(event, ::onSaveServerSettings)
|
||||||
Event.OnNextClicked -> navigateNext()
|
Event.OnNextClicked -> navigateNext()
|
||||||
Event.OnBackClicked -> navigateBack()
|
Event.OnBackClicked -> navigateBack()
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ open class IncomingServerSettingsViewModel(
|
||||||
@Suppress("CyclomaticComplexMethod")
|
@Suppress("CyclomaticComplexMethod")
|
||||||
override fun event(event: Event) {
|
override fun event(event: Event) {
|
||||||
when (event) {
|
when (event) {
|
||||||
Event.LoadAccountState -> loadAccountState()
|
Event.LoadAccountState -> handleOneTimeEvent(event, ::loadAccountState)
|
||||||
|
|
||||||
is Event.ProtocolTypeChanged -> updateProtocolType(event.protocolType)
|
is Event.ProtocolTypeChanged -> updateProtocolType(event.protocolType)
|
||||||
is Event.ServerChanged -> updateState { it.copy(server = it.server.updateValue(event.server)) }
|
is Event.ServerChanged -> updateState { it.copy(server = it.server.updateValue(event.server)) }
|
||||||
|
|
|
@ -21,7 +21,7 @@ open class OutgoingServerSettingsViewModel(
|
||||||
|
|
||||||
override fun event(event: Event) {
|
override fun event(event: Event) {
|
||||||
when (event) {
|
when (event) {
|
||||||
Event.LoadAccountState -> loadAccountState()
|
Event.LoadAccountState -> handleOneTimeEvent(event, ::loadAccountState)
|
||||||
|
|
||||||
is Event.ServerChanged -> updateState { it.copy(server = it.server.updateValue(event.server)) }
|
is Event.ServerChanged -> updateState { it.copy(server = it.server.updateValue(event.server)) }
|
||||||
is Event.SecurityChanged -> updateSecurity(event.security)
|
is Event.SecurityChanged -> updateSecurity(event.security)
|
||||||
|
|
|
@ -37,7 +37,7 @@ abstract class BaseServerValidationViewModel(
|
||||||
|
|
||||||
override fun event(event: Event) {
|
override fun event(event: Event) {
|
||||||
when (event) {
|
when (event) {
|
||||||
Event.LoadAccountStateAndValidate -> loadAccountStateAndValidate()
|
Event.LoadAccountStateAndValidate -> handleOneTimeEvent(event, ::loadAccountStateAndValidate)
|
||||||
is Event.OnOAuthResult -> onOAuthResult(event.result)
|
is Event.OnOAuthResult -> onOAuthResult(event.result)
|
||||||
Event.ValidateServerSettings -> onValidateConfig()
|
Event.ValidateServerSettings -> onValidateConfig()
|
||||||
Event.OnNextClicked -> navigateNext()
|
Event.OnNextClicked -> navigateNext()
|
||||||
|
|
|
@ -20,7 +20,7 @@ internal class AccountOptionsViewModel(
|
||||||
|
|
||||||
override fun event(event: Event) {
|
override fun event(event: Event) {
|
||||||
when (event) {
|
when (event) {
|
||||||
Event.LoadAccountState -> loadAccountState()
|
Event.LoadAccountState -> handleOneTimeEvent(event, ::loadAccountState)
|
||||||
|
|
||||||
is Event.OnAccountNameChanged -> updateState { state ->
|
is Event.OnAccountNameChanged -> updateState { state ->
|
||||||
state.copy(
|
state.copy(
|
||||||
|
|
Loading…
Reference in a new issue