Skip to content

Commit 8033206

Browse files
authored
👮 Update events processing (#3)
1 parent 35aae18 commit 8033206

File tree

7 files changed

+51
-44
lines changed

7 files changed

+51
-44
lines changed

android/src/main/java/com/mews/app/bloc/android/BlocViewModel.kt

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import androidx.lifecycle.ViewModel
44
import androidx.lifecycle.viewModelScope
55
import com.mews.app.bloc.BaseBloc
66
import com.mews.app.bloc.Bloc
7-
import com.mews.app.bloc.Sink
87
import com.mews.app.bloc.Transition
98
import kotlinx.coroutines.InternalCoroutinesApi
109
import kotlinx.coroutines.flow.Flow
@@ -13,21 +12,18 @@ import kotlinx.coroutines.flow.FlowCollector
1312
abstract class BlocViewModel<EVENT : Any, STATE : Any> : ViewModel(), Bloc<EVENT, STATE> {
1413
abstract val initialState: STATE
1514

16-
override fun addAsync(event: EVENT) = bloc.addAsync(event)
17-
1815
@InternalCoroutinesApi
1916
override suspend fun collect(collector: FlowCollector<STATE>) = bloc.collect(collector)
2017

21-
override suspend fun add(value: EVENT) = bloc.add(value)
18+
override fun add(value: EVENT) = bloc.add(value)
2219

2320
override val state: STATE get() = bloc.state
2421

2522
private val bloc = object : BaseBloc<EVENT, STATE>(viewModelScope) {
2623
override val initialState: STATE by lazy { this@BlocViewModel.initialState }
2724

28-
override suspend fun Sink<STATE>.mapEventToState(event: EVENT) {
29-
this@BlocViewModel.apply { mapEventToState(event) }
30-
}
25+
override suspend fun mapEventToState(event: EVENT, emitState: suspend (STATE) -> Unit) =
26+
this@BlocViewModel.mapEventToState(event, emitState)
3127

3228
override suspend fun onTransition(transition: Transition<EVENT, STATE>) =
3329
this@BlocViewModel.onTransition(transition)

core/src/main/kotlin/com/mews/app/bloc/BaseBloc.kt

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package com.mews.app.bloc
33
import kotlinx.coroutines.CoroutineScope
44
import kotlinx.coroutines.InternalCoroutinesApi
55
import kotlinx.coroutines.channels.Channel
6-
import kotlinx.coroutines.channels.ProducerScope
76
import kotlinx.coroutines.flow.*
87
import kotlinx.coroutines.launch
98

@@ -22,7 +21,7 @@ abstract class BaseBloc<EVENT : Any, STATE : Any>(private val scope: CoroutineSc
2221
channel.consumeAsFlow()
2322
.let(::transformEvents)
2423
.flatMapConcat { event ->
25-
channelFlow<STATE> { StateSink(this).mapEventToState(event) }
24+
channelFlow<STATE> { mapEventToState(event, ::send) }
2625
.map { Transition(stateFlow.value, event, it) }
2726
.catch { doOnError(it) }
2827
.let(::transformTransitions)
@@ -40,19 +39,17 @@ abstract class BaseBloc<EVENT : Any, STATE : Any>(private val scope: CoroutineSc
4039
}
4140
}
4241

43-
override suspend fun add(value: EVENT) {
44-
try {
45-
doOnEvent(value)
46-
eventChannel.send(value)
47-
} catch (e: Throwable) {
48-
doOnError(e)
42+
override fun add(value: EVENT) {
43+
scope.launch {
44+
try {
45+
doOnEvent(value)
46+
eventChannel.send(value)
47+
} catch (e: Throwable) {
48+
doOnError(e)
49+
}
4950
}
5051
}
5152

52-
override fun addAsync(event: EVENT) {
53-
scope.launch { add(event) }
54-
}
55-
5653
private suspend fun doOnEvent(event: EVENT) {
5754
BlocSupervisor.delegate?.onEvent(event)
5855
onEvent(event)
@@ -68,7 +65,3 @@ abstract class BaseBloc<EVENT : Any, STATE : Any>(private val scope: CoroutineSc
6865
onError(error)
6966
}
7067
}
71-
72-
private class StateSink<S>(private val producerScope: ProducerScope<S>) : Sink<S> {
73-
override suspend fun add(value: S) = producerScope.send(value)
74-
}

core/src/main/kotlin/com/mews/app/bloc/Bloc.kt

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,12 @@ interface Bloc<EVENT : Any, STATE : Any> : Flow<STATE>, Sink<EVENT> {
1111
/**
1212
* Call this function to emit a new [EVENT] that should be processed by bloc.
1313
*/
14-
override suspend fun add(value: EVENT)
14+
override fun add(value: EVENT)
1515

1616
/**
17-
* Call this function to emit a new [EVENT] asynchronously within bloc scope.
17+
* Processes an incoming [event] and optionally emits a new [STATE] with [emitState].
1818
*/
19-
fun addAsync(event: EVENT)
20-
21-
/**
22-
* Takes an incoming [event] and emits new [STATE].
23-
*/
24-
suspend fun Sink<STATE>.mapEventToState(event: EVENT)
19+
suspend fun mapEventToState(event: EVENT, emitState: suspend (STATE) -> Unit)
2520

2621
/**
2722
* Called whenever [transition] occurs before state is updated.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
package com.mews.app.bloc
22

33
interface Sink<T> {
4-
suspend fun add(value: T)
4+
fun add(value: T)
55
}

core/src/test/kotlin/com/mews/app/bloc/BlocTest.kt

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ class BlocTest {
2626
val bloc = object : BaseBloc<Int, String>(scope) {
2727
override val initialState: String = "0"
2828

29-
override suspend fun Sink<String>.mapEventToState(event: Int) {
30-
add(event.toString())
29+
override suspend fun mapEventToState(event: Int, emitState: suspend (String) -> Unit) {
30+
emitState(event.toString())
3131
}
3232
}
3333
bloc.add(1)
@@ -53,9 +53,9 @@ class BlocTest {
5353
val bloc = object : BaseBloc<Int, String>(scope) {
5454
override val initialState: String = "0"
5555

56-
override suspend fun Sink<String>.mapEventToState(event: Int) {
56+
override suspend fun mapEventToState(event: Int, emitState: suspend (String) -> Unit) {
5757
if (event == 2) throw IllegalArgumentException("Test error")
58-
add(event.toString())
58+
emitState(event.toString())
5959
}
6060
}
6161
bloc.add(1)
@@ -80,7 +80,8 @@ class BlocTest {
8080
val bloc = object : BaseBloc<Int, String>(scope) {
8181
override val initialState: String = "0"
8282

83-
override suspend fun Sink<String>.mapEventToState(event: Int) = add(event.toString())
83+
override suspend fun mapEventToState(event: Int, emitState: suspend (String) -> Unit) =
84+
emitState(event.toString())
8485

8586
override fun transformEvents(flow: Flow<Int>): Flow<Int> =
8687
flow.filter { it != 2 }
@@ -105,7 +106,8 @@ class BlocTest {
105106
val bloc = object : BaseBloc<Int, String>(scope) {
106107
override val initialState: String = "0"
107108

108-
override suspend fun Sink<String>.mapEventToState(event: Int) = add(event.toString())
109+
override suspend fun mapEventToState(event: Int, emitState: suspend (String) -> Unit) =
110+
emitState(event.toString())
109111

110112
// Could be simplified after https://github.com/Kotlin/kotlinx.coroutines/issues/2034
111113
override fun transformEvents(events: Flow<Int>): Flow<Int> = channelFlow {
@@ -130,4 +132,26 @@ class BlocTest {
130132
)
131133
Assert.assertEquals(expectedTransitions, delegate.transitions)
132134
}
135+
136+
@Test
137+
fun `can emit event during mapEventToState`() {
138+
runBlocking {
139+
val bloc = object : BaseBloc<Int, String>(scope) {
140+
override val initialState: String = "0"
141+
142+
override suspend fun mapEventToState(event: Int, emitState: suspend (String) -> Unit) =
143+
if (event == 2) add(10) else emitState(event.toString())
144+
}
145+
bloc.add(1)
146+
bloc.add(2)
147+
bloc.add(3)
148+
}
149+
150+
val expectedTransitions = listOf(
151+
Transition("0", 1, "1"),
152+
Transition("1", 10, "10"),
153+
Transition("10", 3, "3")
154+
)
155+
Assert.assertEquals(expectedTransitions, delegate.transitions)
156+
}
133157
}

example/src/main/java/com/mews/app/bloc/example/MainActivity.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ class MainActivity : AppCompatActivity() {
1414
setContentView(R.layout.activity_main)
1515
setSupportActionBar(toolbar)
1616

17-
plus.setOnClickListener { vm.addAsync(MainEvent.Incremented) }
18-
minus.setOnClickListener { vm.addAsync(MainEvent.Decremented) }
17+
plus.setOnClickListener { vm.add(MainEvent.Incremented) }
18+
minus.setOnClickListener { vm.add(MainEvent.Decremented) }
1919

2020
connect(vm) { text.text = it.value.toString() }
2121
}

example/src/main/java/com/mews/app/bloc/example/MainViewModel.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
package com.mews.app.bloc.example
22

3-
import com.mews.app.bloc.Sink
43
import com.mews.app.bloc.android.BlocViewModel
54
import kotlinx.coroutines.flow.Flow
65
import kotlinx.coroutines.flow.transform
76

87
class MainViewModel : BlocViewModel<MainEvent, MainState>() {
98
override val initialState: MainState = MainState()
109

11-
override suspend fun Sink<MainState>.mapEventToState(event: MainEvent) = when (event) {
12-
MainEvent.Incremented -> add(state.copy(value = state.value + 1))
13-
MainEvent.Decremented -> add(state.copy(value = state.value - 1))
10+
override suspend fun mapEventToState(event: MainEvent, emitState: suspend (MainState) -> Unit) = when (event) {
11+
MainEvent.Incremented -> emitState(state.copy(value = state.value + 1))
12+
MainEvent.Decremented -> emitState(state.copy(value = state.value - 1))
1413
}
1514

1615
override fun transformEvents(events: Flow<MainEvent>): Flow<MainEvent> = events.transform {

0 commit comments

Comments
 (0)