Skip to content

Commit ce19f8f

Browse files
authored
collectIn 🎉 (#12)
* 🎉 * spotless * const
1 parent a97f789 commit ce19f8f

File tree

9 files changed

+62
-35
lines changed

9 files changed

+62
-35
lines changed

.idea/codeStyles/Project.xml

Lines changed: 0 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/compiler.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/gradle.xml

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/kotlinc.xml

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.hoc.flowmvi.core
2+
3+
import android.util.Log
4+
import androidx.fragment.app.Fragment
5+
import androidx.lifecycle.Lifecycle
6+
import androidx.lifecycle.LifecycleOwner
7+
import androidx.lifecycle.addRepeatingJob
8+
import kotlinx.coroutines.Job
9+
import kotlinx.coroutines.flow.Flow
10+
import kotlinx.coroutines.flow.collect
11+
import kotlin.coroutines.CoroutineContext
12+
import kotlin.coroutines.EmptyCoroutineContext
13+
14+
inline fun <T> Flow<T>.collectIn(
15+
owner: LifecycleOwner,
16+
minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
17+
coroutineContext: CoroutineContext = EmptyCoroutineContext,
18+
crossinline action: suspend (value: T) -> Unit,
19+
): Job = owner.addRepeatingJob(state = minActiveState, coroutineContext = coroutineContext) {
20+
Log.d("collectIn", "Start collecting...")
21+
collect { action(it) }
22+
}
23+
24+
@Suppress("unused")
25+
inline fun <T> Flow<T>.collectIn(
26+
fragment: Fragment,
27+
minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
28+
coroutineContext: CoroutineContext = EmptyCoroutineContext,
29+
crossinline action: suspend (value: T) -> Unit,
30+
): Job = collectIn(
31+
fragment.viewLifecycleOwner,
32+
minActiveState = minActiveState,
33+
coroutineContext = coroutineContext,
34+
action = action,
35+
)

feature-add/src/main/java/com/hoc/flowmvi/ui/add/AddActivity.kt

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@ import android.util.Log
77
import android.view.MenuItem
88
import androidx.appcompat.app.AppCompatActivity
99
import androidx.core.view.isInvisible
10-
import androidx.lifecycle.Lifecycle
11-
import androidx.lifecycle.flowWithLifecycle
1210
import androidx.lifecycle.lifecycleScope
1311
import androidx.transition.AutoTransition
1412
import androidx.transition.TransitionManager
1513
import com.hoc.flowmvi.core.clicks
14+
import com.hoc.flowmvi.core.collectIn
1615
import com.hoc.flowmvi.core.firstChange
1716
import com.hoc.flowmvi.core.navigator.IntentProviders
1817
import com.hoc.flowmvi.core.textChanges
@@ -53,15 +52,11 @@ class AddActivity : AppCompatActivity() {
5352
private fun bindVM(addVM: AddVM) {
5453
// observe view model
5554
addVM.viewState
56-
.onEach { render(it) }
57-
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
58-
.launchIn(lifecycleScope)
55+
.collectIn(this) { render(it) }
5956

6057
// observe single event
6158
addVM.singleEvent
62-
.onEach { handleSingleEvent(it) }
63-
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
64-
.launchIn(lifecycleScope)
59+
.collectIn(this) { handleSingleEvent(it) }
6560

6661
// pass view intent to view model
6762
intents()

feature-add/src/main/java/com/hoc/flowmvi/ui/add/AddVM.kt

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.SharingStarted
1919
import kotlinx.coroutines.flow.StateFlow
2020
import kotlinx.coroutines.flow.catch
2121
import kotlinx.coroutines.flow.combine
22+
import kotlinx.coroutines.flow.distinctUntilChanged
2223
import kotlinx.coroutines.flow.filterIsInstance
2324
import kotlinx.coroutines.flow.flow
2425
import kotlinx.coroutines.flow.map
@@ -47,9 +48,9 @@ internal class AddVM(
4748

4849
init {
4950
val initialVS = ViewState.initial(
50-
email = savedStateHandle.get<String?>("email"),
51-
firstName = savedStateHandle.get<String?>("first_name"),
52-
lastName = savedStateHandle.get<String?>("last_name"),
51+
email = savedStateHandle.get<String?>(EMAIL_KEY),
52+
firstName = savedStateHandle.get<String?>(FIRST_NAME_KEY),
53+
lastName = savedStateHandle.get<String?>(LAST_NAME_KEY),
5354
)
5455
Log.d("###", "[ADD_VM] initialVS: $initialVS")
5556

@@ -85,6 +86,7 @@ internal class AddVM(
8586
private fun Flow<ViewIntent>.toPartialStateChangesFlow(): Flow<PartialStateChange> {
8687
val emailErrors = filterIsInstance<ViewIntent.EmailChanged>()
8788
.map { it.email }
89+
.distinctUntilChanged()
8890
.map { validateEmail(it) to it }
8991
.shareIn(
9092
scope = viewModelScope,
@@ -93,6 +95,7 @@ internal class AddVM(
9395

9496
val firstNameErrors = filterIsInstance<ViewIntent.FirstNameChanged>()
9597
.map { it.firstName }
98+
.distinctUntilChanged()
9699
.map { validateFirstName(it) to it }
97100
.shareIn(
98101
scope = viewModelScope,
@@ -101,6 +104,7 @@ internal class AddVM(
101104

102105
val lastNameErrors = filterIsInstance<ViewIntent.LastNameChanged>()
103106
.map { it.lastName }
107+
.distinctUntilChanged()
104108
.map { validateLastName(it) to it }
105109
.shareIn(
106110
scope = viewModelScope,
@@ -151,15 +155,15 @@ internal class AddVM(
151155
val formValuesChanges = merge(
152156
emailErrors
153157
.map { it.second }
154-
.onEach { savedStateHandle.set("email", it) }
158+
.onEach { savedStateHandle.set(EMAIL_KEY, it) }
155159
.map { PartialStateChange.FormValueChange.EmailChanged(it) },
156160
firstNameErrors
157161
.map { it.second }
158-
.onEach { savedStateHandle.set("first_name", it) }
162+
.onEach { savedStateHandle.set(FIRST_NAME_KEY, it) }
159163
.map { PartialStateChange.FormValueChange.FirstNameChanged(it) },
160164
lastNameErrors
161165
.map { it.second }
162-
.onEach { savedStateHandle.set("last_name", it) }
166+
.onEach { savedStateHandle.set(LAST_NAME_KEY, it) }
163167
.map { PartialStateChange.FormValueChange.LastNameChanged(it) },
164168
)
165169

@@ -178,6 +182,10 @@ internal class AddVM(
178182
}
179183

180184
private companion object {
185+
const val EMAIL_KEY = "email"
186+
const val FIRST_NAME_KEY = "first_name"
187+
const val LAST_NAME_KEY = "last_name"
188+
181189
const val MIN_LENGTH_FIRST_NAME = 3
182190
const val MIN_LENGTH_LAST_NAME = 3
183191

feature-main/src/main/java/com/hoc/flowmvi/ui/main/MainActivity.kt

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,14 @@ import android.view.Menu
66
import android.view.MenuItem
77
import androidx.appcompat.app.AppCompatActivity
88
import androidx.core.view.isVisible
9-
import androidx.lifecycle.Lifecycle
10-
import androidx.lifecycle.flowWithLifecycle
119
import androidx.lifecycle.lifecycleScope
1210
import androidx.recyclerview.widget.DividerItemDecoration
1311
import androidx.recyclerview.widget.ItemTouchHelper
1412
import androidx.recyclerview.widget.LinearLayoutManager
1513
import androidx.recyclerview.widget.RecyclerView
1614
import com.hoc.flowmvi.core.SwipeLeftToDeleteCallback
1715
import com.hoc.flowmvi.core.clicks
16+
import com.hoc.flowmvi.core.collectIn
1817
import com.hoc.flowmvi.core.navigator.Navigator
1918
import com.hoc.flowmvi.core.refreshes
2019
import com.hoc.flowmvi.core.safeOffer
@@ -86,15 +85,11 @@ class MainActivity : AppCompatActivity() {
8685
private fun bindVM(mainVM: MainVM) {
8786
// observe view model
8887
mainVM.viewState
89-
.onEach { render(it) }
90-
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
91-
.launchIn(lifecycleScope)
88+
.collectIn(this) { render(it) }
9289

9390
// observe single event
9491
mainVM.singleEvent
95-
.onEach { handleSingleEvent(it) }
96-
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
97-
.launchIn(lifecycleScope)
92+
.collectIn(this) { handleSingleEvent(it) }
9893

9994
// pass view intent to view model
10095
intents()

0 commit comments

Comments
 (0)