Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
dmdevgo committed Feb 29, 2020
2 parents 8315d81 + 50cb9cd commit 960299c
Show file tree
Hide file tree
Showing 42 changed files with 1,426 additions and 127 deletions.
20 changes: 11 additions & 9 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
buildscript {
ext.kotlinVersion = '1.3.50'
ext.kotlinVersion = '1.3.61'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.android.tools.build:gradle:3.6.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
}
}
Expand All @@ -22,25 +22,27 @@ task clean(type: Delete) {
}

ext {

minSdkVersion = 16
compileSdkVersion = 29
targetSdkVersion = 29
rxBindingVersion = '3.0.0'
rxBindingVersion = '3.1.0'

appCompat = 'androidx.appcompat:appcompat:1.0.0'
materialDesign = 'com.google.android.material:material:1.0.0'
annotation = 'androidx.annotation:annotation:1.1.0'
appCompat = 'androidx.appcompat:appcompat:1.2.0-alpha02'
materialDesign = 'com.google.android.material:material:1.2.0-alpha05'

kotlinStdlib = "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"

rxJava2 = "io.reactivex.rxjava2:rxjava:2.2.11"
rxRelay2 = "com.jakewharton.rxrelay2:rxrelay:2.1.0"
rxAndroid2 = "io.reactivex.rxjava2:rxandroid:2.1.0"
rxJava2 = "io.reactivex.rxjava2:rxjava:2.2.18"
rxRelay2 = "com.jakewharton.rxrelay2:rxrelay:2.1.1"
rxAndroid2 = "io.reactivex.rxjava2:rxandroid:2.1.1"

rxBinding = "com.jakewharton.rxbinding3:rxbinding:$rxBindingVersion"
rxBindingAppCompat = "com.jakewharton.rxbinding3:rxbinding-appcompat:$rxBindingVersion"

conductor = "com.bluelinelabs:conductor:3.0.0-rc2"

junitKotlin = "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion"
mockitoKotlin = 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0'
mockitoKotlin = 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0'
}
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Fri Aug 23 00:04:07 MSK 2019
#Sat Feb 29 16:08:15 MSK 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
3 changes: 2 additions & 1 deletion rxpm/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ dependencies {

// Rx
api rootProject.rxJava2
implementation rootProject.annotation
implementation rootProject.rxRelay2
implementation rootProject.rxAndroid2
implementation rootProject.rxBinding
Expand All @@ -62,7 +63,7 @@ ext {
bintrayName = 'RxPM'
publishedGroupId = 'me.dmdev.rxpm'
artifact = 'rxpm'
libraryVersion = '2.0'
libraryVersion = '2.1'
gitUrl = 'https://github.com/dmdevgo/RxPM'
allLicenses = ['MIT']
}
Expand Down
27 changes: 25 additions & 2 deletions rxpm/src/main/kotlin/me/dmdev/rxpm/Action.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package me.dmdev.rxpm

import android.annotation.*
import com.jakewharton.rxrelay2.*
import io.reactivex.*
import io.reactivex.android.schedulers.*
Expand All @@ -25,9 +26,31 @@ class Action<T> internal constructor(internal val pm: PresentationModel) {

/**
* Creates the [Action].
* Optionally subscribes the [action chain][actionChain] to this action.
* This chain will be unsubscribed ON [DESTROY][PresentationModel.Lifecycle.DESTROYED].
*/
fun <T> PresentationModel.action(): Action<T> {
return Action(this)
@SuppressLint("CheckResult")
fun <T> PresentationModel.action(
actionChain: (Observable<T>.() -> Observable<*>)? = null
): Action<T> {
val action = Action<T>(pm = this)

if (actionChain != null) {
lifecycleObservable
.filter { it == PresentationModel.Lifecycle.CREATED }
.take(1)
.subscribe {
actionChain.let { chain ->
action.relay
.chain()
.retry()
.subscribe()
.untilDestroy()
}
}
}

return action
}

/**
Expand Down
42 changes: 34 additions & 8 deletions rxpm/src/main/kotlin/me/dmdev/rxpm/PmExtensions.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@file:Suppress("NOTHING_TO_INLINE")

package me.dmdev.rxpm

import com.jakewharton.rxrelay2.*
Expand All @@ -13,22 +11,22 @@ import me.dmdev.rxpm.util.*
* Convenience to get this [Relay] as an [Observable].
* Helps to ensure code readability.
*/
inline fun <T> Relay<T>.asObservable(): Observable<T> {
fun <T> Relay<T>.asObservable(): Observable<T> {
return this.hide()
}

/**
* Convenience to get this [Relay] as an [Consumer].
* Helps to ensure code readability.
*/
inline fun <T> Relay<T>.asConsumer(): Consumer<T> {
fun <T> Relay<T>.asConsumer(): Consumer<T> {
return this
}

/**
* Convenience to bind the [progress][progressConsumer] to the [Single].
*/
inline fun <T> Single<T>.bindProgress(progressConsumer: Consumer<Boolean>): Single<T> {
fun <T> Single<T>.bindProgress(progressConsumer: Consumer<Boolean>): Single<T> {
return this
.doOnSubscribe { progressConsumer.accept(true) }
.doFinally { progressConsumer.accept(false) }
Expand All @@ -37,7 +35,7 @@ inline fun <T> Single<T>.bindProgress(progressConsumer: Consumer<Boolean>): Sing
/**
* Convenience to bind the [progress][progressConsumer] to the [Maybe].
*/
inline fun <T> Maybe<T>.bindProgress(progressConsumer: Consumer<Boolean>): Maybe<T> {
fun <T> Maybe<T>.bindProgress(progressConsumer: Consumer<Boolean>): Maybe<T> {
return this
.doOnSubscribe { progressConsumer.accept(true) }
.doFinally { progressConsumer.accept(false) }
Expand All @@ -46,16 +44,37 @@ inline fun <T> Maybe<T>.bindProgress(progressConsumer: Consumer<Boolean>): Maybe
/**
* Convenience to bind the [progress][progressConsumer] to the [Completable].
*/
inline fun Completable.bindProgress(progressConsumer: Consumer<Boolean>): Completable {
fun Completable.bindProgress(progressConsumer: Consumer<Boolean>): Completable {
return this
.doOnSubscribe { progressConsumer.accept(true) }
.doFinally { progressConsumer.accept(false) }
}

/**
* Convenience to bind the [progress][state] to the [Single].
*/
fun <T> Single<T>.bindProgress(state: State<Boolean>): Single<T> {
return this.bindProgress(state.relay)
}

/**
* Convenience to bind the [progress][state] to the [Maybe].
*/
fun <T> Maybe<T>.bindProgress(state: State<Boolean>): Maybe<T> {
return this.bindProgress(state.relay)
}

/**
* Convenience to bind the [progress][state] to the [Completable].
*/
fun Completable.bindProgress(state: State<Boolean>): Completable {
return this.bindProgress(state.relay)
}

/**
* Convenience to filter out items emitted by the source [Observable] when in progress ([progressState] last value is `true`).
*/
inline fun <T> Observable<T>.skipWhileInProgress(progressState: Observable<Boolean>): Observable<T> {
fun <T> Observable<T>.skipWhileInProgress(progressState: Observable<Boolean>): Observable<T> {
return this
.withLatestFrom(
progressState.startWith(false),
Expand All @@ -67,6 +86,13 @@ inline fun <T> Observable<T>.skipWhileInProgress(progressState: Observable<Boole
.map { (item, _) -> item }
}

/**
* Convenience to filter out items emitted by the source [Observable] when in progress ([state] last value is `true`).
*/
fun <T> Observable<T>.skipWhileInProgress(state: State<Boolean>): Observable<T> {
return this.skipWhileInProgress(state.observable)
}

/**
* Returns the [Observable] that emits items when active, and buffers them when [idle][isIdle].
* Buffered items is emitted when idle state ends.
Expand Down
98 changes: 98 additions & 0 deletions rxpm/src/main/kotlin/me/dmdev/rxpm/PresentationModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,11 @@ abstract class PresentationModel {
*/
protected val <T> State<T>.consumer: Consumer<T> get() = relay

/**
* Accept the given [value] by the [State].
*/
protected fun <T> State<T>.accept(value: T) = relay.accept(value)

/**
* Observable of the [Action].
* Accessible only from a [PresentationModel].
Expand All @@ -246,6 +251,11 @@ abstract class PresentationModel {
*/
protected val <T> Action<T>.observable: Observable<T> get() = relay

/**
* Accept the given [value] by the [Action].
*/
protected fun <T> Action<T>.accept(value: T) = relay.accept(value)

/**
* Consumer of the [Command].
* Accessible only from a [PresentationModel].
Expand All @@ -254,5 +264,93 @@ abstract class PresentationModel {
*/
protected val <T> Command<T>.consumer: Consumer<T> get() = relay

/**
* Accept the given [value] to the [Command].
*/
protected fun <T> Command<T>.accept(value: T) = relay.accept(value)

/**
* Convenience to subscribe [state] to the [Observable].
*/
protected fun <T> Observable<T>.subscribe(state: State<T>): Disposable {
return this.subscribe(state.relay)
}

/**
* Convenience to subscribe [state] to the [Single].
*/
protected fun <T> Single<T>.subscribe(state: State<T>): Disposable {
return this.subscribe(state.relay)
}

/**
* Convenience to subscribe [state] to the [Flowable].
*/
protected fun <T> Flowable<T>.subscribe(state: State<T>): Disposable {
return this.subscribe(state.relay)
}

/**
* Convenience to subscribe [state] to the [Maybe].
*/
protected fun <T> Maybe<T>.subscribe(state: State<T>): Disposable {
return this.subscribe(state.relay)
}

/**
* Convenience to subscribe [action] to the [Observable].
*/
protected fun <T> Observable<T>.subscribe(action: Action<T>): Disposable {
return this.subscribe(action.relay)
}

/**
* Convenience to subscribe [action] to the [Single].
*/
protected fun <T> Single<T>.subscribe(action: Action<T>): Disposable {
return this.subscribe(action.relay)
}

/**
* Convenience to subscribe [action] to the [Flowable].
*/
protected fun <T> Flowable<T>.subscribe(action: Action<T>): Disposable {
return this.subscribe(action.relay)
}

/**
* Convenience to subscribe [action] to the [Maybe].
*/
protected fun <T> Maybe<T>.subscribe(action: Action<T>): Disposable {
return this.subscribe(action.relay)
}

/**
* Convenience to subscribe [command] to the [Observable].
*/
protected fun <T> Observable<T>.subscribe(command: Command<T>): Disposable {
return this.subscribe(command.relay)
}

/**
* Convenience to subscribe [command] to the [Single].
*/
protected fun <T> Single<T>.subscribe(command: Command<T>): Disposable {
return this.subscribe(command.relay)
}

/**
* Convenience to subscribe [command] to the [Flowable].
*/
protected fun <T> Flowable<T>.subscribe(command: Command<T>): Disposable {
return this.subscribe(command.relay)
}

/**
* Convenience to subscribe [command] to the [Maybe].
*/
protected fun <T> Maybe<T>.subscribe(command: Command<T>): Disposable {
return this.subscribe(command.relay)
}
}

34 changes: 32 additions & 2 deletions rxpm/src/main/kotlin/me/dmdev/rxpm/State.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package me.dmdev.rxpm

import android.annotation.*
import com.jakewharton.rxrelay2.*
import io.reactivex.*
import io.reactivex.android.schedulers.*
Expand Down Expand Up @@ -77,18 +78,47 @@ class State<T> internal constructor(
fun hasValue() = behaviorRelay.hasValue()
}

private val UNPROCESSED_ERROR_CONSUMER = Consumer<Throwable> { throwable ->
throw IllegalStateException(
"Unprocessed error encountered in the State. " +
"State accepts only emitted items, so you need to process errors yourself.",
throwable
)
}

/**
* Creates the [State].
* Optionally subscribes to the provided [state source][stateSource] and
* unsubscribes from it ON [DESTROY][PresentationModel.Lifecycle.DESTROYED].
*
* @param [initialValue] initial value.
* @param [diffStrategy] diff strategy.
* @param [stateSource] source of the state.
*/
@SuppressLint("CheckResult")
@Suppress("UNCHECKED_CAST")
fun <T> PresentationModel.state(
initialValue: T? = null,
diffStrategy: DiffStrategy<T>? = DiffByEquals as DiffStrategy<T>
diffStrategy: DiffStrategy<T>? = DiffByEquals as DiffStrategy<T>,
stateSource: (() -> Observable<T>)? = null
): State<T> {
return State(this, initialValue, diffStrategy)

val state = State(pm = this, initialValue = initialValue, diffStrategy = diffStrategy)

if (stateSource != null) {
lifecycleObservable
.filter { it == PresentationModel.Lifecycle.CREATED }
.take(1)
.subscribe {
stateSource.let { source ->
source()
.subscribe(state.relay, UNPROCESSED_ERROR_CONSUMER)
.untilDestroy()
}
}
}

return state
}

/**
Expand Down
Loading

0 comments on commit 960299c

Please sign in to comment.