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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ class MainViewModel(application: Application): RCKViewModel<MainState>(applicati
}

override fun on(newState: MainState) {
route.accept(newState.route).afterReset(MainRoute.None)
route.accept(newState.route).afterFlow(MainRoute.None)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.app.Application
import com.github.skyfe79.android.library.app.examples.counter.action.AsyncIncreaseAction
import com.github.skyfe79.android.library.app.examples.counter.action.DecreaseAction
import com.github.skyfe79.android.library.app.examples.counter.action.IncreaseAction
import com.github.skyfe79.android.library.app.examples.emojicollection.actions.AddEmojiAction
import com.github.skyfe79.android.reactcomponentkit.redux.*
import com.github.skyfe79.android.reactcomponentkit.viewmodel.RCKViewModel
import io.reactivex.Single
Expand All @@ -25,21 +26,23 @@ class CounterViewModel(application: Application): RCKViewModel<CounterState>(app
fun asyncIncrease(state: CounterState, action: AsyncIncreaseAction) = asyncReducer(state, action) {
Single.create<CounterState> { emitter ->
Thread.sleep(1000L)
withState { state ->
emitter.onSuccess(state.copy(count = state.count + action.payload))
withState {
emitter.onSuccess(copy(count = count + action.payload))
}
}.toObservable()
}

fun increasement(payload: Int) = setState {
copy(count = count + payload)
}

override fun setupStore() {
initStore { store ->
store.initialState(CounterState(0))

store.flow<AsyncIncreaseAction>(
{ _, _ ->
setState {
it.copy(asyncCount = Async.Loading)
}
setState { copy(asyncCount = Async.Loading) }
},
{ state, action ->
state.copy(count = state.count + action.payload)
Expand All @@ -48,8 +51,8 @@ class CounterViewModel(application: Application): RCKViewModel<CounterState>(app
asyncFlow { action ->
Single.create<CounterState> { emitter ->
Thread.sleep(2000L)
withState { state ->
emitter.onSuccess(state.copy(count = state.count + action.payload, asyncCount = Async.Success(state.count + action.payload)))
withState {
emitter.onSuccess(copy(count = count + action.payload, asyncCount = Async.Success(count + action.payload)))
}
}.toObservable()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ class EmojiCollectionViewModel(application: Application): RCKViewModel<EmojiColl
initStore { store ->

store.initialState(EmojiCollectionState())
store.afterFlow({
it.copy(route = EmojiRoute.None)
})


store.flow<ClickEmojiAction>({ state, action ->
state.copy(route = EmojiRoute.AlertEmoji(action.emoji))
Expand All @@ -58,12 +54,16 @@ class EmojiCollectionViewModel(application: Application): RCKViewModel<EmojiColl
::shuffleEmoji,
{ state, _ -> makeItemModels(state) }
)

store.afterStateFlow({
copy(route = EmojiRoute.None)
})
}
}

override fun on(newState: EmojiCollectionState) {
itemModels.accept(newState.itemModels)
routes.accept(newState.route).afterReset(EmojiRoute.None)
routes.accept(newState.route).afterFlow(EmojiRoute.None)
}

override fun on(error: Error) {
Expand Down
6 changes: 3 additions & 3 deletions reactcomponentkit/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ publish {
userOrg = 'skyfe79'
groupId = 'com.github.skyfe79.android'
artifactId = 'reactcomponentkit'
publishVersion = '2.0.0'
publishVersion = '2.0.1'
desc = 'AndroidReactComponentKit = Component + MVVM + Redux for Android!!!'
website = 'https://github.com/ReactComponentKit/AndroidReactComponentKit'
}
Expand All @@ -29,8 +29,8 @@ android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 29
versionCode 20
versionName "2.0.0"
versionCode 21
versionName "2.0.1"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ package com.github.skyfe79.android.reactcomponentkit.redux

// Utility for something like as reset some state.
// Run it after dispatching new state to Components
typealias Effect<STATE> = (STATE) -> STATE
typealias Effect<STATE> = STATE.(STATE) -> STATE
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Output<T>(defaultValue: T?) {
val value: T?
get() = behaviorRelay.value

fun accept(value: T, withoutCompare: Boolean = false): ResetChanin {
fun accept(value: T, withoutCompare: Boolean = false): FlowChanin {
if (withoutCompare) {
behaviorRelay.accept(value)
} else {
Expand All @@ -19,15 +19,15 @@ class Output<T>(defaultValue: T?) {
behaviorRelay.accept(value)
}
}
return ResetChanin()
return FlowChanin()
}

fun asObservable(): Observable<T> {
return behaviorRelay.observeOn(AndroidSchedulers.mainThread())
}

inner class ResetChanin {
fun afterReset(value: T) {
inner class FlowChanin {
fun afterFlow(value: T) {
accept(value, withoutCompare = true)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import io.reactivex.schedulers.Schedulers
import kotlin.reflect.KClass




class Store<S: State> {

inner class Flow<STATE: State, A: Action>(private val reducerList: List<Any>) {
Expand Down Expand Up @@ -36,6 +38,7 @@ class Store<S: State> {

lateinit var state: S
internal set
private var beforeActionFlow: (S.(Action) -> Action)? = null
lateinit var actionFlowMap: MutableMap<KClass<*>, Flow<S, Action>>
private set
private lateinit var effects: List<Effect<S>>
Expand Down Expand Up @@ -65,6 +68,15 @@ class Store<S: State> {
disposables.clear()
}

fun beforeActionFlow(actionFlow: S.(Action) -> Action) {
this.beforeActionFlow = actionFlow
}

@Suppress("UNCHECKED_CAST")
internal fun actionFlow(action: Action): Action {
return beforeActionFlow?.invoke(this.state.copyState() as S, action) ?: action
}

/**
* Make a flow of reducers for an action
*/
Expand All @@ -75,7 +87,7 @@ class Store<S: State> {
/**
* do some side effect after finishing a flow.
*/
fun afterFlow(vararg effects: Effect<S>) {
fun afterStateFlow(vararg effects: Effect<S>) {
this.effects = effects.toList()
}

Expand All @@ -86,7 +98,7 @@ class Store<S: State> {
internal fun doAfterEffects() {
var mutatedState = state
effects.forEach {
mutatedState = it(mutatedState)
mutatedState = it(mutatedState, mutatedState)
}
state = mutatedState
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,25 @@ import java.lang.ref.WeakReference
import java.util.concurrent.locks.ReentrantLock

abstract class RCKViewModel<S: State>(application: Application): AndroidViewModel(application) {
val token: Token =
Token()
val token: Token = Token()

private val rx_action: BehaviorRelay<Action> = BehaviorRelay.createDefault(
VoidAction
)
private val store = Store<S>()
private val disposables = CompositeDisposable()
private var actionQueue: Queue<Action> = Queue()
private val writeLock = ReentrantLock()
private val readLock = ReentrantLock()
private var subscribers: MutableList<WeakReference<StateSubscriber>> = mutableListOf()

init {
setupRxStream()
this.setupRxStream()
this.setupStore()
}

override fun onCleared() {
dispose()
this.dispose()
super.onCleared()
}

Expand All @@ -44,6 +44,7 @@ abstract class RCKViewModel<S: State>(application: Application): AndroidViewMode
*/
fun dispose() {
RCK.unregisterViewModel(token)
actionQueue.clear()
subscribers = mutableListOf()
disposables.dispose()
store.deinitialize()
Expand All @@ -55,12 +56,21 @@ abstract class RCKViewModel<S: State>(application: Application): AndroidViewMode
.filter { action ->
action !is VoidAction
}
.filter { action ->
store.actionFlow(action) !is VoidAction
}
.flatMap { action ->
store.dispatch(action).toObservable()
}
.observeOn(AndroidSchedulers.mainThread())
.doAfterNext {
store.doAfterEffects()
if (actionQueue.isNotEmpty) {
val nextAction = actionQueue.dequeue()
nextAction?.let {
rx_action.accept(it)
}
}
}
.subscribe { newState ->
if (newState.error != null) {
Expand All @@ -70,6 +80,8 @@ abstract class RCKViewModel<S: State>(application: Application): AndroidViewMode
} else {
dispatchStateToSubscribers(newState)
}


}

disposables.add(disposable)
Expand Down Expand Up @@ -101,6 +113,17 @@ abstract class RCKViewModel<S: State>(application: Application): AndroidViewMode
rx_action.accept(action)
}

/**
* dispatch action on the next run loop to the store.
*/
fun nextDispatch(action: Action) {
if (actionQueue.isEmpty) {
rx_action.accept(action)
} else {
actionQueue.enqueue(action)
}
}

/**
* Called when receive the new state from store
*/
Expand Down Expand Up @@ -129,12 +152,14 @@ abstract class RCKViewModel<S: State>(application: Application): AndroidViewMode
* Set state and dispatch the mutated state to subscribers
*/
@Suppress("UNCHECKED_CAST")
fun setState(block: RCKViewModel<S>.(S) -> S): S {
fun setState(block: S.(S) -> S): S {
writeLock.lock()
try {
val newState = block(this.store.state.copyState() as S)
val state = this.store.state.copyState() as S
val newState = state.block(state)
this.store.state = newState
runOnUiThread {
this.on(newState)
dispatchStateToSubscribers(newState)
}
return newState
} finally {
Expand All @@ -146,10 +171,11 @@ abstract class RCKViewModel<S: State>(application: Application): AndroidViewMode
* Read state value
*/
@Suppress("UNCHECKED_CAST")
fun <R> withState(block: RCKViewModel<S>.(S) -> R): R {
fun <R> withState(block: S.(S) -> R): R {
readLock.lock()
try {
return block(this.store.state.copyState() as S)
val state = this.store.state.copyState() as S
return state.block(state)
} finally {
readLock.unlock()
}
Expand Down