Skip to content

Commit

Permalink
icerockdev#183 return resource state loading and empty generic type
Browse files Browse the repository at this point in the history
  • Loading branch information
mdubkov committed Sep 6, 2022
1 parent da2b37c commit 9d9de65
Show file tree
Hide file tree
Showing 12 changed files with 47 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
package dev.icerock.moko.mvvm.state

sealed class ResourceState<out TData, out TError> {
data class Success<out TData>(val data: TData) : ResourceState<TData, Nothing>()
data class Failed<out TError>(val error: TError) : ResourceState<Nothing, TError>()
object Loading : ResourceState<Nothing, Nothing>()
object Empty : ResourceState<Nothing, Nothing>()
data class Success<out TData, out TError>(val data: TData) : ResourceState<TData, TError>()
data class Failed<out TData, out TError>(val error: TError) : ResourceState<TData, TError>()
class Loading<out TData, out TError> : ResourceState<TData, TError>()
class Empty<out TData, out TError> : ResourceState<TData, TError>()

fun isLoading(): Boolean = this is Loading
fun isSuccess(): Boolean = this is Success
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fun <T, E> T?.asState(whenNull: () -> ResourceState<T, E>): ResourceState<T, E>
this?.asState() ?: whenNull()

fun <T, E> List<T>.asState(): ResourceState<List<T>, E> = if (this.isEmpty()) {
ResourceState.Empty
ResourceState.Empty()
} else {
ResourceState.Success(this)
}
Expand All @@ -20,7 +20,7 @@ fun <T, E> List<T>?.asState(whenNull: () -> ResourceState<List<T>, E>): Resource
this?.asState() ?: whenNull()

inline fun <reified T, reified E> ResourceState<T, E>?.nullAsEmpty(): ResourceState<T, E> =
this ?: ResourceState.Empty
this ?: ResourceState.Empty()

inline fun <reified T, reified E> ResourceState<T, E>?.nullAsLoading(): ResourceState<T, E> =
this ?: ResourceState.Loading
this ?: ResourceState.Loading()
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@ fun <TData, TError> Flow<ResourceState<TData, TError>>.isEmptyState(): Flow<Bool

inline fun <TData, TError, reified ST : ResourceState<TData, TError>, FL : Flow<ST>> List<FL>.isSuccessState(): Flow<Boolean> =
combine(this) { list ->
list.firstOrNull { it !is ResourceState.Success<*> } == null
list.firstOrNull { it !is ResourceState.Success<*, *> } == null
}

inline fun <TData, TError, reified ST : ResourceState<TData, TError>, LD : Flow<ST>> List<LD>.isLoadingState(): Flow<Boolean> =
combine(this) { list ->
list.firstOrNull() { it is ResourceState.Loading } != null
list.firstOrNull() { it is ResourceState.Loading<*, *> } != null
}

inline fun <TData, TError, reified ST : ResourceState<TData, TError>, LD : Flow<ST>> List<LD>.isErrorState(): Flow<Boolean> =
combine(this) { list ->
list.firstOrNull { it is ResourceState.Failed<*> } != null
list.firstOrNull { it is ResourceState.Failed<*, *> } != null
}

inline fun <TData, TError, reified ST : ResourceState<TData, TError>, LD : Flow<ST>> List<LD>.isEmptyState(): Flow<Boolean> =
combine(this) { list ->
list.firstOrNull { it is ResourceState.Empty } != null
list.firstOrNull { it is ResourceState.Empty<*, *> } != null
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ inline fun <TData, TError, reified ST : ResourceState<TData, TError>, LD : Flow<
combine(this) { list ->
@Suppress("UNCHECKED_CAST")
val errorItem = list.firstOrNull {
(it is ResourceState.Failed<*>)
} as? ResourceState.Failed<TError>
(it is ResourceState.Failed<*, *>)
} as? ResourceState.Failed<TData, TError>
errorItem?.error
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ fun <T1, E, T2, OT> Flow<ResourceState<T1, E>>.concatData(
flow
) { firstState, secondState ->
val state: ResourceState<OT, E> = when {
(firstState is ResourceState.Loading || secondState is ResourceState.Loading) -> ResourceState.Loading
(firstState is ResourceState.Loading || secondState is ResourceState.Loading) -> ResourceState.Loading()
(firstState is ResourceState.Failed) -> ResourceState.Failed(firstState.error)
(secondState is ResourceState.Failed) -> ResourceState.Failed(secondState.error)
(firstState is ResourceState.Empty || secondState is ResourceState.Empty) -> ResourceState.Empty
(firstState is ResourceState.Empty || secondState is ResourceState.Empty) -> ResourceState.Empty()
(firstState is ResourceState.Success && secondState is ResourceState.Success) -> ResourceState.Success(
function(
firstState.data,
secondState.data
)
)
else -> ResourceState.Empty
else -> ResourceState.Empty()
}

state
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
* Copyright 2022 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

@file:OptIn(ExperimentalCoroutinesApi::class)

package dev.icerock.moko.mvvm.flow

import dev.icerock.moko.mvvm.state.ResourceState
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
Expand All @@ -17,9 +20,9 @@ fun <IT, E, OT> Flow<ResourceState<IT, E>>.dataTransform(
is ResourceState.Success -> transform
.invoke(flowOf(state.data))
.map { ResourceState.Success(it) }
ResourceState.Empty -> flowOf(ResourceState.Empty)
is ResourceState.Empty -> flowOf(ResourceState.Empty())
is ResourceState.Failed -> flowOf(ResourceState.Failed(state.error))
ResourceState.Loading -> flowOf(ResourceState.Loading)
is ResourceState.Loading -> flowOf(ResourceState.Loading())
}

}
Expand All @@ -29,10 +32,10 @@ fun <T, IE, OE> Flow<ResourceState<T, IE>>.errorTransform(
): Flow<ResourceState<T, OE>> = flatMapLatest { state ->
when (state) {
is ResourceState.Success -> flowOf(ResourceState.Success(state.data))
is ResourceState.Loading -> flowOf(ResourceState.Loading)
is ResourceState.Empty -> flowOf(ResourceState.Empty)
is ResourceState.Loading -> flowOf(ResourceState.Loading())
is ResourceState.Empty -> flowOf(ResourceState.Empty())
is ResourceState.Failed -> transform.invoke(flowOf(state.error))
.map { ResourceState.Failed<OE>(it) }
.map { ResourceState.Failed(it) }
}
}

Expand All @@ -58,7 +61,7 @@ fun <T, E> Flow<ResourceState<T, E>>.emptyIf(
emptyPredicate: (T) -> Boolean
): Flow<ResourceState<T, E>> = map {
when {
it is ResourceState.Success && emptyPredicate(it.data) -> ResourceState.Empty
it is ResourceState.Success && emptyPredicate(it.data) -> ResourceState.Empty()
else -> it
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,23 @@ fun <T, E> LiveData<ResourceState<T, E>>.isEmptyState(): LiveData<Boolean> =
fun <T, E, ST : ResourceState<T, E>, LD : LiveData<out ST>> List<LD>.isSuccessState(): LiveData<Boolean> =
MediatorLiveData(false)
.composition(this) { list ->
list.firstOrNull { it !is ResourceState.Success<*>} == null
list.firstOrNull { it !is ResourceState.Success<*, *>} == null
}

fun <T, E, ST : ResourceState<T, E>, LD : LiveData<out ST>> List<LD>.isLoadingState(): LiveData<Boolean> =
MediatorLiveData(false)
.composition(this) { list ->
list.firstOrNull { it is ResourceState.Loading } != null
list.firstOrNull { it is ResourceState.Loading<*, *> } != null
}

fun <T, E, ST : ResourceState<T, E>, LD : LiveData<out ST>> List<LD>.isErrorState(): LiveData<Boolean> =
MediatorLiveData(false)
.composition(this) { list ->
list.firstOrNull { it is ResourceState.Failed<*> } != null
list.firstOrNull { it is ResourceState.Failed<*, *> } != null
}

fun <T, E, ST : ResourceState<T, E>, LD : LiveData<out ST>> List<LD>.isEmptyState(): LiveData<Boolean> =
MediatorLiveData(false)
.composition(this) { list ->
list.firstOrNull { it is ResourceState.Empty } != null
list.firstOrNull { it is ResourceState.Empty<*, *> } != null
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fun <T, E, ST : ResourceState<T, E>, LD : LiveData<out ST>> List<LD>.error(): Li
.composition(this) { list ->
@Suppress("UNCHECKED_CAST")
val errorItem = list.firstOrNull {
(it is ResourceState.Failed<*>)
} as? ResourceState.Failed<E>
(it is ResourceState.Failed<*, *>)
} as? ResourceState.Failed<T, E>
errorItem?.error
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ fun <T1, E, T2, OT> LiveData<ResourceState<T1, E>>.concatData(
): LiveData<ResourceState<OT, E>> =
mediatorOf(this, liveData) { firstState, secondState ->
val state: ResourceState<OT, E> = when {
(firstState is ResourceState.Loading || secondState is ResourceState.Loading) -> ResourceState.Loading
(firstState is ResourceState.Loading || secondState is ResourceState.Loading) -> ResourceState.Loading()
(firstState is ResourceState.Failed) -> ResourceState.Failed(firstState.error)
(secondState is ResourceState.Failed) -> ResourceState.Failed(secondState.error)
(firstState is ResourceState.Empty || secondState is ResourceState.Empty) -> ResourceState.Empty
(firstState is ResourceState.Empty || secondState is ResourceState.Empty) -> ResourceState.Empty()
(firstState is ResourceState.Success && secondState is ResourceState.Success) -> ResourceState.Success(
function(
firstState.data,
secondState.data
)
)
else -> ResourceState.Empty
else -> ResourceState.Empty()
}

state
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ fun <IT, E, OT> LiveData<ResourceState<IT, E>>.dataTransform(
when (state) {
is ResourceState.Success -> transform.invoke(MutableLiveData(state.data))
.map { ResourceState.Success(it) }
is ResourceState.Loading -> MutableLiveData(ResourceState.Loading)
is ResourceState.Empty -> MutableLiveData(ResourceState.Empty)
is ResourceState.Loading -> MutableLiveData(ResourceState.Loading())
is ResourceState.Empty -> MutableLiveData(ResourceState.Empty())
is ResourceState.Failed -> MutableLiveData(ResourceState.Failed(state.error))
}
}
Expand All @@ -23,10 +23,10 @@ fun <T, IE, OE> LiveData<ResourceState<T, IE>>.errorTransform(
): LiveData<ResourceState<T, OE>> = flatMap { state ->
when (state) {
is ResourceState.Success -> MutableLiveData(ResourceState.Success(state.data))
is ResourceState.Loading -> MutableLiveData(ResourceState.Loading)
is ResourceState.Empty -> MutableLiveData(ResourceState.Empty)
is ResourceState.Loading -> MutableLiveData(ResourceState.Loading())
is ResourceState.Empty -> MutableLiveData(ResourceState.Empty())
is ResourceState.Failed -> transform.invoke(MutableLiveData(state.error))
.map { ResourceState.Failed<OE>(it) }
.map { ResourceState.Failed(it) }
}
}

Expand All @@ -52,7 +52,7 @@ fun <T, E> LiveData<ResourceState<T, E>>.emptyIf(
emptyPredicate: (T) -> Boolean
): LiveData<ResourceState<T, E>> = map {
when {
it is ResourceState.Success && emptyPredicate(it.data) -> ResourceState.Empty
it is ResourceState.Success && emptyPredicate(it.data) -> ResourceState.Empty()
else -> it
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ open class FlowTest {
mergeWithCounter++
if (a2) a1.toLong() else -a1.toLong()
}
}.stateIn(coroutineScope, SharingStarted.Eagerly, ResourceState.Loading)
}.stateIn(coroutineScope, SharingStarted.Eagerly, ResourceState.Loading())

assertEquals(actual = dataTransformCounter, expected = 1)
assertEquals(actual = mergeWithCounter, expected = 1)
Expand Down Expand Up @@ -78,7 +78,7 @@ open class FlowTest {

val vmIsAuthorized = MutableStateFlow(true)
val state: MutableStateFlow<ResourceState<Int, Throwable>> =
MutableStateFlow(ResourceState.Empty)
MutableStateFlow(ResourceState.Empty())
val isLoading: MutableStateFlow<Boolean> = MutableStateFlow(false)

var dataTransformCounter = 0
Expand All @@ -91,7 +91,7 @@ open class FlowTest {
mergeWithDataTransformCounter++
if (a2) a1.toLong() else -a1.toLong()
}
}.stateIn(coroutineScope, SharingStarted.Eagerly, ResourceState.Loading)
}.stateIn(coroutineScope, SharingStarted.Eagerly, ResourceState.Loading())

val result: StateFlow<ResourceState<Long, Throwable>> = combine(
vmIsAuthorized,
Expand All @@ -103,14 +103,14 @@ open class FlowTest {
} else {
ResourceState.Failed(Exception())
}
}.stateIn(coroutineScope, SharingStarted.Eagerly, ResourceState.Loading)
}.stateIn(coroutineScope, SharingStarted.Eagerly, ResourceState.Loading())

assertEquals(actual = dataTransformCounter, expected = 0)
assertEquals(actual = mergeWithDataTransformCounter, expected = 0)
assertEquals(actual = mergeWithIsAuthorizedCounter, expected = 1)
assertTrue { result.value.isEmpty() }

state.value = ResourceState.Loading
state.value = ResourceState.Loading()

assertEquals(actual = dataTransformCounter, expected = 0)
assertEquals(actual = mergeWithDataTransformCounter, expected = 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ open class LiveDataTest {
open fun dataTransformMergeWithTest() {
val vmIsAuthorized = MutableLiveData(true)
val state: MutableLiveData<ResourceState<Int, Throwable>> =
MutableLiveData(ResourceState.Empty)
MutableLiveData(ResourceState.Empty())
val isLoading: MutableLiveData<Boolean> = MutableLiveData(false)

var dataTransformCounter = 0
Expand Down Expand Up @@ -101,7 +101,7 @@ open class LiveDataTest {
assertEquals(actual = mergeWithIsAuthorizedCounter, expected = 3)
assertTrue { result.value.isEmpty() }

state.value = ResourceState.Loading
state.value = ResourceState.Loading()

assertEquals(actual = dataTransformCounter, expected = 0)
assertEquals(actual = mergeWithDataTransformCounter, expected = 0)
Expand Down

0 comments on commit 9d9de65

Please sign in to comment.