Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change CoroutineScope-based Loadable-Flow-creator functions return type to MutableStateFlow #46

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions buildSrc/src/main/java/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ object Versions {
val java = JavaVersion.VERSION_11

object Loadable {
const val CODE = 18
const val NAME = "1.6.5"
const val CODE = 19
const val NAME = "1.6.6"
const val SDK_COMPILE = 33
const val SDK_MIN = 21
const val SDK_TARGET = SDK_COMPILE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,15 @@ import com.jeanbarrossilva.loadable.ifLoaded
import com.jeanbarrossilva.loadable.map
import java.io.Serializable
import kotlin.experimental.ExperimentalTypeInference
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.withIndex
import kotlinx.coroutines.launch

/** Returns a [Flow] containing only [failed][Loadable.Failed] values. **/
fun <T : Serializable?> Flow<Loadable<T>>.filterIsFailed(): Flow<Loadable.Failed<T>> {
Expand All @@ -46,21 +41,6 @@ fun <I : Serializable?, O : Serializable?> Flow<Loadable<I>>.innerMap(transform:
}
}

/**
* Maps each emission made to this [Flow] to a [Loadable].
*
* Emits, initially, [Loadable.Loading], [Loadable.Loaded] for each value and [Loadable.Failed] for
* thrown [Throwable]s.
*
* @param coroutineScope [CoroutineScope] in which the resulting [StateFlow] will be started and its
* value will be shared.
**/
fun <T : Serializable?> Flow<T>.loadable(coroutineScope: CoroutineScope): StateFlow<Loadable<T>> {
return loadableFlow(coroutineScope) {
collect(::load)
}
}

/**
* Maps each emission made to this [Flow] to a [Loadable].
*
Expand Down Expand Up @@ -124,29 +104,6 @@ fun <T : Serializable> Flow<Loadable<T?>>.unwrapContent(): Flow<Loadable<T>> {
} as Flow<Loadable<T>>
}

/**
* Creates a [StateFlow] of [Loadable]s that's started and shared in the [coroutineScope] and emits
* them through [load] with a [LoadableScope]. Its initial [value][StateFlow.value] is
* [loading][Loadable.Loading].
*
* @param coroutineScope [CoroutineScope] in which the resulting [StateFlow] will be started and its
* value will be shared.
* @param load Operations to be made on the [LoadableScope] responsible for emitting [Loadable]s
* sent to it to the created [StateFlow].
**/
fun <T : Serializable?> loadableFlow(
coroutineScope: CoroutineScope,
load: suspend LoadableScope<T>.() -> Unit
): StateFlow<Loadable<T>> {
return loadableFlow<T>()
.apply {
coroutineScope.launch {
emitAll(emptyLoadableFlow(load))
}
}
.asStateFlow()
}

/**
* Creates a [Flow] of [Loadable]s that emits them through [load] with a [LoadableScope] and has an
* initial [loading][Loadable.Loading] value.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,50 @@
package com.jeanbarrossilva.loadable.flow

import com.jeanbarrossilva.loadable.Loadable
import com.jeanbarrossilva.loadable.LoadableScope
import java.io.Serializable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.launch

/**
* Maps each emission made to this [Flow] to a [Loadable].
*
* Emits, initially, [Loadable.Loading], [Loadable.Loaded] for each value and [Loadable.Failed] for
* thrown [Throwable]s.
*
* @param coroutineScope [CoroutineScope] in which the resulting [MutableStateFlow] will be started
* and its [value][MutableStateFlow.value] will be shared.
**/
fun <T : Serializable?> Flow<T>.loadable(coroutineScope: CoroutineScope):
MutableStateFlow<Loadable<T>> {
return loadableFlow(coroutineScope) {
collect(::load)
}
}

/**
* Creates a [MutableStateFlow] of [Loadable]s that's started and shared in the [coroutineScope] and
* emits them through [load] with a [LoadableScope]. Its initial [value][MutableStateFlow.value] is
* [loading][Loadable.Loading].
*
* @param coroutineScope [CoroutineScope] in which the resulting [MutableStateFlow] will be started
* and its [value][MutableStateFlow.value] will be shared.
* @param load Operations to be made on the [LoadableScope] responsible for emitting [Loadable]s
* sent to it to the created [MutableStateFlow].
**/
fun <T : Serializable?> loadableFlow(
coroutineScope: CoroutineScope,
load: suspend LoadableScope<T>.() -> Unit
): MutableStateFlow<Loadable<T>> {
return loadableFlow<T>().apply {
coroutineScope.launch {
emitAll(emptyLoadableFlow(load))
}
}
}

/** Creates a [MutableStateFlow] with a [Loadable.Loading] as its initial value. **/
fun <T : Serializable?> loadableFlow(): MutableStateFlow<Loadable<T>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,6 @@ internal class FlowExtensionsTests {
}
}

@Test
fun `GIVEN a Loadable Flow with an initial content WHEN collecting it THEN the value that's emitted first is a Loaded one`() { // ktlint-disable max-line-length
runTest {
loadableFlowOf(0).test {
assertEquals(Loadable.Loaded(0), awaitItem())
}
}
}

@Test
fun `GIVEN a Loadable Flow WHEN filtering Failed values THEN they're all emitted`() {
runTest {
Expand Down Expand Up @@ -158,9 +149,10 @@ internal class FlowExtensionsTests {
@Test
fun `GIVEN a Flow WHEN converting it into a Loadable one in a CoroutineScope THEN Loading is only emitted once as an initial value`() { // ktlint-disable max-line-length
runTest {
flowOf(0).loadable(this).test {
flowOf(0).loadable().test {
assertIs<Loadable.Loading<Int>>(awaitItem())
assertEquals(Loadable.Loaded(0), awaitItem())
awaitComplete()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.jeanbarrossilva.loadable.flow

import app.cash.turbine.test
import com.jeanbarrossilva.loadable.Loadable
import kotlin.test.Test
import kotlinx.coroutines.test.runTest
import org.junit.Assert

internal class MutableStateFlowExtensionsTests {
@Test
fun `GIVEN a Loadable Flow with an initial content WHEN collecting it THEN the value that's emitted first is a Loaded one`() { // ktlint-disable max-line-length
runTest {
loadableFlowOf(0).test {
Assert.assertEquals(Loadable.Loaded(0), awaitItem())
}
}
}
}
Loading