Loading core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/mvi/BaseViewModel.kt +18 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,8 @@ abstract class BaseViewModel<STATE, EVENT, EFFECT>( private val _effect = MutableSharedFlow<EFFECT>() override val effect: SharedFlow<EFFECT> = _effect.asSharedFlow() private val handledOneTimeEvents = mutableSetOf<EVENT>() /** * Updates the [STATE] of the ViewModel. * Loading @@ -54,4 +56,20 @@ abstract class BaseViewModel<STATE, EVENT, 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() } } } core/ui/compose/common/src/test/kotlin/app/k9mail/core/ui/compose/common/mvi/BaseViewModelTest.kt +53 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,8 @@ import app.cash.turbine.test import app.k9mail.core.ui.compose.testing.MainDispatcherRule import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isTrue import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test Loading Loading @@ -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") { override fun event(event: String) { updateState { event } emitEffect(event) } fun callHandleOneTimeEvent(event: String, block: () -> Unit) { handleOneTimeEvent(event, block) } } } feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/ui/server/settings/save/BaseSaveServerSettingsViewModel.kt +1 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ abstract class BaseSaveServerSettingsViewModel( override fun event(event: Event) { when (event) { Event.SaveServerSettings -> onSaveServerSettings() Event.SaveServerSettings -> handleOneTimeEvent(event, ::onSaveServerSettings) Event.OnNextClicked -> navigateNext() Event.OnBackClicked -> navigateBack() } Loading feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsViewModel.kt +1 −1 Original line number Diff line number Diff line Loading @@ -23,7 +23,7 @@ open class IncomingServerSettingsViewModel( @Suppress("CyclomaticComplexMethod") override fun event(event: Event) { when (event) { Event.LoadAccountState -> loadAccountState() Event.LoadAccountState -> handleOneTimeEvent(event, ::loadAccountState) is Event.ProtocolTypeChanged -> updateProtocolType(event.protocolType) is Event.ServerChanged -> updateState { it.copy(server = it.server.updateValue(event.server)) } Loading feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsViewModel.kt +1 −1 Original line number Diff line number Diff line Loading @@ -21,7 +21,7 @@ open class OutgoingServerSettingsViewModel( override fun event(event: 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.SecurityChanged -> updateSecurity(event.security) Loading Loading
core/ui/compose/common/src/main/kotlin/app/k9mail/core/ui/compose/common/mvi/BaseViewModel.kt +18 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,8 @@ abstract class BaseViewModel<STATE, EVENT, EFFECT>( private val _effect = MutableSharedFlow<EFFECT>() override val effect: SharedFlow<EFFECT> = _effect.asSharedFlow() private val handledOneTimeEvents = mutableSetOf<EVENT>() /** * Updates the [STATE] of the ViewModel. * Loading @@ -54,4 +56,20 @@ abstract class BaseViewModel<STATE, EVENT, 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() } } }
core/ui/compose/common/src/test/kotlin/app/k9mail/core/ui/compose/common/mvi/BaseViewModelTest.kt +53 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,8 @@ import app.cash.turbine.test import app.k9mail.core.ui.compose.testing.MainDispatcherRule import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isTrue import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test Loading Loading @@ -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") { override fun event(event: String) { updateState { event } emitEffect(event) } fun callHandleOneTimeEvent(event: String, block: () -> Unit) { handleOneTimeEvent(event, block) } } }
feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/ui/server/settings/save/BaseSaveServerSettingsViewModel.kt +1 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ abstract class BaseSaveServerSettingsViewModel( override fun event(event: Event) { when (event) { Event.SaveServerSettings -> onSaveServerSettings() Event.SaveServerSettings -> handleOneTimeEvent(event, ::onSaveServerSettings) Event.OnNextClicked -> navigateNext() Event.OnBackClicked -> navigateBack() } Loading
feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/incoming/IncomingServerSettingsViewModel.kt +1 −1 Original line number Diff line number Diff line Loading @@ -23,7 +23,7 @@ open class IncomingServerSettingsViewModel( @Suppress("CyclomaticComplexMethod") override fun event(event: Event) { when (event) { Event.LoadAccountState -> loadAccountState() Event.LoadAccountState -> handleOneTimeEvent(event, ::loadAccountState) is Event.ProtocolTypeChanged -> updateProtocolType(event.protocolType) is Event.ServerChanged -> updateState { it.copy(server = it.server.updateValue(event.server)) } Loading
feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsViewModel.kt +1 −1 Original line number Diff line number Diff line Loading @@ -21,7 +21,7 @@ open class OutgoingServerSettingsViewModel( override fun event(event: 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.SecurityChanged -> updateSecurity(event.security) Loading