Skip to content

Commit 20b9ff2

Browse files
committed
more more 🔥
1 parent f86d000 commit 20b9ff2

File tree

3 files changed

+79
-9
lines changed

3 files changed

+79
-9
lines changed

sample/app/src/main/java/com/hoc081098/channeleventbus/sample/android/ui/home/detail/DetailScreen.kt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ import androidx.compose.material3.Text
1717
import androidx.compose.runtime.Composable
1818
import androidx.compose.runtime.getValue
1919
import androidx.compose.runtime.remember
20+
import androidx.compose.runtime.rememberUpdatedState
2021
import androidx.compose.ui.Alignment
2122
import androidx.compose.ui.Modifier
2223
import androidx.compose.ui.text.input.ImeAction
2324
import androidx.compose.ui.text.style.TextAlign
2425
import androidx.compose.ui.unit.dp
2526
import androidx.lifecycle.compose.collectAsStateWithLifecycle
27+
import com.hoc081098.channeleventbus.sample.android.common.CollectWithLifecycleEffect
2628
import kotlinx.coroutines.Dispatchers
2729
import org.koin.androidx.compose.koinViewModel
2830

@@ -32,6 +34,13 @@ fun DetailScreen(
3234
modifier: Modifier = Modifier,
3335
vm: DetailVM = koinViewModel(),
3436
) {
37+
val currentNavigateBack by rememberUpdatedState(navigateBack)
38+
vm.singleEventFlow.CollectWithLifecycleEffect { event ->
39+
when (event) {
40+
DetailSingleEvent.Complete -> currentNavigateBack()
41+
}
42+
}
43+
3544
val text by vm
3645
.textStateFlow
3746
.collectAsStateWithLifecycle(context = Dispatchers.Main.immediate)
@@ -70,10 +79,7 @@ fun DetailScreen(
7079

7180
ElevatedButton(
7281
enabled = text.isNotBlank(),
73-
onClick = {
74-
vm.sendResultToHome()
75-
navigateBack()
76-
},
82+
onClick = remember { vm::sendResultToHome },
7783
) {
7884
Text(text = "Send to home screen")
7985
}

sample/app/src/main/java/com/hoc081098/channeleventbus/sample/android/ui/home/detail/DetailVM.kt

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,67 @@
11
package com.hoc081098.channeleventbus.sample.android.ui.home.detail
22

3+
import androidx.compose.runtime.Immutable
34
import androidx.lifecycle.SavedStateHandle
45
import androidx.lifecycle.ViewModel
6+
import androidx.lifecycle.viewModelScope
57
import com.hoc081098.channeleventbus.ChannelEventBus
8+
import com.hoc081098.channeleventbus.sample.android.common.HasSingleEventFlow
69
import com.hoc081098.channeleventbus.sample.android.common.SafeSavedStateHandle
710
import com.hoc081098.channeleventbus.sample.android.common.SavedStateHandleKey
11+
import com.hoc081098.channeleventbus.sample.android.common.SingleEventChannel
812
import com.hoc081098.channeleventbus.sample.android.ui.home.DetailResultToHomeEvent
913
import com.hoc081098.channeleventbus.sample.android.utils.NonBlankString.Companion.toNonBlankString
14+
import com.hoc081098.channeleventbus.sample.android.utils.launchNowIn
15+
import com.hoc081098.flowext.flowFromSuspend
16+
import kotlinx.coroutines.ExperimentalCoroutinesApi
17+
import kotlinx.coroutines.delay
18+
import kotlinx.coroutines.flow.Flow
19+
import kotlinx.coroutines.flow.MutableSharedFlow
1020
import kotlinx.coroutines.flow.StateFlow
21+
import kotlinx.coroutines.flow.flatMapLatest
22+
import kotlinx.coroutines.launch
23+
import timber.log.Timber
1124

25+
@Immutable
26+
sealed interface DetailSingleEvent {
27+
data object Complete : DetailSingleEvent
28+
}
29+
30+
@OptIn(ExperimentalCoroutinesApi::class)
1231
class DetailVM(
1332
private val channelEventBus: ChannelEventBus,
33+
private val singleEventChannel: SingleEventChannel<DetailSingleEvent>,
1434
savedStateHandle: SavedStateHandle,
15-
) : ViewModel() {
35+
) : ViewModel(singleEventChannel),
36+
HasSingleEventFlow<DetailSingleEvent> by singleEventChannel {
1637
private val safeSavedStateHandle = SafeSavedStateHandle(savedStateHandle)
38+
private val sendResultFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
1739

1840
internal val textStateFlow: StateFlow<String> = safeSavedStateHandle.getStateFlow(TextKey)
1941

42+
init {
43+
fun process(): Flow<Unit> = flowFromSuspend {
44+
delay(500) // simulate a long-running task
45+
46+
textStateFlow.value
47+
.toNonBlankString()
48+
.map(::DetailResultToHomeEvent)
49+
.onSuccess(channelEventBus::send)
50+
.onSuccess { singleEventChannel.sendEvent(DetailSingleEvent.Complete) }
51+
.onFailure { Timber.e(it, "Error while sending result to home") }
52+
}
53+
54+
sendResultFlow
55+
.flatMapLatest { process() }
56+
.launchNowIn(viewModelScope)
57+
}
58+
2059
internal fun onTextChanged(text: String) {
2160
safeSavedStateHandle[TextKey] = text
2261
}
2362

2463
internal fun sendResultToHome() {
25-
textStateFlow.value
26-
.toNonBlankString()
27-
.map(::DetailResultToHomeEvent)
28-
.onSuccess(channelEventBus::send)
64+
viewModelScope.launch { sendResultFlow.emit(Unit) }
2965
}
3066

3167
private companion object {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.hoc081098.channeleventbus.sample.android.utils
2+
3+
import kotlinx.coroutines.CoroutineDispatcher
4+
import kotlinx.coroutines.CoroutineScope
5+
import kotlinx.coroutines.CoroutineStart
6+
import kotlinx.coroutines.Job
7+
import kotlinx.coroutines.flow.Flow
8+
import kotlinx.coroutines.flow.collect
9+
import kotlinx.coroutines.launch
10+
11+
/**
12+
* Launch a coroutine immediately to collect the flow.
13+
* It is a shortcut for `scope.launch(CoroutineStart.UNDISPATCHED) { flow.collect() }`.
14+
*
15+
* This differs from [kotlinx.coroutines.flow.launchIn] in that the collection is started immediately _in the current thread_
16+
* until the first suspension point, without dispatching to the [CoroutineDispatcher] of the scope context.
17+
* However, when the coroutine is resumed from suspension, it is dispatched according to the [CoroutineDispatcher] in its context.
18+
*
19+
* This is useful when collecting a [kotlinx.coroutines.flow.SharedFlow] which does not replay or buffer values,
20+
* and you don't want to miss any values due to the dispatching to the [CoroutineDispatcher].
21+
*
22+
* @see kotlinx.coroutines.flow.launchIn
23+
* @see CoroutineStart.UNDISPATCHED
24+
*/
25+
fun <T> Flow<T>.launchNowIn(scope: CoroutineScope): Job =
26+
scope.launch(start = CoroutineStart.UNDISPATCHED) {
27+
collect() // tail-call
28+
}

0 commit comments

Comments
 (0)