Skip to content

Commit

Permalink
Merge pull request #9 from ikarenkov/cancelable-migration
Browse files Browse the repository at this point in the history
Replaced Cancelable with AutoCloseable
  • Loading branch information
ikarenkov authored Feb 16, 2024
2 parents 2a7a250 + 8cce81e commit f06c6d4
Show file tree
Hide file tree
Showing 16 changed files with 40 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ import androidx.lifecycle.ViewModelStoreOwner
import com.arkivanov.essenty.instancekeeper.InstanceKeeper
import com.arkivanov.essenty.instancekeeper.getOrCreate
import com.arkivanov.essenty.instancekeeper.instanceKeeper
import kotlinx.coroutines.flow.StateFlow
import io.github.ikarenkov.kombucha.Cancelable
import io.github.ikarenkov.kombucha.store.Store
import kotlinx.coroutines.flow.StateFlow

/**
* This interface provides Ui related part of TEA for compose.
* @param UiMsg - messages that can be dispatched to store
* @param Model - model of screen that can be observed
* @param UiEff - single event effects that can be consumed by compose screen (WIP)
*/
interface ComposeKombucha<UiMsg : Any, Model : Any, UiEff : Any> : Cancelable {
interface ComposeKombucha<UiMsg : Any, Model : Any, UiEff : Any> : AutoCloseable {

fun accept(msg: UiMsg)

Expand All @@ -32,8 +31,8 @@ open class ComposeKombuchaImpl<Msg : Any, UiMsg : Msg, Model : Any, Eff : Any, U
store.accept(msg)
}

override fun cancel() {
store.cancel()
override fun close() {
store.close()
}

}
Expand All @@ -42,7 +41,7 @@ class ComposeKombuchaInstance<UiMsg : Any, Model : Any, UiEff : Any>(
composeKombucha: ComposeKombucha<UiMsg, Model, UiEff>
) : ComposeKombucha<UiMsg, Model, UiEff> by composeKombucha, InstanceKeeper.Instance {
override fun onDestroy() {
cancel()
close()
}
}

Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ abstract class AggregatorStore<Msg : Any, State : Any, Eff : Any>(
coroutineExceptionHandler: CoroutineExceptionHandler = DefaultStoreCoroutineExceptionHandler()
) : this(StoreScope(name, coroutineExceptionHandler))

override fun cancel() {
override fun close() {
coroutineScope.cancel()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ class TwoStoreAggregatorStore<
}
}

override fun cancel() {
super.cancel()
store1.cancel()
store2.cancel()
override fun close() {
super.close()
store1.close()
store2.close()
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ open class CoroutinesStore<Msg : Any, State : Any, Eff : Any>(

override fun accept(msg: Msg) {
if (!isActive) {
error("Trying to call accept in canceled store with name \"$name\".")
error("Trying to call accept in closed store with name \"$name\".")
}
coroutinesScope.launch {
val storeUpdate = stateUpdateMutex.withLock {
Expand Down Expand Up @@ -90,7 +90,7 @@ open class CoroutinesStore<Msg : Any, State : Any, Eff : Any>(
}
}

override fun cancel() {
override fun close() {
coroutinesScope.cancel()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package io.github.ikarenkov.kombucha.store

import io.github.ikarenkov.kombucha.Cancelable
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow

/**
* The base component of library that hold state and converst incoming [Msg] to a new [State] and [Eff].
* Take a look and the main implementation: [CoroutinesStore]
*/
interface Store<Msg : Any, State : Any, Eff : Any> : Cancelable {
@OptIn(ExperimentalStdlibApi::class)
interface Store<Msg : Any, State : Any, Eff : Any> : AutoCloseable {

/**
* Represent current state of this store. Can be modified only through [accept].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFails

class CoroutinesStoreCancellationTest {
class CoroutinesStoreCloseTest {

@Test
@JsName("test1")
fun `When accept on canceled store - Then crush`() {
val store = NoOpTestStore()
store.accept(Any())
store.cancel()
store.close()
assertFails {
store.accept(Any())
}
Expand All @@ -22,7 +22,7 @@ class CoroutinesStoreCancellationTest {
@JsName("test2")
fun `When canceled - Then isActive false`() {
val store = NoOpTestStore()
store.cancel()
store.close()
assertEquals(false, store.isActive)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ private class StoreInstance<out T : Store<*, *, *>>(
) : InstanceKeeper.Instance {

override fun onDestroy() {
store.cancel()
store.close()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ import kotlinx.coroutines.isActive
* Wrapper for store, that allows handle basic UI scenarios:
* 1. Convert models to Ui Models
* 2. Cache ui effects when there is no subscribers and emit cached effects with a first subscription
* @param cancelOriginalStoreOnCancel shows if we need to call [Store.cancel] on the [store] when this store is canceling.
* @param propagateCloseToOriginal shows if we need to call [Store.close] on the [store] when this store is closed.
*/
class UiStore<UiMsg : Any, UiState : Any, UiEff : Any, Msg : Any, State : Any, Eff : Any>(
private val store: Store<Msg, State, Eff>,
private val uiMsgToMsgConverter: (UiMsg) -> Msg,
private val uiStateConverter: (State) -> UiState,
private val uiEffConverter: (Eff) -> UiEff?,
private val cancelOriginalStoreOnCancel: Boolean = true,
private val propagateCloseToOriginal: Boolean = true,
coroutineExceptionHandler: CoroutineExceptionHandler = DefaultStoreCoroutineExceptionHandler(),
uiDispatcher: CoroutineDispatcher = Dispatchers.Main,
cacheUiEffects: Boolean = true,
Expand Down Expand Up @@ -64,10 +64,10 @@ class UiStore<UiMsg : Any, UiState : Any, UiEff : Any, Msg : Any, State : Any, E
store.accept(uiMsgToMsgConverter(msg))
}

override fun cancel() {
override fun close() {
coroutineScope.cancel()
if (cancelOriginalStoreOnCancel) {
store.cancel()
if (propagateCloseToOriginal) {
store.close()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ class UiStoreBuilder<Msg : Any, State : Any, Eff : Any>(
uiStateConverter: (State) -> UiState,
uiEffConverter: (Eff) -> UiEff?,
cacheUiEffects: Boolean = true,
cancelOriginalStoreOnCancel: Boolean = true,
propagateCloseToOriginal: Boolean = true,
uiDispatcher: CoroutineDispatcher = Dispatchers.Main,
): UiStore<UiMsg, UiState, UiEff, Msg, State, Eff> = UiStore(
store = store,
uiMsgToMsgConverter = uiMsgToMsgConverter,
uiStateConverter = uiStateConverter,
uiEffConverter = uiEffConverter,
cancelOriginalStoreOnCancel = cancelOriginalStoreOnCancel,
propagateCloseToOriginal = propagateCloseToOriginal,
cacheUiEffects = cacheUiEffects,
uiDispatcher = uiDispatcher
)
Expand All @@ -34,7 +34,7 @@ class UiStoreBuilder<Msg : Any, State : Any, Eff : Any>(
*/
inline fun <UiMsg : Msg, UiState : Any, reified UiEff : Eff> using(
cacheUiEffects: Boolean = true,
cancelOriginalStoreOnCancel: Boolean = true,
propagateCloseToOriginal: Boolean = true,
uiDispatcher: CoroutineDispatcher = Dispatchers.Main,
noinline uiStateConverter: (State) -> UiState,
): UiStore<UiMsg, UiState, UiEff, Msg, State, Eff> = UiStore(
Expand All @@ -43,7 +43,7 @@ class UiStoreBuilder<Msg : Any, State : Any, Eff : Any>(
uiStateConverter = uiStateConverter,
uiEffConverter = { it as? UiEff },
uiDispatcher = uiDispatcher,
cancelOriginalStoreOnCancel = cancelOriginalStoreOnCancel,
propagateCloseToOriginal = propagateCloseToOriginal,
cacheUiEffects = cacheUiEffects
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,30 @@ class UiStoreTest {

@Test
@JsName("test1")
fun `When cancel original is false - original is not canceled`() {
fun `When close original is false - original is not closed`() {
val store = DummyStore()
val uiStore = store.uiBuilder()
.using<Any, Any, Any>(
cancelOriginalStoreOnCancel = false,
propagateCloseToOriginal = false,
uiDispatcher = StandardTestDispatcher()
) { it }

uiStore.cancel()
uiStore.close()

assertTrue(store.isActive)
}

@Test
@JsName("test2")
fun `When cancel original is true - original is canceled`() {
fun `When close original is true - original is closed`() {
val store = DummyStore()
val uiStore = store.uiBuilder()
.using<Any, Any, Any>(
cancelOriginalStoreOnCancel = true,
propagateCloseToOriginal = true,
uiDispatcher = StandardTestDispatcher()
) { it }

uiStore.cancel()
uiStore.close()

assertFalse(store.isActive)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class ModoKombuchaScreenModel<UiMsg : Any, UiState : Any, UiEff : Any>(
) : ScreenModel {

override fun onDispose() {
store.cancel()
store.close()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ internal class CounterScreenModel(
val state = store.state

override fun onDispose() {
store.cancel()
store.close()
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ internal class AnimesScreenModel(
val store = animesStoreAgregatorFactory.createStore()

override fun onDispose() {
store.cancel()
store.close()
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ internal class AnimesAggregatorStore(
}
}

override fun cancel() {
super.cancel()
paginationStore.cancel()
animesStore.cancel()
override fun close() {
super.close()
paginationStore.close()
animesStore.close()
}

data class State(
Expand Down

0 comments on commit f06c6d4

Please sign in to comment.