From 698050114a41df68279b4cc6ac7bd5ea95a6b3d8 Mon Sep 17 00:00:00 2001 From: Matheus Barbieri Corregiari Date: Wed, 14 Aug 2024 21:07:27 -0300 Subject: [PATCH 1/8] Add Combine and Chain to ResponseLiveData --- .../arch/toolkit/livedata/ResponseLiveData.kt | 349 +++++++++++++++++- .../kotlin/br/com/arch/toolkit/util/_chain.kt | 16 +- .../br/com/arch/toolkit/util/_combine.kt | 32 +- .../br/com/arch/toolkit/util/_dataResult.kt | 6 +- .../br/com/arch/toolkit/util/_liveData.kt | 1 + .../kotlin/br/com/arch/toolkit/util/_pair.kt | 6 +- .../com/arch/toolkit/util/_responseChain.kt | 81 ++++ .../com/arch/toolkit/util/_responseCombine.kt | 79 ++++ 8 files changed, 519 insertions(+), 51 deletions(-) create mode 100644 toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseChain.kt create mode 100644 toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/livedata/ResponseLiveData.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/livedata/ResponseLiveData.kt index fdc9aaa..a754dc5 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/livedata/ResponseLiveData.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/livedata/ResponseLiveData.kt @@ -1,4 +1,10 @@ -@file:Suppress("KotlinNullnessAnnotation", "TooManyFunctions") +@file:Suppress( + "KotlinNullnessAnnotation", + "TooManyFunctions", + "MemberVisibilityCanBePrivate", + "DeprecatedCallableAddReplaceWith", + "unused" +) package br.com.arch.toolkit.livedata @@ -12,11 +18,23 @@ import br.com.arch.toolkit.annotation.Experimental import br.com.arch.toolkit.result.DataResult import br.com.arch.toolkit.result.DataResultStatus import br.com.arch.toolkit.result.ObserveWrapper +import br.com.arch.toolkit.util.dataResultError +import br.com.arch.toolkit.util.dataResultSuccess +import br.com.arch.toolkit.util.internalChainNotNullWith +import br.com.arch.toolkit.util.internalChainWith +import br.com.arch.toolkit.util.internalCombine +import br.com.arch.toolkit.util.internalCombineNotNull import br.com.arch.toolkit.util.mapNotNull +import br.com.arch.toolkit.util.onlyWithValues +import br.com.arch.toolkit.util.plus import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.mapNotNull +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext /** * Custom implementation of LiveData made to help the data handling with needs the interpretation of: @@ -126,9 +144,7 @@ open class ResponseLiveData : LiveData> { * @see ResponseLiveData.onError */ @NonNull - fun mapError( - @NonNull transformation: (Throwable) -> Throwable - ): ResponseLiveData { + fun mapError(@NonNull transformation: (Throwable) -> Throwable): ResponseLiveData { val liveData = SwapResponseLiveData() .scope(scope) .transformDispatcher(transformDispatcher) @@ -153,9 +169,7 @@ open class ResponseLiveData : LiveData> { * @see ResponseLiveData.onErrorReturn */ @NonNull - fun onErrorReturn( - @NonNull onErrorReturn: ((Throwable) -> T) - ): ResponseLiveData { + fun onErrorReturn(@NonNull onErrorReturn: ((Throwable) -> T)): ResponseLiveData { val liveData = SwapResponseLiveData() .scope(scope) .transformDispatcher(transformDispatcher) @@ -172,6 +186,7 @@ open class ResponseLiveData : LiveData> { */ @NonNull @Experimental + @Deprecated("Use combine instead") fun mergeWith(@NonNull source: ResponseLiveData): ResponseLiveData> = withDelegate { merge(this@ResponseLiveData, source, scope, transformDispatcher) @@ -187,6 +202,7 @@ open class ResponseLiveData : LiveData> { */ @NonNull @Experimental + @Deprecated("Use combine instead") fun mergeWith( @NonNull tag: String, @NonNull vararg sources: Pair> @@ -209,6 +225,7 @@ open class ResponseLiveData : LiveData> { */ @NonNull @Experimental + @Deprecated("Use chainWith instead") fun followedBy( @NonNull source: (T) -> ResponseLiveData, @NonNull condition: (T) -> Boolean, @@ -235,6 +252,7 @@ open class ResponseLiveData : LiveData> { */ @NonNull @Experimental + @Deprecated("Use chainWith instead") fun followedBy( @NonNull source: (T) -> ResponseLiveData, @NonNull condition: (T) -> Boolean @@ -250,14 +268,325 @@ open class ResponseLiveData : LiveData> { */ @NonNull @Experimental - fun followedBy( - @NonNull source: (T) -> ResponseLiveData - ): ResponseLiveData> = followedBy(source) { true } + @Deprecated("Use chainWith instead") + fun followedBy(@NonNull source: (T) -> ResponseLiveData) = followedBy(source) { true } + //endregion + + //region Chain With + /* Response + LiveData Functions ---------------------------------------------------------------- */ + /* Nullable ------------------------------------------------------------------------------------- */ + @NonNull + @Experimental + fun regularChainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult?) -> LiveData, + condition: suspend (DataResult?) -> Boolean, + ) = responseLiveData(context = context) { + internalChainWith(other, condition) + .mapNotNull { (result, data) -> result + data?.let(::dataResultSuccess) } + .collect(::emit) + } + + @NonNull + @Experimental + fun regularChainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult?) -> LiveData, + condition: suspend (DataResult?) -> Boolean, + transform: Pair>) -> DataResult> + ) = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalChainWith(other, condition) + .mapNotNull { (result, data) -> result + data?.let(::dataResultSuccess) } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) + } + + @NonNull + @Experimental + fun regularChainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult?) -> LiveData, + condition: suspend (DataResult?) -> Boolean, + transform: suspend (DataResult>) -> DataResult + ) = regularChainWith(context, other, condition, Dispatchers.IO to transform) + + /* Non Nullable --------------------------------------------------------------------------------- */ + + @NonNull + @Experimental + fun regularChainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult) -> LiveData, + condition: suspend (DataResult) -> Boolean, + ) = responseLiveData(context = context) { + internalChainNotNullWith(other, condition) + .mapNotNull { (result, data) -> (result + dataResultSuccess(data)).onlyWithValues() } + .collect(::emit) + } + + @NonNull + @Experimental + fun regularChainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult) -> LiveData, + condition: suspend (DataResult) -> Boolean, + transform: Pair>) -> DataResult> + ) = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalChainNotNullWith(other, condition) + .mapNotNull { (result, data) -> (result + dataResultSuccess(data)).onlyWithValues() } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) + } + + @NonNull + @Experimental + fun regularChainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult) -> LiveData, + condition: suspend (DataResult) -> Boolean, + transform: suspend (DataResult>) -> DataResult + ) = regularChainNotNullWith(context, other, condition, Dispatchers.IO to transform) + + /* Response + Response Functions ---------------------------------------------------------------- */ + /* Nullable ------------------------------------------------------------------------------------- */ + @NonNull + @Experimental + fun chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult?) -> ResponseLiveData, + condition: suspend (DataResult?) -> Boolean, + ) = responseLiveData(context = context) { + internalChainWith(other, condition) + .mapNotNull { (resultA, resultB) -> resultA + resultB } + .collect(::emit) + } + + @NonNull + @Experimental + fun chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult?) -> ResponseLiveData, + condition: suspend (DataResult?) -> Boolean, + transform: Pair>) -> DataResult> + ) = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalChainWith(other, condition) + .mapNotNull { (resultA, resultB) -> resultA + resultB } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) + } + + @NonNull + @Experimental + fun chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult?) -> ResponseLiveData, + condition: suspend (DataResult?) -> Boolean, + transform: suspend (DataResult>) -> DataResult + ) = chainWith(context, other, condition, Dispatchers.IO to transform) + + /* Non Nullable --------------------------------------------------------------------------------- */ + + @NonNull + @Experimental + fun chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult) -> ResponseLiveData, + condition: suspend (DataResult) -> Boolean, + ) = responseLiveData(context = context) { + internalChainNotNullWith(other, condition) + .mapNotNull { (resultA, resultB) -> (resultA + resultB).onlyWithValues() } + .collect(::emit) + } + + @NonNull + @Experimental + fun chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult) -> ResponseLiveData, + condition: suspend (DataResult) -> Boolean, + transform: Pair>) -> DataResult> + ) = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalChainNotNullWith(other, condition) + .mapNotNull { (resultA, resultB) -> (resultA + resultB).onlyWithValues() } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) + } + + @NonNull + @Experimental + fun chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult) -> ResponseLiveData, + condition: suspend (DataResult) -> Boolean, + transform: suspend (DataResult>) -> DataResult + ) = chainNotNullWith(context, other, condition, Dispatchers.IO to transform) + //endregion + + //region Combine + /* Response + LiveData Functions ---------------------------------------------------------------- */ + /* Nullable ------------------------------------------------------------------------------------- */ + + @NonNull + @Experimental + fun combine( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData + ) = responseLiveData(context = context) { + internalCombine(other).mapNotNull { (result, data) -> result + data?.let(::dataResultSuccess) } + .collect(::emit) + } + + @NonNull + @Experimental + fun combine( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData, + transform: Pair>) -> DataResult> + ) = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalCombine(other) + .mapNotNull { (result, data) -> result + data?.let(::dataResultSuccess) } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) + } + + @NonNull + @Experimental + fun combine( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData, + transform: suspend (DataResult>) -> DataResult + ) = combine(context, other, Dispatchers.IO to transform) + + /* Non Nullable --------------------------------------------------------------------------------- */ + @NonNull + @Experimental + fun combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData + ) = responseLiveData(context = context) { + internalCombineNotNull(other) + .mapNotNull { (result, data) -> (result + dataResultSuccess(data)).onlyWithValues() } + .collect(::emit) + } + + @NonNull + @Experimental + fun combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData, + transform: Pair>) -> DataResult> + ) = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalCombineNotNull(other) + .mapNotNull { (result, data) -> (result + dataResultSuccess(data)).onlyWithValues() } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) + } + + @NonNull + @Experimental + fun combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData, + transform: suspend (DataResult>) -> DataResult + ) = combineNotNull(context, other, Dispatchers.IO to transform) + + /* Response + Response Functions ---------------------------------------------------------------- */ + /* Nullable ------------------------------------------------------------------------------------- */ + + @NonNull + @Experimental + fun combine( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData + ) = responseLiveData(context = context) { + internalCombine(other).mapNotNull { (resultA, resultB) -> resultA + resultB } + .collect(::emit) + } + + @NonNull + @Experimental + fun combine( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData, + transform: Pair>) -> DataResult> + ) = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalCombine(other).mapNotNull { (resultA, resultB) -> resultA + resultB } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) + } + + @NonNull + @Experimental + fun combine( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData, + transform: suspend (DataResult>) -> DataResult + ) = combine(context, other, Dispatchers.IO to transform) + + /* Non Nullable --------------------------------------------------------------------------------- */ + @NonNull + @Experimental + fun combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData + ) = responseLiveData(context = context) { + internalCombineNotNull(other) + .mapNotNull { (resultA, resultB) -> (resultA + resultB).onlyWithValues() } + .collect(::emit) + } + + @NonNull + @Experimental + fun combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData, + transform: Pair>) -> DataResult> + ) = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalCombineNotNull(other) + .mapNotNull { (resultA, resultB) -> (resultA + resultB).onlyWithValues() } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) + } + + @NonNull + @Experimental + fun combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData, + transform: suspend (DataResult>) -> DataResult + ) = combineNotNull(context, other, Dispatchers.IO to transform) //endregion //region Operators @Experimental operator fun plus(source: ResponseLiveData) = mergeWith(source) + + @Experimental + operator fun plus(source: LiveData) = combine(other = source) //endregion //region Observability diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_chain.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_chain.kt index 6081535..41eaa1a 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_chain.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_chain.kt @@ -1,4 +1,4 @@ -@file:Suppress("Filename", "TooManyFunctions", "LongParameterList") +@file:Suppress("Filename", "TooManyFunctions", "LongParameterList", "unused") package br.com.arch.toolkit.util @@ -86,7 +86,7 @@ fun LiveData.chainNotNullWith( ): LiveData> = chainWith( other = { it?.let(other) ?: error("Data null in chainNotNullWith") }, condition = { it?.let(condition) ?: false } -).mapNotNull { it.toNotNull() } +).mapNotNull { it.onlyWithValues() } /* Coroutine Functions -------------------------------------------------------------------------- */ @@ -371,7 +371,8 @@ fun LiveData.chainNotNullWith( ): LiveData = chainNotNullWith(EmptyCoroutineContext, other, condition, transform) /** - * Chains this non-nullable [LiveData] with another non-nullable [LiveData] based on a condition, using a simple transformation function and the default [CoroutineContext] and [CoroutineDispatcher]. + * Chains this non-nullable [LiveData] with another non-nullable [LiveData] based on a condition, + * using a simple transformation function and the default [CoroutineContext] and [CoroutineDispatcher]. * * @param other A suspend function that returns another non-nullable [LiveData] based on the value of this [LiveData]. * @param condition A suspend function that determines whether to chain with the other [LiveData] based on a non-nullable value. @@ -391,7 +392,8 @@ fun LiveData.chainNotNullWith( * transform = { a, b -> "$a$b" } * ) * ``` - */fun LiveData.chainNotNullWith( + */ +fun LiveData.chainNotNullWith( other: suspend (T) -> LiveData, condition: suspend (T) -> Boolean, transform: suspend (T, R) -> X @@ -399,15 +401,15 @@ fun LiveData.chainNotNullWith( /* Auxiliary Functions -------------------------------------------------------------------------- */ -private suspend inline fun LiveData.internalChainNotNullWith( +internal suspend inline fun LiveData.internalChainNotNullWith( noinline other: suspend (T) -> LiveData, noinline condition: suspend (T) -> Boolean, ) = internalChainWith( other = { data -> data?.let { other(it) } ?: error("Data null in chainNotNullWith") }, condition = { data -> data?.let { condition(it) } ?: false } -).mapNotNull { it.toNotNull() } +).mapNotNull { it.onlyWithValues() } -private suspend inline fun LiveData.internalChainWith( +internal suspend inline fun LiveData.internalChainWith( noinline other: suspend (T?) -> LiveData, noinline condition: suspend (T?) -> Boolean, ) = channelFlow { diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_combine.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_combine.kt index a8d4bb5..40926c7 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_combine.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_combine.kt @@ -44,28 +44,6 @@ import kotlin.coroutines.EmptyCoroutineContext */ operator fun LiveData.plus(other: LiveData) = combine(other = other) -/** - * Combines two [LiveData] objects using the `+` operator with a specified [CoroutineContext]. - * - * This method allows you to merge two [LiveData] sources into a single [LiveData] while specifying the context in which the operation should occur. - * - * @param context The [CoroutineContext] in which to perform the combination. - * @param other The other [LiveData] to combine with. - * @return A [LiveData] that emits pairs of values from both [LiveData] sources. - * - * ### Example Usage: - * ``` - * val liveData1: LiveData = MutableLiveData(1) - * val liveData2: LiveData = MutableLiveData("A") - * val combinedLiveData: LiveData> = liveData1.plus(Dispatchers.IO, liveData2) - * ``` - * - * @see [combine] - * @see [plus] - */ -fun LiveData.plus(context: CoroutineContext, other: LiveData) = - combine(context = context, other = other) - /* Regular Functions ---------------------------------------------------------------------------- */ /** @@ -130,18 +108,18 @@ fun LiveData.combineNotNull(other: LiveData): LiveData> val initial = (value to other.value).takeIf { isInitialized || other.isInitialized } val mediator = when { initial == null -> MediatorLiveData() - initial.toNotNull() == null -> MediatorLiveData() - else -> MediatorLiveData>(initial.toNotNull()) + initial.onlyWithValues() == null -> MediatorLiveData() + else -> MediatorLiveData>(initial.onlyWithValues()) } mediator.addSource(this) { (it to other.value).takeUnless { ignoreA.compareAndSet(true, false) } - ?.toNotNull() + ?.onlyWithValues() ?.let(mediator::setValue) } mediator.addSource(other) { (value to it).takeUnless { ignoreB.compareAndSet(true, false) } - ?.toNotNull() + ?.onlyWithValues() ?.let(mediator::setValue) } return mediator @@ -442,7 +420,7 @@ fun LiveData.combineNotNull( /* Auxiliary Functions -------------------------------------------------------------------------- */ internal suspend inline fun LiveData.internalCombineNotNull(other: LiveData) = - internalCombine(other).mapNotNull { it.toNotNull() } + internalCombine(other).mapNotNull { it.onlyWithValues() } internal suspend inline fun LiveData.internalCombine(other: LiveData) = channelFlow { val aFlow = this@internalCombine.asFlow() diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_dataResult.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_dataResult.kt index 9146cee..a788a68 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_dataResult.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_dataResult.kt @@ -109,7 +109,7 @@ fun dataResultNone() = DataResult(null, null, NONE) * * @return A [DataResult] with a pair of non-null values, or null if any value is null. */ -fun DataResult>.toNotNull(): DataResult>? = when (val data = data) { +fun DataResult>.onlyWithValues(): DataResult>? = when (val data = data) { null -> DataResult(null, error, status) else -> data.runCatching { requireNotNull(first) to requireNotNull(second) } .mapCatching { DataResult>(it, error, status) } @@ -215,9 +215,7 @@ fun DataResult?.merge(second: DataResult?): DataResult fun DataResult?.mergeNotNull(second: DataResult?): DataResult> { val mergeNullable = merge(second) - val data = mergeNullable.data?.runCatching { - requireNotNull(this.first) to requireNotNull(this.second) - }?.getOrNull() + val data = mergeNullable.data?.onlyWithValues() val error = mergeNullable.error val status = mergeNullable.status diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_liveData.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_liveData.kt index 47b3b36..595fcbf 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_liveData.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_liveData.kt @@ -1,4 +1,5 @@ @file:JvmName("LiveDataUtils") +@file:Suppress("unused") package br.com.arch.toolkit.util diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_pair.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_pair.kt index 10eca31..f01a6fe 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_pair.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_pair.kt @@ -13,15 +13,15 @@ package br.com.arch.toolkit.util * Example usage: * ``` * val pair: Pair = "Hello" to 42 - * val nonNullPair: Pair? = pair.toNotNull() + * val nonNullPair: Pair? = pair.onlyWithValues() * nonNullPair?.let { (first, second) -> * println("First: $first, Second: $second") // Output: First: Hello, Second: 42 * } * * val nullablePair: Pair = null to 42 - * val result = nullablePair.toNotNull() + * val result = nullablePair.onlyWithValues() * println(result) // Output: null * ``` */ -fun Pair.toNotNull(): Pair? = +fun Pair.onlyWithValues(): Pair? = runCatching { requireNotNull(first) to requireNotNull(second) }.getOrNull() diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseChain.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseChain.kt new file mode 100644 index 0000000..134f1a2 --- /dev/null +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseChain.kt @@ -0,0 +1,81 @@ +@file:Suppress("Filename", "TooManyFunctions", "LongParameterList", "unused") + +package br.com.arch.toolkit.util + +import androidx.lifecycle.LiveData +import br.com.arch.toolkit.livedata.ResponseLiveData +import br.com.arch.toolkit.livedata.responseLiveData +import br.com.arch.toolkit.result.DataResult +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.mapNotNull +import kotlin.coroutines.CoroutineContext + +/* LiveData + Response Functions ---------------------------------------------------------------- */ +/* Nullable ------------------------------------------------------------------------------------- */ +fun LiveData.chainWith( + context: CoroutineContext, + other: suspend (T?) -> ResponseLiveData, + condition: suspend (T?) -> Boolean, +) = responseLiveData(context = context) { + internalChainWith(other, condition) + .mapNotNull { (data, result) -> data?.let(::dataResultSuccess) + result } + .collect(::emit) +} + +fun LiveData.chainWith( + context: CoroutineContext, + other: suspend (T?) -> ResponseLiveData, + condition: suspend (T?) -> Boolean, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalChainWith(other, condition) + .mapNotNull { (data, result) -> data?.let(::dataResultSuccess) + result } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +fun LiveData.chainWith( + context: CoroutineContext, + other: suspend (T?) -> ResponseLiveData, + condition: suspend (T?) -> Boolean, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = chainWith(context, other, condition, Dispatchers.IO to transform) + +/* Non Nullable --------------------------------------------------------------------------------- */ + +fun LiveData.chainNotNullWith( + context: CoroutineContext, + other: suspend (T) -> ResponseLiveData, + condition: suspend (T) -> Boolean, +) = responseLiveData(context = context) { + internalChainNotNullWith(other, condition) + .mapNotNull { (data, result) -> (dataResultSuccess(data) + result).onlyWithValues() } + .collect(::emit) +} + +fun LiveData.chainNotNullWith( + context: CoroutineContext, + other: suspend (T) -> ResponseLiveData, + condition: suspend (T) -> Boolean, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalChainNotNullWith(other, condition) + .mapNotNull { (data, result) -> (dataResultSuccess(data) + result).onlyWithValues() } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +fun LiveData.chainNotNullWith( + context: CoroutineContext, + other: suspend (T) -> ResponseLiveData, + condition: suspend (T) -> Boolean, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = chainNotNullWith(context, other, condition, Dispatchers.IO to transform) diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt new file mode 100644 index 0000000..c785502 --- /dev/null +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt @@ -0,0 +1,79 @@ +@file:Suppress("Filename", "TooManyFunctions", "unused") + +package br.com.arch.toolkit.util + +import androidx.lifecycle.LiveData +import br.com.arch.toolkit.livedata.ResponseLiveData +import br.com.arch.toolkit.livedata.responseLiveData +import br.com.arch.toolkit.result.DataResult +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.mapNotNull +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext + +/* Operator Functions --------------------------------------------------------------------------- */ + +operator fun LiveData.plus(other: ResponseLiveData) = + combine(context = EmptyCoroutineContext, other = other) + +/* LiveData + Response Functions ---------------------------------------------------------------- */ +/* Nullable ------------------------------------------------------------------------------------- */ + +fun LiveData.combine( + context: CoroutineContext, + other: ResponseLiveData +) = responseLiveData(context = context) { + internalCombine(other).mapNotNull { (data, result) -> data?.let(::dataResultSuccess) + result } + .collect(::emit) +} + +fun LiveData.combine( + context: CoroutineContext, + other: ResponseLiveData, + transform: Pair>) -> DataResult> +) = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalCombine(other).mapNotNull { (data, result) -> data?.let(::dataResultSuccess) + result } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +fun LiveData.combine( + context: CoroutineContext, + other: ResponseLiveData, + transform: suspend (DataResult>) -> DataResult +) = combine(context, other, Dispatchers.IO to transform) + +/* Non Nullable --------------------------------------------------------------------------------- */ +fun LiveData.combineNotNull( + context: CoroutineContext, + other: ResponseLiveData +) = responseLiveData(context = context) { + internalCombineNotNull(other) + .mapNotNull { (data, result) -> (dataResultSuccess(data) + result).onlyWithValues() } + .collect(::emit) +} + +fun LiveData.combineNotNull( + context: CoroutineContext, + other: ResponseLiveData, + transform: Pair>) -> DataResult> +) = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalCombineNotNull(other) + .mapNotNull { (data, result) -> (dataResultSuccess(data) + result).onlyWithValues() } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +fun LiveData.combineNotNull( + context: CoroutineContext, + other: ResponseLiveData, + transform: suspend (DataResult>) -> DataResult +) = combineNotNull(context, other, Dispatchers.IO to transform) From cc7873bab162f042b8f2833cd0a9c9399fea963c Mon Sep 17 00:00:00 2001 From: Matheus Barbieri Corregiari Date: Mon, 19 Aug 2024 10:23:44 -0300 Subject: [PATCH 2/8] =?UTF-8?q?bumpando=20algumas=20vers=C3=B5es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 18 +++++------ .../main/kotlin/com/toolkit/plugin/_common.kt | 4 +-- .../toolkit/plugin/util/_projectExtensions.kt | 8 ++++- samples/github-list-project/build.gradle.kts | 2 +- samples/playground/build.gradle.kts | 2 +- settings.gradle.kts | 3 ++ tools/libs.versions.toml | 32 ++----------------- tools/plugin.versions.toml | 28 ++++++++++++++-- 8 files changed, 51 insertions(+), 46 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index d864102..3e04c55 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,14 +1,14 @@ plugins { id("jacoco") - alias(libraries.plugins.android.application) apply false - alias(libraries.plugins.android.library) apply false - alias(libraries.plugins.google.ksp) apply false - alias(libraries.plugins.jetbrains.kotlin) apply false - alias(libraries.plugins.jetbrains.multiplatform) apply false - alias(libraries.plugins.jetbrains.serialization) apply false - alias(libraries.plugins.dexcount) apply false - alias(libraries.plugins.detekt) apply false + alias(pluginLibraries.plugins.android.application) apply false + alias(pluginLibraries.plugins.android.library) apply false + alias(pluginLibraries.plugins.google.ksp) apply false + alias(pluginLibraries.plugins.jetbrains.kotlin) apply false + alias(pluginLibraries.plugins.jetbrains.multiplatform) apply false + alias(pluginLibraries.plugins.jetbrains.serialization) apply false + alias(pluginLibraries.plugins.dexcount) apply false + alias(pluginLibraries.plugins.detekt) apply false // TODO Pedrinho, help me -// alias(libraries.plugins.pedrinho_publish) apply false +// alias(pluginLibraries.plugins.pedrinho_publish) apply false } diff --git a/plugin/src/main/kotlin/com/toolkit/plugin/_common.kt b/plugin/src/main/kotlin/com/toolkit/plugin/_common.kt index bdf6368..812b9bb 100644 --- a/plugin/src/main/kotlin/com/toolkit/plugin/_common.kt +++ b/plugin/src/main/kotlin/com/toolkit/plugin/_common.kt @@ -3,7 +3,7 @@ package com.toolkit.plugin import com.android.build.api.dsl.CommonExtension import com.toolkit.plugin.util.projectJavaVersion -internal fun CommonExtension<*, *, *, *, *>.commonSetup() { +internal fun CommonExtension<*, *, *, *, *, *>.commonSetup() { androidResources { noCompress.add("") } compileOptions { @@ -18,7 +18,7 @@ internal fun CommonExtension<*, *, *, *, *>.commonSetup() { } } -internal fun CommonExtension<*, *, *, *, *>.regularSourceSets() { +internal fun CommonExtension<*, *, *, *, *, *>.regularSourceSets() { sourceSets { maybeCreate("main").java.srcDirs("src/main/kotlin") maybeCreate("test").java.srcDirs("src/test/kotlin") diff --git a/plugin/src/main/kotlin/com/toolkit/plugin/util/_projectExtensions.kt b/plugin/src/main/kotlin/com/toolkit/plugin/util/_projectExtensions.kt index 153f288..efa2ccc 100644 --- a/plugin/src/main/kotlin/com/toolkit/plugin/util/_projectExtensions.kt +++ b/plugin/src/main/kotlin/com/toolkit/plugin/util/_projectExtensions.kt @@ -24,6 +24,12 @@ internal val Project.libraries: VersionCatalog return extensions.findByType(VersionCatalogsExtension::class.java)?.named("libraries") ?: error("Cannot find libraries in version catalog!") } +internal val Project.pluginLibraries: VersionCatalog + @Throws(IllegalStateException::class) + get() { + return extensions.findByType(VersionCatalogsExtension::class.java)?.named("pluginLibraries") + ?: error("Cannot find libraries in version catalog!") + } internal val Project.ktLint: KtlintExtension @Throws(IllegalStateException::class) @@ -97,6 +103,6 @@ internal val Project.sign: SigningExtension internal fun Project.applyPlugins(vararg id: String) { id.forEach { - plugins.apply(libraries.findPlugin(it).get().get().pluginId) + plugins.apply(pluginLibraries.findPlugin(it).get().get().pluginId) } } diff --git a/samples/github-list-project/build.gradle.kts b/samples/github-list-project/build.gradle.kts index b2cac8e..75721e9 100644 --- a/samples/github-list-project/build.gradle.kts +++ b/samples/github-list-project/build.gradle.kts @@ -1,7 +1,7 @@ plugins { id("toolkit-android-sample") // id("toolkit-compose") --> Commented until Compose finally work on API 34 -.-" - alias(libraries.plugins.google.ksp) + alias(pluginLibraries.plugins.google.ksp) } android.namespace = "br.com.arch.toolkit.sample.github" diff --git a/samples/playground/build.gradle.kts b/samples/playground/build.gradle.kts index b8682a4..27a12ff 100644 --- a/samples/playground/build.gradle.kts +++ b/samples/playground/build.gradle.kts @@ -1,6 +1,6 @@ plugins { id("toolkit-android-sample") - alias(libraries.plugins.jetbrains.serialization) + alias(pluginLibraries.plugins.jetbrains.serialization) } android.namespace = "br.com.arch.toolkit.sample.livedata" diff --git a/settings.gradle.kts b/settings.gradle.kts index 46246a7..6f35ef4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -16,6 +16,9 @@ dependencyResolutionManagement { register("libraries") { from(files("$rootDir/tools/libs.versions.toml")) } + register("pluginLibraries") { + from(files("$rootDir/tools/plugin.versions.toml")) + } } } diff --git a/tools/libs.versions.toml b/tools/libs.versions.toml index 4cfd9d0..1de8e21 100644 --- a/tools/libs.versions.toml +++ b/tools/libs.versions.toml @@ -1,12 +1,8 @@ # ------------------------------------------------------------------------------------------------ # [versions] ## --- Plugins and more general stuff --- ## -android_plugin = "8.1.4" jacoco = "0.8.9" -dexcount = "4.0.0" -pedrinho_publish = "0.0.2" detekt = "1.23.3" -ktlint = "11.6.1" ## --- Build Config and SDK --- ## build-version-name = "1.0.0" @@ -18,7 +14,7 @@ build-sdk-target = "35" build-tools = "35.0.0" ## JetBrains -jetbrains-kotlin = "2.0.0" +jetbrains-kotlin = "2.0.10" jetbrains-coroutines = "1.8.1" jetbrains-serialization = "1.6.3" jetbrains-kover = "0.7.4" @@ -55,7 +51,6 @@ square-timber = "5.0.1" ## Google google-material = "1.12.0" google-gson = "2.11.0" -google-ksp = "2.0.0-1.0.22" ## Tools mockito = "5.6.0" @@ -125,8 +120,6 @@ junit-test = { group = "junit", name = "junit", version.ref = "junit" } robolectric-test = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } detekt-formatting = { group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version.ref = "detekt" } - - # ------------------------------------------------------------------------------------------------ # ## Comment this when you start to update some libraries x-normalize-001 = { group = "androidx.activity", name = "activity", version.ref = "androidx-compose-activity" } @@ -140,8 +133,8 @@ x-normalize-008 = { group = "androidx.customview", name = "customview", version x-normalize-009 = { group = "androidx.drawerlayout", name = "drawerlayout", version = "1.2.0" } x-normalize-010 = { group = "androidx.lifecycle", name = "lifecycle-common", version.ref = "androidx-lifecycle" } x-normalize-011 = { group = "androidx.viewpager2", name = "viewpager2", version = "1.1.0" } -x-normalize-012 = { group = "androidx.test", name = "monitor", version = "1.7.1" } -x-normalize-013 = { group = "androidx.test", name = "runner", version = "1.6.1" } +x-normalize-012 = { group = "androidx.test", name = "monitor", version = "1.7.2" } +x-normalize-013 = { group = "androidx.test", name = "runner", version = "1.6.2" } x-normalize-014 = { group = "com.google.auto.value", name = "auto-value-annotations", version = "1.9" } x-normalize-015 = { group = "com.google.code.findbugs", name = "jsr305", version = "3.0.2" } x-normalize-016 = { group = "com.squareup", name = "javawritter", version = "2.5.0" } @@ -153,25 +146,6 @@ x-normalize-020 = { group = "org.jetbrains", name = "annotations", version = "23 # ------------------------------------------------------------------------------------------------ # [plugins] -## Android -android-application = { id = "com.android.application", version.ref = "android_plugin" } -android-library = { id = "com.android.library", version.ref = "android_plugin" } - -## Android -google-ksp = { id = "com.google.devtools.ksp", version.ref = "google-ksp" } - -## JetBrains -jetbrains-kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "jetbrains-kotlin" } -jetbrains-kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "jetbrains-kover" } -jetbrains-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "jetbrains-kotlin" } -jetbrains-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "jetbrains-kotlin" } - -## Tools to make my life easier -dexcount = { id = "com.getkeepsafe.dexcount", version.ref = "dexcount" } -detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } -ktlint = { id = "org.jlleitschuh.gradle.ktlint-idea", version.ref = "ktlint" } -pedrinho_publish = { id = "io.github.pedro-bachiega.sonatype-maven-publish", version.ref = "pedrinho_publish" } - # ------------------------------------------------------------------------------------------------ # [bundles] diff --git a/tools/plugin.versions.toml b/tools/plugin.versions.toml index a8f92f7..63ac658 100644 --- a/tools/plugin.versions.toml +++ b/tools/plugin.versions.toml @@ -1,10 +1,13 @@ # ------------------------------------------------------------------------------------------------ # [versions] -android_plugin = "8.1.4" +android_plugin = "8.5.2" detekt = "1.23.3" ktlint = "11.6.1" -jetbrains-kotlin = "2.0.0" +jetbrains-kotlin = "2.0.10" jetbrains-kover = "0.7.4" +google-ksp = "2.0.10-1.0.24" +pedrinho_publish = "0.0.2" +dexcount = "4.0.0" # ------------------------------------------------------------------------------------------------ # [libraries] @@ -23,4 +26,23 @@ ktlint = { group = "org.jlleitschuh.gradle.ktlint-idea", name = "org.jlleitschuh # ------------------------------------------------------------------------------------------------ # [plugins] -jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrains-kotlin" } \ No newline at end of file +jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrains-kotlin" } + +## Android +android-application = { id = "com.android.application", version.ref = "android_plugin" } +android-library = { id = "com.android.library", version.ref = "android_plugin" } + +## Android +google-ksp = { id = "com.google.devtools.ksp", version.ref = "google-ksp" } + +## JetBrains +jetbrains-kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "jetbrains-kotlin" } +jetbrains-kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "jetbrains-kover" } +jetbrains-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "jetbrains-kotlin" } +jetbrains-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "jetbrains-kotlin" } + +## Tools to make my life easier +dexcount = { id = "com.getkeepsafe.dexcount", version.ref = "dexcount" } +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +ktlint = { id = "org.jlleitschuh.gradle.ktlint-idea", version.ref = "ktlint" } +pedrinho_publish = { id = "io.github.pedro-bachiega.sonatype-maven-publish", version.ref = "pedrinho_publish" } From 46a96a1049fc9315d0fa22a3fccc9c079c2dc5fb Mon Sep 17 00:00:00 2001 From: Matheus Barbieri Corregiari Date: Mon, 19 Aug 2024 10:23:56 -0300 Subject: [PATCH 3/8] =?UTF-8?q?bumpando=20algumas=20vers=C3=B5es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../toolkit/plugin/multiplatform/ToolkitBasePlugin.kt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/plugin/src/main/kotlin/com/toolkit/plugin/multiplatform/ToolkitBasePlugin.kt b/plugin/src/main/kotlin/com/toolkit/plugin/multiplatform/ToolkitBasePlugin.kt index d6cbfd6..be3047c 100644 --- a/plugin/src/main/kotlin/com/toolkit/plugin/multiplatform/ToolkitBasePlugin.kt +++ b/plugin/src/main/kotlin/com/toolkit/plugin/multiplatform/ToolkitBasePlugin.kt @@ -5,7 +5,6 @@ package com.toolkit.plugin.multiplatform import com.toolkit.plugin.util.applyPlugins import com.toolkit.plugin.util.multiplatform import com.toolkit.plugin.util.projectJavaVersionCode -import com.toolkit.plugin.util.projectJavaVersionName import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.Plugin @@ -22,13 +21,7 @@ internal class ToolkitBasePlugin : Plugin { } with(target.multiplatform) { - androidTarget { - compilations.all { compilation -> - compilation.kotlinOptions { - jvmTarget = projectJavaVersionName - } - } - } + androidTarget {} } target.plugins.apply("toolkit-optimize") From ae8e9d9ac93b9922c7468b138af2c8fe03d0bd3f Mon Sep 17 00:00:00 2001 From: Matheus Barbieri Corregiari Date: Mon, 19 Aug 2024 10:32:45 -0300 Subject: [PATCH 4/8] arrumando package --- .../sample/github/ui/xml/item/RepositoryItemView.kt | 2 +- .../list/withoutPagination/RepositoryListActivity.kt | 2 +- .../playground/recyclerAdapter/SimpleListActivity.kt | 2 +- .../recyclerAdapter/StickyHeadersActivity.kt | 6 +++--- .../adapter/MultipleViewTypesAdapter.kt | 4 ++-- .../recyclerAdapter/itemView/AnotherSimpleItemView.kt | 2 +- .../recyclerAdapter/itemView/SimpleItemView.kt | 2 +- .../recyclerAdapter/itemView/StickyItemView.kt | 2 +- toolkit/recycler-adapter/build.gradle.kts | 2 +- .../recycler/adapter/stickyheader/StickyHeaderModel.kt | 5 ----- .../adapter => recyclerAdapter}/BaseRecyclerAdapter.kt | 4 ++-- .../adapter => recyclerAdapter}/BaseViewHolder.kt | 2 +- .../adapter => recyclerAdapter}/DefaultItemDiffer.kt | 2 +- .../adapter => recyclerAdapter}/SimpleAdapter.kt | 4 ++-- .../adapter => recyclerAdapter}/ViewBinder.kt | 2 +- .../recyclerAdapter/stickyheader/StickyHeaderModel.kt | 5 +++++ .../stickyheader/StickyHeaders.kt | 2 +- .../stickyheader/StickyHeadersLinearLayoutManager.kt | 10 +++++----- 18 files changed, 30 insertions(+), 30 deletions(-) delete mode 100644 toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeaderModel.kt rename toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/{recycler/adapter => recyclerAdapter}/BaseRecyclerAdapter.kt (96%) rename toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/{recycler/adapter => recyclerAdapter}/BaseViewHolder.kt (78%) rename toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/{recycler/adapter => recyclerAdapter}/DefaultItemDiffer.kt (89%) rename toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/{recycler/adapter => recyclerAdapter}/SimpleAdapter.kt (91%) rename toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/{recycler/adapter => recyclerAdapter}/ViewBinder.kt (77%) create mode 100644 toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeaderModel.kt rename toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/{recycler/adapter => recyclerAdapter}/stickyheader/StickyHeaders.kt (92%) rename toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/{recycler/adapter => recyclerAdapter}/stickyheader/StickyHeadersLinearLayoutManager.kt (98%) diff --git a/samples/github-list-project/src/main/kotlin/br/com/arch/toolkit/sample/github/ui/xml/item/RepositoryItemView.kt b/samples/github-list-project/src/main/kotlin/br/com/arch/toolkit/sample/github/ui/xml/item/RepositoryItemView.kt index b521419..2f38518 100644 --- a/samples/github-list-project/src/main/kotlin/br/com/arch/toolkit/sample/github/ui/xml/item/RepositoryItemView.kt +++ b/samples/github-list-project/src/main/kotlin/br/com/arch/toolkit/sample/github/ui/xml/item/RepositoryItemView.kt @@ -2,7 +2,7 @@ package br.com.arch.toolkit.sample.github.ui.xml.item import android.content.Context import androidx.appcompat.widget.AppCompatTextView -import br.com.arch.toolkit.playground.recyclerAdapter.ViewBinder +import br.com.arch.toolkit.recyclerAdapter.ViewBinder import br.com.arch.toolkit.sample.github.data.remote.model.RepoDTO class RepositoryItemView(context: Context) : AppCompatTextView(context), ViewBinder { diff --git a/samples/github-list-project/src/main/kotlin/br/com/arch/toolkit/sample/github/ui/xml/list/withoutPagination/RepositoryListActivity.kt b/samples/github-list-project/src/main/kotlin/br/com/arch/toolkit/sample/github/ui/xml/list/withoutPagination/RepositoryListActivity.kt index 85384f7..60c77c2 100644 --- a/samples/github-list-project/src/main/kotlin/br/com/arch/toolkit/sample/github/ui/xml/list/withoutPagination/RepositoryListActivity.kt +++ b/samples/github-list-project/src/main/kotlin/br/com/arch/toolkit/sample/github/ui/xml/list/withoutPagination/RepositoryListActivity.kt @@ -7,7 +7,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.RecyclerView import br.com.arch.toolkit.delegate.viewModelProvider import br.com.arch.toolkit.delegate.viewProvider -import br.com.arch.toolkit.playground.recyclerAdapter.SimpleAdapter +import br.com.arch.toolkit.recyclerAdapter.SimpleAdapter import br.com.arch.toolkit.result.DataResultStatus import br.com.arch.toolkit.sample.github.R import br.com.arch.toolkit.sample.github.data.remote.model.RepoDTO diff --git a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/SimpleListActivity.kt b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/SimpleListActivity.kt index 5e29f6d..4c9a456 100644 --- a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/SimpleListActivity.kt +++ b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/SimpleListActivity.kt @@ -7,7 +7,7 @@ import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import br.com.arch.toolkit.delegate.viewProvider -import br.com.arch.toolkit.playground.recyclerAdapter.SimpleAdapter +import br.com.arch.toolkit.recyclerAdapter.SimpleAdapter import br.com.arch.toolkit.sample.playground.R import br.com.arch.toolkit.sample.playground.recyclerAdapter.itemView.SimpleItemView diff --git a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/StickyHeadersActivity.kt b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/StickyHeadersActivity.kt index e4449e6..8b8af0d 100644 --- a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/StickyHeadersActivity.kt +++ b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/StickyHeadersActivity.kt @@ -6,9 +6,9 @@ import android.widget.Toast import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.RecyclerView import br.com.arch.toolkit.delegate.viewProvider -import br.com.arch.toolkit.playground.recyclerAdapter.SimpleStickyAdapter -import br.com.arch.toolkit.playground.recyclerAdapter.stickyheader.StickyHeaderModel -import br.com.arch.toolkit.playground.recyclerAdapter.stickyheader.StickyHeadersLinearLayoutManager +import br.com.arch.toolkit.recyclerAdapter.SimpleStickyAdapter +import br.com.arch.toolkit.recyclerAdapter.stickyheader.StickyHeaderModel +import br.com.arch.toolkit.recyclerAdapter.stickyheader.StickyHeadersLinearLayoutManager import br.com.arch.toolkit.sample.playground.R import br.com.arch.toolkit.sample.playground.recyclerAdapter.itemView.StickyItemView diff --git a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/adapter/MultipleViewTypesAdapter.kt b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/adapter/MultipleViewTypesAdapter.kt index 0ca2b79..f1e1546 100644 --- a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/adapter/MultipleViewTypesAdapter.kt +++ b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/adapter/MultipleViewTypesAdapter.kt @@ -1,8 +1,8 @@ package br.com.arch.toolkit.sample.playground.recyclerAdapter.adapter import android.content.Context -import br.com.arch.toolkit.playground.recyclerAdapter.BaseRecyclerAdapter -import br.com.arch.toolkit.playground.recyclerAdapter.ViewBinder +import br.com.arch.toolkit.recyclerAdapter.BaseRecyclerAdapter +import br.com.arch.toolkit.recyclerAdapter.ViewBinder import br.com.arch.toolkit.sample.playground.recyclerAdapter.itemView.AnotherSimpleItemView import br.com.arch.toolkit.sample.playground.recyclerAdapter.itemView.SimpleItemView diff --git a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/AnotherSimpleItemView.kt b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/AnotherSimpleItemView.kt index a04041c..f5d109e 100644 --- a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/AnotherSimpleItemView.kt +++ b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/AnotherSimpleItemView.kt @@ -6,7 +6,7 @@ import android.view.ViewGroup import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import androidx.appcompat.widget.AppCompatTextView -import br.com.arch.toolkit.playground.recyclerAdapter.ViewBinder +import br.com.arch.toolkit.recyclerAdapter.ViewBinder import br.com.arch.toolkit.sample.playground.R class AnotherSimpleItemView : AppCompatTextView, ViewBinder { diff --git a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/SimpleItemView.kt b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/SimpleItemView.kt index 229a600..eaaad7b 100644 --- a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/SimpleItemView.kt +++ b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/SimpleItemView.kt @@ -6,7 +6,7 @@ import android.view.ViewGroup import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import androidx.appcompat.widget.AppCompatTextView -import br.com.arch.toolkit.playground.recyclerAdapter.ViewBinder +import br.com.arch.toolkit.recyclerAdapter.ViewBinder import br.com.arch.toolkit.sample.playground.R class SimpleItemView : AppCompatTextView, ViewBinder { diff --git a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/StickyItemView.kt b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/StickyItemView.kt index 3b3ae2d..975d74e 100644 --- a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/StickyItemView.kt +++ b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/StickyItemView.kt @@ -4,7 +4,7 @@ import android.content.Context import android.util.AttributeSet import android.view.ViewGroup import androidx.appcompat.widget.AppCompatTextView -import br.com.arch.toolkit.playground.recyclerAdapter.ViewBinder +import br.com.arch.toolkit.recyclerAdapter.ViewBinder import br.com.arch.toolkit.sample.playground.R import br.com.arch.toolkit.sample.playground.recyclerAdapter.StickyHeadersActivity diff --git a/toolkit/recycler-adapter/build.gradle.kts b/toolkit/recycler-adapter/build.gradle.kts index 1dbb6fa..480de8e 100644 --- a/toolkit/recycler-adapter/build.gradle.kts +++ b/toolkit/recycler-adapter/build.gradle.kts @@ -3,7 +3,7 @@ plugins { id("toolkit-publish") } -android.namespace = "br.com.arch.toolkit.playground.recyclerAdapter" +android.namespace = "br.com.arch.toolkit.recyclerAdapter" dependencies { // Libraries diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeaderModel.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeaderModel.kt deleted file mode 100644 index a762f29..0000000 --- a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeaderModel.kt +++ /dev/null @@ -1,5 +0,0 @@ -package br.com.arch.toolkit.playground.recyclerAdapter.stickyheader - -interface StickyHeaderModel { - var isSticky: Boolean -} diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/BaseRecyclerAdapter.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/BaseRecyclerAdapter.kt similarity index 96% rename from toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/BaseRecyclerAdapter.kt rename to toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/BaseRecyclerAdapter.kt index 76e5c22..9a99696 100644 --- a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/BaseRecyclerAdapter.kt +++ b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/BaseRecyclerAdapter.kt @@ -1,4 +1,4 @@ -package br.com.arch.toolkit.playground.recyclerAdapter +package br.com.arch.toolkit.recyclerAdapter import android.content.Context import android.view.View @@ -6,7 +6,7 @@ import android.view.ViewGroup import androidx.recyclerview.widget.AsyncListDiffer import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView -import br.com.arch.toolkit.playground.recyclerAdapter.stickyheader.StickyHeaders +import br.com.arch.toolkit.recyclerAdapter.stickyheader.StickyHeaders /** * Basic implementation of RecyclerView.Adapter using AsyncListDiffer and CustomViews as items diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/BaseViewHolder.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/BaseViewHolder.kt similarity index 78% rename from toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/BaseViewHolder.kt rename to toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/BaseViewHolder.kt index 95b8b50..ddaf35d 100644 --- a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/BaseViewHolder.kt +++ b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/BaseViewHolder.kt @@ -1,4 +1,4 @@ -package br.com.arch.toolkit.playground.recyclerAdapter +package br.com.arch.toolkit.recyclerAdapter import android.view.View import androidx.recyclerview.widget.RecyclerView diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/DefaultItemDiffer.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/DefaultItemDiffer.kt similarity index 89% rename from toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/DefaultItemDiffer.kt rename to toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/DefaultItemDiffer.kt index 15a832f..216097a 100644 --- a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/DefaultItemDiffer.kt +++ b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/DefaultItemDiffer.kt @@ -1,4 +1,4 @@ -package br.com.arch.toolkit.playground.recyclerAdapter +package br.com.arch.toolkit.recyclerAdapter import android.annotation.SuppressLint import androidx.recyclerview.widget.DiffUtil diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/SimpleAdapter.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/SimpleAdapter.kt similarity index 91% rename from toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/SimpleAdapter.kt rename to toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/SimpleAdapter.kt index 86a7467..e54210b 100644 --- a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/SimpleAdapter.kt +++ b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/SimpleAdapter.kt @@ -1,8 +1,8 @@ -package br.com.arch.toolkit.playground.recyclerAdapter +package br.com.arch.toolkit.recyclerAdapter import android.content.Context import android.view.View -import br.com.arch.toolkit.playground.recyclerAdapter.stickyheader.StickyHeaderModel +import br.com.arch.toolkit.recyclerAdapter.stickyheader.StickyHeaderModel /** * Implementation of BaseRecyclerAdapter representing a single viewType diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/ViewBinder.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/ViewBinder.kt similarity index 77% rename from toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/ViewBinder.kt rename to toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/ViewBinder.kt index 295aa19..7d026e6 100644 --- a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/ViewBinder.kt +++ b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/ViewBinder.kt @@ -1,4 +1,4 @@ -package br.com.arch.toolkit.playground.recyclerAdapter +package br.com.arch.toolkit.recyclerAdapter /** * Implement this on your CustomView classes to start using your BaseRecyclerAdapter implementations like a charm =) diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeaderModel.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeaderModel.kt new file mode 100644 index 0000000..8575255 --- /dev/null +++ b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeaderModel.kt @@ -0,0 +1,5 @@ +package br.com.arch.toolkit.recyclerAdapter.stickyheader + +interface StickyHeaderModel { + var isSticky: Boolean +} diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeaders.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeaders.kt similarity index 92% rename from toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeaders.kt rename to toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeaders.kt index 57c23df..f11e140 100644 --- a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeaders.kt +++ b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeaders.kt @@ -1,4 +1,4 @@ -package br.com.arch.toolkit.playground.recyclerAdapter.stickyheader +package br.com.arch.toolkit.recyclerAdapter.stickyheader import android.view.View diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeadersLinearLayoutManager.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeadersLinearLayoutManager.kt similarity index 98% rename from toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeadersLinearLayoutManager.kt rename to toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeadersLinearLayoutManager.kt index 8024edb..556d4f9 100644 --- a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeadersLinearLayoutManager.kt +++ b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeadersLinearLayoutManager.kt @@ -1,4 +1,4 @@ -package br.com.arch.toolkit.playground.recyclerAdapter.stickyheader +package br.com.arch.toolkit.recyclerAdapter.stickyheader import android.content.Context import android.view.View @@ -250,10 +250,10 @@ class StickyHeadersLinearLayoutManager : // - There's one to show; // - It's on the edge or it's not the anchor view; // - Isn't followed by another sticky header; - if (headerPos != -1 && - (headerPos != anchorPos || isViewOnBoundary(anchorView)) && - nextHeaderPos != headerPos + 1 - ) { + val oneToShow = headerPos != -1 + val onEdgeAndNotAnchor = (headerPos != anchorPos || isViewOnBoundary(anchorView)) + val notFollowedByNext = nextHeaderPos != headerPos + 1 + if (oneToShow && onEdgeAndNotAnchor && notFollowedByNext) { // Ensure existing sticky header, if any, is of correct type. if (mStickyHeader != null && mAdapter != null && From 103e78b923f7b1bcbe1ec05d9048fb27b4b64805 Mon Sep 17 00:00:00 2001 From: Matheus Barbieri Corregiari Date: Mon, 19 Aug 2024 11:22:28 -0300 Subject: [PATCH 5/8] move combine to proper file --- .../arch/toolkit/livedata/ResponseLiveData.kt | 157 -------------- .../com/arch/toolkit/util/_responseCombine.kt | 192 ++++++++++++++++-- .../livedata/response/ResponseLiveDataTest.kt | 1 + 3 files changed, 178 insertions(+), 172 deletions(-) diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/livedata/ResponseLiveData.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/livedata/ResponseLiveData.kt index a754dc5..d8c1ef9 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/livedata/ResponseLiveData.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/livedata/ResponseLiveData.kt @@ -22,8 +22,6 @@ import br.com.arch.toolkit.util.dataResultError import br.com.arch.toolkit.util.dataResultSuccess import br.com.arch.toolkit.util.internalChainNotNullWith import br.com.arch.toolkit.util.internalChainWith -import br.com.arch.toolkit.util.internalCombine -import br.com.arch.toolkit.util.internalCombineNotNull import br.com.arch.toolkit.util.mapNotNull import br.com.arch.toolkit.util.onlyWithValues import br.com.arch.toolkit.util.plus @@ -434,161 +432,6 @@ open class ResponseLiveData : LiveData> { ) = chainNotNullWith(context, other, condition, Dispatchers.IO to transform) //endregion - //region Combine - /* Response + LiveData Functions ---------------------------------------------------------------- */ - /* Nullable ------------------------------------------------------------------------------------- */ - - @NonNull - @Experimental - fun combine( - context: CoroutineContext = EmptyCoroutineContext, - other: LiveData - ) = responseLiveData(context = context) { - internalCombine(other).mapNotNull { (result, data) -> result + data?.let(::dataResultSuccess) } - .collect(::emit) - } - - @NonNull - @Experimental - fun combine( - context: CoroutineContext = EmptyCoroutineContext, - other: LiveData, - transform: Pair>) -> DataResult> - ) = responseLiveData(context = context) { - val (dispatcher, block) = transform - internalCombine(other) - .mapNotNull { (result, data) -> result + data?.let(::dataResultSuccess) } - .flowOn(dispatcher) - .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } - .flowOn(context) - .collect(::emit) - } - - @NonNull - @Experimental - fun combine( - context: CoroutineContext = EmptyCoroutineContext, - other: LiveData, - transform: suspend (DataResult>) -> DataResult - ) = combine(context, other, Dispatchers.IO to transform) - - /* Non Nullable --------------------------------------------------------------------------------- */ - @NonNull - @Experimental - fun combineNotNull( - context: CoroutineContext = EmptyCoroutineContext, - other: LiveData - ) = responseLiveData(context = context) { - internalCombineNotNull(other) - .mapNotNull { (result, data) -> (result + dataResultSuccess(data)).onlyWithValues() } - .collect(::emit) - } - - @NonNull - @Experimental - fun combineNotNull( - context: CoroutineContext = EmptyCoroutineContext, - other: LiveData, - transform: Pair>) -> DataResult> - ) = responseLiveData(context = context) { - val (dispatcher, block) = transform - internalCombineNotNull(other) - .mapNotNull { (result, data) -> (result + dataResultSuccess(data)).onlyWithValues() } - .flowOn(dispatcher) - .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } - .flowOn(context) - .collect(::emit) - } - - @NonNull - @Experimental - fun combineNotNull( - context: CoroutineContext = EmptyCoroutineContext, - other: LiveData, - transform: suspend (DataResult>) -> DataResult - ) = combineNotNull(context, other, Dispatchers.IO to transform) - - /* Response + Response Functions ---------------------------------------------------------------- */ - /* Nullable ------------------------------------------------------------------------------------- */ - - @NonNull - @Experimental - fun combine( - context: CoroutineContext = EmptyCoroutineContext, - other: ResponseLiveData - ) = responseLiveData(context = context) { - internalCombine(other).mapNotNull { (resultA, resultB) -> resultA + resultB } - .collect(::emit) - } - - @NonNull - @Experimental - fun combine( - context: CoroutineContext = EmptyCoroutineContext, - other: ResponseLiveData, - transform: Pair>) -> DataResult> - ) = responseLiveData(context = context) { - val (dispatcher, block) = transform - internalCombine(other).mapNotNull { (resultA, resultB) -> resultA + resultB } - .flowOn(dispatcher) - .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } - .flowOn(context) - .collect(::emit) - } - - @NonNull - @Experimental - fun combine( - context: CoroutineContext = EmptyCoroutineContext, - other: ResponseLiveData, - transform: suspend (DataResult>) -> DataResult - ) = combine(context, other, Dispatchers.IO to transform) - - /* Non Nullable --------------------------------------------------------------------------------- */ - @NonNull - @Experimental - fun combineNotNull( - context: CoroutineContext = EmptyCoroutineContext, - other: ResponseLiveData - ) = responseLiveData(context = context) { - internalCombineNotNull(other) - .mapNotNull { (resultA, resultB) -> (resultA + resultB).onlyWithValues() } - .collect(::emit) - } - - @NonNull - @Experimental - fun combineNotNull( - context: CoroutineContext = EmptyCoroutineContext, - other: ResponseLiveData, - transform: Pair>) -> DataResult> - ) = responseLiveData(context = context) { - val (dispatcher, block) = transform - internalCombineNotNull(other) - .mapNotNull { (resultA, resultB) -> (resultA + resultB).onlyWithValues() } - .flowOn(dispatcher) - .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } - .flowOn(context) - .collect(::emit) - } - - @NonNull - @Experimental - fun combineNotNull( - context: CoroutineContext = EmptyCoroutineContext, - other: ResponseLiveData, - transform: suspend (DataResult>) -> DataResult - ) = combineNotNull(context, other, Dispatchers.IO to transform) - //endregion - - //region Operators - @Experimental - operator fun plus(source: ResponseLiveData) = mergeWith(source) - - @Experimental - operator fun plus(source: LiveData) = combine(other = source) - //endregion - //region Observability /** * Execute the function onNext before any observe set after this method be called diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt index c785502..1fb1c6b 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt @@ -2,7 +2,9 @@ package br.com.arch.toolkit.util +import androidx.annotation.NonNull import androidx.lifecycle.LiveData +import br.com.arch.toolkit.annotation.Experimental import br.com.arch.toolkit.livedata.ResponseLiveData import br.com.arch.toolkit.livedata.responseLiveData import br.com.arch.toolkit.result.DataResult @@ -13,27 +15,38 @@ import kotlinx.coroutines.flow.mapNotNull import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -/* Operator Functions --------------------------------------------------------------------------- */ +/* region Operator Functions -------------------------------------------------------------------- */ -operator fun LiveData.plus(other: ResponseLiveData) = +@Experimental +operator fun LiveData.plus(other: ResponseLiveData): ResponseLiveData> = combine(context = EmptyCoroutineContext, other = other) -/* LiveData + Response Functions ---------------------------------------------------------------- */ +@Experimental +operator fun ResponseLiveData.plus(source: LiveData): ResponseLiveData> = + combine(context = EmptyCoroutineContext, other = source) + +@Experimental +operator fun ResponseLiveData.plus(source: ResponseLiveData): ResponseLiveData> = + combine(context = EmptyCoroutineContext, other = source) + +/* endregion ------------------------------------------------------------------------------------ */ + +/* region LiveData + Response Functions --------------------------------------------------------- */ /* Nullable ------------------------------------------------------------------------------------- */ fun LiveData.combine( - context: CoroutineContext, + context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData -) = responseLiveData(context = context) { +): ResponseLiveData> = responseLiveData(context = context) { internalCombine(other).mapNotNull { (data, result) -> data?.let(::dataResultSuccess) + result } .collect(::emit) } fun LiveData.combine( - context: CoroutineContext, + context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData, transform: Pair>) -> DataResult> -) = responseLiveData(context = context) { +): ResponseLiveData = responseLiveData(context = context) { val (dispatcher, block) = transform internalCombine(other).mapNotNull { (data, result) -> data?.let(::dataResultSuccess) + result } .flowOn(dispatcher) @@ -43,26 +56,26 @@ fun LiveData.combine( } fun LiveData.combine( - context: CoroutineContext, + context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData, transform: suspend (DataResult>) -> DataResult -) = combine(context, other, Dispatchers.IO to transform) +): ResponseLiveData = combine(context, other, Dispatchers.IO to transform) /* Non Nullable --------------------------------------------------------------------------------- */ fun LiveData.combineNotNull( - context: CoroutineContext, + context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData -) = responseLiveData(context = context) { +): ResponseLiveData> = responseLiveData(context = context) { internalCombineNotNull(other) .mapNotNull { (data, result) -> (dataResultSuccess(data) + result).onlyWithValues() } .collect(::emit) } fun LiveData.combineNotNull( - context: CoroutineContext, + context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData, transform: Pair>) -> DataResult> -) = responseLiveData(context = context) { +): ResponseLiveData = responseLiveData(context = context) { val (dispatcher, block) = transform internalCombineNotNull(other) .mapNotNull { (data, result) -> (dataResultSuccess(data) + result).onlyWithValues() } @@ -73,7 +86,156 @@ fun LiveData.combineNotNull( } fun LiveData.combineNotNull( - context: CoroutineContext, + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = combineNotNull(context, other, Dispatchers.IO to transform) +/* endregion ------------------------------------------------------------------------------------ */ + +/* region Response + LiveData Functions ---------------------------------------------------------------- */ +/* Nullable ------------------------------------------------------------------------------------- */ + +@NonNull +@Experimental +fun ResponseLiveData.combine( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData +): ResponseLiveData> = responseLiveData(context = context) { + internalCombine(other).mapNotNull { (result, data) -> result + data?.let(::dataResultSuccess) } + .collect(::emit) +} + +@NonNull +@Experimental +fun ResponseLiveData.combine( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalCombine(other) + .mapNotNull { (result, data) -> result + data?.let(::dataResultSuccess) } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@NonNull +@Experimental +fun ResponseLiveData.combine( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = combine(context, other, Dispatchers.IO to transform) + +/* Non Nullable --------------------------------------------------------------------------------- */ +@NonNull +@Experimental +fun ResponseLiveData.combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData +): ResponseLiveData> = responseLiveData(context = context) { + internalCombineNotNull(other) + .mapNotNull { (result, data) -> (result + dataResultSuccess(data)).onlyWithValues() } + .collect(::emit) +} + +@NonNull +@Experimental +fun ResponseLiveData.combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalCombineNotNull(other) + .mapNotNull { (result, data) -> (result + dataResultSuccess(data)).onlyWithValues() } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@NonNull +@Experimental +fun ResponseLiveData.combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = combineNotNull(context, other, Dispatchers.IO to transform) +/* endregion ------------------------------------------------------------------------------------ */ + +/* region Response + Response Functions --------------------------------------------------------- */ +/* Nullable ------------------------------------------------------------------------------------- */ + +@NonNull +@Experimental +fun ResponseLiveData.combine( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData +): ResponseLiveData> = responseLiveData(context = context) { + internalCombine(other).mapNotNull { (resultA, resultB) -> resultA + resultB } + .collect(::emit) +} + +@NonNull +@Experimental +fun ResponseLiveData.combine( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalCombine(other).mapNotNull { (resultA, resultB) -> resultA + resultB } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@NonNull +@Experimental +fun ResponseLiveData.combine( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = combine(context, other, Dispatchers.IO to transform) + +/* Non Nullable --------------------------------------------------------------------------------- */ +@NonNull +@Experimental +fun ResponseLiveData.combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData +): ResponseLiveData> = responseLiveData(context = context) { + internalCombineNotNull(other) + .mapNotNull { (resultA, resultB) -> (resultA + resultB).onlyWithValues() } + .collect(::emit) +} + +@NonNull +@Experimental +fun ResponseLiveData.combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalCombineNotNull(other) + .mapNotNull { (resultA, resultB) -> (resultA + resultB).onlyWithValues() } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@NonNull +@Experimental +fun ResponseLiveData.combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData, transform: suspend (DataResult>) -> DataResult -) = combineNotNull(context, other, Dispatchers.IO to transform) +): ResponseLiveData = combineNotNull(context, other, Dispatchers.IO to transform) + +/* endregion ------------------------------------------------------------------------------------ */ diff --git a/toolkit/event-observer/src/test/kotlin/br/com/arch/toolkit/livedata/response/ResponseLiveDataTest.kt b/toolkit/event-observer/src/test/kotlin/br/com/arch/toolkit/livedata/response/ResponseLiveDataTest.kt index 5bf4e43..c5527b7 100644 --- a/toolkit/event-observer/src/test/kotlin/br/com/arch/toolkit/livedata/response/ResponseLiveDataTest.kt +++ b/toolkit/event-observer/src/test/kotlin/br/com/arch/toolkit/livedata/response/ResponseLiveDataTest.kt @@ -9,6 +9,7 @@ import br.com.arch.toolkit.testSetValue import br.com.arch.toolkit.util.dataResultError import br.com.arch.toolkit.util.dataResultLoading import br.com.arch.toolkit.util.dataResultSuccess +import br.com.arch.toolkit.util.plus import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher From aa8559401250913fe8abcbe92fb4edd3dc7ef859 Mon Sep 17 00:00:00 2001 From: Matheus Barbieri Corregiari Date: Mon, 19 Aug 2024 11:22:56 -0300 Subject: [PATCH 6/8] move combine to proper file --- .../com/arch/toolkit/util/_responseCombine.kt | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt index 1fb1c6b..75e1bec 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt @@ -2,7 +2,6 @@ package br.com.arch.toolkit.util -import androidx.annotation.NonNull import androidx.lifecycle.LiveData import br.com.arch.toolkit.annotation.Experimental import br.com.arch.toolkit.livedata.ResponseLiveData @@ -16,7 +15,6 @@ import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext /* region Operator Functions -------------------------------------------------------------------- */ - @Experimental operator fun LiveData.plus(other: ResponseLiveData): ResponseLiveData> = combine(context = EmptyCoroutineContext, other = other) @@ -28,12 +26,11 @@ operator fun ResponseLiveData.plus(source: LiveData): ResponseLiveD @Experimental operator fun ResponseLiveData.plus(source: ResponseLiveData): ResponseLiveData> = combine(context = EmptyCoroutineContext, other = source) - /* endregion ------------------------------------------------------------------------------------ */ /* region LiveData + Response Functions --------------------------------------------------------- */ /* Nullable ------------------------------------------------------------------------------------- */ - +@Experimental fun LiveData.combine( context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData @@ -42,6 +39,7 @@ fun LiveData.combine( .collect(::emit) } +@Experimental fun LiveData.combine( context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData, @@ -55,6 +53,7 @@ fun LiveData.combine( .collect(::emit) } +@Experimental fun LiveData.combine( context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData, @@ -62,6 +61,7 @@ fun LiveData.combine( ): ResponseLiveData = combine(context, other, Dispatchers.IO to transform) /* Non Nullable --------------------------------------------------------------------------------- */ +@Experimental fun LiveData.combineNotNull( context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData @@ -71,6 +71,7 @@ fun LiveData.combineNotNull( .collect(::emit) } +@Experimental fun LiveData.combineNotNull( context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData, @@ -85,6 +86,7 @@ fun LiveData.combineNotNull( .collect(::emit) } +@Experimental fun LiveData.combineNotNull( context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData, @@ -94,8 +96,6 @@ fun LiveData.combineNotNull( /* region Response + LiveData Functions ---------------------------------------------------------------- */ /* Nullable ------------------------------------------------------------------------------------- */ - -@NonNull @Experimental fun ResponseLiveData.combine( context: CoroutineContext = EmptyCoroutineContext, @@ -105,7 +105,6 @@ fun ResponseLiveData.combine( .collect(::emit) } -@NonNull @Experimental fun ResponseLiveData.combine( context: CoroutineContext = EmptyCoroutineContext, @@ -121,7 +120,6 @@ fun ResponseLiveData.combine( .collect(::emit) } -@NonNull @Experimental fun ResponseLiveData.combine( context: CoroutineContext = EmptyCoroutineContext, @@ -130,7 +128,6 @@ fun ResponseLiveData.combine( ): ResponseLiveData = combine(context, other, Dispatchers.IO to transform) /* Non Nullable --------------------------------------------------------------------------------- */ -@NonNull @Experimental fun ResponseLiveData.combineNotNull( context: CoroutineContext = EmptyCoroutineContext, @@ -141,7 +138,6 @@ fun ResponseLiveData.combineNotNull( .collect(::emit) } -@NonNull @Experimental fun ResponseLiveData.combineNotNull( context: CoroutineContext = EmptyCoroutineContext, @@ -157,7 +153,6 @@ fun ResponseLiveData.combineNotNull( .collect(::emit) } -@NonNull @Experimental fun ResponseLiveData.combineNotNull( context: CoroutineContext = EmptyCoroutineContext, @@ -168,8 +163,6 @@ fun ResponseLiveData.combineNotNull( /* region Response + Response Functions --------------------------------------------------------- */ /* Nullable ------------------------------------------------------------------------------------- */ - -@NonNull @Experimental fun ResponseLiveData.combine( context: CoroutineContext = EmptyCoroutineContext, @@ -179,7 +172,6 @@ fun ResponseLiveData.combine( .collect(::emit) } -@NonNull @Experimental fun ResponseLiveData.combine( context: CoroutineContext = EmptyCoroutineContext, @@ -194,7 +186,6 @@ fun ResponseLiveData.combine( .collect(::emit) } -@NonNull @Experimental fun ResponseLiveData.combine( context: CoroutineContext = EmptyCoroutineContext, @@ -203,7 +194,6 @@ fun ResponseLiveData.combine( ): ResponseLiveData = combine(context, other, Dispatchers.IO to transform) /* Non Nullable --------------------------------------------------------------------------------- */ -@NonNull @Experimental fun ResponseLiveData.combineNotNull( context: CoroutineContext = EmptyCoroutineContext, @@ -214,7 +204,6 @@ fun ResponseLiveData.combineNotNull( .collect(::emit) } -@NonNull @Experimental fun ResponseLiveData.combineNotNull( context: CoroutineContext = EmptyCoroutineContext, @@ -230,12 +219,10 @@ fun ResponseLiveData.combineNotNull( .collect(::emit) } -@NonNull @Experimental fun ResponseLiveData.combineNotNull( context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData, transform: suspend (DataResult>) -> DataResult ): ResponseLiveData = combineNotNull(context, other, Dispatchers.IO to transform) - /* endregion ------------------------------------------------------------------------------------ */ From 6af784a267ca2b0c9c7d5bfe3f24bba677aafa79 Mon Sep 17 00:00:00 2001 From: Matheus Barbieri Corregiari Date: Mon, 19 Aug 2024 11:25:37 -0300 Subject: [PATCH 7/8] fix test --- .../com/arch/toolkit/livedata/response/ResponseLiveDataTest.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/toolkit/event-observer/src/test/kotlin/br/com/arch/toolkit/livedata/response/ResponseLiveDataTest.kt b/toolkit/event-observer/src/test/kotlin/br/com/arch/toolkit/livedata/response/ResponseLiveDataTest.kt index c5527b7..44a5d35 100644 --- a/toolkit/event-observer/src/test/kotlin/br/com/arch/toolkit/livedata/response/ResponseLiveDataTest.kt +++ b/toolkit/event-observer/src/test/kotlin/br/com/arch/toolkit/livedata/response/ResponseLiveDataTest.kt @@ -9,7 +9,6 @@ import br.com.arch.toolkit.testSetValue import br.com.arch.toolkit.util.dataResultError import br.com.arch.toolkit.util.dataResultLoading import br.com.arch.toolkit.util.dataResultSuccess -import br.com.arch.toolkit.util.plus import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher @@ -286,7 +285,7 @@ class ResponseLiveDataTest { fun `09 - mergeWith - plus`() = runTest { val liveDataA = ResponseLiveData(dataResultSuccess(123)) val liveDataB = ResponseLiveData(dataResultSuccess("String")) - val liveDataMerge = liveDataA + liveDataB + val liveDataMerge = liveDataA.mergeWith(liveDataB) Assert.assertEquals(dataResultSuccess(123), liveDataA.value) Assert.assertEquals(dataResultSuccess("String"), liveDataB.value) From b6dde3e582834fffa380ce9d92196b2051e31c91 Mon Sep 17 00:00:00 2001 From: Matheus Barbieri Corregiari Date: Mon, 19 Aug 2024 16:34:57 -0300 Subject: [PATCH 8/8] chain num arquivo separado --- .../arch/toolkit/livedata/ResponseLiveData.kt | 172 ------------ .../kotlin/br/com/arch/toolkit/util/_chain.kt | 69 ++++- .../br/com/arch/toolkit/util/_combine.kt | 46 +++- .../com/arch/toolkit/util/_responseChain.kt | 248 ++++++++++++++++-- .../com/arch/toolkit/util/_responseCombine.kt | 36 ++- 5 files changed, 349 insertions(+), 222 deletions(-) diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/livedata/ResponseLiveData.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/livedata/ResponseLiveData.kt index d8c1ef9..d391d41 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/livedata/ResponseLiveData.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/livedata/ResponseLiveData.kt @@ -18,21 +18,11 @@ import br.com.arch.toolkit.annotation.Experimental import br.com.arch.toolkit.result.DataResult import br.com.arch.toolkit.result.DataResultStatus import br.com.arch.toolkit.result.ObserveWrapper -import br.com.arch.toolkit.util.dataResultError -import br.com.arch.toolkit.util.dataResultSuccess -import br.com.arch.toolkit.util.internalChainNotNullWith -import br.com.arch.toolkit.util.internalChainWith import br.com.arch.toolkit.util.mapNotNull -import br.com.arch.toolkit.util.onlyWithValues -import br.com.arch.toolkit.util.plus import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.mapNotNull -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.EmptyCoroutineContext /** * Custom implementation of LiveData made to help the data handling with needs the interpretation of: @@ -270,168 +260,6 @@ open class ResponseLiveData : LiveData> { fun followedBy(@NonNull source: (T) -> ResponseLiveData) = followedBy(source) { true } //endregion - //region Chain With - /* Response + LiveData Functions ---------------------------------------------------------------- */ - /* Nullable ------------------------------------------------------------------------------------- */ - @NonNull - @Experimental - fun regularChainWith( - context: CoroutineContext = EmptyCoroutineContext, - other: suspend (DataResult?) -> LiveData, - condition: suspend (DataResult?) -> Boolean, - ) = responseLiveData(context = context) { - internalChainWith(other, condition) - .mapNotNull { (result, data) -> result + data?.let(::dataResultSuccess) } - .collect(::emit) - } - - @NonNull - @Experimental - fun regularChainWith( - context: CoroutineContext = EmptyCoroutineContext, - other: suspend (DataResult?) -> LiveData, - condition: suspend (DataResult?) -> Boolean, - transform: Pair>) -> DataResult> - ) = responseLiveData(context = context) { - val (dispatcher, block) = transform - internalChainWith(other, condition) - .mapNotNull { (result, data) -> result + data?.let(::dataResultSuccess) } - .flowOn(dispatcher) - .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } - .flowOn(context) - .collect(::emit) - } - - @NonNull - @Experimental - fun regularChainWith( - context: CoroutineContext = EmptyCoroutineContext, - other: suspend (DataResult?) -> LiveData, - condition: suspend (DataResult?) -> Boolean, - transform: suspend (DataResult>) -> DataResult - ) = regularChainWith(context, other, condition, Dispatchers.IO to transform) - - /* Non Nullable --------------------------------------------------------------------------------- */ - - @NonNull - @Experimental - fun regularChainNotNullWith( - context: CoroutineContext = EmptyCoroutineContext, - other: suspend (DataResult) -> LiveData, - condition: suspend (DataResult) -> Boolean, - ) = responseLiveData(context = context) { - internalChainNotNullWith(other, condition) - .mapNotNull { (result, data) -> (result + dataResultSuccess(data)).onlyWithValues() } - .collect(::emit) - } - - @NonNull - @Experimental - fun regularChainNotNullWith( - context: CoroutineContext = EmptyCoroutineContext, - other: suspend (DataResult) -> LiveData, - condition: suspend (DataResult) -> Boolean, - transform: Pair>) -> DataResult> - ) = responseLiveData(context = context) { - val (dispatcher, block) = transform - internalChainNotNullWith(other, condition) - .mapNotNull { (result, data) -> (result + dataResultSuccess(data)).onlyWithValues() } - .flowOn(dispatcher) - .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } - .flowOn(context) - .collect(::emit) - } - - @NonNull - @Experimental - fun regularChainNotNullWith( - context: CoroutineContext = EmptyCoroutineContext, - other: suspend (DataResult) -> LiveData, - condition: suspend (DataResult) -> Boolean, - transform: suspend (DataResult>) -> DataResult - ) = regularChainNotNullWith(context, other, condition, Dispatchers.IO to transform) - - /* Response + Response Functions ---------------------------------------------------------------- */ - /* Nullable ------------------------------------------------------------------------------------- */ - @NonNull - @Experimental - fun chainWith( - context: CoroutineContext = EmptyCoroutineContext, - other: suspend (DataResult?) -> ResponseLiveData, - condition: suspend (DataResult?) -> Boolean, - ) = responseLiveData(context = context) { - internalChainWith(other, condition) - .mapNotNull { (resultA, resultB) -> resultA + resultB } - .collect(::emit) - } - - @NonNull - @Experimental - fun chainWith( - context: CoroutineContext = EmptyCoroutineContext, - other: suspend (DataResult?) -> ResponseLiveData, - condition: suspend (DataResult?) -> Boolean, - transform: Pair>) -> DataResult> - ) = responseLiveData(context = context) { - val (dispatcher, block) = transform - internalChainWith(other, condition) - .mapNotNull { (resultA, resultB) -> resultA + resultB } - .flowOn(dispatcher) - .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } - .flowOn(context) - .collect(::emit) - } - - @NonNull - @Experimental - fun chainWith( - context: CoroutineContext = EmptyCoroutineContext, - other: suspend (DataResult?) -> ResponseLiveData, - condition: suspend (DataResult?) -> Boolean, - transform: suspend (DataResult>) -> DataResult - ) = chainWith(context, other, condition, Dispatchers.IO to transform) - - /* Non Nullable --------------------------------------------------------------------------------- */ - - @NonNull - @Experimental - fun chainNotNullWith( - context: CoroutineContext = EmptyCoroutineContext, - other: suspend (DataResult) -> ResponseLiveData, - condition: suspend (DataResult) -> Boolean, - ) = responseLiveData(context = context) { - internalChainNotNullWith(other, condition) - .mapNotNull { (resultA, resultB) -> (resultA + resultB).onlyWithValues() } - .collect(::emit) - } - - @NonNull - @Experimental - fun chainNotNullWith( - context: CoroutineContext = EmptyCoroutineContext, - other: suspend (DataResult) -> ResponseLiveData, - condition: suspend (DataResult) -> Boolean, - transform: Pair>) -> DataResult> - ) = responseLiveData(context = context) { - val (dispatcher, block) = transform - internalChainNotNullWith(other, condition) - .mapNotNull { (resultA, resultB) -> (resultA + resultB).onlyWithValues() } - .flowOn(dispatcher) - .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } - .flowOn(context) - .collect(::emit) - } - - @NonNull - @Experimental - fun chainNotNullWith( - context: CoroutineContext = EmptyCoroutineContext, - other: suspend (DataResult) -> ResponseLiveData, - condition: suspend (DataResult) -> Boolean, - transform: suspend (DataResult>) -> DataResult - ) = chainNotNullWith(context, other, condition, Dispatchers.IO to transform) - //endregion - //region Observability /** * Execute the function onNext before any observe set after this method be called diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_chain.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_chain.kt index 41eaa1a..8676208 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_chain.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_chain.kt @@ -47,13 +47,15 @@ fun LiveData.chainWith( val mediator = MediatorLiveData>() var bLiveData: LiveData? = null + fun onBReceived(bValue: R?) = (value to bValue).let(mediator::setValue) + fun onAReceived(aValue: T?) { bLiveData?.let(mediator::removeSource) val isConditionMet = condition.runCatching { invoke(aValue) }.getOrDefault(false) bLiveData = if (isConditionMet) other.runCatching { invoke(aValue) }.getOrNull() else null bLiveData?.let { liveData -> if (liveData.isInitialized.not()) mediator.value = aValue to null - mediator.addSource(liveData) { bValue -> (aValue to bValue).let(mediator::setValue) } + mediator.addSource(liveData, ::onBReceived) } } @@ -83,10 +85,23 @@ fun LiveData.chainWith( fun LiveData.chainNotNullWith( other: (T) -> LiveData, condition: (T) -> Boolean -): LiveData> = chainWith( - other = { it?.let(other) ?: error("Data null in chainNotNullWith") }, - condition = { it?.let(condition) ?: false } -).mapNotNull { it.onlyWithValues() } +): LiveData> { + val mediator = MediatorLiveData>() + var bLiveData: LiveData? = null + + fun onBReceived(bValue: R?) = (value to bValue).onlyWithValues()?.let(mediator::setValue) + + fun onAReceived(aValue: T?) { + bLiveData?.let(mediator::removeSource) + aValue ?: return + val isConditionMet = condition.runCatching { invoke(aValue) }.getOrDefault(false) + bLiveData = if (isConditionMet) other.runCatching { invoke(aValue) }.getOrNull() else null + bLiveData?.let { liveData -> mediator.addSource(liveData, ::onBReceived) } + } + + mediator.addSource(this, ::onAReceived) + return mediator +} /* Coroutine Functions -------------------------------------------------------------------------- */ @@ -115,7 +130,7 @@ fun LiveData.chainWith( context: CoroutineContext, other: suspend (T?) -> LiveData, condition: suspend (T?) -> Boolean, -) = liveData(context) { internalChainWith(other, condition).collect(::emit) } +): LiveData> = liveData(context) { internalChainWith(other, condition).collect(::emit) } /** * Chains this [LiveData] with another [LiveData] based on a condition, using a transformation function. @@ -184,7 +199,12 @@ fun LiveData.chainWith( other: suspend (T?) -> LiveData, condition: suspend (T?) -> Boolean, transform: suspend (T?, R?) -> X? -): LiveData = chainWith(context, other, condition, Dispatchers.IO to transform) +): LiveData = chainWith( + context = context, + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) /** * Chains this [LiveData] with another [LiveData] based on a condition, using a simple transformation function and the default [CoroutineContext]. @@ -212,7 +232,12 @@ fun LiveData.chainWith( other: suspend (T?) -> LiveData, condition: suspend (T?) -> Boolean, transform: Pair X?> -): LiveData = chainWith(EmptyCoroutineContext, other, condition, transform) +): LiveData = chainWith( + context = EmptyCoroutineContext, + other = other, + condition = condition, + transform = transform +) /** * Chains this [LiveData] with another [LiveData] based on a condition, @@ -241,7 +266,11 @@ fun LiveData.chainWith( other: suspend (T?) -> LiveData, condition: suspend (T?) -> Boolean, transform: suspend (T?, R?) -> X? -): LiveData = chainWith(other, condition, Dispatchers.IO to transform) +): LiveData = chainWith( + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) /** * Chains this [LiveData] with another non-nullable [LiveData] based on a condition, using coroutines. @@ -268,7 +297,7 @@ fun LiveData.chainNotNullWith( context: CoroutineContext, other: suspend (T) -> LiveData, condition: suspend (T) -> Boolean, -) = liveData>(context) { +): LiveData> = liveData(context) { internalChainNotNullWith(other, condition).collect(::emit) } @@ -339,7 +368,12 @@ fun LiveData.chainNotNullWith( other: suspend (T) -> LiveData, condition: suspend (T) -> Boolean, transform: suspend (T, R) -> X -): LiveData = chainNotNullWith(context, other, condition, Dispatchers.IO to transform) +): LiveData = chainNotNullWith( + context = context, + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) /** * Chains this non-nullable [LiveData] with another non-nullable [LiveData] based on a condition, @@ -368,7 +402,12 @@ fun LiveData.chainNotNullWith( other: suspend (T) -> LiveData, condition: suspend (T) -> Boolean, transform: Pair X> -): LiveData = chainNotNullWith(EmptyCoroutineContext, other, condition, transform) +): LiveData = chainNotNullWith( + context = EmptyCoroutineContext, + other = other, + condition = condition, + transform = transform +) /** * Chains this non-nullable [LiveData] with another non-nullable [LiveData] based on a condition, @@ -397,7 +436,11 @@ fun LiveData.chainNotNullWith( other: suspend (T) -> LiveData, condition: suspend (T) -> Boolean, transform: suspend (T, R) -> X -): LiveData = chainNotNullWith(other, condition, Dispatchers.IO to transform) +): LiveData = chainNotNullWith( + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) /* Auxiliary Functions -------------------------------------------------------------------------- */ diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_combine.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_combine.kt index 40926c7..3b901d9 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_combine.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_combine.kt @@ -146,8 +146,10 @@ fun LiveData.combineNotNull(other: LiveData): LiveData> * @see [combineNotNull] * @see [plus] */ -fun LiveData.combine(context: CoroutineContext, other: LiveData) = - liveData(context) { internalCombine(other).collect(::emit) } +fun LiveData.combine( + context: CoroutineContext, + other: LiveData +): LiveData> = liveData(context) { internalCombine(other).collect(::emit) } /** * Combines two [LiveData] objects with a transformation function using coroutines. @@ -214,7 +216,11 @@ fun LiveData.combine( context: CoroutineContext, other: LiveData, transform: suspend (T?, R?) -> X? -): LiveData = combine(context, other, Dispatchers.IO to transform) +): LiveData = combine( + context = context, + other = other, + transform = Dispatchers.IO to transform +) /** * Combines two [LiveData] objects with a transformation function and an optional [CoroutineContext]. @@ -240,7 +246,11 @@ fun LiveData.combine( fun LiveData.combine( other: LiveData, transform: Pair X?> -): LiveData = combine(EmptyCoroutineContext, other, transform) +): LiveData = combine( + context = EmptyCoroutineContext, + other = other, + transform = transform +) /** * Combines two [LiveData] objects with a transformation function using a default [CoroutineDispatcher]. @@ -265,7 +275,10 @@ fun LiveData.combine( fun LiveData.combine( other: LiveData, transform: suspend (T?, R?) -> X? -): LiveData = combine(other, Dispatchers.IO to transform) +): LiveData = combine( + other = other, + transform = Dispatchers.IO to transform +) /** * Combines two non-null [LiveData] objects using coroutines. @@ -286,8 +299,10 @@ fun LiveData.combine( * @see [combine] * @see [plus] */ -fun LiveData.combineNotNull(context: CoroutineContext, other: LiveData) = - liveData>(context) { internalCombineNotNull(other).collect(::emit) } +fun LiveData.combineNotNull( + context: CoroutineContext, + other: LiveData +): LiveData> = liveData(context) { internalCombineNotNull(other).collect(::emit) } /** * Combines two non-null [LiveData] objects with a transformation function using coroutines. @@ -364,7 +379,11 @@ fun LiveData.combineNotNull( context: CoroutineContext, other: LiveData, transform: suspend (T, R) -> X -): LiveData = combineNotNull(context, other, Dispatchers.IO to transform) +): LiveData = combineNotNull( + context = context, + other = other, + transform = Dispatchers.IO to transform +) /** * Combines two non-null [LiveData] objects with a transformation function and an optional [CoroutineContext]. @@ -390,7 +409,11 @@ fun LiveData.combineNotNull( fun LiveData.combineNotNull( other: LiveData, transform: Pair X> -): LiveData = combineNotNull(EmptyCoroutineContext, other, transform) +): LiveData = combineNotNull( + context = EmptyCoroutineContext, + other = other, + transform = transform +) /** * Combines two non-null [LiveData] objects with a transformation function using a default [CoroutineDispatcher]. @@ -415,7 +438,10 @@ fun LiveData.combineNotNull( fun LiveData.combineNotNull( other: LiveData, transform: suspend (T, R) -> X -): LiveData = combineNotNull(other, Dispatchers.IO to transform) +): LiveData = combineNotNull( + other = other, + transform = Dispatchers.IO to transform +) /* Auxiliary Functions -------------------------------------------------------------------------- */ diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseChain.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseChain.kt index 134f1a2..0d5036b 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseChain.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseChain.kt @@ -3,6 +3,7 @@ package br.com.arch.toolkit.util import androidx.lifecycle.LiveData +import br.com.arch.toolkit.annotation.Experimental import br.com.arch.toolkit.livedata.ResponseLiveData import br.com.arch.toolkit.livedata.responseLiveData import br.com.arch.toolkit.result.DataResult @@ -11,27 +12,35 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.mapNotNull import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext -/* LiveData + Response Functions ---------------------------------------------------------------- */ +/* region LiveData + Response Functions --------------------------------------------------------- */ /* Nullable ------------------------------------------------------------------------------------- */ +@FunctionalInterface +fun interface WithResponse { + suspend fun invoke(result: T?): ResponseLiveData +} + +@Experimental fun LiveData.chainWith( - context: CoroutineContext, - other: suspend (T?) -> ResponseLiveData, + context: CoroutineContext = EmptyCoroutineContext, + other: WithResponse, condition: suspend (T?) -> Boolean, -) = responseLiveData(context = context) { - internalChainWith(other, condition) +): ResponseLiveData> = responseLiveData(context = context) { + internalChainWith(other::invoke, condition) .mapNotNull { (data, result) -> data?.let(::dataResultSuccess) + result } .collect(::emit) } +@Experimental fun LiveData.chainWith( - context: CoroutineContext, - other: suspend (T?) -> ResponseLiveData, + context: CoroutineContext = EmptyCoroutineContext, + other: WithResponse, condition: suspend (T?) -> Boolean, transform: Pair>) -> DataResult> ): ResponseLiveData = responseLiveData(context = context) { val (dispatcher, block) = transform - internalChainWith(other, condition) + internalChainWith(other::invoke, condition) .mapNotNull { (data, result) -> data?.let(::dataResultSuccess) + result } .flowOn(dispatcher) .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } @@ -39,33 +48,45 @@ fun LiveData.chainWith( .collect(::emit) } +@Experimental fun LiveData.chainWith( - context: CoroutineContext, - other: suspend (T?) -> ResponseLiveData, + context: CoroutineContext = EmptyCoroutineContext, + other: WithResponse, condition: suspend (T?) -> Boolean, transform: suspend (DataResult>) -> DataResult -): ResponseLiveData = chainWith(context, other, condition, Dispatchers.IO to transform) +): ResponseLiveData = chainWith( + context = context, + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) /* Non Nullable --------------------------------------------------------------------------------- */ +@FunctionalInterface +fun interface NotNullWithResponse { + suspend fun invoke(result: T): ResponseLiveData +} +@Experimental fun LiveData.chainNotNullWith( - context: CoroutineContext, - other: suspend (T) -> ResponseLiveData, + context: CoroutineContext = EmptyCoroutineContext, + other: NotNullWithResponse, condition: suspend (T) -> Boolean, -) = responseLiveData(context = context) { - internalChainNotNullWith(other, condition) +): ResponseLiveData> = responseLiveData(context = context) { + internalChainNotNullWith(other::invoke, condition) .mapNotNull { (data, result) -> (dataResultSuccess(data) + result).onlyWithValues() } .collect(::emit) } +@Experimental fun LiveData.chainNotNullWith( - context: CoroutineContext, - other: suspend (T) -> ResponseLiveData, + context: CoroutineContext = EmptyCoroutineContext, + other: NotNullWithResponse, condition: suspend (T) -> Boolean, transform: Pair>) -> DataResult> ): ResponseLiveData = responseLiveData(context = context) { val (dispatcher, block) = transform - internalChainNotNullWith(other, condition) + internalChainNotNullWith(other::invoke, condition) .mapNotNull { (data, result) -> (dataResultSuccess(data) + result).onlyWithValues() } .flowOn(dispatcher) .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } @@ -73,9 +94,194 @@ fun LiveData.chainNotNullWith( .collect(::emit) } +@Experimental fun LiveData.chainNotNullWith( - context: CoroutineContext, - other: suspend (T) -> ResponseLiveData, + context: CoroutineContext = EmptyCoroutineContext, + other: NotNullWithResponse, condition: suspend (T) -> Boolean, transform: suspend (DataResult>) -> DataResult -): ResponseLiveData = chainNotNullWith(context, other, condition, Dispatchers.IO to transform) +): ResponseLiveData = chainNotNullWith( + context = context, + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) +/* endregion ------------------------------------------------------------------------------------ */ + +/* region Response + LiveData Functions ---------------------------------------------------------------- */ +/* Nullable ------------------------------------------------------------------------------------- */ +@FunctionalInterface +fun interface ResponseWith { + suspend fun invoke(result: DataResult?): LiveData +} + +@Experimental +fun ResponseLiveData.chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseWith, + condition: suspend (DataResult?) -> Boolean, +): ResponseLiveData> = responseLiveData(context = context) { + internalChainWith(other::invoke, condition) + .mapNotNull { (result, data) -> result + data?.let(::dataResultSuccess) } + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseWith, + condition: suspend (DataResult?) -> Boolean, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalChainWith(other::invoke, condition) + .mapNotNull { (result, data) -> result + data?.let(::dataResultSuccess) } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseWith, + condition: suspend (DataResult?) -> Boolean, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = chainWith( + context = context, + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) + +/* Non Nullable --------------------------------------------------------------------------------- */ +@FunctionalInterface +fun interface ResponseNotNullWith { + suspend fun invoke(result: DataResult): LiveData +} + +@Experimental +fun ResponseLiveData.chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseNotNullWith, + condition: suspend (DataResult) -> Boolean, +): ResponseLiveData> = responseLiveData(context = context) { + internalChainNotNullWith(other::invoke, condition) + .mapNotNull { (result, data) -> (result + dataResultSuccess(data)).onlyWithValues() } + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseNotNullWith, + condition: suspend (DataResult) -> Boolean, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalChainNotNullWith(other::invoke, condition) + .mapNotNull { (result, data) -> (result + dataResultSuccess(data)).onlyWithValues() } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseNotNullWith, + condition: suspend (DataResult) -> Boolean, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = chainNotNullWith( + context = context, + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) +/* endregion ------------------------------------------------------------------------------------ */ + +/* region Response + Response Functions ---------------------------------------------------------------- */ +/* Nullable ------------------------------------------------------------------------------------- */ +@Experimental +fun ResponseLiveData.chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult?) -> ResponseLiveData, + condition: suspend (DataResult?) -> Boolean, +): ResponseLiveData> = responseLiveData(context = context) { + internalChainWith(other, condition) + .mapNotNull { (resultA, resultB) -> resultA + resultB } + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult?) -> ResponseLiveData, + condition: suspend (DataResult?) -> Boolean, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalChainWith(other, condition) + .mapNotNull { (resultA, resultB) -> resultA + resultB } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult?) -> ResponseLiveData, + condition: suspend (DataResult?) -> Boolean, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = chainWith( + context = context, + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) + +/* Non Nullable --------------------------------------------------------------------------------- */ +@Experimental +fun ResponseLiveData.chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult) -> ResponseLiveData, + condition: suspend (DataResult) -> Boolean, +): ResponseLiveData> = responseLiveData(context = context) { + internalChainNotNullWith(other, condition) + .mapNotNull { (resultA, resultB) -> (resultA + resultB).onlyWithValues() } + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult) -> ResponseLiveData, + condition: suspend (DataResult) -> Boolean, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalChainNotNullWith(other, condition) + .mapNotNull { (resultA, resultB) -> (resultA + resultB).onlyWithValues() } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult) -> ResponseLiveData, + condition: suspend (DataResult) -> Boolean, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = chainNotNullWith( + context = context, + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) +/* endregion ------------------------------------------------------------------------------------ */ diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt index 75e1bec..de85f46 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt @@ -58,7 +58,11 @@ fun LiveData.combine( context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData, transform: suspend (DataResult>) -> DataResult -): ResponseLiveData = combine(context, other, Dispatchers.IO to transform) +): ResponseLiveData = combine( + context = context, + other = other, + transform = Dispatchers.IO to transform +) /* Non Nullable --------------------------------------------------------------------------------- */ @Experimental @@ -91,7 +95,11 @@ fun LiveData.combineNotNull( context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData, transform: suspend (DataResult>) -> DataResult -): ResponseLiveData = combineNotNull(context, other, Dispatchers.IO to transform) +): ResponseLiveData = combineNotNull( + context = context, + other = other, + transform = Dispatchers.IO to transform +) /* endregion ------------------------------------------------------------------------------------ */ /* region Response + LiveData Functions ---------------------------------------------------------------- */ @@ -125,7 +133,11 @@ fun ResponseLiveData.combine( context: CoroutineContext = EmptyCoroutineContext, other: LiveData, transform: suspend (DataResult>) -> DataResult -): ResponseLiveData = combine(context, other, Dispatchers.IO to transform) +): ResponseLiveData = combine( + context = context, + other = other, + transform = Dispatchers.IO to transform +) /* Non Nullable --------------------------------------------------------------------------------- */ @Experimental @@ -158,7 +170,11 @@ fun ResponseLiveData.combineNotNull( context: CoroutineContext = EmptyCoroutineContext, other: LiveData, transform: suspend (DataResult>) -> DataResult -): ResponseLiveData = combineNotNull(context, other, Dispatchers.IO to transform) +): ResponseLiveData = combineNotNull( + context = context, + other = other, + transform = Dispatchers.IO to transform +) /* endregion ------------------------------------------------------------------------------------ */ /* region Response + Response Functions --------------------------------------------------------- */ @@ -191,7 +207,11 @@ fun ResponseLiveData.combine( context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData, transform: suspend (DataResult>) -> DataResult -): ResponseLiveData = combine(context, other, Dispatchers.IO to transform) +): ResponseLiveData = combine( + context = context, + other = other, + transform = Dispatchers.IO to transform +) /* Non Nullable --------------------------------------------------------------------------------- */ @Experimental @@ -224,5 +244,9 @@ fun ResponseLiveData.combineNotNull( context: CoroutineContext = EmptyCoroutineContext, other: ResponseLiveData, transform: suspend (DataResult>) -> DataResult -): ResponseLiveData = combineNotNull(context, other, Dispatchers.IO to transform) +): ResponseLiveData = combineNotNull( + context = context, + other = other, + transform = Dispatchers.IO to transform +) /* endregion ------------------------------------------------------------------------------------ */