From 731c363ed4bb15fc293c327adc8d30b4bafa76c4 Mon Sep 17 00:00:00 2001 From: skyfe79 Date: Sun, 25 Aug 2019 21:38:00 +0900 Subject: [PATCH 01/12] Redefine Reducer, middleware and postware types as plain function with specific state type. --- .../collectionview/postwares/Postwares.kt | 10 ++-- .../collectionview/reducer/Reducers.kt | 8 +-- .../app/examples/counter/redux/Reducers.kt | 16 ++--- .../emojicollection/middlewares/Route.kt | 8 +-- .../emojicollection/postwares/Postwares.kt | 12 ++-- .../emojicollection/reducers/Reducers.kt | 60 ++++++------------- .../android/library/app/redux/Reducers.kt | 26 ++++---- reactcomponentkit/build.gradle | 1 - .../component/ViewComponent.kt | 2 +- .../reactcomponentkit/eventbus/EventBus.kt | 4 +- .../reactcomponentkit/eventbus/Token.kt | 2 +- .../recyclerview/RecyclerViewCell.kt | 8 +-- .../android/reactcomponentkit/redux/After.kt | 2 +- .../reactcomponentkit/redux/Middleware.kt | 4 +- .../reactcomponentkit/redux/Postware.kt | 4 +- .../reactcomponentkit/redux/Reducer.kt | 4 +- .../android/reactcomponentkit/redux/Store.kt | 60 ++++++++----------- 17 files changed, 86 insertions(+), 145 deletions(-) diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/postwares/Postwares.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/postwares/Postwares.kt index b3d62ce..487a28b 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/postwares/Postwares.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/postwares/Postwares.kt @@ -11,9 +11,7 @@ import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State import io.reactivex.Observable -fun CollectionViewModel.makeSectionModels(state: State, action: Action): Observable { - val collectionState = (state as? CollectionState) ?: return Observable.just(state) - +fun CollectionViewModel.makeSectionModels(state: CollectionState, action: Action): CollectionState { return when (action) { is LoadAction -> { @@ -25,7 +23,7 @@ fun CollectionViewModel.makeSectionModels(state: State, action: Action): Observa 0xff58c8d8.toInt() ) - val sectionModels = collectionState.emojis.mapIndexed { index, list -> + val sectionModels = state.emojis.mapIndexed { index, list -> val emojiBoxModels = list.map { EmojiBoxModel(it) } DefaultSectionModel( emojiBoxModels, @@ -46,8 +44,8 @@ fun CollectionViewModel.makeSectionModels(state: State, action: Action): Observa } } } - Observable.just(collectionState.copy(sections = sectionModels)) + state.copy(sections = sectionModels) } - else -> Observable.just(state) + else -> state } } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt index 95ed7dc..d5bb1c1 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt @@ -8,17 +8,15 @@ import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State import io.reactivex.Observable -fun CollectionViewModel.loadEmoji(state: State, action: Action): Observable { - val collectionState = (state as? CollectionState) ?: return Observable.just(state) - +fun CollectionViewModel.loadEmoji(state: CollectionState, action: Action): CollectionState { return when (action) { is LoadAction -> { val emojiCollection = (1..5) .map { (1..(40..80).random()).map { EmojiHelper.emoji } } - Observable.just(collectionState.copy(emojis = emojiCollection)) + state.copy(emojis = emojiCollection) } - else -> Observable.just(state) + else -> state } } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt index 24fc5c5..68216a3 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt @@ -8,20 +8,16 @@ import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State import io.reactivex.Observable -fun countReducer(state: State, action: Action): Observable { - val counterState = (state as? CounterState) ?: return Observable.just(state) - +fun countReducer(state: CounterState, action: Action): CounterState { return when(action) { is IncreaseAction -> { - val mutatedState = counterState.copy(count = counterState.count + action.payload) - Observable.just(mutatedState) + val mutatedState = state.copy(count = state.count + action.payload) + mutatedState } is DecreaseAction -> { - val mutatedState = counterState.copy(count = counterState.count - action.payload) - Observable.just(mutatedState) - } - else -> { - Observable.just(state) + val mutatedState = state.copy(count = state.count - action.payload) + mutatedState } + else -> state } } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/middlewares/Route.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/middlewares/Route.kt index be167e9..c25b6b2 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/middlewares/Route.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/middlewares/Route.kt @@ -8,11 +8,9 @@ import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State import io.reactivex.Observable -fun EmojiCollectionViewModel.route(state: State, action: Action): Observable { - val emojiCollectionState = (state as? EmojiCollectionState) ?: return Observable.just(state) - +fun EmojiCollectionViewModel.route(state: EmojiCollectionState, action: Action): EmojiCollectionState { return when(action) { - is ClickEmojiAction -> Observable.just(emojiCollectionState.copy(route = EmojiRoute.AlertEmoji(action.emoji))) - else -> Observable.just(emojiCollectionState.copy(route = EmojiRoute.None)) + is ClickEmojiAction -> state.copy(route = EmojiRoute.AlertEmoji(action.emoji)) + else -> state.copy(route = EmojiRoute.None) } } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/postwares/Postwares.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/postwares/Postwares.kt index d101a9d..3216780 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/postwares/Postwares.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/postwares/Postwares.kt @@ -11,15 +11,13 @@ import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State import io.reactivex.Observable -fun EmojiCollectionViewModel.makeItemModels(state: State, action: Action): Observable { - val emojiCollectionState = (state as? EmojiCollectionState) ?: return Observable.just(state) - +fun EmojiCollectionViewModel.makeItemModels(state: EmojiCollectionState, action: Action): EmojiCollectionState { return when (action) { is MakeItemModelsAction -> { - val emojiBoxModels = emojiCollectionState.emojis.map { EmojiBoxModel(it) } - val mutatedState = emojiCollectionState.copy(itemModels = emojiBoxModels) - Observable.just(mutatedState) + val emojiBoxModels = state.emojis.map { EmojiBoxModel(it) } + val mutatedState = state.copy(itemModels = emojiBoxModels) + mutatedState } - else -> Observable.just(state) + else -> state } } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt index 9a4d9fd..7c4f5c7 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt @@ -15,72 +15,48 @@ import io.reactivex.rxkotlin.subscribeBy import java.lang.Exception import kotlin.random.Random -fun asyncAPI(): Single { - return Single.create { - Thread.sleep(0L) - it.onSuccess(1) - } -} - -fun EmojiCollectionViewModel.addEmoji(state: State, action: Action): Observable { - val emojiCollectionState = (state as? EmojiCollectionState) ?: return Observable.just(state) - +fun EmojiCollectionViewModel.addEmoji(state: EmojiCollectionState, action: Action): EmojiCollectionState { return when (action) { is AddEmojiAction -> { - Single.create { emitter -> - asyncAPI().subscribeBy( - onSuccess = { - val mutableEmojiList = emojiCollectionState.emojis.toMutableList() - val index = if (mutableEmojiList.isEmpty()) 0 else (0 until mutableEmojiList.size).random() - mutableEmojiList.add(index, EmojiHelper.emoji) - emitter.onSuccess(emojiCollectionState.copy(emojis = mutableEmojiList)) - nextDispatch(MakeItemModelsAction, applyNewState = true) - //nextDispatch(AddEmojiAction(EmojiHelper.emoji), applyNewState = true) - }, - onError = { - emitter.onError(it) - } - ) - }.toObservable() - + val mutableEmojiList = state.emojis.toMutableList() + val index = if (mutableEmojiList.isEmpty()) 0 else (0 until mutableEmojiList.size).random() + mutableEmojiList.add(index, EmojiHelper.emoji) + nextDispatch(MakeItemModelsAction, applyNewState = true) + state.copy(emojis = mutableEmojiList) } - else -> Observable.just(state) + else -> state } } -fun EmojiCollectionViewModel.removeEmoji(state: State, action: Action): Observable { - val emojiCollectionState = (state as? EmojiCollectionState) ?: return Observable.just(state) - +fun EmojiCollectionViewModel.removeEmoji(state: EmojiCollectionState, action: Action): EmojiCollectionState { return when (action) { is RemoveEmojiAction -> { return try { - val mutableEmojiList = emojiCollectionState.emojis.toMutableList() + val mutableEmojiList = state.emojis.toMutableList() val index = if (mutableEmojiList.isEmpty()) 0 else (0 until mutableEmojiList.size).random() mutableEmojiList.removeAt(index) - val mutatedState = emojiCollectionState.copy(emojis = mutableEmojiList) + val mutatedState = state.copy(emojis = mutableEmojiList) nextDispatch(MakeItemModelsAction, applyNewState = true) - Observable.just(mutatedState) + mutatedState } catch (e: Exception) { // ignore - Observable.just(state) + state } } - else -> Observable.just(state) + else -> state } } -fun EmojiCollectionViewModel.shuffleEmoji(state: State, action: Action): Observable { - val emojiCollectionState = (state as? EmojiCollectionState) ?: return Observable.just(state) - +fun EmojiCollectionViewModel.shuffleEmoji(state: EmojiCollectionState, action: Action): EmojiCollectionState { return when (action) { is ShuffleEmojiAction -> { - val mutableEmojiList = emojiCollectionState.emojis.toMutableList() + val mutableEmojiList = state.emojis.toMutableList() mutableEmojiList.shuffle() - val mutatedState = emojiCollectionState.copy(emojis = mutableEmojiList) + val mutatedState = state.copy(emojis = mutableEmojiList) nextDispatch(MakeItemModelsAction, applyNewState = true) - Observable.just(mutatedState) + mutatedState } - else -> Observable.just(state) + else -> state } } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/redux/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/redux/Reducers.kt index b1b8e86..71de92f 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/redux/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/redux/Reducers.kt @@ -8,30 +8,28 @@ import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State import io.reactivex.Observable -fun MainViewModel.routeReducer(state: State, action: Action): Observable { - val mainState = (state as? MainState) ?: return Observable.just(state) - +fun MainViewModel.routeReducer(state: MainState, action: Action): MainState { return when (action) { is ClickCounterExampleButtonAction -> { - val mutatedState = mainState.copy(route = MainRoute.CounterExample) - Observable.just(mutatedState) + val mutatedState = state.copy(route = MainRoute.CounterExample) + mutatedState } is ClickCounterExample2ButtonAction -> { - val mutatedState = mainState.copy(route = MainRoute.CounterExample2) - Observable.just(mutatedState) + val mutatedState = state.copy(route = MainRoute.CounterExample2) + mutatedState } is ClickRecyclerViewExampleButtonAction -> { - val mutatedState = mainState.copy(route = MainRoute.RecyclerViewExample) - Observable.just(mutatedState) + val mutatedState = state.copy(route = MainRoute.RecyclerViewExample) + mutatedState } is ClickEmojiExampleButtonAction -> { - val mutatedState = mainState.copy(route = MainRoute.EmojiCollectionExample) - Observable.just(mutatedState) + val mutatedState = state.copy(route = MainRoute.EmojiCollectionExample) + mutatedState } is ClickCollectionViewExampleButtonAction -> { - val mutatedState = mainState.copy(route = MainRoute.CollectionViewExample) - Observable.just(mutatedState) + val mutatedState = state.copy(route = MainRoute.CollectionViewExample) + mutatedState } - else -> Observable.just(state) + else -> state } } \ No newline at end of file diff --git a/reactcomponentkit/build.gradle b/reactcomponentkit/build.gradle index 601b0a3..47228a5 100644 --- a/reactcomponentkit/build.gradle +++ b/reactcomponentkit/build.gradle @@ -41,7 +41,6 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } - } tasks.withType(Javadoc).all { diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt index 2d8a954..e5dbf28 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt @@ -77,7 +77,7 @@ abstract class ViewComponent: AnkoComponent, ReactComponent { * component(MyViewComponent(...)) * } */ -inline fun ViewManager.component(component: ViewComponent, theme: Int = 0): View { +fun ViewManager.component(component: ViewComponent, theme: Int = 0): View { return component(component, theme) {} } diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/eventbus/EventBus.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/eventbus/EventBus.kt index 20535c2..d671e29 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/eventbus/EventBus.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/eventbus/EventBus.kt @@ -1,3 +1,5 @@ +@file:Suppress("UNCHECKED_CAST") + package com.github.skyfe79.android.reactcomponentkit.eventbus import org.greenrobot.eventbus.Subscribe @@ -69,7 +71,7 @@ class EventBus(val token: Token? = null) { internal fun processNotification(notification: Notification) { if (!isAlive) return - val sender = notification.sender as? EventBus + val sender = notification.sender as? EventBus<*> if (sender == null || sender === this) return val userInfo = notification.userInfo diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/eventbus/Token.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/eventbus/Token.kt index b867398..1f4fb53 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/eventbus/Token.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/eventbus/Token.kt @@ -6,7 +6,7 @@ import java.util.* data class Token(val token: String = UUID.randomUUID().toString()) : Parcelable { constructor(source: Parcel) : this( - source.readString() + source.readString() ?: UUID.randomUUID().toString() ) override fun describeContents() = 0 diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewCell.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewCell.kt index f0758e6..a4a8e36 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewCell.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewCell.kt @@ -2,15 +2,11 @@ package com.github.skyfe79.android.reactcomponentkit.recyclerview import android.view.View import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView -import com.github.skyfe79.android.reactcomponentkit.R import com.github.skyfe79.android.reactcomponentkit.collectionmodels.ItemModel import com.github.skyfe79.android.reactcomponentkit.collectionview.SectionContent import com.github.skyfe79.android.reactcomponentkit.component.ViewComponent import com.github.skyfe79.android.reactcomponentkit.eventbus.Token -import kotlinx.android.synthetic.main.rck_nested_collection_view_component.view.* import org.jetbrains.anko.AnkoContext -import kotlin.reflect.KClass internal class RecyclerViewCell(private val token: Token, private val receiveState: Boolean = false) { @@ -42,8 +38,8 @@ internal class RecyclerViewCell(private val token: Token, private val receiveSta } inner class CollectionViewHolder(cellView: View): CollectionViewCellViewHolder(cellView) { - override fun onBind(content: SectionContent, position: Int) { - configure(content, position) + override fun onBind(contents: SectionContent, position: Int) { + configure(contents, position) } } } \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/After.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/After.kt index 50b171e..3984bcc 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/After.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/After.kt @@ -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 After = (S) -> S \ No newline at end of file +typealias After = (STATE) -> STATE \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Middleware.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Middleware.kt index 88a28db..47c025d 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Middleware.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Middleware.kt @@ -1,5 +1,3 @@ package com.github.skyfe79.android.reactcomponentkit.redux -import io.reactivex.Observable - -typealias Middleware = (State, Action) -> Observable \ No newline at end of file +typealias Middleware = (STATE, Action) -> STATE \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Postware.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Postware.kt index e86a6bf..19f9ac1 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Postware.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Postware.kt @@ -1,5 +1,3 @@ package com.github.skyfe79.android.reactcomponentkit.redux -import io.reactivex.Observable - -typealias Postware = (State, Action) -> Observable \ No newline at end of file +typealias Postware = (STATE, Action) -> STATE \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt index 9a1c439..8e50c76 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt @@ -1,5 +1,3 @@ package com.github.skyfe79.android.reactcomponentkit.redux -import io.reactivex.Observable - -typealias Reducer = (State, Action) -> Observable \ No newline at end of file +typealias Reducer = (STATE, Action) -> STATE \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt index a849ad8..f9ebac7 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt @@ -13,9 +13,9 @@ class Store { lateinit var state: S private set - private lateinit var middlewares: Array - private lateinit var reducers: Array - private lateinit var postwares: Array + private lateinit var middlewares: Array> + private lateinit var reducers: Array> + private lateinit var postwares: Array> private lateinit var afters: Array> private val disposables = CompositeDisposable() @@ -28,9 +28,9 @@ class Store { fun set( initialState: S, - middlewares: Array = emptyArray(), - reducers: Array = emptyArray(), - postwares: Array = emptyArray(), + middlewares: Array> = emptyArray(), + reducers: Array> = emptyArray(), + postwares: Array> = emptyArray(), afters: Array> = emptyArray()) { this.state = initialState this.middlewares = middlewares @@ -59,8 +59,7 @@ class Store { state = mutatedState } - @Suppress("UNCHECKED_CAST") - fun dispatch(action: Action): Single { + fun dispatch(action: Action): Single { return Single.create { single -> // reset error @@ -78,9 +77,7 @@ class Store { .observeOn(AndroidSchedulers.mainThread()) .subscribeBy( onNext = { newState -> - (newState as? S)?.let { - this@Store.state = it - } + this@Store.state = newState single.onSuccess(this@Store.state) }, onError = { error -> @@ -92,27 +89,24 @@ class Store { } } - @Suppress("UNCHECKED_CAST") - private fun middleware(state: State, action: Action): Observable { + private fun middleware(state: S, action: Action): Observable { if (middlewares.isEmpty()) return Observable.just(state) - return Single.create { single -> + return Single.create { single -> val disposable = middlewares.toObservable() .subscribeOn(Schedulers.single()) .observeOn(Schedulers.single()) - .flatMap { m -> + .map { m -> m(this@Store.state, action) } .doOnNext { modifiedState -> - (modifiedState as? S)?.let { - this@Store.state = it - } + this@Store.state = modifiedState } - .reduce { _: State, nextState: State -> + .reduce { _: S, nextState: S -> nextState } .subscribeBy( - onSuccess = { finalState: State -> + onSuccess = { finalState: S -> single.onSuccess(finalState) }, onError = { error -> @@ -124,24 +118,21 @@ class Store { }.toObservable() } - @Suppress("UNCHECKED_CAST") - private fun reduce(state: State, action: Action): Observable { + private fun reduce(state: S, action: Action): Observable { if (reducers.isEmpty()) return Observable.just(state) if (state.error != null) return Observable.just(state) - return Single.create { single -> + return Single.create { single -> val disposable = reducers.toObservable() .subscribeOn(Schedulers.single()) .observeOn(Schedulers.single()) - .flatMap { r -> + .map { r -> r(this@Store.state, action) } .doOnNext { modifiedState -> - (modifiedState as? S)?.let { - this@Store.state = it - } + this@Store.state = modifiedState } - .reduce { _: State, nextState: State -> + .reduce { _: S, nextState: S -> nextState } .subscribeBy( @@ -157,24 +148,21 @@ class Store { }.toObservable() } - @Suppress("UNCHECKED_CAST") - private fun postware(state: State, action: Action): Observable { + private fun postware(state: S, action: Action): Observable { if (postwares.isEmpty()) return Observable.just(state) if (state.error != null) return Observable.just(state) - return Single.create { single -> + return Single.create { single -> val disposable = postwares.toObservable() .subscribeOn(Schedulers.single()) .observeOn(Schedulers.single()) - .flatMap { p -> + .map { p -> p(this@Store.state, action) } .doOnNext { modifiedState -> - (modifiedState as? S)?.let { - this@Store.state = it - } + this@Store.state = modifiedState } - .reduce { _: State, nextState: State -> + .reduce { _: S, nextState: S -> nextState } .subscribeBy( From 7b3a355f0e80772e16a46cec5234c9f4300732dc Mon Sep 17 00:00:00 2001 From: skyfe79 Date: Sun, 25 Aug 2019 22:06:46 +0900 Subject: [PATCH 02/12] Remove Middleware and Postware --- .../collectionview/CollectionViewModel.kt | 5 +- .../collectionview/postwares/Postwares.kt | 51 ----- .../collectionview/reducer/Reducers.kt | 47 +++++ .../app/examples/counter/CounterViewModel.kt | 4 +- .../examples/counter2/CounterViewModel2.kt | 4 +- .../EmojiCollectionViewModel.kt | 16 +- .../emojicollection/middlewares/Route.kt | 16 -- .../emojicollection/postwares/Postwares.kt | 23 -- .../emojicollection/reducers/Reducers.kt | 24 ++- .../reactcomponentkit/redux/Middleware.kt | 2 +- .../reactcomponentkit/redux/Postware.kt | 2 +- .../android/reactcomponentkit/redux/Store.kt | 199 +++++++++++------- 12 files changed, 201 insertions(+), 192 deletions(-) delete mode 100644 app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/postwares/Postwares.kt delete mode 100644 app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/middlewares/Route.kt delete mode 100644 app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/postwares/Postwares.kt diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt index cd204aa..bd25b8c 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt @@ -2,7 +2,7 @@ package com.github.skyfe79.android.library.app.examples.collectionview import android.app.Application import com.github.skyfe79.android.library.app.examples.collectionview.action.LoadAction -import com.github.skyfe79.android.library.app.examples.collectionview.postwares.makeSectionModels +import com.github.skyfe79.android.library.app.examples.collectionview.reducer.makeSectionModels import com.github.skyfe79.android.library.app.examples.collectionview.reducer.loadEmoji import com.github.skyfe79.android.reactcomponentkit.collectionmodels.DefaultSectionModel import com.github.skyfe79.android.reactcomponentkit.redux.Action @@ -26,8 +26,7 @@ class CollectionViewModel(application: Application): RootAndroidViewModelType { - - val colors = listOf( - 0xffd01774.toInt(), - 0xfff7f93c.toInt(), - 0xfff07777.toInt(), - 0xfffcce62.toInt(), - 0xff58c8d8.toInt() - ) - - val sectionModels = state.emojis.mapIndexed { index, list -> - val emojiBoxModels = list.map { EmojiBoxModel(it) } - DefaultSectionModel( - emojiBoxModels, - header = TextMessage("Section Header #$index", colors[index], true), - footer = TextMessage("Section Footer #$index", colors[index], false)) - { recyclerView -> - recyclerView.layoutManager = when(index) { - 0 -> GridLayoutManager(getApplication(), 8) - 1 -> GridLayoutManager(getApplication(), 4) - 2 -> StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL) - 3 -> GridLayoutManager(getApplication(), 3) - else -> LinearLayoutManager(getApplication(), LinearLayoutManager.HORIZONTAL, false) - } - - if (index == 4) { - val snapHelper = LinearSnapHelper() - snapHelper.attachToRecyclerView(recyclerView) - } - } - } - state.copy(sections = sectionModels) - } - else -> state - } -} \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt index d5bb1c1..d5725e2 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt @@ -1,9 +1,16 @@ package com.github.skyfe79.android.library.app.examples.collectionview.reducer +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.LinearSnapHelper +import androidx.recyclerview.widget.StaggeredGridLayoutManager import com.github.skyfe79.android.library.app.examples.collectionview.CollectionState import com.github.skyfe79.android.library.app.examples.collectionview.CollectionViewModel import com.github.skyfe79.android.library.app.examples.collectionview.action.LoadAction +import com.github.skyfe79.android.library.app.examples.emojicollection.models.EmojiBoxModel import com.github.skyfe79.android.library.app.examples.emojicollection.util.EmojiHelper +import com.github.skyfe79.android.library.app.examples.recyclerview.model.TextMessage +import com.github.skyfe79.android.reactcomponentkit.collectionmodels.DefaultSectionModel import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State import io.reactivex.Observable @@ -19,4 +26,44 @@ fun CollectionViewModel.loadEmoji(state: CollectionState, action: Action): Colle } else -> state } +} + +fun CollectionViewModel.makeSectionModels(state: CollectionState, action: Action): CollectionState { + return when (action) { + is LoadAction -> { + + val colors = listOf( + 0xffd01774.toInt(), + 0xfff7f93c.toInt(), + 0xfff07777.toInt(), + 0xfffcce62.toInt(), + 0xff58c8d8.toInt() + ) + + val sectionModels = state.emojis.mapIndexed { index, list -> + val emojiBoxModels = list.map { EmojiBoxModel(it) } + DefaultSectionModel( + emojiBoxModels, + header = TextMessage("Section Header #$index", colors[index], true), + footer = TextMessage("Section Footer #$index", colors[index], false) + ) + { recyclerView -> + recyclerView.layoutManager = when(index) { + 0 -> GridLayoutManager(getApplication(), 8) + 1 -> GridLayoutManager(getApplication(), 4) + 2 -> StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL) + 3 -> GridLayoutManager(getApplication(), 3) + else -> LinearLayoutManager(getApplication(), LinearLayoutManager.HORIZONTAL, false) + } + + if (index == 4) { + val snapHelper = LinearSnapHelper() + snapHelper.attachToRecyclerView(recyclerView) + } + } + } + state.copy(sections = sectionModels) + } + else -> state + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt index 6c8579e..18d072a 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt @@ -13,9 +13,7 @@ class CounterViewModel: RootViewModelType() { override fun setupStore() { store.set( initialState = CounterState(0), - middlewares = arrayOf(), - reducers = arrayOf(::countReducer), - postwares = arrayOf() + reducers = arrayOf(::countReducer) ) } diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt index 7a24a67..d7d530c 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt @@ -11,9 +11,7 @@ class CounterViewModel2: RootViewModelType() { override fun setupStore() { store.set( initialState = CounterState(0), - middlewares = arrayOf(), - reducers = arrayOf(::countReducer), - postwares = arrayOf() + reducers = arrayOf(::countReducer) ) } diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt index 0ac7c22..124f2db 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt @@ -1,11 +1,9 @@ package com.github.skyfe79.android.library.app.examples.emojicollection -import com.github.skyfe79.android.library.app.examples.emojicollection.middlewares.route -import com.github.skyfe79.android.library.app.examples.emojicollection.postwares.* -import com.github.skyfe79.android.library.app.examples.emojicollection.reducers.addEmoji -import com.github.skyfe79.android.library.app.examples.emojicollection.reducers.removeEmoji -import com.github.skyfe79.android.library.app.examples.emojicollection.reducers.shuffleEmoji +import android.util.Log +import com.github.skyfe79.android.library.app.examples.emojicollection.reducers.* import com.github.skyfe79.android.reactcomponentkit.collectionmodels.ItemModel +import com.github.skyfe79.android.reactcomponentkit.redux.Error import com.github.skyfe79.android.reactcomponentkit.redux.Output import com.github.skyfe79.android.reactcomponentkit.redux.State import com.github.skyfe79.android.reactcomponentkit.viewmodel.RootViewModelType @@ -29,9 +27,7 @@ class EmojiCollectionViewModel: RootViewModelType() { override fun setupStore() { store.set( initialState = EmojiCollectionState(listOf(), listOf()), - middlewares = arrayOf(::route), - reducers = arrayOf(::addEmoji, ::removeEmoji, ::shuffleEmoji), - postwares = arrayOf(::makeItemModels) + reducers = arrayOf(::route, ::addEmoji, ::removeEmoji, ::shuffleEmoji, ::makeItemModels) ) } @@ -39,4 +35,8 @@ class EmojiCollectionViewModel: RootViewModelType() { itemModels.accept(newState.itemModels) routes.accept(newState.route).afterReset(EmojiRoute.None) } + + override fun on(error: Error) { + Log.d("ERROR", "$error") + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/middlewares/Route.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/middlewares/Route.kt deleted file mode 100644 index c25b6b2..0000000 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/middlewares/Route.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.github.skyfe79.android.library.app.examples.emojicollection.middlewares - -import com.github.skyfe79.android.library.app.examples.emojicollection.EmojiCollectionState -import com.github.skyfe79.android.library.app.examples.emojicollection.EmojiCollectionViewModel -import com.github.skyfe79.android.library.app.examples.emojicollection.EmojiRoute -import com.github.skyfe79.android.library.app.examples.emojicollection.components.ClickEmojiAction -import com.github.skyfe79.android.reactcomponentkit.redux.Action -import com.github.skyfe79.android.reactcomponentkit.redux.State -import io.reactivex.Observable - -fun EmojiCollectionViewModel.route(state: EmojiCollectionState, action: Action): EmojiCollectionState { - return when(action) { - is ClickEmojiAction -> state.copy(route = EmojiRoute.AlertEmoji(action.emoji)) - else -> state.copy(route = EmojiRoute.None) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/postwares/Postwares.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/postwares/Postwares.kt deleted file mode 100644 index 3216780..0000000 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/postwares/Postwares.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.github.skyfe79.android.library.app.examples.emojicollection.postwares - -import com.github.skyfe79.android.library.app.examples.emojicollection.EmojiCollectionState -import com.github.skyfe79.android.library.app.examples.emojicollection.EmojiCollectionViewModel -import com.github.skyfe79.android.library.app.examples.emojicollection.actions.AddEmojiAction -import com.github.skyfe79.android.library.app.examples.emojicollection.actions.MakeItemModelsAction -import com.github.skyfe79.android.library.app.examples.emojicollection.actions.RemoveEmojiAction -import com.github.skyfe79.android.library.app.examples.emojicollection.actions.ShuffleEmojiAction -import com.github.skyfe79.android.library.app.examples.emojicollection.models.EmojiBoxModel -import com.github.skyfe79.android.reactcomponentkit.redux.Action -import com.github.skyfe79.android.reactcomponentkit.redux.State -import io.reactivex.Observable - -fun EmojiCollectionViewModel.makeItemModels(state: EmojiCollectionState, action: Action): EmojiCollectionState { - return when (action) { - is MakeItemModelsAction -> { - val emojiBoxModels = state.emojis.map { EmojiBoxModel(it) } - val mutatedState = state.copy(itemModels = emojiBoxModels) - mutatedState - } - else -> state - } -} \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt index 7c4f5c7..7839414 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt @@ -2,10 +2,13 @@ package com.github.skyfe79.android.library.app.examples.emojicollection.reducers import com.github.skyfe79.android.library.app.examples.emojicollection.EmojiCollectionState import com.github.skyfe79.android.library.app.examples.emojicollection.EmojiCollectionViewModel +import com.github.skyfe79.android.library.app.examples.emojicollection.EmojiRoute import com.github.skyfe79.android.library.app.examples.emojicollection.actions.AddEmojiAction import com.github.skyfe79.android.library.app.examples.emojicollection.actions.MakeItemModelsAction import com.github.skyfe79.android.library.app.examples.emojicollection.actions.RemoveEmojiAction import com.github.skyfe79.android.library.app.examples.emojicollection.actions.ShuffleEmojiAction +import com.github.skyfe79.android.library.app.examples.emojicollection.components.ClickEmojiAction +import com.github.skyfe79.android.library.app.examples.emojicollection.models.EmojiBoxModel import com.github.skyfe79.android.library.app.examples.emojicollection.util.EmojiHelper import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State @@ -15,6 +18,13 @@ import io.reactivex.rxkotlin.subscribeBy import java.lang.Exception import kotlin.random.Random +fun EmojiCollectionViewModel.route(state: EmojiCollectionState, action: Action): EmojiCollectionState { + return when(action) { + is ClickEmojiAction -> state.copy(route = EmojiRoute.AlertEmoji(action.emoji)) + else -> state.copy(route = EmojiRoute.None) + } +} + fun EmojiCollectionViewModel.addEmoji(state: EmojiCollectionState, action: Action): EmojiCollectionState { return when (action) { is AddEmojiAction -> { @@ -40,8 +50,7 @@ fun EmojiCollectionViewModel.removeEmoji(state: EmojiCollectionState, action: Ac nextDispatch(MakeItemModelsAction, applyNewState = true) mutatedState } catch (e: Exception) { - // ignore - state + throw e } } else -> state @@ -59,4 +68,15 @@ fun EmojiCollectionViewModel.shuffleEmoji(state: EmojiCollectionState, action: A } else -> state } +} + +fun EmojiCollectionViewModel.makeItemModels(state: EmojiCollectionState, action: Action): EmojiCollectionState { + return when (action) { + is MakeItemModelsAction -> { + val emojiBoxModels = state.emojis.map { EmojiBoxModel(it) } + val mutatedState = state.copy(itemModels = emojiBoxModels) + mutatedState + } + else -> state + } } \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Middleware.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Middleware.kt index 47c025d..f669565 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Middleware.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Middleware.kt @@ -1,3 +1,3 @@ package com.github.skyfe79.android.reactcomponentkit.redux -typealias Middleware = (STATE, Action) -> STATE \ No newline at end of file +//typealias Middleware = (STATE, Action) -> STATE \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Postware.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Postware.kt index 19f9ac1..f952a62 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Postware.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Postware.kt @@ -1,3 +1,3 @@ package com.github.skyfe79.android.reactcomponentkit.redux -typealias Postware = (STATE, Action) -> STATE \ No newline at end of file +//typealias Postware = (STATE, Action) -> STATE \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt index f9ebac7..1f5f2d4 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt @@ -13,9 +13,9 @@ class Store { lateinit var state: S private set - private lateinit var middlewares: Array> +// private lateinit var middlewares: Array> private lateinit var reducers: Array> - private lateinit var postwares: Array> +// private lateinit var postwares: Array> private lateinit var afters: Array> private val disposables = CompositeDisposable() @@ -28,21 +28,21 @@ class Store { fun set( initialState: S, - middlewares: Array> = emptyArray(), +// middlewares: Array> = emptyArray(), reducers: Array> = emptyArray(), - postwares: Array> = emptyArray(), +// postwares: Array> = emptyArray(), afters: Array> = emptyArray()) { this.state = initialState - this.middlewares = middlewares +// this.middlewares = middlewares this.reducers = reducers - this.postwares = postwares +// this.postwares = postwares this.afters = afters } fun deinitialize() { - middlewares = emptyArray() +// middlewares = emptyArray() reducers = emptyArray() - postwares = emptyArray() +// postwares = emptyArray() afters = emptyArray() disposables.clear() } @@ -61,43 +61,14 @@ class Store { fun dispatch(action: Action): Single { return Single.create { single -> - // reset error this@Store.state.error = null - val disposable = middleware(this@Store.state, action) - .subscribeOn(Schedulers.single()) - .observeOn(Schedulers.single()) - .flatMap { middlewareState -> - reduce(middlewareState, action) - } - .flatMap { reducedState -> - postware(reducedState, action) - } - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onNext = { newState -> - this@Store.state = newState - single.onSuccess(this@Store.state) - }, - onError = { error -> - this@Store.state.error = Error(error, action) - single.onSuccess(this@Store.state) - } - ) - disposables.add(disposable) - } - } - - private fun middleware(state: S, action: Action): Observable { - if (middlewares.isEmpty()) return Observable.just(state) - - return Single.create { single -> - val disposable = middlewares.toObservable() + val disposable = reducers.toObservable() .subscribeOn(Schedulers.single()) .observeOn(Schedulers.single()) - .map { m -> - m(this@Store.state, action) + .map { reducer -> + reducer(this@Store.state, action) } .doOnNext { modifiedState -> this@Store.state = modifiedState @@ -114,30 +85,33 @@ class Store { single.onSuccess(this@Store.state) } ) + disposables.add(disposable) - }.toObservable() + } } - private fun reduce(state: S, action: Action): Observable { - if (reducers.isEmpty()) return Observable.just(state) - if (state.error != null) return Observable.just(state) + /* - return Single.create { single -> - val disposable = reducers.toObservable() + fun dispatch(action: Action): Single { + return Single.create { single -> + + // reset error + this@Store.state.error = null + + val disposable = middleware(this@Store.state, action) .subscribeOn(Schedulers.single()) .observeOn(Schedulers.single()) - .map { r -> - r(this@Store.state, action) - } - .doOnNext { modifiedState -> - this@Store.state = modifiedState + .flatMap { middlewareState -> + reduce(middlewareState, action) } - .reduce { _: S, nextState: S -> - nextState + .flatMap { reducedState -> + postware(reducedState, action) } + .observeOn(AndroidSchedulers.mainThread()) .subscribeBy( - onSuccess = { finalState -> - single.onSuccess(finalState) + onNext = { newState -> + this@Store.state = newState + single.onSuccess(this@Store.state) }, onError = { error -> this@Store.state.error = Error(error, action) @@ -145,36 +119,99 @@ class Store { } ) disposables.add(disposable) - }.toObservable() + } } - private fun postware(state: S, action: Action): Observable { - if (postwares.isEmpty()) return Observable.just(state) - if (state.error != null) return Observable.just(state) - return Single.create { single -> - val disposable = postwares.toObservable() - .subscribeOn(Schedulers.single()) - .observeOn(Schedulers.single()) - .map { p -> - p(this@Store.state, action) + +private fun middleware(state: S, action: Action): Observable { + if (middlewares.isEmpty()) return Observable.just(state) + + return Single.create { single -> + val disposable = middlewares.toObservable() + .subscribeOn(Schedulers.single()) + .observeOn(Schedulers.single()) + .map { m -> + m(this@Store.state, action) + } + .doOnNext { modifiedState -> + this@Store.state = modifiedState + } + .reduce { _: S, nextState: S -> + nextState + } + .subscribeBy( + onSuccess = { finalState: S -> + single.onSuccess(finalState) + }, + onError = { error -> + this@Store.state.error = Error(error, action) + single.onSuccess(this@Store.state) } - .doOnNext { modifiedState -> - this@Store.state = modifiedState + ) + disposables.add(disposable) + }.toObservable() +} + +private fun reduce(state: S, action: Action): Observable { + if (reducers.isEmpty()) return Observable.just(state) + if (state.error != null) return Observable.just(state) + + return Single.create { single -> + val disposable = reducers.toObservable() + .subscribeOn(Schedulers.single()) + .observeOn(Schedulers.single()) + .map { r -> + r(this@Store.state, action) + } + .doOnNext { modifiedState -> + this@Store.state = modifiedState + } + .reduce { _: S, nextState: S -> + nextState + } + .subscribeBy( + onSuccess = { finalState -> + single.onSuccess(finalState) + }, + onError = { error -> + this@Store.state.error = Error(error, action) + single.onSuccess(this@Store.state) } - .reduce { _: S, nextState: S -> - nextState + ) + disposables.add(disposable) + }.toObservable() +} + +private fun postware(state: S, action: Action): Observable { + if (postwares.isEmpty()) return Observable.just(state) + if (state.error != null) return Observable.just(state) + + return Single.create { single -> + val disposable = postwares.toObservable() + .subscribeOn(Schedulers.single()) + .observeOn(Schedulers.single()) + .map { p -> + p(this@Store.state, action) + } + .doOnNext { modifiedState -> + this@Store.state = modifiedState + } + .reduce { _: S, nextState: S -> + nextState + } + .subscribeBy( + onSuccess = { finalState -> + single.onSuccess(finalState) + }, + onError = { error -> + this@Store.state.error = Error(error, action) + single.onSuccess(this@Store.state) } - .subscribeBy( - onSuccess = { finalState -> - single.onSuccess(finalState) - }, - onError = { error -> - this@Store.state.error = Error(error, action) - single.onSuccess(this@Store.state) - } - ) - disposables.add(disposable) - }.toObservable() - } + ) + disposables.add(disposable) + }.toObservable() +} + + */ } \ No newline at end of file From bce3358b76869988264406c2e7891ce68c78b263 Mon Sep 17 00:00:00 2001 From: skyfe79 Date: Sun, 25 Aug 2019 22:13:25 +0900 Subject: [PATCH 03/12] Cleanup useless codes --- .../skyfe79/android/reactcomponentkit/redux/Store.kt | 8 -------- .../android/reactcomponentkit/redux/ViewModelType.kt | 6 +----- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt index 1f5f2d4..2690a43 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt @@ -13,9 +13,7 @@ class Store { lateinit var state: S private set -// private lateinit var middlewares: Array> private lateinit var reducers: Array> -// private lateinit var postwares: Array> private lateinit var afters: Array> private val disposables = CompositeDisposable() @@ -28,21 +26,15 @@ class Store { fun set( initialState: S, -// middlewares: Array> = emptyArray(), reducers: Array> = emptyArray(), -// postwares: Array> = emptyArray(), afters: Array> = emptyArray()) { this.state = initialState -// this.middlewares = middlewares this.reducers = reducers -// this.postwares = postwares this.afters = afters } fun deinitialize() { -// middlewares = emptyArray() reducers = emptyArray() -// postwares = emptyArray() afters = emptyArray() disposables.clear() } diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/ViewModelType.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/ViewModelType.kt index 7360155..cfb71ba 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/ViewModelType.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/ViewModelType.kt @@ -1,6 +1,5 @@ package com.github.skyfe79.android.reactcomponentkit.redux -import android.util.Log import androidx.lifecycle.ViewModel import com.jakewharton.rxrelay2.BehaviorRelay import io.reactivex.android.schedulers.AndroidSchedulers @@ -20,7 +19,7 @@ abstract class ViewModelType: ViewModel() { init { setupRxStream() - setupStore() + this.setupStore() } override fun onCleared() { @@ -62,9 +61,6 @@ abstract class ViewModelType: ViewModel() { store.dispatch(action).toObservable() } .observeOn(AndroidSchedulers.mainThread()) - .map { state -> - state as? S - } .subscribe { newState -> rx_state.accept(newState) } From fa981c35277d487cf6d2c68598af0deafab30e59 Mon Sep 17 00:00:00 2001 From: skyfe79 Date: Sun, 25 Aug 2019 23:36:42 +0900 Subject: [PATCH 04/12] Make store as private and some helper methods on ViewModel --- .../android/library/app/MainViewModel.kt | 10 ++-- .../app/examples/counter/CounterViewModel.kt | 10 ++-- .../app/examples/counter/redux/Reducers.kt | 46 ++++++++++++++++--- .../examples/counter2/CounterViewModel2.kt | 10 ++-- .../EmojiCollectionViewModel.kt | 10 ++-- .../reactcomponentkit/redux/Middleware.kt | 3 -- .../reactcomponentkit/redux/Postware.kt | 3 -- .../android/reactcomponentkit/redux/State.kt | 1 + .../reactcomponentkit/redux/ViewModelType.kt | 31 ++++++++++++- 9 files changed, 94 insertions(+), 30 deletions(-) delete mode 100644 reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Middleware.kt delete mode 100644 reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Postware.kt diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt index f406d8b..5635743 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt @@ -20,10 +20,12 @@ class MainViewModel: RootViewModelType() { val route: Output = Output(MainRoute.None) override fun setupStore() { - store.set( - initialState = MainState(MainRoute.None), - reducers = arrayOf(::routeReducer) - ) + initStore { store -> + store.set( + initialState = MainState(MainRoute.None), + reducers = arrayOf(::routeReducer) + ) + } } override fun on(newState: MainState) { diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt index 18d072a..7668138 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt @@ -11,10 +11,12 @@ class CounterViewModel: RootViewModelType() { val count: Output = Output(0) override fun setupStore() { - store.set( - initialState = CounterState(0), - reducers = arrayOf(::countReducer) - ) + initStore { store -> + store.set( + initialState = CounterState(0), + reducers = arrayOf(::countReducer) + ) + } } override fun on(newState: CounterState) { diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt index 68216a3..433fde6 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt @@ -2,22 +2,54 @@ package com.github.skyfe79.android.library.app.examples.counter.redux import com.github.skyfe79.android.library.app.examples.counter.CounterState import com.github.skyfe79.android.library.app.examples.counter.CounterViewModel +import com.github.skyfe79.android.library.app.examples.counter.CounterViewModel2 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.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State +import com.github.skyfe79.android.reactcomponentkit.util.async import io.reactivex.Observable -fun countReducer(state: CounterState, action: Action): CounterState { + +//목표는 리듀서와 액션을 없애는 것이다. +//단 주의할 점은 RCK는 상태 mutation을 모두 백그라운드에서 실행하고 있다. +//과연 이것이 필요한 것일까? +//async 만 백그라운드에서 실행하면 되지 않을까? +fun CounterViewModel.countReducer(state: CounterState, action: Action): CounterState { + return when(action) { + is IncreaseAction -> increment(action.payload) + is DecreaseAction -> decrement(action.payload) + else -> state + } +} + +fun CounterViewModel2.countReducer(state: CounterState, action: Action): CounterState { return when(action) { - is IncreaseAction -> { - val mutatedState = state.copy(count = state.count + action.payload) - mutatedState + is IncreaseAction -> setState { + with(it) { copy(count = count + action.payload) } } - is DecreaseAction -> { - val mutatedState = state.copy(count = state.count - action.payload) - mutatedState + is DecreaseAction -> setState { + with(it) { copy(count = count - action.payload) } } else -> state } +} + + +// 이렇게 정의한 리듀서를 액션 없이 각 컴포넌트에서 사용할 수 있어야 한다. +// 각 컴포넌트는 ViewModel을 받는다. 그리고 dispatch wrapper 함수로 감싼다. +fun CounterViewModel.increment(payload: Int) = setState { + with(it) { copy(count = count + payload) } + // 여기 내부에서 호출할 수 있는 다른 메서드는 오직 리듀서 뿐이다. + // decrement(payload) + // async를 어떻게 할 것인가? + /* + async { + increment(payload) + } + */ +} + +fun CounterViewModel.decrement(payload: Int) = setState { + with(it) { copy(count = count - payload) } } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt index d7d530c..179bfed 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt @@ -9,10 +9,12 @@ class CounterViewModel2: RootViewModelType() { val count: Output = Output(0) override fun setupStore() { - store.set( - initialState = CounterState(0), - reducers = arrayOf(::countReducer) - ) + initStore { store -> + store.set( + initialState = CounterState(0), + reducers = arrayOf(::countReducer) + ) + } } override fun on(newState: CounterState) { diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt index 124f2db..6658400 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt @@ -25,10 +25,12 @@ class EmojiCollectionViewModel: RootViewModelType() { val routes = Output(EmojiRoute.None) override fun setupStore() { - store.set( - initialState = EmojiCollectionState(listOf(), listOf()), - reducers = arrayOf(::route, ::addEmoji, ::removeEmoji, ::shuffleEmoji, ::makeItemModels) - ) + initStore { store -> + store.set( + initialState = EmojiCollectionState(listOf(), listOf()), + reducers = arrayOf(::route, ::addEmoji, ::removeEmoji, ::shuffleEmoji, ::makeItemModels) + ) + } } override fun on(newState: EmojiCollectionState) { diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Middleware.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Middleware.kt deleted file mode 100644 index f669565..0000000 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Middleware.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.github.skyfe79.android.reactcomponentkit.redux - -//typealias Middleware = (STATE, Action) -> STATE \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Postware.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Postware.kt deleted file mode 100644 index f952a62..0000000 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Postware.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.github.skyfe79.android.reactcomponentkit.redux - -//typealias Postware = (STATE, Action) -> STATE \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/State.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/State.kt index c4c49ff..36c6505 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/State.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/State.kt @@ -1,5 +1,6 @@ package com.github.skyfe79.android.reactcomponentkit.redux + abstract class State { var error: Error? = null } diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/ViewModelType.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/ViewModelType.kt index cfb71ba..d38b761 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/ViewModelType.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/ViewModelType.kt @@ -5,17 +5,20 @@ import com.jakewharton.rxrelay2.BehaviorRelay import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.locks.ReentrantLock abstract class ViewModelType: ViewModel() { private val rx_action: BehaviorRelay = BehaviorRelay.createDefault(VoidAction) private val rx_state: BehaviorRelay = BehaviorRelay.create() - val store = Store() + private val store = Store() private val disposables = CompositeDisposable() private var applyNewState: Boolean = false private var actionQueue: Queue> = Queue() private var isProcessingAction: AtomicBoolean = AtomicBoolean(false) + private val writeLock = ReentrantLock() + private val readLock = ReentrantLock() init { setupRxStream() @@ -149,4 +152,30 @@ abstract class ViewModelType: ViewModel() { open fun on(error: Error) = Unit abstract fun on(newState: S) + + fun initStore(block: ViewModelType.(Store) -> Unit) { + block(this.store) + } + + fun setState(block: ViewModelType.(S) -> S): S { + writeLock.lock() + try { + val newState = block(this.store.state) + this.on(newState) + return newState + } finally { + writeLock.unlock() + } + } + + fun withState(block: ViewModelType.(S) -> Unit) { + readLock.lock() + try { + block(this.store.state) + } finally { + readLock.unlock() + } + } + + } \ No newline at end of file From a15aa803b7827bf1d052a20f28eedb96d2cb2b6f Mon Sep 17 00:00:00 2001 From: skyfe79 Date: Sun, 25 Aug 2019 23:52:32 +0900 Subject: [PATCH 05/12] Removing EventBus & Actions --- .../android/library/app/MainViewModel.kt | 3 +- .../collectionview/CollectionViewModel.kt | 21 ++- .../app/examples/counter/CounterViewModel.kt | 3 +- .../app/examples/counter/redux/Reducers.kt | 3 - .../examples/counter2/CounterViewModel2.kt | 3 +- .../EmojiCollectionViewModel.kt | 3 +- .../redux/AndroidViewModelType.kt | 158 ------------------ .../reactcomponentkit/redux/Reducer.kt | 2 +- .../reactcomponentkit/redux/ViewModelType.kt | 5 +- .../android/reactcomponentkit/util/Tasks.kt | 5 +- .../viewmodel/RootAndroidViewModelType.kt | 35 ---- .../viewmodel/RootViewModelType.kt | 5 +- 12 files changed, 30 insertions(+), 216 deletions(-) delete mode 100644 reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/AndroidViewModelType.kt delete mode 100644 reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RootAndroidViewModelType.kt diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt index 5635743..765d546 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt @@ -1,5 +1,6 @@ package com.github.skyfe79.android.library.app +import android.app.Application import com.github.skyfe79.android.reactcomponentkit.redux.Output import com.github.skyfe79.android.reactcomponentkit.redux.State import com.github.skyfe79.android.reactcomponentkit.viewmodel.RootViewModelType @@ -15,7 +16,7 @@ enum class MainRoute { } data class MainState(var route: MainRoute): State() -class MainViewModel: RootViewModelType() { +class MainViewModel(application: Application): RootViewModelType(application) { val route: Output = Output(MainRoute.None) diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt index bd25b8c..6dcb9cc 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt @@ -2,14 +2,13 @@ package com.github.skyfe79.android.library.app.examples.collectionview import android.app.Application import com.github.skyfe79.android.library.app.examples.collectionview.action.LoadAction -import com.github.skyfe79.android.library.app.examples.collectionview.reducer.makeSectionModels -import com.github.skyfe79.android.library.app.examples.collectionview.reducer.loadEmoji +import com.github.skyfe79.android.library.app.examples.collectionview.reducer.* import com.github.skyfe79.android.reactcomponentkit.collectionmodels.DefaultSectionModel import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.Output import com.github.skyfe79.android.reactcomponentkit.redux.State import com.github.skyfe79.android.reactcomponentkit.redux.VoidAction -import com.github.skyfe79.android.reactcomponentkit.viewmodel.RootAndroidViewModelType +import com.github.skyfe79.android.reactcomponentkit.viewmodel.RootViewModelType data class CollectionState( @@ -19,20 +18,24 @@ data class CollectionState( -class CollectionViewModel(application: Application): RootAndroidViewModelType(application) { +class CollectionViewModel(application: Application): RootViewModelType(application) { val sections: Output> = Output(emptyList()) override fun setupStore() { - store.set( - initialState = CollectionState(), - reducers = arrayOf(::loadEmoji, ::makeSectionModels) - ) + initStore { store -> + store.set( + initialState = CollectionState(), + reducers = arrayOf(::loadEmoji, ::makeSectionModels) + ) + } + } override fun beforeDispatch(action: Action): Action = when(action) { is LoadAction -> { - if (store.state.emojis.isNotEmpty()) VoidAction else action + VoidAction + //if (store.state.emojis.isNotEmpty()) VoidAction else action } else -> action } diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt index 7668138..f00d1e5 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt @@ -1,12 +1,13 @@ package com.github.skyfe79.android.library.app.examples.counter +import android.app.Application import com.github.skyfe79.android.library.app.examples.counter.redux.countReducer import com.github.skyfe79.android.reactcomponentkit.redux.* import com.github.skyfe79.android.reactcomponentkit.viewmodel.RootViewModelType data class CounterState(val count: Int): State() -class CounterViewModel: RootViewModelType() { +class CounterViewModel(application: Application): RootViewModelType(application) { val count: Output = Output(0) diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt index 433fde6..d2629ba 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt @@ -6,9 +6,6 @@ import com.github.skyfe79.android.library.app.examples.counter.CounterViewModel2 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.reactcomponentkit.redux.Action -import com.github.skyfe79.android.reactcomponentkit.redux.State -import com.github.skyfe79.android.reactcomponentkit.util.async -import io.reactivex.Observable //목표는 리듀서와 액션을 없애는 것이다. diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt index 179bfed..9ad0890 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt @@ -1,10 +1,11 @@ package com.github.skyfe79.android.library.app.examples.counter +import android.app.Application import com.github.skyfe79.android.library.app.examples.counter.redux.countReducer import com.github.skyfe79.android.reactcomponentkit.redux.Output import com.github.skyfe79.android.reactcomponentkit.viewmodel.RootViewModelType -class CounterViewModel2: RootViewModelType() { +class CounterViewModel2(application: Application): RootViewModelType(application) { val count: Output = Output(0) diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt index 6658400..abcdc77 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt @@ -1,5 +1,6 @@ package com.github.skyfe79.android.library.app.examples.emojicollection +import android.app.Application import android.util.Log import com.github.skyfe79.android.library.app.examples.emojicollection.reducers.* import com.github.skyfe79.android.reactcomponentkit.collectionmodels.ItemModel @@ -19,7 +20,7 @@ data class EmojiCollectionState( val route: EmojiRoute = EmojiRoute.None ): State() -class EmojiCollectionViewModel: RootViewModelType() { +class EmojiCollectionViewModel(application: Application): RootViewModelType(application) { val itemModels = Output>(listOf()) val routes = Output(EmojiRoute.None) diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/AndroidViewModelType.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/AndroidViewModelType.kt deleted file mode 100644 index 536d067..0000000 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/AndroidViewModelType.kt +++ /dev/null @@ -1,158 +0,0 @@ -package com.github.skyfe79.android.reactcomponentkit.redux - -import android.app.Application -import android.util.Log -import androidx.lifecycle.AndroidViewModel -import com.github.skyfe79.android.reactcomponentkit.redux.State -import com.jakewharton.rxrelay2.BehaviorRelay -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable -import java.util.concurrent.atomic.AtomicBoolean - -abstract class AndroidViewModelType(application: Application): AndroidViewModel(application) { - private val rx_action: BehaviorRelay = BehaviorRelay.createDefault(VoidAction) - private val rx_state: BehaviorRelay = BehaviorRelay.create() - - val store = Store() - private val disposables = CompositeDisposable() - private var nextAction: Action? = null - private var applyNewState: Boolean = false - private var actionQueue: Queue> = Queue() - private var isProcessingAction: AtomicBoolean = AtomicBoolean(false) - - init { - setupRxStream() - setupStore() - } - - override fun onCleared() { - clear() - super.onCleared() - } - - - fun clear() { - disposables.dispose() - store.deinitialize() - } - - private fun setupRxStream() { - val disposable1 = rx_action - .doOnNext { - isProcessingAction.set(true) - } - .filter { action -> - if (action !is VoidAction) { - true - } else { - isProcessingAction.set(false) - false - } - } - .map { action -> - beforeDispatch(action) - } - .filter { action -> - if (action !is VoidAction) { - true - } else { - isProcessingAction.set(false) - false - } - } - .flatMap { action -> - store.dispatch(action).toObservable() - } - .observeOn(AndroidSchedulers.mainThread()) - .map { state -> - state as? S - } - .subscribe { newState -> - rx_state.accept(newState) - } - - disposables.add(disposable1) - - val disposable2 = rx_state - .observeOn(AndroidSchedulers.mainThread()) - .doAfterNext { - isProcessingAction.set(false) - - val actionItem = actionQueue.peek() - if (actionItem != null ) { - val (nextAction, _) = actionItem - rx_action.accept(nextAction) - // deque actions after processing - actionQueue.dequeue() - } - } - .doOnError { - isProcessingAction.set(false) - val actionItem = actionQueue.peek() - if (actionItem != null ) { - actionQueue.dequeue() - } - } - .subscribe { newState -> - if (newState == null) return@subscribe - if (newState.error != null) { - newState.error?.let { - on(it) - store.doAfters() - } - } else { - val actionItem = actionQueue.peek() - if (actionItem != null) { - val (_, apply) = actionItem - if (apply) { - on(newState) - store.doAfters() - } - } else { - on(newState) - store.doAfters() - } - } - } - disposables.add(disposable2) - } - - abstract fun setupStore() - - fun dispatch(action: Action) { - if (actionQueue.isNotEmpty) { - if (isProcessingAction.get()) { - actionQueue.enqueue(Pair(action, applyNewState)) - } else { - rx_action.accept(action) - } - } else { - if (isProcessingAction.get()) { - actionQueue.enqueue(Pair(action, applyNewState)) - } else { - rx_action.accept(action) - } - } - } - - fun nextDispatch(action: Action, applyNewState: Boolean = false) { - if (actionQueue.isNotEmpty) { - if (isProcessingAction.get()) { - actionQueue.enqueue(Pair(action, applyNewState)) - } else { - rx_action.accept(action) - } - } else { - if (isProcessingAction.get()) { - actionQueue.enqueue(Pair(action, applyNewState)) - } else { - rx_action.accept(action) - } - } - } - - open fun beforeDispatch(action: Action): Action = action - open fun on(error: Error) = Unit - - abstract fun on(newState: S) -} \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt index 8e50c76..ee58a5a 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt @@ -1,3 +1,3 @@ package com.github.skyfe79.android.reactcomponentkit.redux -typealias Reducer = (STATE, Action) -> STATE \ No newline at end of file +typealias Reducer = (STATE, Action) -> STATE diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/ViewModelType.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/ViewModelType.kt index d38b761..172cf48 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/ViewModelType.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/ViewModelType.kt @@ -1,13 +1,14 @@ package com.github.skyfe79.android.reactcomponentkit.redux -import androidx.lifecycle.ViewModel +import android.app.Application +import androidx.lifecycle.AndroidViewModel import com.jakewharton.rxrelay2.BehaviorRelay import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.locks.ReentrantLock -abstract class ViewModelType: ViewModel() { +abstract class ViewModelType(application: Application): AndroidViewModel(application) { private val rx_action: BehaviorRelay = BehaviorRelay.createDefault(VoidAction) private val rx_state: BehaviorRelay = BehaviorRelay.create() diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/util/Tasks.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/util/Tasks.kt index bf2f422..b354f79 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/util/Tasks.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/util/Tasks.kt @@ -12,7 +12,7 @@ import java.util.concurrent.Future private val uiHandler = Handler(Looper.getMainLooper()) - +/* fun Any?.runOnUiThread(runnable: () -> Unit) { uiHandler.post(runnable) } @@ -27,4 +27,5 @@ fun Any?.async(runnable: () -> Unit) { fun Any?.async(executor: ExecutorService, runnable: () -> Unit): Future { return executor.submit(runnable) -} \ No newline at end of file +} +*/ \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RootAndroidViewModelType.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RootAndroidViewModelType.kt deleted file mode 100644 index 34d049f..0000000 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RootAndroidViewModelType.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.github.skyfe79.android.reactcomponentkit.viewmodel - -import android.app.Application -import com.github.skyfe79.android.reactcomponentkit.ComponentDispatchEvent -import com.github.skyfe79.android.reactcomponentkit.ComponentNewStateEvent -import com.github.skyfe79.android.reactcomponentkit.eventbus.EventBus -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token -import com.github.skyfe79.android.reactcomponentkit.redux.AndroidViewModelType -import com.github.skyfe79.android.reactcomponentkit.redux.State - -abstract class RootAndroidViewModelType(application: Application): AndroidViewModelType(application) { - val token: Token = Token() - private val newStateEventBus: EventBus = EventBus(token) - private val dispatchEventBus: EventBus = EventBus(token) - private var previousState: S? = null - - init { - dispatchEventBus.on { event -> - when(event) { - is ComponentDispatchEvent.Dispatch -> this.dispatch(event.action) - } - } - } - - @Suppress("UNCHECKED_CAST") - fun propagate(state: State) { - val someState = state as? S - if (previousState != someState) { - someState?.let { - newStateEventBus.post(ComponentNewStateEvent.On(state)) - } - } - previousState = someState - } -} \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RootViewModelType.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RootViewModelType.kt index 8a1983c..99fa6f6 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RootViewModelType.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RootViewModelType.kt @@ -1,5 +1,6 @@ package com.github.skyfe79.android.reactcomponentkit.viewmodel +import android.app.Application import com.github.skyfe79.android.reactcomponentkit.ComponentDispatchEvent import com.github.skyfe79.android.reactcomponentkit.ComponentNewStateEvent import com.github.skyfe79.android.reactcomponentkit.eventbus.EventBus @@ -7,7 +8,7 @@ import com.github.skyfe79.android.reactcomponentkit.eventbus.Token import com.github.skyfe79.android.reactcomponentkit.redux.State import com.github.skyfe79.android.reactcomponentkit.redux.ViewModelType -abstract class RootViewModelType: ViewModelType() { +abstract class RootViewModelType(application: Application): ViewModelType(application) { val token: Token = Token() private val newStateEventBus: EventBus = EventBus(token) private val dispatchEventBus: EventBus = EventBus(token) @@ -26,7 +27,7 @@ abstract class RootViewModelType: ViewModelType() { val someState = state as? S if (previousState != someState) { someState?.let { - newStateEventBus.post(ComponentNewStateEvent.On(state)) + newStateEventBus.post(ComponentNewStateEvent.On(it)) } } previousState = someState From 9c2e4b9ab4d23da5828ec457e2d264566232cc77 Mon Sep 17 00:00:00 2001 From: skyfe79 Date: Mon, 26 Aug 2019 22:53:27 +0900 Subject: [PATCH 06/12] Remove Eventbus --- .../android/library/app/MainViewLayout.kt | 2 +- .../android/library/app/MainViewModel.kt | 4 +- .../collectionview/CollectionViewModel.kt | 8 +--- .../app/examples/counter/CounterActivity.kt | 2 +- .../app/examples/counter/CounterLayout.kt | 2 +- .../app/examples/counter/CounterViewModel.kt | 4 +- .../app/examples/counter2/CounterActivity2.kt | 2 +- .../app/examples/counter2/CounterLayout2.kt | 2 +- .../examples/counter2/CounterViewModel2.kt | 4 +- .../EmojiCollectionViewModel.kt | 4 +- .../components/EmojiViewComponent.kt | 2 +- .../component/TextMessageViewComponent.kt | 6 +-- .../skyfe79/android/reactcomponentkit/RCK.kt | 26 ++++++++++ .../reactcomponentkit/ReactComponent.kt | 6 +-- .../collectionview/CollectionViewAdapter.kt | 12 +++-- .../NestedCollectionViewComponent.kt | 6 ++- .../component/FragmentComponent.kt | 48 +++---------------- .../component/LayoutComponent.kt | 24 ++-------- .../component/ViewComponent.kt | 23 ++------- .../recyclerview/RecyclerViewAdapter.kt | 4 +- .../recyclerview/RecyclerViewCell.kt | 2 +- .../{ViewModelType.kt => RCKViewModel.kt} | 14 ++++-- .../viewmodel/RootViewModelType.kt | 14 ++---- 23 files changed, 87 insertions(+), 134 deletions(-) create mode 100644 reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt rename reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/{ViewModelType.kt => RCKViewModel.kt} (91%) diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewLayout.kt b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewLayout.kt index b30e90c..b34e360 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewLayout.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewLayout.kt @@ -11,7 +11,7 @@ import org.jetbrains.anko.include import org.jetbrains.anko.sdk27.coroutines.onClick class MainViewLayout(token: Token) - : LayoutComponent(token, false) { + : LayoutComponent(token) { /** * You can use layout xml file like below. diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt index 765d546..209b5d0 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt @@ -3,8 +3,8 @@ package com.github.skyfe79.android.library.app import android.app.Application import com.github.skyfe79.android.reactcomponentkit.redux.Output import com.github.skyfe79.android.reactcomponentkit.redux.State -import com.github.skyfe79.android.reactcomponentkit.viewmodel.RootViewModelType import com.github.skyfe79.android.library.app.redux.routeReducer +import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel enum class MainRoute { None, @@ -16,7 +16,7 @@ enum class MainRoute { } data class MainState(var route: MainRoute): State() -class MainViewModel(application: Application): RootViewModelType(application) { +class MainViewModel(application: Application): RCKViewModel(application) { val route: Output = Output(MainRoute.None) diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt index 6dcb9cc..f325f6c 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt @@ -4,11 +4,7 @@ import android.app.Application import com.github.skyfe79.android.library.app.examples.collectionview.action.LoadAction import com.github.skyfe79.android.library.app.examples.collectionview.reducer.* import com.github.skyfe79.android.reactcomponentkit.collectionmodels.DefaultSectionModel -import com.github.skyfe79.android.reactcomponentkit.redux.Action -import com.github.skyfe79.android.reactcomponentkit.redux.Output -import com.github.skyfe79.android.reactcomponentkit.redux.State -import com.github.skyfe79.android.reactcomponentkit.redux.VoidAction -import com.github.skyfe79.android.reactcomponentkit.viewmodel.RootViewModelType +import com.github.skyfe79.android.reactcomponentkit.redux.* data class CollectionState( @@ -18,7 +14,7 @@ data class CollectionState( -class CollectionViewModel(application: Application): RootViewModelType(application) { +class CollectionViewModel(application: Application): RCKViewModel(application) { val sections: Output> = Output(emptyList()) diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterActivity.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterActivity.kt index 11e46e1..76a7f53 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterActivity.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterActivity.kt @@ -8,7 +8,7 @@ import org.jetbrains.anko.setContentView class CounterActivity: AppCompatActivity() { private lateinit var viewModel: CounterViewModel private val layout: CounterLayout by lazy { - CounterLayout(viewModel.token, true) + CounterLayout(viewModel.token) } override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt index b40c120..28b0ba3 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt @@ -13,7 +13,7 @@ import com.github.skyfe79.android.reactcomponentkit.redux.State import org.jetbrains.anko.* import org.jetbrains.anko.sdk27.coroutines.onClick -class CounterLayout(token: Token, receiveState: Boolean): LayoutComponent(token, receiveState) { +class CounterLayout(token: Token): LayoutComponent(token) { private enum class IDs { TextView diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt index f00d1e5..29376c3 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt @@ -3,11 +3,10 @@ package com.github.skyfe79.android.library.app.examples.counter import android.app.Application import com.github.skyfe79.android.library.app.examples.counter.redux.countReducer import com.github.skyfe79.android.reactcomponentkit.redux.* -import com.github.skyfe79.android.reactcomponentkit.viewmodel.RootViewModelType data class CounterState(val count: Int): State() -class CounterViewModel(application: Application): RootViewModelType(application) { +class CounterViewModel(application: Application): RCKViewModel(application) { val count: Output = Output(0) @@ -22,6 +21,5 @@ class CounterViewModel(application: Application): RootViewModelType(token, receiveState) { +class CounterLayout2(token: Token): LayoutComponent(token) { private enum class IDs { TextView diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt index 9ad0890..12fdb6c 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt @@ -3,9 +3,9 @@ package com.github.skyfe79.android.library.app.examples.counter import android.app.Application import com.github.skyfe79.android.library.app.examples.counter.redux.countReducer import com.github.skyfe79.android.reactcomponentkit.redux.Output -import com.github.skyfe79.android.reactcomponentkit.viewmodel.RootViewModelType +import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel -class CounterViewModel2(application: Application): RootViewModelType(application) { +class CounterViewModel2(application: Application): RCKViewModel(application) { val count: Output = Output(0) diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt index abcdc77..7dfc34d 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt @@ -6,8 +6,8 @@ import com.github.skyfe79.android.library.app.examples.emojicollection.reducers. import com.github.skyfe79.android.reactcomponentkit.collectionmodels.ItemModel import com.github.skyfe79.android.reactcomponentkit.redux.Error import com.github.skyfe79.android.reactcomponentkit.redux.Output +import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel import com.github.skyfe79.android.reactcomponentkit.redux.State -import com.github.skyfe79.android.reactcomponentkit.viewmodel.RootViewModelType sealed class EmojiRoute { object None: EmojiRoute() @@ -20,7 +20,7 @@ data class EmojiCollectionState( val route: EmojiRoute = EmojiRoute.None ): State() -class EmojiCollectionViewModel(application: Application): RootViewModelType(application) { +class EmojiCollectionViewModel(application: Application): RCKViewModel(application) { val itemModels = Output>(listOf()) val routes = Output(EmojiRoute.None) diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt index e070c40..33cc260 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt @@ -17,7 +17,7 @@ import org.jetbrains.anko.sdk27.coroutines.onClick data class ClickEmojiAction(val emoji: String): Action -class EmojiViewComponent(token: Token, receiveState: Boolean): ViewComponent(token, receiveState) { +class EmojiViewComponent(token: Token): ViewComponent(token) { private lateinit var emojiTextView: TextView override fun layout(ui: AnkoContext): View = with(ui) { diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/recyclerview/component/TextMessageViewComponent.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/recyclerview/component/TextMessageViewComponent.kt index 2e1e564..39a0818 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/recyclerview/component/TextMessageViewComponent.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/recyclerview/component/TextMessageViewComponent.kt @@ -17,7 +17,7 @@ import com.github.skyfe79.android.reactcomponentkit.redux.State import org.jetbrains.anko.* import org.jetbrains.anko.coroutines.experimental.bg -class TextMessageViewComponent(override var token: Token, override var receiveState: Boolean): ViewComponent(token, receiveState) { +class TextMessageViewComponent(token: Token): ViewComponent(token) { lateinit var textView: TextView lateinit var rootLayout: View override fun layout(ui: AnkoContext): View = with(ui) { @@ -46,8 +46,8 @@ class TextMessageViewComponent(override var token: Token, override var receiveSt } } -class SectionViewComponent(override var token: Token, override var receiveState: Boolean): ViewComponent(token, receiveState) { - private var textMessageViewComponent = TextMessageViewComponent(token, receiveState) +class SectionViewComponent(token: Token): ViewComponent(token) { + private var textMessageViewComponent = TextMessageViewComponent(token) override fun layout(ui: AnkoContext): View = with(ui) { verticalLayout { diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt new file mode 100644 index 0000000..1760259 --- /dev/null +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt @@ -0,0 +1,26 @@ +package com.github.skyfe79.android.reactcomponentkit + +import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel +import com.github.skyfe79.android.reactcomponentkit.redux.State +import java.lang.ref.WeakReference + +internal object RCK { + + private val map: MutableMap> = mutableMapOf() + + internal fun registerViewModel(token: Token, viewModel: RCKViewModel) { + this.map[token] = WeakReference(viewModel) + } + + internal fun unregisterViewModel(token: Token) { + this.map.remove(token) + } + + @Suppress("UNCHECKED_CAST") + internal fun viewModel(token: Token): RCKViewModel? { + val weakViewModel = map[token] + return weakViewModel?.get() as? RCKViewModel + } + +} \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/ReactComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/ReactComponent.kt index 7980d9a..69285c3 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/ReactComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/ReactComponent.kt @@ -1,6 +1,5 @@ package com.github.skyfe79.android.reactcomponentkit -import com.github.skyfe79.android.reactcomponentkit.eventbus.EventBus import com.github.skyfe79.android.reactcomponentkit.eventbus.EventType import com.github.skyfe79.android.reactcomponentkit.eventbus.Token import com.github.skyfe79.android.reactcomponentkit.redux.Action @@ -16,11 +15,8 @@ sealed class ComponentDispatchEvent: EventType { interface ReactComponent { var token: Token - var receiveState: Boolean - var newStateEventBus: EventBus? - var dispatchEventBus: EventBus fun dispatch(action: Action) { - dispatchEventBus.post(ComponentDispatchEvent.Dispatch(action)) + //dispatchEventBus.post(ComponentDispatchEvent.Dispatch(action)) } } \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/CollectionViewAdapter.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/CollectionViewAdapter.kt index 539d037..483afaf 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/CollectionViewAdapter.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/CollectionViewAdapter.kt @@ -11,6 +11,9 @@ import com.github.skyfe79.android.reactcomponentkit.recyclerview.CollectionViewC import com.github.skyfe79.android.reactcomponentkit.recyclerview.RecyclerViewCell import com.github.skyfe79.android.reactcomponentkit.recyclerview.RecyclerViewCellViewHolder import com.github.skyfe79.android.reactcomponentkit.recyclerview.sticky.StickyHeaders +import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel +import com.github.skyfe79.android.reactcomponentkit.redux.State +import java.lang.ref.WeakReference import kotlin.reflect.KClass @@ -60,6 +63,7 @@ open class CollectionViewAdapter(private val token: Token): RecyclerView.Adapter private val viewComponents: MutableMap> = mutableMapOf() private var sections: MutableList = mutableListOf() private var items: MutableList = mutableListOf() + override fun getItemCount(): Int { return items.size } @@ -91,8 +95,8 @@ open class CollectionViewAdapter(private val token: Token): RecyclerView.Adapter override fun onCreateViewHolder(parent: ViewGroup, position: Int): RecyclerView.ViewHolder { return when(val item = items[position]) { is SectionContent -> { - val cell = RecyclerViewCell(token = token, receiveState = false) - val viewCompoent = NestedCollectionViewComponent(token, false) + val cell = RecyclerViewCell(token) + val viewCompoent = NestedCollectionViewComponent(token) cell.viewComponent = viewCompoent val viewHolder = cell.onCreateCollectionViewHolder(parent) viewCompoent.setup(item, viewComponents) @@ -101,8 +105,8 @@ open class CollectionViewAdapter(private val token: Token): RecyclerView.Adapter else -> { val cls = viewComponents[item.componentClass.qualifiedName.hashCode()] ?: throw IllegalStateException("viewComponent is null") - val cell = RecyclerViewCell(token = token, receiveState = false) - cell.viewComponent = cls.java.constructors.first().newInstance(token, false) as ViewComponent + val cell = RecyclerViewCell(token) + cell.viewComponent = cls.java.constructors.first().newInstance(token) as ViewComponent cell.onCreateViewHolder(parent) } } diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/NestedCollectionViewComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/NestedCollectionViewComponent.kt index cd87bc9..1af32d3 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/NestedCollectionViewComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/NestedCollectionViewComponent.kt @@ -10,14 +10,16 @@ import com.github.skyfe79.android.reactcomponentkit.recyclerview.RecyclerViewAda import org.jetbrains.anko.* import kotlin.reflect.KClass -class NestedCollectionViewComponent(override var token: Token, override var receiveState: Boolean = false): ViewComponent(token, receiveState) { +class NestedCollectionViewComponent( + override var token: Token +): ViewComponent(token) { internal lateinit var recyclerView: RecyclerView private lateinit var adapter: RecyclerViewAdapter private lateinit var currentContent: SectionContent internal fun setup(section: SectionContent, viewComponents: MutableMap>) { currentContent = section - adapter = RecyclerViewAdapter(token, false) + adapter = RecyclerViewAdapter(token) adapter.update(viewComponents) recyclerView.adapter = adapter recyclerView.setRecycledViewPool(RecyclerView.RecycledViewPool()) diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/FragmentComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/FragmentComponent.kt index bfa2b5d..62905db 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/FragmentComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/FragmentComponent.kt @@ -9,7 +9,9 @@ import com.github.skyfe79.android.reactcomponentkit.ComponentNewStateEvent import com.github.skyfe79.android.reactcomponentkit.ReactComponent import com.github.skyfe79.android.reactcomponentkit.eventbus.EventBus import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel import com.github.skyfe79.android.reactcomponentkit.redux.State +import java.lang.ref.WeakReference internal enum class FragmentComponentState { SHOW, @@ -20,19 +22,16 @@ internal enum class FragmentComponentState { abstract class FragmentComponent: Fragment(), ReactComponent { companion object { - inline fun newInstance(token: Token, receiveState: Boolean): T { + inline fun newInstance(token: Token): T { val args = Bundle() args.putParcelable("token", token) - args.putBoolean("receiveState", receiveState) val component = T::class.java.newInstance() component.arguments = args return component } } + override var token: Token = Token.empty - override var receiveState: Boolean = false - override var newStateEventBus: EventBus? = null - override var dispatchEventBus: EventBus = EventBus(token) internal var state: FragmentComponentState = FragmentComponentState.NONE override fun onAttach(context: Context) { @@ -40,49 +39,16 @@ abstract class FragmentComponent: Fragment(), ReactComponent { if (arguments != null) { arguments?.let { - this@FragmentComponent.receiveState = it.getBoolean("receiveState", false) this@FragmentComponent.token = (it.getParcelable("token") as? Token) ?: Token.empty } } - - newStateEventBus = if (receiveState) EventBus(token) else null - dispatchEventBus = EventBus(token) - - newStateEventBus?.let { - it.on { event -> - when (event) { - is ComponentNewStateEvent.On -> on(event.state) - } - } - } - - if (state == FragmentComponentState.SHOW) { - startEventBus() - } else { - stopEventBus() - } - } - - override fun onDetach() { - super.onDetach() - stopEventBus() } abstract fun on(state: State) - - internal fun startEventBus() { - newStateEventBus?.start() - dispatchEventBus.start() - } - - internal fun stopEventBus() { - newStateEventBus?.stop() - dispatchEventBus.stop() - } } -inline fun FragmentActivity.fragmentComponent(token: Token, receiveState: Boolean = true): T { - return FragmentComponent.newInstance(token, receiveState) +inline fun FragmentActivity.fragmentComponent(token: Token): T { + return FragmentComponent.newInstance(token) } fun FragmentActivity.replaceFragment(component: FragmentComponent, containerViewId: Int, tag: String? = null) { @@ -103,7 +69,6 @@ fun FragmentActivity.addFragment(component: FragmentComponent, containerViewId: fun FragmentActivity.hideFragment(component: FragmentComponent) { component.state = FragmentComponentState.HIDE - component.stopEventBus() this.supportFragmentManager .beginTransaction() @@ -113,7 +78,6 @@ fun FragmentActivity.hideFragment(component: FragmentComponent) { fun FragmentActivity.showFragment(component: FragmentComponent) { component.state = FragmentComponentState.SHOW - component.startEventBus() this.supportFragmentManager .beginTransaction() diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/LayoutComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/LayoutComponent.kt index 195bead..9f51549 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/LayoutComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/LayoutComponent.kt @@ -1,15 +1,11 @@ package com.github.skyfe79.android.reactcomponentkit.component -import androidx.appcompat.app.AppCompatActivity -import android.view.View -import com.github.skyfe79.android.reactcomponentkit.ComponentDispatchEvent -import com.github.skyfe79.android.reactcomponentkit.ComponentNewStateEvent import com.github.skyfe79.android.reactcomponentkit.ReactComponent -import com.github.skyfe79.android.reactcomponentkit.eventbus.EventBus import com.github.skyfe79.android.reactcomponentkit.eventbus.Token import com.github.skyfe79.android.reactcomponentkit.redux.State +import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel import org.jetbrains.anko.AnkoComponent -import org.jetbrains.anko.AnkoContext +import java.lang.ref.WeakReference /** * Use this component for Activity Root Layout @@ -17,23 +13,9 @@ import org.jetbrains.anko.AnkoContext abstract class LayoutComponent : AnkoComponent, ReactComponent { override var token: Token - override var receiveState: Boolean - override var newStateEventBus: EventBus? - override var dispatchEventBus: EventBus - constructor(token: Token, receiveState: Boolean) { + constructor(token: Token) { this.token = token - this.receiveState = receiveState - this.newStateEventBus = if (receiveState) EventBus(token) else null - this.dispatchEventBus = EventBus(token) - - newStateEventBus?.let { - it.on { event -> - when (event) { - is ComponentNewStateEvent.On -> on(event.state) - } - } - } } abstract fun on(state: State) diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt index e5dbf28..cc68d1e 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt @@ -11,38 +11,23 @@ import com.github.skyfe79.android.reactcomponentkit.collectionmodels.ItemModel import com.github.skyfe79.android.reactcomponentkit.collectionview.SectionContent import com.github.skyfe79.android.reactcomponentkit.eventbus.EventBus import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel import com.github.skyfe79.android.reactcomponentkit.redux.State import org.jetbrains.anko.AnkoComponent import org.jetbrains.anko.AnkoContext import org.jetbrains.anko.custom.ankoView +import java.lang.ref.WeakReference import kotlin.reflect.KClass abstract class ViewComponent: AnkoComponent, ReactComponent { - override var token: Token - override var receiveState: Boolean - override var newStateEventBus: EventBus? - override var dispatchEventBus: EventBus + private var cachedView: View? = null - constructor(token: Token, receiveState: Boolean = true) { + constructor(token: Token) { this.token = token - this.receiveState = receiveState - this.newStateEventBus = if (receiveState) EventBus(token) else null - this.dispatchEventBus = EventBus(token) - - newStateEventBus?.let { - it.on { event -> - when (event) { - is ComponentNewStateEvent.On -> on(event.state) - } - } - } } - - - private var cachedView: View? = null override fun createView(ui: AnkoContext): View { val view = cachedView ?: layout(ui) cachedView = view diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewAdapter.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewAdapter.kt index 1697be1..9a6ba0b 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewAdapter.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewAdapter.kt @@ -36,8 +36,8 @@ open class RecyclerViewAdapter(private val token: Token, private val useDiff: Bo override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val cls = viewComponents[viewType] ?: throw IllegalStateException("viewComponent is null") - val cell = RecyclerViewCell(token = token, receiveState = false) - cell.viewComponent = cls.java.constructors.first().newInstance(token, false) as ViewComponent + val cell = RecyclerViewCell(token) + cell.viewComponent = cls.java.constructors.first().newInstance(token) as ViewComponent return cell.onCreateViewHolder(parent) } diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewCell.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewCell.kt index a4a8e36..1a3fee7 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewCell.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewCell.kt @@ -9,7 +9,7 @@ import com.github.skyfe79.android.reactcomponentkit.eventbus.Token import org.jetbrains.anko.AnkoContext -internal class RecyclerViewCell(private val token: Token, private val receiveState: Boolean = false) { +internal class RecyclerViewCell(private val token: Token) { lateinit var viewComponent: ViewComponent diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/ViewModelType.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/RCKViewModel.kt similarity index 91% rename from reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/ViewModelType.kt rename to reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/RCKViewModel.kt index 172cf48..823c715 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/ViewModelType.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/RCKViewModel.kt @@ -2,17 +2,21 @@ package com.github.skyfe79.android.reactcomponentkit.redux import android.app.Application import androidx.lifecycle.AndroidViewModel +import com.github.skyfe79.android.reactcomponentkit.RCK +import com.github.skyfe79.android.reactcomponentkit.eventbus.Token import com.jakewharton.rxrelay2.BehaviorRelay import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.locks.ReentrantLock -abstract class ViewModelType(application: Application): AndroidViewModel(application) { +abstract class RCKViewModel(application: Application): AndroidViewModel(application) { + val token: Token = Token() private val rx_action: BehaviorRelay = BehaviorRelay.createDefault(VoidAction) private val rx_state: BehaviorRelay = BehaviorRelay.create() + private val store = Store() private val disposables = CompositeDisposable() private var applyNewState: Boolean = false @@ -33,6 +37,7 @@ abstract class ViewModelType(application: Application): AndroidViewMod fun dispose() { + RCK.unregisterViewModel(token) disposables.dispose() store.deinitialize() } @@ -154,11 +159,12 @@ abstract class ViewModelType(application: Application): AndroidViewMod abstract fun on(newState: S) - fun initStore(block: ViewModelType.(Store) -> Unit) { + fun initStore(block: RCKViewModel.(Store) -> Unit) { + RCK.registerViewModel(token, this) block(this.store) } - fun setState(block: ViewModelType.(S) -> S): S { + fun setState(block: RCKViewModel.(S) -> S): S { writeLock.lock() try { val newState = block(this.store.state) @@ -169,7 +175,7 @@ abstract class ViewModelType(application: Application): AndroidViewMod } } - fun withState(block: ViewModelType.(S) -> Unit) { + fun withState(block: RCKViewModel.(S) -> Unit) { readLock.lock() try { block(this.store.state) diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RootViewModelType.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RootViewModelType.kt index 99fa6f6..f191931 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RootViewModelType.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RootViewModelType.kt @@ -1,14 +1,7 @@ package com.github.skyfe79.android.reactcomponentkit.viewmodel -import android.app.Application -import com.github.skyfe79.android.reactcomponentkit.ComponentDispatchEvent -import com.github.skyfe79.android.reactcomponentkit.ComponentNewStateEvent -import com.github.skyfe79.android.reactcomponentkit.eventbus.EventBus -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token -import com.github.skyfe79.android.reactcomponentkit.redux.State -import com.github.skyfe79.android.reactcomponentkit.redux.ViewModelType - -abstract class RootViewModelType(application: Application): ViewModelType(application) { +/* +abstract class RootViewModelType(application: Application): RCKViewModel(application) { val token: Token = Token() private val newStateEventBus: EventBus = EventBus(token) private val dispatchEventBus: EventBus = EventBus(token) @@ -32,4 +25,5 @@ abstract class RootViewModelType(application: Application): ViewModelT } previousState = someState } -} \ No newline at end of file +} +*/ \ No newline at end of file From 175a7fd150c8655f3bdba6bb7ee34c98a6907b89 Mon Sep 17 00:00:00 2001 From: skyfe79 Date: Mon, 26 Aug 2019 23:38:16 +0900 Subject: [PATCH 07/12] Redefine Reducers --- .../android/library/app/MainViewLayout.kt | 1 + .../android/library/app/MainViewModel.kt | 5 +++ .../library/app/component/LoadingComponent.kt | 2 - .../collectionview/reducer/Reducers.kt | 8 ++-- .../app/examples/counter/CounterActivity.kt | 16 ++++++++ .../app/examples/counter/CounterLayout.kt | 5 +++ .../app/examples/counter/redux/Reducers.kt | 8 ++-- .../app/examples/counter2/CounterLayout2.kt | 1 + .../components/EmojiViewComponent.kt | 1 + .../emojicollection/reducers/Reducers.kt | 22 +++++------ .../android/library/app/redux/Reducers.kt | 4 +- .../skyfe79/android/reactcomponentkit/RCK.kt | 4 +- .../reactcomponentkit/ReactComponent.kt | 14 ------- .../component/FragmentComponent.kt | 11 +++--- .../component/ViewComponent.kt | 8 ---- .../dispatcher/ActionDispatcher.kt | 34 ++++++++++++++++ .../dispatcher/DispatcherExtension.kt | 39 +++++++++++++++++++ .../android/reactcomponentkit/redux/Output.kt | 3 +- .../reactcomponentkit/redux/Reducer.kt | 2 +- .../android/reactcomponentkit/redux/Store.kt | 2 +- .../util/ActionDispatcher.kt | 21 ---------- 21 files changed, 134 insertions(+), 77 deletions(-) create mode 100644 reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/ActionDispatcher.kt create mode 100644 reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/DispatcherExtension.kt delete mode 100644 reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/util/ActionDispatcher.kt diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewLayout.kt b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewLayout.kt index b34e360..8081086 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewLayout.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewLayout.kt @@ -3,6 +3,7 @@ package com.github.skyfe79.android.library.app import android.view.View import com.github.skyfe79.android.library.app.action.* import com.github.skyfe79.android.reactcomponentkit.component.LayoutComponent +import com.github.skyfe79.android.reactcomponentkit.dispatcher.dispatch import com.github.skyfe79.android.reactcomponentkit.eventbus.Token import com.github.skyfe79.android.reactcomponentkit.redux.State import kotlinx.android.synthetic.main.activity_main.view.* diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt index 209b5d0..aa34960 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt @@ -1,6 +1,7 @@ package com.github.skyfe79.android.library.app import android.app.Application +import android.util.Log import com.github.skyfe79.android.reactcomponentkit.redux.Output import com.github.skyfe79.android.reactcomponentkit.redux.State import com.github.skyfe79.android.library.app.redux.routeReducer @@ -32,4 +33,8 @@ class MainViewModel(application: Application): RCKViewModel(applicati override fun on(newState: MainState) { route.accept(newState.route).afterReset(MainRoute.None) } + + fun hello() { + Log.d("TAG", "HELLO") + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/component/LoadingComponent.kt b/app/src/main/java/com/github/skyfe79/android/library/app/component/LoadingComponent.kt index 9e4d0f8..cbe3c00 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/component/LoadingComponent.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/component/LoadingComponent.kt @@ -8,8 +8,6 @@ import android.view.View import android.view.ViewGroup import com.github.skyfe79.android.library.app.MainState import com.github.skyfe79.android.reactcomponentkit.component.FragmentComponent -import com.github.skyfe79.android.reactcomponentkit.component.LayoutComponent -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token import com.github.skyfe79.android.reactcomponentkit.redux.State import org.jetbrains.anko.* import org.jetbrains.anko.support.v4.UI diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt index d5725e2..e0bde44 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt @@ -15,8 +15,8 @@ import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State import io.reactivex.Observable -fun CollectionViewModel.loadEmoji(state: CollectionState, action: Action): CollectionState { - return when (action) { +fun CollectionViewModel.loadEmoji(action: Action) = setState { state -> + when (action) { is LoadAction -> { val emojiCollection = (1..5) .map { @@ -28,8 +28,8 @@ fun CollectionViewModel.loadEmoji(state: CollectionState, action: Action): Colle } } -fun CollectionViewModel.makeSectionModels(state: CollectionState, action: Action): CollectionState { - return when (action) { +fun CollectionViewModel.makeSectionModels(action: Action) = setState { state -> + when (action) { is LoadAction -> { val colors = listOf( diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterActivity.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterActivity.kt index 76a7f53..cb9cf33 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterActivity.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterActivity.kt @@ -3,9 +3,15 @@ package com.github.skyfe79.android.library.app.examples.counter import androidx.lifecycle.ViewModelProviders import android.os.Bundle import androidx.appcompat.app.AppCompatActivity +import com.github.skyfe79.android.reactcomponentkit.rx.AutoDisposeBag +import com.github.skyfe79.android.reactcomponentkit.rx.disposedBy import org.jetbrains.anko.setContentView class CounterActivity: AppCompatActivity() { + private val disposeBag: AutoDisposeBag by lazy { + AutoDisposeBag(this) + } + private lateinit var viewModel: CounterViewModel private val layout: CounterLayout by lazy { CounterLayout(viewModel.token) @@ -16,6 +22,16 @@ class CounterActivity: AppCompatActivity() { viewModel = ViewModelProviders.of(this).get(CounterViewModel::class.java) layout.setContentView(this) + layout.countTextView.text = "${viewModel.count.value}" + + viewModel + .count + .asObservable() + .subscribe { + layout.countTextView.text = "$it" + } + .disposedBy(disposeBag) + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt index 28b0ba3..d9cb6a2 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt @@ -7,7 +7,9 @@ import android.widget.LinearLayout import android.widget.TextView 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.counter.redux.increment import com.github.skyfe79.android.reactcomponentkit.component.LayoutComponent +import com.github.skyfe79.android.reactcomponentkit.dispatcher.dispatch import com.github.skyfe79.android.reactcomponentkit.eventbus.Token import com.github.skyfe79.android.reactcomponentkit.redux.State import org.jetbrains.anko.* @@ -55,6 +57,9 @@ class CounterLayout(token: Token): LayoutComponent(token) { increaseButton.onClick { dispatch(IncreaseAction()) +// dispatch { +// (it as? CounterViewModel)?.increment(10) +// } } decreaseButton.onClick { diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt index d2629ba..c28d0c4 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt @@ -12,16 +12,16 @@ import com.github.skyfe79.android.reactcomponentkit.redux.Action //단 주의할 점은 RCK는 상태 mutation을 모두 백그라운드에서 실행하고 있다. //과연 이것이 필요한 것일까? //async 만 백그라운드에서 실행하면 되지 않을까? -fun CounterViewModel.countReducer(state: CounterState, action: Action): CounterState { - return when(action) { +fun CounterViewModel.countReducer(action: Action) = setState { state -> + when(action) { is IncreaseAction -> increment(action.payload) is DecreaseAction -> decrement(action.payload) else -> state } } -fun CounterViewModel2.countReducer(state: CounterState, action: Action): CounterState { - return when(action) { +fun CounterViewModel2.countReducer(action: Action) = setState { state -> + when(action) { is IncreaseAction -> setState { with(it) { copy(count = count + action.payload) } } diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterLayout2.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterLayout2.kt index 4d0b91e..89af4ee 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterLayout2.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterLayout2.kt @@ -8,6 +8,7 @@ import android.widget.TextView 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.reactcomponentkit.component.LayoutComponent +import com.github.skyfe79.android.reactcomponentkit.dispatcher.dispatch import com.github.skyfe79.android.reactcomponentkit.eventbus.Token import com.github.skyfe79.android.reactcomponentkit.redux.State import org.jetbrains.anko.* diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt index 33cc260..e3ee39e 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt @@ -8,6 +8,7 @@ import android.widget.TextView import com.github.skyfe79.android.library.app.examples.emojicollection.models.EmojiProvider import com.github.skyfe79.android.reactcomponentkit.collectionmodels.ItemModel import com.github.skyfe79.android.reactcomponentkit.component.ViewComponent +import com.github.skyfe79.android.reactcomponentkit.dispatcher.dispatch import com.github.skyfe79.android.reactcomponentkit.eventbus.Token import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt index 7839414..bf36d12 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt @@ -18,15 +18,15 @@ import io.reactivex.rxkotlin.subscribeBy import java.lang.Exception import kotlin.random.Random -fun EmojiCollectionViewModel.route(state: EmojiCollectionState, action: Action): EmojiCollectionState { - return when(action) { +fun EmojiCollectionViewModel.route(action: Action) = setState { state -> + when(action) { is ClickEmojiAction -> state.copy(route = EmojiRoute.AlertEmoji(action.emoji)) else -> state.copy(route = EmojiRoute.None) } } -fun EmojiCollectionViewModel.addEmoji(state: EmojiCollectionState, action: Action): EmojiCollectionState { - return when (action) { +fun EmojiCollectionViewModel.addEmoji(action: Action) = setState { state -> + when (action) { is AddEmojiAction -> { val mutableEmojiList = state.emojis.toMutableList() val index = if (mutableEmojiList.isEmpty()) 0 else (0 until mutableEmojiList.size).random() @@ -39,10 +39,10 @@ fun EmojiCollectionViewModel.addEmoji(state: EmojiCollectionState, action: Actio } -fun EmojiCollectionViewModel.removeEmoji(state: EmojiCollectionState, action: Action): EmojiCollectionState { - return when (action) { +fun EmojiCollectionViewModel.removeEmoji(action: Action) = setState { state -> + when (action) { is RemoveEmojiAction -> { - return try { + try { val mutableEmojiList = state.emojis.toMutableList() val index = if (mutableEmojiList.isEmpty()) 0 else (0 until mutableEmojiList.size).random() mutableEmojiList.removeAt(index) @@ -57,8 +57,8 @@ fun EmojiCollectionViewModel.removeEmoji(state: EmojiCollectionState, action: Ac } } -fun EmojiCollectionViewModel.shuffleEmoji(state: EmojiCollectionState, action: Action): EmojiCollectionState { - return when (action) { +fun EmojiCollectionViewModel.shuffleEmoji(action: Action) = setState { state -> + when (action) { is ShuffleEmojiAction -> { val mutableEmojiList = state.emojis.toMutableList() mutableEmojiList.shuffle() @@ -70,8 +70,8 @@ fun EmojiCollectionViewModel.shuffleEmoji(state: EmojiCollectionState, action: A } } -fun EmojiCollectionViewModel.makeItemModels(state: EmojiCollectionState, action: Action): EmojiCollectionState { - return when (action) { +fun EmojiCollectionViewModel.makeItemModels(action: Action) = setState { state -> + when (action) { is MakeItemModelsAction -> { val emojiBoxModels = state.emojis.map { EmojiBoxModel(it) } val mutatedState = state.copy(itemModels = emojiBoxModels) diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/redux/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/redux/Reducers.kt index 71de92f..9f4d281 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/redux/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/redux/Reducers.kt @@ -8,8 +8,8 @@ import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State import io.reactivex.Observable -fun MainViewModel.routeReducer(state: MainState, action: Action): MainState { - return when (action) { +fun MainViewModel.routeReducer(action: Action): MainState = setState { state -> + when (action) { is ClickCounterExampleButtonAction -> { val mutatedState = state.copy(route = MainRoute.CounterExample) mutatedState diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt index 1760259..5618a40 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt @@ -18,9 +18,9 @@ internal object RCK { } @Suppress("UNCHECKED_CAST") - internal fun viewModel(token: Token): RCKViewModel? { + internal fun viewModel(token: Token): RCKViewModel<*>? { val weakViewModel = map[token] - return weakViewModel?.get() as? RCKViewModel + return weakViewModel?.get() as? RCKViewModel<*> } } \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/ReactComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/ReactComponent.kt index 69285c3..ba894f7 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/ReactComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/ReactComponent.kt @@ -1,22 +1,8 @@ package com.github.skyfe79.android.reactcomponentkit -import com.github.skyfe79.android.reactcomponentkit.eventbus.EventType import com.github.skyfe79.android.reactcomponentkit.eventbus.Token -import com.github.skyfe79.android.reactcomponentkit.redux.Action -import com.github.skyfe79.android.reactcomponentkit.redux.State -sealed class ComponentNewStateEvent: EventType { - data class On(val state: State): ComponentNewStateEvent() -} - -sealed class ComponentDispatchEvent: EventType { - data class Dispatch(val action: Action): ComponentDispatchEvent() -} interface ReactComponent { var token: Token - - fun dispatch(action: Action) { - //dispatchEventBus.post(ComponentDispatchEvent.Dispatch(action)) - } } \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/FragmentComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/FragmentComponent.kt index 62905db..3078e2e 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/FragmentComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/FragmentComponent.kt @@ -4,14 +4,11 @@ import android.content.Context import android.os.Bundle import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity -import com.github.skyfe79.android.reactcomponentkit.ComponentDispatchEvent -import com.github.skyfe79.android.reactcomponentkit.ComponentNewStateEvent +import com.github.skyfe79.android.reactcomponentkit.RCK import com.github.skyfe79.android.reactcomponentkit.ReactComponent -import com.github.skyfe79.android.reactcomponentkit.eventbus.EventBus import com.github.skyfe79.android.reactcomponentkit.eventbus.Token -import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel +import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State -import java.lang.ref.WeakReference internal enum class FragmentComponentState { SHOW, @@ -31,6 +28,8 @@ abstract class FragmentComponent: Fragment(), ReactComponent { } } + + override var token: Token = Token.empty internal var state: FragmentComponentState = FragmentComponentState.NONE @@ -99,4 +98,4 @@ fun FragmentActivity.removeAllFragments() { .map { it as? FragmentComponent } .filterNotNull() .forEach { removeFragment(it) } -} \ No newline at end of file +} diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt index cc68d1e..1456543 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt @@ -3,22 +3,14 @@ package com.github.skyfe79.android.reactcomponentkit.component import android.content.Context import android.view.View import android.view.ViewManager -import androidx.recyclerview.widget.RecyclerView -import com.github.skyfe79.android.reactcomponentkit.ComponentDispatchEvent -import com.github.skyfe79.android.reactcomponentkit.ComponentNewStateEvent import com.github.skyfe79.android.reactcomponentkit.ReactComponent import com.github.skyfe79.android.reactcomponentkit.collectionmodels.ItemModel import com.github.skyfe79.android.reactcomponentkit.collectionview.SectionContent -import com.github.skyfe79.android.reactcomponentkit.eventbus.EventBus import com.github.skyfe79.android.reactcomponentkit.eventbus.Token -import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel import com.github.skyfe79.android.reactcomponentkit.redux.State import org.jetbrains.anko.AnkoComponent import org.jetbrains.anko.AnkoContext import org.jetbrains.anko.custom.ankoView -import java.lang.ref.WeakReference -import kotlin.reflect.KClass - abstract class ViewComponent: AnkoComponent, ReactComponent { override var token: Token diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/ActionDispatcher.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/ActionDispatcher.kt new file mode 100644 index 0000000..526e107 --- /dev/null +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/ActionDispatcher.kt @@ -0,0 +1,34 @@ +package com.github.skyfe79.android.reactcomponentkit.dispatcher + +import com.github.skyfe79.android.reactcomponentkit.eventbus.EventBus +import com.github.skyfe79.android.reactcomponentkit.eventbus.EventType +import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.redux.Action +import com.github.skyfe79.android.reactcomponentkit.redux.State + +sealed class ComponentNewStateEvent: EventType { + data class On(val state: State): ComponentNewStateEvent() +} + +sealed class ComponentDispatchEvent: EventType { + data class Dispatch(val action: Action): ComponentDispatchEvent() +} + +/** + * Dispatch actions where to has same token + * It is used among components which has root view models. + */ +class ActionDispatcher(private val token: Token) { + private val dispatchEventBus = EventBus(token) + + /** + * dispatch action where to has same token. + */ + fun dispatch(action: Action) { + dispatchEventBus.post( + ComponentDispatchEvent.Dispatch( + action + ) + ) + } +} \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/DispatcherExtension.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/DispatcherExtension.kt new file mode 100644 index 0000000..1922bf1 --- /dev/null +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/DispatcherExtension.kt @@ -0,0 +1,39 @@ +package com.github.skyfe79.android.reactcomponentkit.dispatcher + +import com.github.skyfe79.android.reactcomponentkit.RCK +import com.github.skyfe79.android.reactcomponentkit.component.FragmentComponent +import com.github.skyfe79.android.reactcomponentkit.component.LayoutComponent +import com.github.skyfe79.android.reactcomponentkit.component.ViewComponent +import com.github.skyfe79.android.reactcomponentkit.redux.Action +import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel + + +fun FragmentComponent.dispatch(action: Action) { + val viewModel = RCK.viewModel(token) + viewModel.let { it?.dispatch(action) } +} + +fun FragmentComponent.dispatch(action: (RCKViewModel<*>) -> Unit) { + val viewModel = RCK.viewModel(token) + viewModel?.let { action(it) } +} + +fun LayoutComponent.dispatch(action: Action) { + val viewModel = RCK.viewModel(token) + viewModel.let { it?.dispatch(action) } +} + +fun LayoutComponent.dispatch(action: (RCKViewModel<*>) -> Unit) { + val viewModel = RCK.viewModel(token) + viewModel?.let { action(it) } +} + +fun ViewComponent.dispatch(action: Action) { + val viewModel = RCK.viewModel(token) + viewModel.let { it?.dispatch(action) } +} + +fun ViewComponent.dispatch(action: (RCKViewModel<*>) -> Unit) { + val viewModel = RCK.viewModel(token) + viewModel?.let { action(it) } +} \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Output.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Output.kt index 6edc120..06b2374 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Output.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Output.kt @@ -2,6 +2,7 @@ package com.github.skyfe79.android.reactcomponentkit.redux import com.jakewharton.rxrelay2.BehaviorRelay import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers class Output(defaultValue: T?) { private val behaviorRelay = if (defaultValue != null) BehaviorRelay.createDefault(defaultValue) else BehaviorRelay.create() @@ -22,7 +23,7 @@ class Output(defaultValue: T?) { } fun asObservable(): Observable { - return behaviorRelay + return behaviorRelay.observeOn(AndroidSchedulers.mainThread()) } inner class ResetChanin { diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt index ee58a5a..4eaa203 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt @@ -1,3 +1,3 @@ package com.github.skyfe79.android.reactcomponentkit.redux -typealias Reducer = (STATE, Action) -> STATE +typealias Reducer = (Action) -> STATE diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt index 2690a43..cd69ba2 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt @@ -60,7 +60,7 @@ class Store { .subscribeOn(Schedulers.single()) .observeOn(Schedulers.single()) .map { reducer -> - reducer(this@Store.state, action) + reducer(action) } .doOnNext { modifiedState -> this@Store.state = modifiedState diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/util/ActionDispatcher.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/util/ActionDispatcher.kt deleted file mode 100644 index a618747..0000000 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/util/ActionDispatcher.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.skyfe79.android.reactcomponentkit.util - -import com.github.skyfe79.android.reactcomponentkit.ComponentDispatchEvent -import com.github.skyfe79.android.reactcomponentkit.eventbus.EventBus -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token -import com.github.skyfe79.android.reactcomponentkit.redux.Action - -/** - * Dispatch actions where to has same token - * It is used among components which has root view models. - */ -class ActionDispatcher(private val token: Token) { - private val dispatchEventBus = EventBus(token) - - /** - * dispatch action where to has same token. - */ - fun dispatch(action: Action) { - dispatchEventBus.post(ComponentDispatchEvent.Dispatch(action)) - } -} \ No newline at end of file From 6658b1cdb66e5ba50bd02ecfc1c4197ac88bc2a6 Mon Sep 17 00:00:00 2001 From: skyfe79 Date: Tue, 27 Aug 2019 23:45:05 +0900 Subject: [PATCH 08/12] Remove when(action) statement or expression --- .../android/library/app/MainViewModel.kt | 14 ++++--- .../collectionview/CollectionViewActivity.kt | 2 + .../collectionview/CollectionViewModel.kt | 24 ++++++++--- .../collectionview/reducer/Reducers.kt | 42 +++++++++++++++++++ .../android/library/app/redux/Reducers.kt | 14 ++++++- .../reactcomponentkit/redux/RCKViewModel.kt | 8 ++-- .../reactcomponentkit/redux/Reducer.kt | 1 + .../android/reactcomponentkit/redux/Store.kt | 15 ++++--- 8 files changed, 98 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt index aa34960..e34e724 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt @@ -1,10 +1,10 @@ package com.github.skyfe79.android.library.app import android.app.Application -import android.util.Log +import com.github.skyfe79.android.library.app.action.* import com.github.skyfe79.android.reactcomponentkit.redux.Output import com.github.skyfe79.android.reactcomponentkit.redux.State -import com.github.skyfe79.android.library.app.redux.routeReducer +import com.github.skyfe79.android.library.app.redux.* import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel enum class MainRoute { @@ -27,14 +27,16 @@ class MainViewModel(application: Application): RCKViewModel(applicati initialState = MainState(MainRoute.None), reducers = arrayOf(::routeReducer) ) + + store.map(ClickCounterExampleButtonAction, ::routeClickCounterExampleButtonAction) + store.map(ClickCounterExample2ButtonAction, ::routeClickCounterExample2ButtonAction) + store.map(ClickRecyclerViewExampleButtonAction, ::routeClickRecyclerViewExampleButtonAction) + store.map(ClickEmojiExampleButtonAction, ::routeClickEmojiExampleButtonAction) + store.map(ClickCollectionViewExampleButtonAction, { it.copy(route = MainRoute.CollectionViewExample) }) } } override fun on(newState: MainState) { route.accept(newState.route).afterReset(MainRoute.None) } - - fun hello() { - Log.d("TAG", "HELLO") - } } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewActivity.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewActivity.kt index e629e00..1c5bfc1 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewActivity.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewActivity.kt @@ -6,6 +6,8 @@ import android.os.Bundle import androidx.lifecycle.ViewModelProviders import com.github.skyfe79.android.library.app.R import com.github.skyfe79.android.library.app.examples.collectionview.action.LoadAction +import com.github.skyfe79.android.library.app.examples.collectionview.reducer.loadEmoji2 +import com.github.skyfe79.android.library.app.examples.collectionview.reducer.makeSectionModels2 import com.github.skyfe79.android.library.app.examples.emojicollection.components.EmojiViewComponent import com.github.skyfe79.android.library.app.examples.recyclerview.component.SectionViewComponent import com.github.skyfe79.android.reactcomponentkit.collectionview.CollectionViewAdapter diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt index f325f6c..abaa041 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt @@ -21,17 +21,29 @@ class CollectionViewModel(application: Application): RCKViewModel store.set( - initialState = CollectionState(), - reducers = arrayOf(::loadEmoji, ::makeSectionModels) + initialState = CollectionState() + //reducers = arrayOf(::loadEmoji, ::makeSectionModels) ) - } + // map action to reducers + store.map(LoadAction, ::loadEmoji2, ::makeSectionModels2) + // or + store.map(LoadAction, + { state -> + //it's reducer + state + }, + { + //it's reducer + it + } + ) + } } override fun beforeDispatch(action: Action): Action = when(action) { - is LoadAction -> { - VoidAction - //if (store.state.emojis.isNotEmpty()) VoidAction else action + is LoadAction -> withState { state -> + if (state.emojis.isNotEmpty()) VoidAction else action } else -> action } diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt index e0bde44..ff5fdbe 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt @@ -66,4 +66,46 @@ fun CollectionViewModel.makeSectionModels(action: Action) = setState { state -> } else -> state } +} + +fun CollectionViewModel.loadEmoji2(state: CollectionState): CollectionState { + val emojiCollection = (1..5) + .map { + (1..(40..80).random()).map { EmojiHelper.emoji } + } + return state.copy(emojis = emojiCollection) +} + +fun CollectionViewModel.makeSectionModels2(state: CollectionState): CollectionState { + val colors = listOf( + 0xffd01774.toInt(), + 0xfff7f93c.toInt(), + 0xfff07777.toInt(), + 0xfffcce62.toInt(), + 0xff58c8d8.toInt() + ) + + val sectionModels = state.emojis.mapIndexed { index, list -> + val emojiBoxModels = list.map { EmojiBoxModel(it) } + DefaultSectionModel( + emojiBoxModels, + header = TextMessage("Section Header #$index", colors[index], true), + footer = TextMessage("Section Footer #$index", colors[index], false) + ) + { recyclerView -> + recyclerView.layoutManager = when(index) { + 0 -> GridLayoutManager(getApplication(), 8) + 1 -> GridLayoutManager(getApplication(), 4) + 2 -> StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL) + 3 -> GridLayoutManager(getApplication(), 3) + else -> LinearLayoutManager(getApplication(), LinearLayoutManager.HORIZONTAL, false) + } + + if (index == 4) { + val snapHelper = LinearSnapHelper() + snapHelper.attachToRecyclerView(recyclerView) + } + } + } + return state.copy(sections = sectionModels) } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/redux/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/redux/Reducers.kt index 9f4d281..719cd47 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/redux/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/redux/Reducers.kt @@ -32,4 +32,16 @@ fun MainViewModel.routeReducer(action: Action): MainState = setState { state -> } else -> state } -} \ No newline at end of file +} + +fun MainViewModel.routeClickCounterExampleButtonAction(state: MainState) = state.copy(route = MainRoute.CounterExample) + +fun MainViewModel.routeClickCounterExample2ButtonAction(state: MainState) = state.copy(route = MainRoute.CounterExample2) + +fun MainViewModel.routeClickRecyclerViewExampleButtonAction(state: MainState) = state.copy(route = MainRoute.RecyclerViewExample) + +fun MainViewModel.routeClickEmojiExampleButtonAction(state: MainState) = state.copy(route = MainRoute.EmojiCollectionExample) + +fun MainViewModel.routeClickCollectionViewExampleButtonAction(state: MainState) = state.copy(route = MainRoute.CollectionViewExample) + + diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/RCKViewModel.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/RCKViewModel.kt index 823c715..6bf6017 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/RCKViewModel.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/RCKViewModel.kt @@ -7,6 +7,8 @@ import com.github.skyfe79.android.reactcomponentkit.eventbus.Token import com.jakewharton.rxrelay2.BehaviorRelay import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.toObservable +import io.reactivex.schedulers.Schedulers import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.locks.ReentrantLock @@ -175,14 +177,12 @@ abstract class RCKViewModel(application: Application): AndroidViewMode } } - fun withState(block: RCKViewModel.(S) -> Unit) { + fun withState(block: RCKViewModel.(S) -> R): R { readLock.lock() try { - block(this.store.state) + return block(this.store.state) } finally { readLock.unlock() } } - - } \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt index 4eaa203..e0e8279 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt @@ -1,3 +1,4 @@ package com.github.skyfe79.android.reactcomponentkit.redux typealias Reducer = (Action) -> STATE +typealias Reducer2 = (STATE) -> STATE \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt index cd69ba2..d57dcbf 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt @@ -1,8 +1,6 @@ package com.github.skyfe79.android.reactcomponentkit.redux -import io.reactivex.Observable import io.reactivex.Single -import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.plugins.RxJavaPlugins import io.reactivex.rxkotlin.subscribeBy @@ -14,6 +12,7 @@ class Store { lateinit var state: S private set private lateinit var reducers: Array> + private lateinit var reducers2: MutableMap>> private lateinit var afters: Array> private val disposables = CompositeDisposable() @@ -30,15 +29,21 @@ class Store { afters: Array> = emptyArray()) { this.state = initialState this.reducers = reducers + this.reducers2 = mutableMapOf() this.afters = afters } fun deinitialize() { reducers = emptyArray() + reducers2 = mutableMapOf() afters = emptyArray() disposables.clear() } + fun map(action: Action, vararg r: Reducer2) { + reducers2[action] = r.toList() + } + /** * Do something after dispatching new state. * For example, reset route on Android @@ -55,12 +60,12 @@ class Store { return Single.create { single -> // reset error this@Store.state.error = null - - val disposable = reducers.toObservable() + val reducersForAction: List> = reducers2[action] ?: emptyList() + val disposable = reducersForAction.toObservable() .subscribeOn(Schedulers.single()) .observeOn(Schedulers.single()) .map { reducer -> - reducer(action) + reducer(this@Store.state) } .doOnNext { modifiedState -> this@Store.state = modifiedState From 6861fd9c4af723f286cc4513268e0346334df22e Mon Sep 17 00:00:00 2001 From: skyfe79 Date: Thu, 29 Aug 2019 12:51:40 +0900 Subject: [PATCH 09/12] Define Flow and apply it --- .../android/library/app/MainViewModel.kt | 23 ++-- .../collectionview/CollectionViewActivity.kt | 2 - .../collectionview/CollectionViewModel.kt | 31 +++--- .../collectionview/reducer/Reducers.kt | 57 +--------- .../app/examples/counter/CounterLayout.kt | 8 +- .../app/examples/counter/CounterViewModel.kt | 23 +++- .../app/examples/counter/redux/Reducers.kt | 52 --------- .../examples/counter2/CounterViewModel2.kt | 17 ++- .../EmojiCollectionViewModel.kt | 46 ++++++-- .../components/EmojiViewComponent.kt | 2 +- .../emojicollection/reducers/Reducers.kt | 74 ++++--------- .../android/library/app/redux/Reducers.kt | 41 ++----- .../dispatcher/DispatcherExtension.kt | 24 ++-- .../reactcomponentkit/redux/RCKViewModel.kt | 5 + .../reactcomponentkit/redux/Reducer.kt | 3 +- .../android/reactcomponentkit/redux/State.kt | 6 +- .../android/reactcomponentkit/redux/Store.kt | 104 +++++++++++------- 17 files changed, 213 insertions(+), 305 deletions(-) delete mode 100644 app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt index e34e724..a819e42 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt @@ -15,7 +15,11 @@ enum class MainRoute { EmojiCollectionExample, CollectionViewExample } -data class MainState(var route: MainRoute): State() +data class MainState(var route: MainRoute): State() { + override fun copyState(): MainState { + return this.copy() + } +} class MainViewModel(application: Application): RCKViewModel(application) { @@ -23,16 +27,15 @@ class MainViewModel(application: Application): RCKViewModel(applicati override fun setupStore() { initStore { store -> - store.set( - initialState = MainState(MainRoute.None), - reducers = arrayOf(::routeReducer) - ) + store.initialState(MainState(MainRoute.None)) - store.map(ClickCounterExampleButtonAction, ::routeClickCounterExampleButtonAction) - store.map(ClickCounterExample2ButtonAction, ::routeClickCounterExample2ButtonAction) - store.map(ClickRecyclerViewExampleButtonAction, ::routeClickRecyclerViewExampleButtonAction) - store.map(ClickEmojiExampleButtonAction, ::routeClickEmojiExampleButtonAction) - store.map(ClickCollectionViewExampleButtonAction, { it.copy(route = MainRoute.CollectionViewExample) }) + store.flow(::routeToCounterExample) + store.flow(::routeToCounterExample2) + store.flow(::routeToRecyclerViewExample) + store.flow(::routeToEmojiExample) + store.flow({ state, _ -> + state.copy(route = MainRoute.CollectionViewExample) + }) } } diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewActivity.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewActivity.kt index 1c5bfc1..e629e00 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewActivity.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewActivity.kt @@ -6,8 +6,6 @@ import android.os.Bundle import androidx.lifecycle.ViewModelProviders import com.github.skyfe79.android.library.app.R import com.github.skyfe79.android.library.app.examples.collectionview.action.LoadAction -import com.github.skyfe79.android.library.app.examples.collectionview.reducer.loadEmoji2 -import com.github.skyfe79.android.library.app.examples.collectionview.reducer.makeSectionModels2 import com.github.skyfe79.android.library.app.examples.emojicollection.components.EmojiViewComponent import com.github.skyfe79.android.library.app.examples.recyclerview.component.SectionViewComponent import com.github.skyfe79.android.reactcomponentkit.collectionview.CollectionViewAdapter diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt index abaa041..0a193a0 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt @@ -2,7 +2,8 @@ package com.github.skyfe79.android.library.app.examples.collectionview import android.app.Application import com.github.skyfe79.android.library.app.examples.collectionview.action.LoadAction -import com.github.skyfe79.android.library.app.examples.collectionview.reducer.* +import com.github.skyfe79.android.library.app.examples.collectionview.reducer.loadEmoji +import com.github.skyfe79.android.library.app.examples.collectionview.reducer.makeSectionModels import com.github.skyfe79.android.reactcomponentkit.collectionmodels.DefaultSectionModel import com.github.skyfe79.android.reactcomponentkit.redux.* @@ -10,7 +11,12 @@ import com.github.skyfe79.android.reactcomponentkit.redux.* data class CollectionState( var emojis: List> = emptyList(), var sections: List = emptyList() -): State() +): State() { + + override fun copyState(): CollectionState { + return this.copy() + } +} @@ -20,22 +26,11 @@ class CollectionViewModel(application: Application): RCKViewModel - store.set( - initialState = CollectionState() - //reducers = arrayOf(::loadEmoji, ::makeSectionModels) - ) - - // map action to reducers - store.map(LoadAction, ::loadEmoji2, ::makeSectionModels2) - // or - store.map(LoadAction, - { state -> - //it's reducer - state - }, - { - //it's reducer - it + store.initialState(CollectionState()) + store.flow( + ::loadEmoji, + { state, _ -> + makeSectionModels(state) } ) } diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt index ff5fdbe..15dea42 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/reducer/Reducers.kt @@ -15,60 +15,7 @@ import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State import io.reactivex.Observable -fun CollectionViewModel.loadEmoji(action: Action) = setState { state -> - when (action) { - is LoadAction -> { - val emojiCollection = (1..5) - .map { - (1..(40..80).random()).map { EmojiHelper.emoji } - } - state.copy(emojis = emojiCollection) - } - else -> state - } -} - -fun CollectionViewModel.makeSectionModels(action: Action) = setState { state -> - when (action) { - is LoadAction -> { - - val colors = listOf( - 0xffd01774.toInt(), - 0xfff7f93c.toInt(), - 0xfff07777.toInt(), - 0xfffcce62.toInt(), - 0xff58c8d8.toInt() - ) - - val sectionModels = state.emojis.mapIndexed { index, list -> - val emojiBoxModels = list.map { EmojiBoxModel(it) } - DefaultSectionModel( - emojiBoxModels, - header = TextMessage("Section Header #$index", colors[index], true), - footer = TextMessage("Section Footer #$index", colors[index], false) - ) - { recyclerView -> - recyclerView.layoutManager = when(index) { - 0 -> GridLayoutManager(getApplication(), 8) - 1 -> GridLayoutManager(getApplication(), 4) - 2 -> StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL) - 3 -> GridLayoutManager(getApplication(), 3) - else -> LinearLayoutManager(getApplication(), LinearLayoutManager.HORIZONTAL, false) - } - - if (index == 4) { - val snapHelper = LinearSnapHelper() - snapHelper.attachToRecyclerView(recyclerView) - } - } - } - state.copy(sections = sectionModels) - } - else -> state - } -} - -fun CollectionViewModel.loadEmoji2(state: CollectionState): CollectionState { +fun CollectionViewModel.loadEmoji(state: CollectionState, action: LoadAction): CollectionState { val emojiCollection = (1..5) .map { (1..(40..80).random()).map { EmojiHelper.emoji } @@ -76,7 +23,7 @@ fun CollectionViewModel.loadEmoji2(state: CollectionState): CollectionState { return state.copy(emojis = emojiCollection) } -fun CollectionViewModel.makeSectionModels2(state: CollectionState): CollectionState { +fun CollectionViewModel.makeSectionModels(state: CollectionState): CollectionState { val colors = listOf( 0xffd01774.toInt(), 0xfff7f93c.toInt(), diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt index d9cb6a2..d15033c 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt @@ -7,7 +7,6 @@ import android.widget.LinearLayout import android.widget.TextView 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.counter.redux.increment import com.github.skyfe79.android.reactcomponentkit.component.LayoutComponent import com.github.skyfe79.android.reactcomponentkit.dispatcher.dispatch import com.github.skyfe79.android.reactcomponentkit.eventbus.Token @@ -55,14 +54,11 @@ class CounterLayout(token: Token): LayoutComponent(token) { } - increaseButton.onClick { + increaseButton.setOnClickListener { dispatch(IncreaseAction()) -// dispatch { -// (it as? CounterViewModel)?.increment(10) -// } } - decreaseButton.onClick { + decreaseButton.setOnClickListener { dispatch(DecreaseAction()) } diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt index 29376c3..f4f46fc 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt @@ -1,10 +1,15 @@ package com.github.skyfe79.android.library.app.examples.counter import android.app.Application -import com.github.skyfe79.android.library.app.examples.counter.redux.countReducer +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.reactcomponentkit.redux.* -data class CounterState(val count: Int): State() +data class CounterState(val count: Int): State() { + override fun copyState(): CounterState { + return this.copy() + } +} class CounterViewModel(application: Application): RCKViewModel(application) { @@ -12,10 +17,16 @@ class CounterViewModel(application: Application): RCKViewModel(app override fun setupStore() { initStore { store -> - store.set( - initialState = CounterState(0), - reducers = arrayOf(::countReducer) - ) + store.initialState(CounterState(0)) + + + store.flow({ state, action -> + state.copy(count = state.count + action.payload) + }) + + store.flow({ state, action -> + state.copy(count = state.count - action.payload) + }) } } diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt deleted file mode 100644 index c28d0c4..0000000 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/redux/Reducers.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.github.skyfe79.android.library.app.examples.counter.redux - -import com.github.skyfe79.android.library.app.examples.counter.CounterState -import com.github.skyfe79.android.library.app.examples.counter.CounterViewModel -import com.github.skyfe79.android.library.app.examples.counter.CounterViewModel2 -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.reactcomponentkit.redux.Action - - -//목표는 리듀서와 액션을 없애는 것이다. -//단 주의할 점은 RCK는 상태 mutation을 모두 백그라운드에서 실행하고 있다. -//과연 이것이 필요한 것일까? -//async 만 백그라운드에서 실행하면 되지 않을까? -fun CounterViewModel.countReducer(action: Action) = setState { state -> - when(action) { - is IncreaseAction -> increment(action.payload) - is DecreaseAction -> decrement(action.payload) - else -> state - } -} - -fun CounterViewModel2.countReducer(action: Action) = setState { state -> - when(action) { - is IncreaseAction -> setState { - with(it) { copy(count = count + action.payload) } - } - is DecreaseAction -> setState { - with(it) { copy(count = count - action.payload) } - } - else -> state - } -} - - -// 이렇게 정의한 리듀서를 액션 없이 각 컴포넌트에서 사용할 수 있어야 한다. -// 각 컴포넌트는 ViewModel을 받는다. 그리고 dispatch wrapper 함수로 감싼다. -fun CounterViewModel.increment(payload: Int) = setState { - with(it) { copy(count = count + payload) } - // 여기 내부에서 호출할 수 있는 다른 메서드는 오직 리듀서 뿐이다. - // decrement(payload) - // async를 어떻게 할 것인가? - /* - async { - increment(payload) - } - */ -} - -fun CounterViewModel.decrement(payload: Int) = setState { - with(it) { copy(count = count - payload) } -} \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt index 12fdb6c..4c59e5c 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt @@ -1,7 +1,8 @@ package com.github.skyfe79.android.library.app.examples.counter import android.app.Application -import com.github.skyfe79.android.library.app.examples.counter.redux.countReducer +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.reactcomponentkit.redux.Output import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel @@ -11,11 +12,17 @@ class CounterViewModel2(application: Application): RCKViewModel(ap override fun setupStore() { initStore { store -> - store.set( - initialState = CounterState(0), - reducers = arrayOf(::countReducer) - ) + store.initialState(CounterState(0)) + + store.flow({ state, action -> + state.copy(count = state.count + action.payload) + }) + + store.flow({ state, action -> + state.copy(count = state.count - action.payload) + }) } + } override fun on(newState: CounterState) { diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt index 7dfc34d..44e892d 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt @@ -2,12 +2,13 @@ package com.github.skyfe79.android.library.app.examples.emojicollection import android.app.Application import android.util.Log +import com.github.skyfe79.android.library.app.examples.emojicollection.actions.AddEmojiAction +import com.github.skyfe79.android.library.app.examples.emojicollection.actions.RemoveEmojiAction +import com.github.skyfe79.android.library.app.examples.emojicollection.actions.ShuffleEmojiAction +import com.github.skyfe79.android.library.app.examples.emojicollection.components.ClickEmojiAction import com.github.skyfe79.android.library.app.examples.emojicollection.reducers.* import com.github.skyfe79.android.reactcomponentkit.collectionmodels.ItemModel -import com.github.skyfe79.android.reactcomponentkit.redux.Error -import com.github.skyfe79.android.reactcomponentkit.redux.Output -import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel -import com.github.skyfe79.android.reactcomponentkit.redux.State +import com.github.skyfe79.android.reactcomponentkit.redux.* sealed class EmojiRoute { object None: EmojiRoute() @@ -15,10 +16,14 @@ sealed class EmojiRoute { } data class EmojiCollectionState( - val emojis: List, - val itemModels: List, + val emojis: List = emptyList(), + val itemModels: List = emptyList(), val route: EmojiRoute = EmojiRoute.None -): State() +): State() { + override fun copyState(): EmojiCollectionState { + return this.copy() + } +} class EmojiCollectionViewModel(application: Application): RCKViewModel(application) { @@ -27,9 +32,30 @@ class EmojiCollectionViewModel(application: Application): RCKViewModel - store.set( - initialState = EmojiCollectionState(listOf(), listOf()), - reducers = arrayOf(::route, ::addEmoji, ::removeEmoji, ::shuffleEmoji, ::makeItemModels) + + store.initialState(EmojiCollectionState()) + store.afterFlow({ + it.copy(route = EmojiRoute.None) + }) + + + store.flow({ state, action -> + state.copy(route = EmojiRoute.AlertEmoji(action.emoji)) + }) + + store.flow( + ::addEmoji, + { state, _ -> makeItemModels(state) } + ) + + store.flow( + ::removeEmoji, + { state, _ -> makeItemModels(state) } + ) + + store.flow( + ::shuffleEmoji, + { state, _ -> makeItemModels(state) } ) } } diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt index e3ee39e..1bdd3fd 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt @@ -33,7 +33,7 @@ class EmojiViewComponent(token: Token): ViewComponent(token) { } } - rootLayout.onClick { + rootLayout.setOnClickListener { this@EmojiViewComponent.token dispatch(ClickEmojiAction(emojiTextView.text.toString())) } diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt index bf36d12..7aee07c 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt @@ -18,65 +18,35 @@ import io.reactivex.rxkotlin.subscribeBy import java.lang.Exception import kotlin.random.Random -fun EmojiCollectionViewModel.route(action: Action) = setState { state -> - when(action) { - is ClickEmojiAction -> state.copy(route = EmojiRoute.AlertEmoji(action.emoji)) - else -> state.copy(route = EmojiRoute.None) - } -} -fun EmojiCollectionViewModel.addEmoji(action: Action) = setState { state -> - when (action) { - is AddEmojiAction -> { - val mutableEmojiList = state.emojis.toMutableList() - val index = if (mutableEmojiList.isEmpty()) 0 else (0 until mutableEmojiList.size).random() - mutableEmojiList.add(index, EmojiHelper.emoji) - nextDispatch(MakeItemModelsAction, applyNewState = true) - state.copy(emojis = mutableEmojiList) - } - else -> state - } +fun EmojiCollectionViewModel.addEmoji(state: EmojiCollectionState, action: AddEmojiAction): EmojiCollectionState { + val mutableEmojiList = state.emojis.toMutableList() + val index = if (mutableEmojiList.isEmpty()) 0 else (0 until mutableEmojiList.size).random() + mutableEmojiList.add(index, EmojiHelper.emoji) + return state.copy(emojis = mutableEmojiList) } -fun EmojiCollectionViewModel.removeEmoji(action: Action) = setState { state -> - when (action) { - is RemoveEmojiAction -> { - try { - val mutableEmojiList = state.emojis.toMutableList() - val index = if (mutableEmojiList.isEmpty()) 0 else (0 until mutableEmojiList.size).random() - mutableEmojiList.removeAt(index) - val mutatedState = state.copy(emojis = mutableEmojiList) - nextDispatch(MakeItemModelsAction, applyNewState = true) - mutatedState - } catch (e: Exception) { - throw e - } - } - else -> state +fun EmojiCollectionViewModel.removeEmoji(state: EmojiCollectionState, action: RemoveEmojiAction): EmojiCollectionState { + try { + val mutableEmojiList = state.emojis.toMutableList() + val index = if (mutableEmojiList.isEmpty()) 0 else (0 until mutableEmojiList.size).random() + mutableEmojiList.removeAt(index) + return state.copy(emojis = mutableEmojiList) + } catch (e: Exception) { + throw e } } -fun EmojiCollectionViewModel.shuffleEmoji(action: Action) = setState { state -> - when (action) { - is ShuffleEmojiAction -> { - val mutableEmojiList = state.emojis.toMutableList() - mutableEmojiList.shuffle() - val mutatedState = state.copy(emojis = mutableEmojiList) - nextDispatch(MakeItemModelsAction, applyNewState = true) - mutatedState - } - else -> state - } + +fun EmojiCollectionViewModel.shuffleEmoji(state: EmojiCollectionState, action: ShuffleEmojiAction): EmojiCollectionState { + val mutableEmojiList = state.emojis.toMutableList() + mutableEmojiList.shuffle() + return state.copy(emojis = mutableEmojiList) } -fun EmojiCollectionViewModel.makeItemModels(action: Action) = setState { state -> - when (action) { - is MakeItemModelsAction -> { - val emojiBoxModels = state.emojis.map { EmojiBoxModel(it) } - val mutatedState = state.copy(itemModels = emojiBoxModels) - mutatedState - } - else -> state - } + +fun EmojiCollectionViewModel.makeItemModels(state: EmojiCollectionState): EmojiCollectionState { + val emojiBoxModels = state.emojis.map { EmojiBoxModel(it) } + return state.copy(itemModels = emojiBoxModels) } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/redux/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/redux/Reducers.kt index 719cd47..88a17ac 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/redux/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/redux/Reducers.kt @@ -5,43 +5,18 @@ import com.github.skyfe79.android.library.app.MainState import com.github.skyfe79.android.library.app.MainViewModel import com.github.skyfe79.android.library.app.action.* import com.github.skyfe79.android.reactcomponentkit.redux.Action -import com.github.skyfe79.android.reactcomponentkit.redux.State -import io.reactivex.Observable - -fun MainViewModel.routeReducer(action: Action): MainState = setState { state -> - when (action) { - is ClickCounterExampleButtonAction -> { - val mutatedState = state.copy(route = MainRoute.CounterExample) - mutatedState - } - is ClickCounterExample2ButtonAction -> { - val mutatedState = state.copy(route = MainRoute.CounterExample2) - mutatedState - } - is ClickRecyclerViewExampleButtonAction -> { - val mutatedState = state.copy(route = MainRoute.RecyclerViewExample) - mutatedState - } - is ClickEmojiExampleButtonAction -> { - val mutatedState = state.copy(route = MainRoute.EmojiCollectionExample) - mutatedState - } - is ClickCollectionViewExampleButtonAction -> { - val mutatedState = state.copy(route = MainRoute.CollectionViewExample) - mutatedState - } - else -> state - } -} -fun MainViewModel.routeClickCounterExampleButtonAction(state: MainState) = state.copy(route = MainRoute.CounterExample) -fun MainViewModel.routeClickCounterExample2ButtonAction(state: MainState) = state.copy(route = MainRoute.CounterExample2) +fun MainViewModel.routeToCounterExample(state: MainState, action: ClickCounterExampleButtonAction): MainState { + return state.copy(route = MainRoute.CounterExample) +} + +fun MainViewModel.routeToCounterExample2(state: MainState, action: ClickCounterExample2ButtonAction) = state.copy(route = MainRoute.CounterExample2) -fun MainViewModel.routeClickRecyclerViewExampleButtonAction(state: MainState) = state.copy(route = MainRoute.RecyclerViewExample) +fun MainViewModel.routeToRecyclerViewExample(state: MainState, action: ClickRecyclerViewExampleButtonAction) = state.copy(route = MainRoute.RecyclerViewExample) -fun MainViewModel.routeClickEmojiExampleButtonAction(state: MainState) = state.copy(route = MainRoute.EmojiCollectionExample) +fun MainViewModel.routeToEmojiExample(state: MainState, action: ClickEmojiExampleButtonAction) = state.copy(route = MainRoute.EmojiCollectionExample) -fun MainViewModel.routeClickCollectionViewExampleButtonAction(state: MainState) = state.copy(route = MainRoute.CollectionViewExample) +fun MainViewModel.routeToCollectionViewExample(state: MainState) = state.copy(route = MainRoute.CollectionViewExample) diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/DispatcherExtension.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/DispatcherExtension.kt index 1922bf1..85c2055 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/DispatcherExtension.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/DispatcherExtension.kt @@ -13,27 +13,27 @@ fun FragmentComponent.dispatch(action: Action) { viewModel.let { it?.dispatch(action) } } -fun FragmentComponent.dispatch(action: (RCKViewModel<*>) -> Unit) { - val viewModel = RCK.viewModel(token) - viewModel?.let { action(it) } -} +//fun FragmentComponent.dispatch(action: (RCKViewModel<*>) -> Unit) { +// val viewModel = RCK.viewModel(token) +// viewModel?.let { action(it) } +//} fun LayoutComponent.dispatch(action: Action) { val viewModel = RCK.viewModel(token) viewModel.let { it?.dispatch(action) } } -fun LayoutComponent.dispatch(action: (RCKViewModel<*>) -> Unit) { - val viewModel = RCK.viewModel(token) - viewModel?.let { action(it) } -} +//fun LayoutComponent.dispatch(action: (RCKViewModel<*>) -> Unit) { +// val viewModel = RCK.viewModel(token) +// viewModel?.let { action(it) } +//} fun ViewComponent.dispatch(action: Action) { val viewModel = RCK.viewModel(token) viewModel.let { it?.dispatch(action) } } -fun ViewComponent.dispatch(action: (RCKViewModel<*>) -> Unit) { - val viewModel = RCK.viewModel(token) - viewModel?.let { action(it) } -} \ No newline at end of file +//fun ViewComponent.dispatch(action: (RCKViewModel<*>) -> Unit) { +// val viewModel = RCK.viewModel(token) +// viewModel?.let { action(it) } +//} \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/RCKViewModel.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/RCKViewModel.kt index 6bf6017..e2146a3 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/RCKViewModel.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/RCKViewModel.kt @@ -24,6 +24,7 @@ abstract class RCKViewModel(application: Application): AndroidViewMode private var applyNewState: Boolean = false private var actionQueue: Queue> = Queue() private var isProcessingAction: AtomicBoolean = AtomicBoolean(false) + private var currentAction: Action = VoidAction private val writeLock = ReentrantLock() private val readLock = ReentrantLock() @@ -46,6 +47,7 @@ abstract class RCKViewModel(application: Application): AndroidViewMode private fun setupRxStream() { val disposable1 = rx_action + .subscribeOn(Schedulers.single()) .doOnNext { isProcessingAction.set(true) } @@ -69,6 +71,7 @@ abstract class RCKViewModel(application: Application): AndroidViewMode } } .flatMap { action -> + currentAction = action store.dispatch(action).toObservable() } .observeOn(AndroidSchedulers.mainThread()) @@ -166,6 +169,7 @@ abstract class RCKViewModel(application: Application): AndroidViewMode block(this.store) } + /* fun setState(block: RCKViewModel.(S) -> S): S { writeLock.lock() try { @@ -176,6 +180,7 @@ abstract class RCKViewModel(application: Application): AndroidViewMode writeLock.unlock() } } + */ fun withState(block: RCKViewModel.(S) -> R): R { readLock.lock() diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt index e0e8279..24be601 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt @@ -1,4 +1,3 @@ package com.github.skyfe79.android.reactcomponentkit.redux -typealias Reducer = (Action) -> STATE -typealias Reducer2 = (STATE) -> STATE \ No newline at end of file +typealias Reducer = (STATE, ACTION) -> STATE \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/State.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/State.kt index 36c6505..0859ad3 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/State.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/State.kt @@ -1,7 +1,11 @@ package com.github.skyfe79.android.reactcomponentkit.redux -abstract class State { +interface StateCopyable { + fun copyState(): S +} + +abstract class State: StateCopyable { var error: Error? = null } diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt index d57dcbf..c2d35fd 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt @@ -4,16 +4,40 @@ import io.reactivex.Single import io.reactivex.disposables.CompositeDisposable import io.reactivex.plugins.RxJavaPlugins import io.reactivex.rxkotlin.subscribeBy -import io.reactivex.rxkotlin.toObservable import io.reactivex.schedulers.Schedulers +import kotlin.reflect.KClass + class Store { + inner class Flow(private val reducerList: List) { + + @Suppress("UNCHECKED_CAST") + fun flow(action: Action): Single { + return Single.create { emitter -> + reducerList.forEach { anyValue -> + val reducer = anyValue as Reducer + val typedAction = action as A + val typedState = this@Store.state.copyState() as STATE + + try { + val mutatedState = reducer(typedState, typedAction) as S + this@Store.state = mutatedState + } catch (e: Throwable) { + emitter.onError(e) + } + } + + emitter.onSuccess(this@Store.state.copyState() as S) + } + } + } + lateinit var state: S private set - private lateinit var reducers: Array> - private lateinit var reducers2: MutableMap>> - private lateinit var afters: Array> + lateinit var actionFlowMap: MutableMap, Flow> + private set + private lateinit var afters: List> private val disposables = CompositeDisposable() companion object { @@ -23,25 +47,32 @@ class Store { } } - fun set( - initialState: S, - reducers: Array> = emptyArray(), - afters: Array> = emptyArray()) { - this.state = initialState - this.reducers = reducers - this.reducers2 = mutableMapOf() - this.afters = afters +// fun set( +// initialState: S, +// afters: Array> = emptyArray()) { +// this.state = initialState +// this.actionFlowMap = mutableMapOf() +// this.afters = afters +// } + + fun initialState(state: S) { + this.state = state + this.actionFlowMap = mutableMapOf() + this.afters = emptyList() } fun deinitialize() { - reducers = emptyArray() - reducers2 = mutableMapOf() - afters = emptyArray() + actionFlowMap = mutableMapOf() + afters = emptyList() disposables.clear() } - fun map(action: Action, vararg r: Reducer2) { - reducers2[action] = r.toList() + inline fun flow(vararg reducers: Reducer) { + actionFlowMap[A::class] = Flow(reducers.toList()) + } + + fun afterFlow(vararg afters: After) { + this.afters = afters.toList() } /** @@ -60,30 +91,23 @@ class Store { return Single.create { single -> // reset error this@Store.state.error = null - val reducersForAction: List> = reducers2[action] ?: emptyList() - val disposable = reducersForAction.toObservable() - .subscribeOn(Schedulers.single()) - .observeOn(Schedulers.single()) - .map { reducer -> - reducer(this@Store.state) - } - .doOnNext { modifiedState -> - this@Store.state = modifiedState - } - .reduce { _: S, nextState: S -> - nextState - } - .subscribeBy( - onSuccess = { finalState: S -> - single.onSuccess(finalState) - }, - onError = { error -> - this@Store.state.error = Error(error, action) - single.onSuccess(this@Store.state) - } - ) - disposables.add(disposable) + val actionFlow = actionFlowMap[action::class] + actionFlow?.let { + val disposable = it.flow(action) + .subscribeOn(Schedulers.single()) + .observeOn(Schedulers.single()) + .subscribeBy( + onSuccess = { newState -> + single.onSuccess(newState) + }, + onError = { error -> + this@Store.state.error = Error(error, action) + single.onSuccess(this@Store.state) + } + ) + disposables.add(disposable) + } } } From 35ae525a78ae173b8b35897e28e2b1302c751290 Mon Sep 17 00:00:00 2001 From: skyfe79 Date: Fri, 30 Aug 2019 00:21:55 +0900 Subject: [PATCH 10/12] Implement async flow :) --- .../android/library/app/MainViewModel.kt | 2 +- .../collectionview/CollectionViewModel.kt | 8 +- .../app/examples/counter/CounterViewModel.kt | 38 +++- .../app/examples/counter/action/Actions.kt | 1 + .../examples/counter2/CounterViewModel2.kt | 2 +- .../EmojiCollectionViewModel.kt | 1 + .../skyfe79/android/reactcomponentkit/RCK.kt | 2 +- .../collectionview/CollectionViewAdapter.kt | 3 - .../component/LayoutComponent.kt | 2 - .../dispatcher/DispatcherExtension.kt | 18 +- .../android/reactcomponentkit/redux/Action.kt | 3 +- .../android/reactcomponentkit/redux/Async.kt | 10 + .../redux/{After.kt => Effect.kt} | 2 +- .../reactcomponentkit/redux/RCKViewModel.kt | 193 ------------------ .../reactcomponentkit/redux/Reducer.kt | 2 +- .../android/reactcomponentkit/redux/State.kt | 3 +- .../android/reactcomponentkit/redux/Store.kt | 171 ++-------------- .../android/reactcomponentkit/util/Tasks.kt | 3 +- .../android/reactcomponentkit/util/ViewId.kt | 2 +- .../viewmodel/RCKViewModel.kt | 155 ++++++++++++++ .../viewmodel/RootViewModelType.kt | 29 --- 21 files changed, 231 insertions(+), 419 deletions(-) create mode 100644 reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Async.kt rename reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/{After.kt => Effect.kt} (79%) delete mode 100644 reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/RCKViewModel.kt create mode 100644 reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RCKViewModel.kt delete mode 100644 reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RootViewModelType.kt diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt index a819e42..fdc8a95 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewModel.kt @@ -5,7 +5,7 @@ import com.github.skyfe79.android.library.app.action.* import com.github.skyfe79.android.reactcomponentkit.redux.Output import com.github.skyfe79.android.reactcomponentkit.redux.State import com.github.skyfe79.android.library.app.redux.* -import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel +import com.github.skyfe79.android.reactcomponentkit.viewmodel.RCKViewModel enum class MainRoute { None, diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt index 0a193a0..f44d5f5 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/collectionview/CollectionViewModel.kt @@ -6,6 +6,7 @@ import com.github.skyfe79.android.library.app.examples.collectionview.reducer.lo import com.github.skyfe79.android.library.app.examples.collectionview.reducer.makeSectionModels import com.github.skyfe79.android.reactcomponentkit.collectionmodels.DefaultSectionModel import com.github.skyfe79.android.reactcomponentkit.redux.* +import com.github.skyfe79.android.reactcomponentkit.viewmodel.RCKViewModel data class CollectionState( @@ -36,13 +37,6 @@ class CollectionViewModel(application: Application): RCKViewModel withState { state -> - if (state.emojis.isNotEmpty()) VoidAction else action - } - else -> action - } - override fun on(newState: CollectionState) { sections.accept(newState.sections) } diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt index f4f46fc..efc1fb4 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt @@ -4,8 +4,13 @@ import android.app.Application 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.reactcomponentkit.redux.* +import com.github.skyfe79.android.reactcomponentkit.viewmodel.RCKViewModel +import io.reactivex.Single -data class CounterState(val count: Int): State() { +data class CounterState( + val count: Int, + val asyncCount: Async = Async.Uninitialized +): State() { override fun copyState(): CounterState { return this.copy() } @@ -15,14 +20,37 @@ class CounterViewModel(application: Application): RCKViewModel(app val count: Output = Output(0) + fun showLoading() = setState { state -> + state.copy(asyncCount = Async.Loading) + } + + fun asyncIncrease(state: CounterState, action: IncreaseAction) = asyncReducer(state, action) { + Single.create { emitter -> + Thread.sleep(1000L) + withState { state -> + emitter.onSuccess(state.copy(count = state.count + action.payload)) + } + }.toObservable() + } + override fun setupStore() { initStore { store -> store.initialState(CounterState(0)) - - store.flow({ state, action -> - state.copy(count = state.count + action.payload) - }) + store.flow( + { state, action -> + state.copy(count = state.count + 3) + }, + ::asyncIncrease, + asyncFlow { action -> + Single.create { emitter -> + Thread.sleep(5000L) + withState { state -> + emitter.onSuccess(state.copy(count = state.count + action.payload)) + } + }.toObservable() + } + ) store.flow({ state, action -> state.copy(count = state.count - action.payload) diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/action/Actions.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/action/Actions.kt index 8fa70b0..16ae7d2 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/action/Actions.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/action/Actions.kt @@ -4,3 +4,4 @@ import com.github.skyfe79.android.reactcomponentkit.redux.Action data class IncreaseAction(val payload: Int = 1): Action data class DecreaseAction(val payload: Int = 1): Action +data class AsyncIncreaseAction(val payload: Int = 1): Action \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt index 4c59e5c..db82ab7 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterViewModel2.kt @@ -4,7 +4,7 @@ import android.app.Application 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.reactcomponentkit.redux.Output -import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel +import com.github.skyfe79.android.reactcomponentkit.viewmodel.RCKViewModel class CounterViewModel2(application: Application): RCKViewModel(application) { diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt index 44e892d..e234867 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt @@ -9,6 +9,7 @@ import com.github.skyfe79.android.library.app.examples.emojicollection.component import com.github.skyfe79.android.library.app.examples.emojicollection.reducers.* import com.github.skyfe79.android.reactcomponentkit.collectionmodels.ItemModel import com.github.skyfe79.android.reactcomponentkit.redux.* +import com.github.skyfe79.android.reactcomponentkit.viewmodel.RCKViewModel sealed class EmojiRoute { object None: EmojiRoute() diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt index 5618a40..5de3b95 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt @@ -1,7 +1,7 @@ package com.github.skyfe79.android.reactcomponentkit import com.github.skyfe79.android.reactcomponentkit.eventbus.Token -import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel +import com.github.skyfe79.android.reactcomponentkit.viewmodel.RCKViewModel import com.github.skyfe79.android.reactcomponentkit.redux.State import java.lang.ref.WeakReference diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/CollectionViewAdapter.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/CollectionViewAdapter.kt index 483afaf..c6733db 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/CollectionViewAdapter.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/CollectionViewAdapter.kt @@ -11,9 +11,6 @@ import com.github.skyfe79.android.reactcomponentkit.recyclerview.CollectionViewC import com.github.skyfe79.android.reactcomponentkit.recyclerview.RecyclerViewCell import com.github.skyfe79.android.reactcomponentkit.recyclerview.RecyclerViewCellViewHolder import com.github.skyfe79.android.reactcomponentkit.recyclerview.sticky.StickyHeaders -import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel -import com.github.skyfe79.android.reactcomponentkit.redux.State -import java.lang.ref.WeakReference import kotlin.reflect.KClass diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/LayoutComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/LayoutComponent.kt index 9f51549..b81b0a1 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/LayoutComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/LayoutComponent.kt @@ -3,9 +3,7 @@ package com.github.skyfe79.android.reactcomponentkit.component import com.github.skyfe79.android.reactcomponentkit.ReactComponent import com.github.skyfe79.android.reactcomponentkit.eventbus.Token import com.github.skyfe79.android.reactcomponentkit.redux.State -import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel import org.jetbrains.anko.AnkoComponent -import java.lang.ref.WeakReference /** * Use this component for Activity Root Layout diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/DispatcherExtension.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/DispatcherExtension.kt index 85c2055..8c3ff86 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/DispatcherExtension.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/DispatcherExtension.kt @@ -5,7 +5,6 @@ import com.github.skyfe79.android.reactcomponentkit.component.FragmentComponent import com.github.skyfe79.android.reactcomponentkit.component.LayoutComponent import com.github.skyfe79.android.reactcomponentkit.component.ViewComponent import com.github.skyfe79.android.reactcomponentkit.redux.Action -import com.github.skyfe79.android.reactcomponentkit.redux.RCKViewModel fun FragmentComponent.dispatch(action: Action) { @@ -13,27 +12,12 @@ fun FragmentComponent.dispatch(action: Action) { viewModel.let { it?.dispatch(action) } } -//fun FragmentComponent.dispatch(action: (RCKViewModel<*>) -> Unit) { -// val viewModel = RCK.viewModel(token) -// viewModel?.let { action(it) } -//} - fun LayoutComponent.dispatch(action: Action) { val viewModel = RCK.viewModel(token) viewModel.let { it?.dispatch(action) } } -//fun LayoutComponent.dispatch(action: (RCKViewModel<*>) -> Unit) { -// val viewModel = RCK.viewModel(token) -// viewModel?.let { action(it) } -//} - fun ViewComponent.dispatch(action: Action) { val viewModel = RCK.viewModel(token) viewModel.let { it?.dispatch(action) } -} - -//fun ViewComponent.dispatch(action: (RCKViewModel<*>) -> Unit) { -// val viewModel = RCK.viewModel(token) -// viewModel?.let { action(it) } -//} \ No newline at end of file +} \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Action.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Action.kt index e70c965..2900ba1 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Action.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Action.kt @@ -1,5 +1,6 @@ package com.github.skyfe79.android.reactcomponentkit.redux + interface Action -object VoidAction: Action \ No newline at end of file +object VoidAction: Action diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Async.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Async.kt new file mode 100644 index 0000000..9a4f381 --- /dev/null +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Async.kt @@ -0,0 +1,10 @@ +package com.github.skyfe79.android.reactcomponentkit.redux + +// Ideas from MvRx +sealed class Async { + object Uninitialized: Async() + object Loading: Async() + data class Success(val value: T): Async() + data class Failed(val error: Throwable): Async() +} + diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/After.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Effect.kt similarity index 79% rename from reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/After.kt rename to reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Effect.kt index 3984bcc..f12dd33 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/After.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Effect.kt @@ -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 After = (STATE) -> STATE \ No newline at end of file +typealias Effect = (STATE) -> STATE \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/RCKViewModel.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/RCKViewModel.kt deleted file mode 100644 index e2146a3..0000000 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/RCKViewModel.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.github.skyfe79.android.reactcomponentkit.redux - -import android.app.Application -import androidx.lifecycle.AndroidViewModel -import com.github.skyfe79.android.reactcomponentkit.RCK -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token -import com.jakewharton.rxrelay2.BehaviorRelay -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.toObservable -import io.reactivex.schedulers.Schedulers -import java.util.concurrent.atomic.AtomicBoolean -import java.util.concurrent.locks.ReentrantLock - -abstract class RCKViewModel(application: Application): AndroidViewModel(application) { - val token: Token = Token() - - private val rx_action: BehaviorRelay = BehaviorRelay.createDefault(VoidAction) - private val rx_state: BehaviorRelay = BehaviorRelay.create() - - - private val store = Store() - private val disposables = CompositeDisposable() - private var applyNewState: Boolean = false - private var actionQueue: Queue> = Queue() - private var isProcessingAction: AtomicBoolean = AtomicBoolean(false) - private var currentAction: Action = VoidAction - private val writeLock = ReentrantLock() - private val readLock = ReentrantLock() - - init { - setupRxStream() - this.setupStore() - } - - override fun onCleared() { - dispose() - super.onCleared() - } - - - fun dispose() { - RCK.unregisterViewModel(token) - disposables.dispose() - store.deinitialize() - } - - private fun setupRxStream() { - val disposable1 = rx_action - .subscribeOn(Schedulers.single()) - .doOnNext { - isProcessingAction.set(true) - } - .filter { action -> - if (action !is VoidAction) { - true - } else { - isProcessingAction.set(false) - false - } - } - .map { action -> - beforeDispatch(action) - } - .filter { action -> - if (action !is VoidAction) { - true - } else { - isProcessingAction.set(false) - false - } - } - .flatMap { action -> - currentAction = action - store.dispatch(action).toObservable() - } - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { newState -> - rx_state.accept(newState) - } - - disposables.add(disposable1) - - val disposable2 = rx_state - .observeOn(AndroidSchedulers.mainThread()) - .doAfterNext { - isProcessingAction.set(false) - - val actionItem = actionQueue.peek() - if (actionItem != null ) { - val (nextAction, _) = actionItem - rx_action.accept(nextAction) - // deque actions after processing - actionQueue.dequeue() - } - } - .doOnError { - isProcessingAction.set(false) - val actionItem = actionQueue.peek() - if (actionItem != null ) { - actionQueue.dequeue() - } - } - .subscribe { newState -> - if (newState == null) return@subscribe - if (newState.error != null) { - newState.error?.let { - on(it) - store.doAfters() - } - } else { - val actionItem = actionQueue.peek() - if (actionItem != null) { - val (_, apply) = actionItem - if (apply) { - on(newState) - store.doAfters() - } - } else { - on(newState) - store.doAfters() - } - } - } - disposables.add(disposable2) - } - - abstract fun setupStore() - - fun dispatch(action: Action) { - if (actionQueue.isNotEmpty) { - if (isProcessingAction.get()) { - actionQueue.enqueue(Pair(action, true)) - } else { - rx_action.accept(action) - } - } else { - if (isProcessingAction.get()) { - actionQueue.enqueue(Pair(action, true)) - } else { - rx_action.accept(action) - } - } - } - - fun nextDispatch(action: Action, applyNewState: Boolean = false) { - if (actionQueue.isNotEmpty) { - if (isProcessingAction.get()) { - actionQueue.enqueue(Pair(action, applyNewState)) - } else { - rx_action.accept(action) - } - } else { - if (isProcessingAction.get()) { - actionQueue.enqueue(Pair(action, applyNewState)) - } else { - rx_action.accept(action) - } - } - } - - open fun beforeDispatch(action: Action): Action = action - open fun on(error: Error) = Unit - - abstract fun on(newState: S) - - fun initStore(block: RCKViewModel.(Store) -> Unit) { - RCK.registerViewModel(token, this) - block(this.store) - } - - /* - fun setState(block: RCKViewModel.(S) -> S): S { - writeLock.lock() - try { - val newState = block(this.store.state) - this.on(newState) - return newState - } finally { - writeLock.unlock() - } - } - */ - - fun withState(block: RCKViewModel.(S) -> R): R { - readLock.lock() - try { - return block(this.store.state) - } finally { - readLock.unlock() - } - } -} \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt index 24be601..ac7abb9 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt @@ -1,3 +1,3 @@ package com.github.skyfe79.android.reactcomponentkit.redux -typealias Reducer = (STATE, ACTION) -> STATE \ No newline at end of file +typealias Reducer = (STATE, ACTION) -> STATE? \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/State.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/State.kt index 0859ad3..e34967e 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/State.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/State.kt @@ -7,5 +7,4 @@ interface StateCopyable { abstract class State: StateCopyable { var error: Error? = null -} - +} \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt index c2d35fd..77b2533 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt @@ -13,16 +13,17 @@ class Store { inner class Flow(private val reducerList: List) { @Suppress("UNCHECKED_CAST") - fun flow(action: Action): Single { + internal fun flow(action: Action): Single { return Single.create { emitter -> reducerList.forEach { anyValue -> - val reducer = anyValue as Reducer - val typedAction = action as A - val typedState = this@Store.state.copyState() as STATE - try { - val mutatedState = reducer(typedState, typedAction) as S - this@Store.state = mutatedState + val reducer = anyValue as Reducer + val typedAction = action as A + val typedState = this@Store.state.copyState() as STATE + val mutatedState = reducer(typedState, typedAction) as? S + if (mutatedState != null) { + this@Store.state = mutatedState + } } catch (e: Throwable) { emitter.onError(e) } @@ -34,36 +35,27 @@ class Store { } lateinit var state: S - private set + internal set lateinit var actionFlowMap: MutableMap, Flow> private set - private lateinit var afters: List> + private lateinit var effects: List> private val disposables = CompositeDisposable() companion object { init { - RxJavaPlugins.setErrorHandler { - } + RxJavaPlugins.setErrorHandler {} } } -// fun set( -// initialState: S, -// afters: Array> = emptyArray()) { -// this.state = initialState -// this.actionFlowMap = mutableMapOf() -// this.afters = afters -// } - fun initialState(state: S) { this.state = state this.actionFlowMap = mutableMapOf() - this.afters = emptyList() + this.effects = emptyList() } fun deinitialize() { actionFlowMap = mutableMapOf() - afters = emptyList() + effects = emptyList() disposables.clear() } @@ -71,17 +63,17 @@ class Store { actionFlowMap[A::class] = Flow(reducers.toList()) } - fun afterFlow(vararg afters: After) { - this.afters = afters.toList() + fun afterFlow(vararg effects: Effect) { + this.effects = effects.toList() } /** * Do something after dispatching new state. * For example, reset route on Android */ - internal fun doAfters() { + internal fun doAfterEffects() { var mutatedState = state - afters.forEach { + effects.forEach { mutatedState = it(mutatedState) } state = mutatedState @@ -95,8 +87,8 @@ class Store { val actionFlow = actionFlowMap[action::class] actionFlow?.let { val disposable = it.flow(action) - .subscribeOn(Schedulers.single()) - .observeOn(Schedulers.single()) + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.io()) .subscribeBy( onSuccess = { newState -> single.onSuccess(newState) @@ -110,129 +102,4 @@ class Store { } } } - - /* - - fun dispatch(action: Action): Single { - return Single.create { single -> - - // reset error - this@Store.state.error = null - - val disposable = middleware(this@Store.state, action) - .subscribeOn(Schedulers.single()) - .observeOn(Schedulers.single()) - .flatMap { middlewareState -> - reduce(middlewareState, action) - } - .flatMap { reducedState -> - postware(reducedState, action) - } - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onNext = { newState -> - this@Store.state = newState - single.onSuccess(this@Store.state) - }, - onError = { error -> - this@Store.state.error = Error(error, action) - single.onSuccess(this@Store.state) - } - ) - disposables.add(disposable) - } - } - - - -private fun middleware(state: S, action: Action): Observable { - if (middlewares.isEmpty()) return Observable.just(state) - - return Single.create { single -> - val disposable = middlewares.toObservable() - .subscribeOn(Schedulers.single()) - .observeOn(Schedulers.single()) - .map { m -> - m(this@Store.state, action) - } - .doOnNext { modifiedState -> - this@Store.state = modifiedState - } - .reduce { _: S, nextState: S -> - nextState - } - .subscribeBy( - onSuccess = { finalState: S -> - single.onSuccess(finalState) - }, - onError = { error -> - this@Store.state.error = Error(error, action) - single.onSuccess(this@Store.state) - } - ) - disposables.add(disposable) - }.toObservable() -} - -private fun reduce(state: S, action: Action): Observable { - if (reducers.isEmpty()) return Observable.just(state) - if (state.error != null) return Observable.just(state) - - return Single.create { single -> - val disposable = reducers.toObservable() - .subscribeOn(Schedulers.single()) - .observeOn(Schedulers.single()) - .map { r -> - r(this@Store.state, action) - } - .doOnNext { modifiedState -> - this@Store.state = modifiedState - } - .reduce { _: S, nextState: S -> - nextState - } - .subscribeBy( - onSuccess = { finalState -> - single.onSuccess(finalState) - }, - onError = { error -> - this@Store.state.error = Error(error, action) - single.onSuccess(this@Store.state) - } - ) - disposables.add(disposable) - }.toObservable() -} - -private fun postware(state: S, action: Action): Observable { - if (postwares.isEmpty()) return Observable.just(state) - if (state.error != null) return Observable.just(state) - - return Single.create { single -> - val disposable = postwares.toObservable() - .subscribeOn(Schedulers.single()) - .observeOn(Schedulers.single()) - .map { p -> - p(this@Store.state, action) - } - .doOnNext { modifiedState -> - this@Store.state = modifiedState - } - .reduce { _: S, nextState: S -> - nextState - } - .subscribeBy( - onSuccess = { finalState -> - single.onSuccess(finalState) - }, - onError = { error -> - this@Store.state.error = Error(error, action) - single.onSuccess(this@Store.state) - } - ) - disposables.add(disposable) - }.toObservable() -} - - */ } \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/util/Tasks.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/util/Tasks.kt index b354f79..71be3bd 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/util/Tasks.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/util/Tasks.kt @@ -12,7 +12,6 @@ import java.util.concurrent.Future private val uiHandler = Handler(Looper.getMainLooper()) -/* fun Any?.runOnUiThread(runnable: () -> Unit) { uiHandler.post(runnable) } @@ -20,7 +19,7 @@ fun Any?.runOnUiThread(runnable: () -> Unit) { fun Any?.runOnUiThreadAfterDelay(delay : Long = 0L, runnable: () -> Unit) { uiHandler.postDelayed(delay, runnable) } - +/* fun Any?.async(runnable: () -> Unit) { Thread(runnable).start() } diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/util/ViewId.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/util/ViewId.kt index 40252b8..00d8cd4 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/util/ViewId.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/util/ViewId.kt @@ -4,7 +4,7 @@ import android.os.Build import android.view.View import java.util.concurrent.atomic.AtomicInteger -class ID { +class ViewId { companion object { private val nextId = AtomicInteger(1) val gen: Int diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RCKViewModel.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RCKViewModel.kt new file mode 100644 index 0000000..5380912 --- /dev/null +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RCKViewModel.kt @@ -0,0 +1,155 @@ +package com.github.skyfe79.android.reactcomponentkit.viewmodel + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import com.github.skyfe79.android.reactcomponentkit.RCK +import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.redux.* +import com.github.skyfe79.android.reactcomponentkit.util.runOnUiThread +import com.jakewharton.rxrelay2.BehaviorRelay +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.addTo +import io.reactivex.rxkotlin.subscribeBy +import io.reactivex.schedulers.Schedulers +import java.util.concurrent.locks.ReentrantLock + +abstract class RCKViewModel(application: Application): AndroidViewModel(application) { + val token: Token = Token() + + private val rx_action: BehaviorRelay = BehaviorRelay.createDefault( + VoidAction + ) + private val store = Store() + private val disposables = CompositeDisposable() + private val writeLock = ReentrantLock() + private val readLock = ReentrantLock() + + init { + setupRxStream() + this.setupStore() + } + + override fun onCleared() { + dispose() + super.onCleared() + } + + + fun dispose() { + RCK.unregisterViewModel(token) + disposables.dispose() + store.deinitialize() + } + + private fun setupRxStream() { + val disposable = rx_action + .subscribeOn(Schedulers.single()) + .filter { action -> + action !is VoidAction + } + .flatMap { action -> + store.dispatch(action).toObservable() + } + .observeOn(AndroidSchedulers.mainThread()) + .doAfterNext { + store.doAfterEffects() + } + .subscribe { newState -> + if (newState.error != null) { + newState.error?.let { + on(it) + } + } else { + on(newState) + } + } + + disposables.add(disposable) + } + + abstract fun setupStore() + + fun dispatch(action: Action) { + rx_action.accept(action) + } + + open fun on(error: Error) = Unit + + abstract fun on(newState: S) + + fun initStore(block: RCKViewModel.(Store) -> Unit) { + RCK.registerViewModel(token, this) + block(this.store) + } + + @Suppress("UNCHECKED_CAST") + fun setState(block: RCKViewModel.(S) -> S): S { + writeLock.lock() + try { + val newState = block(this.store.state.copyState() as S) + runOnUiThread { + this.on(newState) + } + return newState + } finally { + writeLock.unlock() + } + } + + @Suppress("UNCHECKED_CAST") + fun withState(block: RCKViewModel.(S) -> R): R { + readLock.lock() + try { + return block(this.store.state.copyState() as S) + } finally { + readLock.unlock() + } + } + + fun asyncReducer(state: S, action: A, block: RCKViewModel.(A) -> Observable): S { + asyncFlow(block)(state, action) + return state + } + + fun asyncFlow(block: RCKViewModel.(A) -> Observable): Reducer { + return { _: S, action: A -> + block(action) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeBy( + onNext = { newState -> + writeLock.lock() + this.store.state = newState + on(newState) + writeLock.unlock() + }, + onError = { error -> + on(Error(error, action)) + } + ) + .addTo(disposables) + null + } + } + +// @Suppress("UNCHECKED_CAST") +// fun asyncEffect(action: A, block: RCKViewModel.(A) -> Observable) { +// block(action) +// .subscribeOn(Schedulers.io()) +// .observeOn(AndroidSchedulers.mainThread()) +// .subscribeBy( +// onNext = { newState -> +// writeLock.lock() +// this.store.state = newState +// on(newState) +// writeLock.unlock() +// }, +// onError = { error -> +// on(Error(error, action)) +// } +// ) +// .addTo(disposables) +// } +} \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RootViewModelType.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RootViewModelType.kt deleted file mode 100644 index f191931..0000000 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RootViewModelType.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.skyfe79.android.reactcomponentkit.viewmodel - -/* -abstract class RootViewModelType(application: Application): RCKViewModel(application) { - val token: Token = Token() - private val newStateEventBus: EventBus = EventBus(token) - private val dispatchEventBus: EventBus = EventBus(token) - private var previousState: S? = null - - init { - dispatchEventBus.on { event -> - when(event) { - is ComponentDispatchEvent.Dispatch -> this.dispatch(event.action) - } - } - } - - @Suppress("UNCHECKED_CAST") - fun propagate(state: State) { - val someState = state as? S - if (previousState != someState) { - someState?.let { - newStateEventBus.post(ComponentNewStateEvent.On(it)) - } - } - previousState = someState - } -} -*/ \ No newline at end of file From ef78b01e787ecbba1523d30b84b23e51369cf24c Mon Sep 17 00:00:00 2001 From: skyfe79 Date: Sat, 31 Aug 2019 22:23:55 +0900 Subject: [PATCH 11/12] Implement subscription on components and apply new RCK to examples. --- .../app/examples/counter/CounterActivity.kt | 9 -- .../app/examples/counter/CounterLayout.kt | 31 ++++++- .../app/examples/counter/CounterViewModel.kt | 26 ++++-- .../EmojiCollectionViewModel.kt | 2 +- .../emojicollection/actions/Actions.kt | 3 +- .../examples/emojicollection/afters/Reset.kt | 8 -- .../components/EmojiViewComponent.kt | 5 +- .../emojicollection/reducers/Reducers.kt | 13 +-- reactcomponentkit/build.gradle | 8 +- .../skyfe79/android/reactcomponentkit/RCK.kt | 2 +- .../reactcomponentkit/ReactComponent.kt | 6 +- .../component/FragmentComponent.kt | 7 +- .../component/IntentServiceComponent.kt | 21 +++++ .../component/LayoutComponent.kt | 5 +- .../component/ServiceComponent.kt | 15 +++ .../component/ViewComponent.kt | 6 +- .../dispatcher/DispatcherExtension.kt | 14 ++- .../reactcomponentkit/redux/Reducer.kt | 6 ++ .../android/reactcomponentkit/redux/State.kt | 5 +- .../android/reactcomponentkit/redux/Store.kt | 15 +++ .../subscriber/SubscriberExtension.kt | 31 +++++++ .../viewmodel/RCKViewModel.kt | 92 +++++++++++++------ 22 files changed, 241 insertions(+), 89 deletions(-) delete mode 100644 app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/afters/Reset.kt create mode 100644 reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/IntentServiceComponent.kt create mode 100644 reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ServiceComponent.kt create mode 100644 reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/subscriber/SubscriberExtension.kt diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterActivity.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterActivity.kt index cb9cf33..35b70ff 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterActivity.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterActivity.kt @@ -24,14 +24,5 @@ class CounterActivity: AppCompatActivity() { layout.setContentView(this) layout.countTextView.text = "${viewModel.count.value}" - - viewModel - .count - .asObservable() - .subscribe { - layout.countTextView.text = "$it" - } - .disposedBy(disposeBag) - } } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt index d15033c..7bafc40 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt @@ -4,13 +4,17 @@ import android.view.Gravity import android.view.View import android.widget.Button import android.widget.LinearLayout +import android.widget.ProgressBar import android.widget.TextView +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.reactcomponentkit.component.LayoutComponent import com.github.skyfe79.android.reactcomponentkit.dispatcher.dispatch import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.redux.Async import com.github.skyfe79.android.reactcomponentkit.redux.State +import com.github.skyfe79.android.reactcomponentkit.subscriber.subscribeState import org.jetbrains.anko.* import org.jetbrains.anko.sdk27.coroutines.onClick @@ -23,6 +27,12 @@ class CounterLayout(token: Token): LayoutComponent(token) { lateinit var countTextView: TextView private lateinit var increaseButton: Button private lateinit var decreaseButton: Button + private lateinit var asyncIncreaseButton: Button + private lateinit var progress: ProgressBar + + override fun onInit() { + subscribeState() + } override fun createView(ui: AnkoContext): View = with(ui) { val view = relativeLayout { @@ -34,13 +44,18 @@ class CounterLayout(token: Token): LayoutComponent(token) { centerInParent() } + progress = progressBar().lparams { + centerInParent() + } + progress.visibility = View.GONE + linearLayout { orientation = LinearLayout.HORIZONTAL gravity = Gravity.CENTER decreaseButton = button(" - ").lparams(width = 0, weight = 1f) increaseButton = button(" + ").lparams(width = 0, weight = 1f) - + asyncIncreaseButton = button("async +").lparams(width = 0, weight = 1f) }.applyRecursively { when(it) { is Button -> it.textSize = 20f @@ -54,6 +69,10 @@ class CounterLayout(token: Token): LayoutComponent(token) { } + asyncIncreaseButton.setOnClickListener { + dispatch(AsyncIncreaseAction()) + } + increaseButton.setOnClickListener { dispatch(IncreaseAction()) } @@ -68,5 +87,15 @@ class CounterLayout(token: Token): LayoutComponent(token) { override fun on(state: State) { val countState = (state as? CounterState) ?: return countTextView.text = "${countState.count}" + + when (state.asyncCount) { + is Async.Loading -> { + progress.visibility = View.VISIBLE + progress.animate() + } + is Async.Success -> { + progress.visibility = View.GONE + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt index efc1fb4..5fa27c5 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterViewModel.kt @@ -1,6 +1,7 @@ package com.github.skyfe79.android.library.app.examples.counter 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.reactcomponentkit.redux.* @@ -19,12 +20,9 @@ data class CounterState( class CounterViewModel(application: Application): RCKViewModel(application) { val count: Output = Output(0) + val asyncCount: Output> = Output(Async.Uninitialized) - fun showLoading() = setState { state -> - state.copy(asyncCount = Async.Loading) - } - - fun asyncIncrease(state: CounterState, action: IncreaseAction) = asyncReducer(state, action) { + fun asyncIncrease(state: CounterState, action: AsyncIncreaseAction) = asyncReducer(state, action) { Single.create { emitter -> Thread.sleep(1000L) withState { state -> @@ -37,21 +35,30 @@ class CounterViewModel(application: Application): RCKViewModel(app initStore { store -> store.initialState(CounterState(0)) - store.flow( + store.flow( + { _, _ -> + setState { + it.copy(asyncCount = Async.Loading) + } + }, { state, action -> - state.copy(count = state.count + 3) + state.copy(count = state.count + action.payload) }, ::asyncIncrease, asyncFlow { action -> Single.create { emitter -> - Thread.sleep(5000L) + Thread.sleep(2000L) withState { state -> - emitter.onSuccess(state.copy(count = state.count + action.payload)) + emitter.onSuccess(state.copy(count = state.count + action.payload, asyncCount = Async.Success(state.count + action.payload))) } }.toObservable() } ) + store.flow({ state, action -> + state.copy(count = state.count + action.payload) + }) + store.flow({ state, action -> state.copy(count = state.count - action.payload) }) @@ -60,5 +67,6 @@ class CounterViewModel(application: Application): RCKViewModel(app override fun on(newState: CounterState) { count.accept(newState.count) + asyncCount.accept(newState.asyncCount) } } \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt index e234867..424f2b3 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/EmojiCollectionViewModel.kt @@ -41,7 +41,7 @@ class EmojiCollectionViewModel(application: Application): RCKViewModel({ state, action -> - state.copy(route = EmojiRoute.AlertEmoji(action.emoji)) + state.copy(route = EmojiRoute.AlertEmoji(action.emoji)) }) store.flow( diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/actions/Actions.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/actions/Actions.kt index a9312b1..5511804 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/actions/Actions.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/actions/Actions.kt @@ -4,5 +4,4 @@ import com.github.skyfe79.android.reactcomponentkit.redux.Action data class AddEmojiAction(val emoji: String): Action object RemoveEmojiAction: Action -object ShuffleEmojiAction: Action -object MakeItemModelsAction: Action \ No newline at end of file +object ShuffleEmojiAction: Action \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/afters/Reset.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/afters/Reset.kt deleted file mode 100644 index f461e4c..0000000 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/afters/Reset.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.github.skyfe79.android.library.app.examples.emojicollection.afters - -import com.github.skyfe79.android.library.app.examples.emojicollection.EmojiCollectionState -import com.github.skyfe79.android.library.app.examples.emojicollection.EmojiRoute - -fun reset(state: EmojiCollectionState): EmojiCollectionState { - return state.copy(route = EmojiRoute.None) -} \ No newline at end of file diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt index 1bdd3fd..3b5bd1e 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt @@ -12,6 +12,7 @@ import com.github.skyfe79.android.reactcomponentkit.dispatcher.dispatch import com.github.skyfe79.android.reactcomponentkit.eventbus.Token import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State +import com.github.skyfe79.android.reactcomponentkit.subscriber.subscribeState import kotlinx.android.synthetic.main.activity_main.view.* import org.jetbrains.anko.* import org.jetbrains.anko.sdk27.coroutines.onClick @@ -41,10 +42,6 @@ class EmojiViewComponent(token: Token): ViewComponent(token) { return rootLayout } - override fun on(state: State) { - //ignore - } - override fun on(item: ItemModel, position: Int) { val emojiProvider = (item as? EmojiProvider) ?: return emojiTextView.text = emojiProvider.emoji diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt index 7aee07c..dbeb844 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/reducers/Reducers.kt @@ -2,28 +2,19 @@ package com.github.skyfe79.android.library.app.examples.emojicollection.reducers import com.github.skyfe79.android.library.app.examples.emojicollection.EmojiCollectionState import com.github.skyfe79.android.library.app.examples.emojicollection.EmojiCollectionViewModel -import com.github.skyfe79.android.library.app.examples.emojicollection.EmojiRoute import com.github.skyfe79.android.library.app.examples.emojicollection.actions.AddEmojiAction -import com.github.skyfe79.android.library.app.examples.emojicollection.actions.MakeItemModelsAction import com.github.skyfe79.android.library.app.examples.emojicollection.actions.RemoveEmojiAction import com.github.skyfe79.android.library.app.examples.emojicollection.actions.ShuffleEmojiAction -import com.github.skyfe79.android.library.app.examples.emojicollection.components.ClickEmojiAction import com.github.skyfe79.android.library.app.examples.emojicollection.models.EmojiBoxModel import com.github.skyfe79.android.library.app.examples.emojicollection.util.EmojiHelper -import com.github.skyfe79.android.reactcomponentkit.redux.Action -import com.github.skyfe79.android.reactcomponentkit.redux.State -import io.reactivex.Observable -import io.reactivex.Single -import io.reactivex.rxkotlin.subscribeBy import java.lang.Exception -import kotlin.random.Random fun EmojiCollectionViewModel.addEmoji(state: EmojiCollectionState, action: AddEmojiAction): EmojiCollectionState { val mutableEmojiList = state.emojis.toMutableList() val index = if (mutableEmojiList.isEmpty()) 0 else (0 until mutableEmojiList.size).random() mutableEmojiList.add(index, EmojiHelper.emoji) - return state.copy(emojis = mutableEmojiList) + return makeItemModels(state.copy(emojis = mutableEmojiList)) } @@ -32,7 +23,7 @@ fun EmojiCollectionViewModel.removeEmoji(state: EmojiCollectionState, action: Re val mutableEmojiList = state.emojis.toMutableList() val index = if (mutableEmojiList.isEmpty()) 0 else (0 until mutableEmojiList.size).random() mutableEmojiList.removeAt(index) - return state.copy(emojis = mutableEmojiList) + return makeItemModels(state.copy(emojis = mutableEmojiList)) } catch (e: Exception) { throw e } diff --git a/reactcomponentkit/build.gradle b/reactcomponentkit/build.gradle index 47228a5..8896769 100644 --- a/reactcomponentkit/build.gradle +++ b/reactcomponentkit/build.gradle @@ -23,14 +23,14 @@ publish { } android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 16 - targetSdkVersion 28 - versionCode 10 - versionName "1.2.8" + targetSdkVersion 29 + versionCode 11 + versionName "2.0.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt index 5de3b95..6e746ab 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt @@ -13,7 +13,7 @@ internal object RCK { this.map[token] = WeakReference(viewModel) } - internal fun unregisterViewModel(token: Token) { + internal fun unregisterViewModel(token: Token) { this.map.remove(token) } diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/ReactComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/ReactComponent.kt index ba894f7..4146f9e 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/ReactComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/ReactComponent.kt @@ -1,8 +1,12 @@ package com.github.skyfe79.android.reactcomponentkit import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.redux.State +interface StateSubscriber { + fun on(state: State) = Unit +} -interface ReactComponent { +interface ReactComponent: StateSubscriber { var token: Token } \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/FragmentComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/FragmentComponent.kt index 3078e2e..66744c2 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/FragmentComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/FragmentComponent.kt @@ -4,10 +4,8 @@ import android.content.Context import android.os.Bundle import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity -import com.github.skyfe79.android.reactcomponentkit.RCK import com.github.skyfe79.android.reactcomponentkit.ReactComponent import com.github.skyfe79.android.reactcomponentkit.eventbus.Token -import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State internal enum class FragmentComponentState { @@ -41,9 +39,12 @@ abstract class FragmentComponent: Fragment(), ReactComponent { this@FragmentComponent.token = (it.getParcelable("token") as? Token) ?: Token.empty } } + + this.onInit() } - abstract fun on(state: State) + open fun onInit() = Unit + abstract override fun on(state: State) } inline fun FragmentActivity.fragmentComponent(token: Token): T { diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/IntentServiceComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/IntentServiceComponent.kt new file mode 100644 index 0000000..850aa3c --- /dev/null +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/IntentServiceComponent.kt @@ -0,0 +1,21 @@ +package com.github.skyfe79.android.reactcomponentkit.component + +import android.app.IntentService +import com.github.skyfe79.android.reactcomponentkit.ReactComponent +import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.redux.State + +abstract class IntentServiceComponent(token: Token, name: String): IntentService(name), ReactComponent { + override var token: Token = token + + init { + this.onInit() + } + + open fun onInit() = Unit + + /** + * It is called when the component is standalone view component. + */ + override fun on(state: State) = Unit +} \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/LayoutComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/LayoutComponent.kt index b81b0a1..b0cf855 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/LayoutComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/LayoutComponent.kt @@ -14,7 +14,10 @@ abstract class LayoutComponent : AnkoComponent, ReactComponent { constructor(token: Token) { this.token = token + this.onInit() } - abstract fun on(state: State) + open fun onInit() = Unit + + abstract override fun on(state: State) } \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ServiceComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ServiceComponent.kt new file mode 100644 index 0000000..4ea579e --- /dev/null +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ServiceComponent.kt @@ -0,0 +1,15 @@ +package com.github.skyfe79.android.reactcomponentkit.component + +import android.app.Service +import com.github.skyfe79.android.reactcomponentkit.ReactComponent +import com.github.skyfe79.android.reactcomponentkit.eventbus.Token + +abstract class ServiceComponent(token: Token): Service(), ReactComponent { + override var token: Token = token + + init { + this.onInit() + } + + open fun onInit() = Unit +} \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt index 1456543..b7e899b 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt @@ -1,6 +1,7 @@ package com.github.skyfe79.android.reactcomponentkit.component import android.content.Context +import android.util.AttributeSet import android.view.View import android.view.ViewManager import com.github.skyfe79.android.reactcomponentkit.ReactComponent @@ -18,6 +19,7 @@ abstract class ViewComponent: AnkoComponent, ReactComponent { constructor(token: Token) { this.token = token + this.onInit() } override fun createView(ui: AnkoContext): View { @@ -26,6 +28,8 @@ abstract class ViewComponent: AnkoComponent, ReactComponent { return view } + open fun onInit() = Unit + /** * Configure component's ui at here */ @@ -34,7 +38,7 @@ abstract class ViewComponent: AnkoComponent, ReactComponent { /** * It is called when the component is standalone. */ - open fun on(state: State) = Unit + override fun on(state: State) = Unit /** * It is only called when the component is in RecyclerView's row diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/DispatcherExtension.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/DispatcherExtension.kt index 8c3ff86..f2926ae 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/DispatcherExtension.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/DispatcherExtension.kt @@ -1,9 +1,7 @@ package com.github.skyfe79.android.reactcomponentkit.dispatcher import com.github.skyfe79.android.reactcomponentkit.RCK -import com.github.skyfe79.android.reactcomponentkit.component.FragmentComponent -import com.github.skyfe79.android.reactcomponentkit.component.LayoutComponent -import com.github.skyfe79.android.reactcomponentkit.component.ViewComponent +import com.github.skyfe79.android.reactcomponentkit.component.* import com.github.skyfe79.android.reactcomponentkit.redux.Action @@ -20,4 +18,14 @@ fun LayoutComponent.dispatch(action: Action) { fun ViewComponent.dispatch(action: Action) { val viewModel = RCK.viewModel(token) viewModel.let { it?.dispatch(action) } +} + +fun ServiceComponent.dispatch(action: Action) { + val viewModel = RCK.viewModel(token) + viewModel.let { it?.dispatch(action) } +} + +fun IntentServiceComponent.dispatch(action: Action) { + val viewModel = RCK.viewModel(token) + viewModel.let { it?.dispatch(action) } } \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt index ac7abb9..099df61 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Reducer.kt @@ -1,3 +1,9 @@ package com.github.skyfe79.android.reactcomponentkit.redux +/** + * Reducer type. + * Why returns optional STATE?. + * If you define asyncReducer in a flow, asyncFlow makes a Reducer that returns null state + * not to update current state. + */ typealias Reducer = (STATE, ACTION) -> STATE? \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/State.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/State.kt index e34967e..c6413e2 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/State.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/State.kt @@ -1,6 +1,9 @@ package com.github.skyfe79.android.reactcomponentkit.redux - +/** + * Utility interface for copying state. + * It is needed because abstract class State doesn't know how to copy subclass. + */ interface StateCopyable { fun copyState(): S } diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt index 77b2533..34fb8ec 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/redux/Store.kt @@ -47,22 +47,34 @@ class Store { } } + /** + * set initial state for the store + */ fun initialState(state: S) { this.state = state this.actionFlowMap = mutableMapOf() this.effects = emptyList() } + /** + * clean up memories + */ fun deinitialize() { actionFlowMap = mutableMapOf() effects = emptyList() disposables.clear() } + /** + * Make a flow of reducers for an action + */ inline fun flow(vararg reducers: Reducer) { actionFlowMap[A::class] = Flow(reducers.toList()) } + /** + * do some side effect after finishing a flow. + */ fun afterFlow(vararg effects: Effect) { this.effects = effects.toList() } @@ -79,6 +91,9 @@ class Store { state = mutatedState } + /** + * dispatch an action to start a flow for it. + */ fun dispatch(action: Action): Single { return Single.create { single -> // reset error diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/subscriber/SubscriberExtension.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/subscriber/SubscriberExtension.kt new file mode 100644 index 0000000..1efad55 --- /dev/null +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/subscriber/SubscriberExtension.kt @@ -0,0 +1,31 @@ +package com.github.skyfe79.android.reactcomponentkit.subscriber + +import com.github.skyfe79.android.reactcomponentkit.RCK +import com.github.skyfe79.android.reactcomponentkit.component.* +import com.github.skyfe79.android.reactcomponentkit.redux.Action + + +fun FragmentComponent.subscribeState() { + val viewModel = RCK.viewModel(token) + viewModel.let { it?.registerSubscriber(this) } +} + +fun LayoutComponent.subscribeState() { + val viewModel = RCK.viewModel(token) + viewModel.let { it?.registerSubscriber(this) } +} + +fun ViewComponent.subscribeState() { + val viewModel = RCK.viewModel(token) + viewModel.let { it?.registerSubscriber(this) } +} + +fun ServiceComponent.subscribeState() { + val viewModel = RCK.viewModel(token) + viewModel.let { it?.registerSubscriber(this) } +} + +fun IntentServiceComponent.subscribeState() { + val viewModel = RCK.viewModel(token) + viewModel.let { it?.registerSubscriber(this) } +} \ No newline at end of file diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RCKViewModel.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RCKViewModel.kt index 5380912..9386217 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RCKViewModel.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RCKViewModel.kt @@ -3,6 +3,7 @@ package com.github.skyfe79.android.reactcomponentkit.viewmodel import android.app.Application import androidx.lifecycle.AndroidViewModel import com.github.skyfe79.android.reactcomponentkit.RCK +import com.github.skyfe79.android.reactcomponentkit.StateSubscriber import com.github.skyfe79.android.reactcomponentkit.eventbus.Token import com.github.skyfe79.android.reactcomponentkit.redux.* import com.github.skyfe79.android.reactcomponentkit.util.runOnUiThread @@ -13,6 +14,7 @@ import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.addTo import io.reactivex.rxkotlin.subscribeBy import io.reactivex.schedulers.Schedulers +import java.lang.ref.WeakReference import java.util.concurrent.locks.ReentrantLock abstract class RCKViewModel(application: Application): AndroidViewModel(application) { @@ -25,6 +27,7 @@ abstract class RCKViewModel(application: Application): AndroidViewMode private val disposables = CompositeDisposable() private val writeLock = ReentrantLock() private val readLock = ReentrantLock() + private var subscribers: MutableList> = mutableListOf() init { setupRxStream() @@ -36,9 +39,12 @@ abstract class RCKViewModel(application: Application): AndroidViewMode super.onCleared() } - + /** + * clean up memeries + */ fun dispose() { - RCK.unregisterViewModel(token) + RCK.unregisterViewModel(token) + subscribers = mutableListOf() disposables.dispose() store.deinitialize() } @@ -62,28 +68,66 @@ abstract class RCKViewModel(application: Application): AndroidViewMode on(it) } } else { - on(newState) + dispatchStateToSubscribers(newState) } } disposables.add(disposable) } - abstract fun setupStore() + /** + * register subscriber that receive the new state. + */ + fun registerSubscriber(subscriber: StateSubscriber) { + val weakSubscriber = WeakReference(subscriber) + subscribers.add(weakSubscriber) + } + + /** + * dispatch mutated state to subscribers + */ + private fun dispatchStateToSubscribers(state: S) { + on(state) + subscribers.forEach { + it.get()?.on(state) + } + } + + /** + * dispatch action to the store. + */ fun dispatch(action: Action) { rx_action.accept(action) } - open fun on(error: Error) = Unit + /** + * Called when receive the new state from store + */ + protected abstract fun on(newState: S) - abstract fun on(newState: S) + /** + * Called when occured erros on the redux flow. + */ + protected open fun on(error: Error) = Unit - fun initStore(block: RCKViewModel.(Store) -> Unit) { + /** + * Setup store. You should define this method and use initStore method in it. + */ + abstract fun setupStore() + + /** + * init store with block + * You can set initial state and define flows for actions + */ + protected fun initStore(block: RCKViewModel.(Store) -> Unit) { RCK.registerViewModel(token, this) block(this.store) } + /** + * Set state and dispatch the mutated state to subscribers + */ @Suppress("UNCHECKED_CAST") fun setState(block: RCKViewModel.(S) -> S): S { writeLock.lock() @@ -98,6 +142,9 @@ abstract class RCKViewModel(application: Application): AndroidViewMode } } + /** + * Read state value + */ @Suppress("UNCHECKED_CAST") fun withState(block: RCKViewModel.(S) -> R): R { readLock.lock() @@ -108,12 +155,18 @@ abstract class RCKViewModel(application: Application): AndroidViewMode } } + /** + * Make Async Reducer. Using it if you define standalone async reducer function. + */ fun asyncReducer(state: S, action: A, block: RCKViewModel.(A) -> Observable): S { asyncFlow(block)(state, action) return state } - fun asyncFlow(block: RCKViewModel.(A) -> Observable): Reducer { + /** + * Make Async reducer in a flow + */ + protected fun asyncFlow(block: RCKViewModel.(A) -> Observable): Reducer { return { _: S, action: A -> block(action) .subscribeOn(Schedulers.io()) @@ -122,7 +175,7 @@ abstract class RCKViewModel(application: Application): AndroidViewMode onNext = { newState -> writeLock.lock() this.store.state = newState - on(newState) + dispatchStateToSubscribers(newState) writeLock.unlock() }, onError = { error -> @@ -130,26 +183,7 @@ abstract class RCKViewModel(application: Application): AndroidViewMode } ) .addTo(disposables) - null + null // do not update current state. } } - -// @Suppress("UNCHECKED_CAST") -// fun asyncEffect(action: A, block: RCKViewModel.(A) -> Observable) { -// block(action) -// .subscribeOn(Schedulers.io()) -// .observeOn(AndroidSchedulers.mainThread()) -// .subscribeBy( -// onNext = { newState -> -// writeLock.lock() -// this.store.state = newState -// on(newState) -// writeLock.unlock() -// }, -// onError = { error -> -// on(Error(error, action)) -// } -// ) -// .addTo(disposables) -// } } \ No newline at end of file From d1ff35bc3be657a04623b6bb16e54f434e789605 Mon Sep 17 00:00:00 2001 From: skyfe79 Date: Sat, 31 Aug 2019 22:29:28 +0900 Subject: [PATCH 12/12] Update gradle and Deprecate EventBus --- .../github/skyfe79/android/library/app/MainViewLayout.kt | 2 +- .../android/library/app/examples/counter/CounterLayout.kt | 3 +-- .../library/app/examples/counter2/CounterLayout2.kt | 2 +- .../emojicollection/components/EmojiViewComponent.kt | 7 +------ .../library/app/examples/recyclerview/RecyclerActivity.kt | 2 +- .../recyclerview/component/TextMessageViewComponent.kt | 5 +---- reactcomponentkit/build.gradle | 8 ++++---- .../com/github/skyfe79/android/reactcomponentkit/RCK.kt | 2 +- .../skyfe79/android/reactcomponentkit/ReactComponent.kt | 2 +- .../collectionview/CollectionViewAdapter.kt | 2 +- .../collectionview/NestedCollectionViewComponent.kt | 2 +- .../reactcomponentkit/component/FragmentComponent.kt | 2 +- .../reactcomponentkit/component/IntentServiceComponent.kt | 2 +- .../reactcomponentkit/component/LayoutComponent.kt | 2 +- .../reactcomponentkit/component/ServiceComponent.kt | 2 +- .../android/reactcomponentkit/component/ViewComponent.kt | 3 +-- .../reactcomponentkit/dispatcher/ActionDispatcher.kt | 5 ++++- .../android/reactcomponentkit/eventbus/EventBus.kt | 2 ++ .../reactcomponentkit/recyclerview/RecyclerViewAdapter.kt | 2 +- .../reactcomponentkit/recyclerview/RecyclerViewCell.kt | 2 +- .../android/reactcomponentkit/viewmodel/RCKViewModel.kt | 4 ++-- .../reactcomponentkit/{eventbus => viewmodel}/Token.kt | 5 +++-- 22 files changed, 32 insertions(+), 36 deletions(-) rename reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/{eventbus => viewmodel}/Token.kt (88%) diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewLayout.kt b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewLayout.kt index 8081086..2c59ff3 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/MainViewLayout.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/MainViewLayout.kt @@ -4,7 +4,7 @@ import android.view.View import com.github.skyfe79.android.library.app.action.* import com.github.skyfe79.android.reactcomponentkit.component.LayoutComponent import com.github.skyfe79.android.reactcomponentkit.dispatcher.dispatch -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import com.github.skyfe79.android.reactcomponentkit.redux.State import kotlinx.android.synthetic.main.activity_main.view.* import org.jetbrains.anko.AnkoContext diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt index 7bafc40..459f027 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter/CounterLayout.kt @@ -11,12 +11,11 @@ import com.github.skyfe79.android.library.app.examples.counter.action.DecreaseAc import com.github.skyfe79.android.library.app.examples.counter.action.IncreaseAction import com.github.skyfe79.android.reactcomponentkit.component.LayoutComponent import com.github.skyfe79.android.reactcomponentkit.dispatcher.dispatch -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import com.github.skyfe79.android.reactcomponentkit.redux.Async import com.github.skyfe79.android.reactcomponentkit.redux.State import com.github.skyfe79.android.reactcomponentkit.subscriber.subscribeState import org.jetbrains.anko.* -import org.jetbrains.anko.sdk27.coroutines.onClick class CounterLayout(token: Token): LayoutComponent(token) { diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterLayout2.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterLayout2.kt index 89af4ee..0f6c28f 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterLayout2.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/counter2/CounterLayout2.kt @@ -9,7 +9,7 @@ import com.github.skyfe79.android.library.app.examples.counter.action.DecreaseAc import com.github.skyfe79.android.library.app.examples.counter.action.IncreaseAction import com.github.skyfe79.android.reactcomponentkit.component.LayoutComponent import com.github.skyfe79.android.reactcomponentkit.dispatcher.dispatch -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import com.github.skyfe79.android.reactcomponentkit.redux.State import org.jetbrains.anko.* import org.jetbrains.anko.sdk27.coroutines.onClick diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt index 3b5bd1e..79632a9 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/emojicollection/components/EmojiViewComponent.kt @@ -1,7 +1,6 @@ package com.github.skyfe79.android.library.app.examples.emojicollection.components import android.content.Context -import android.util.Log import android.view.Gravity.CENTER import android.view.View import android.widget.TextView @@ -9,13 +8,9 @@ import com.github.skyfe79.android.library.app.examples.emojicollection.models.Em import com.github.skyfe79.android.reactcomponentkit.collectionmodels.ItemModel import com.github.skyfe79.android.reactcomponentkit.component.ViewComponent import com.github.skyfe79.android.reactcomponentkit.dispatcher.dispatch -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import com.github.skyfe79.android.reactcomponentkit.redux.Action -import com.github.skyfe79.android.reactcomponentkit.redux.State -import com.github.skyfe79.android.reactcomponentkit.subscriber.subscribeState -import kotlinx.android.synthetic.main.activity_main.view.* import org.jetbrains.anko.* -import org.jetbrains.anko.sdk27.coroutines.onClick data class ClickEmojiAction(val emoji: String): Action diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/recyclerview/RecyclerActivity.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/recyclerview/RecyclerActivity.kt index 14a91e1..a5ecd33 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/recyclerview/RecyclerActivity.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/recyclerview/RecyclerActivity.kt @@ -6,7 +6,7 @@ import com.github.skyfe79.android.library.app.R import com.github.skyfe79.android.library.app.examples.recyclerview.component.SectionViewComponent import com.github.skyfe79.android.library.app.examples.recyclerview.component.TextMessageViewComponent import com.github.skyfe79.android.library.app.examples.recyclerview.model.TextMessage -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import com.github.skyfe79.android.reactcomponentkit.recyclerview.RecyclerViewAdapter import com.github.skyfe79.android.reactcomponentkit.recyclerview.sticky.StickyHeadersLinearLayoutManager import kotlinx.android.synthetic.main.activity_recycler.* diff --git a/app/src/main/java/com/github/skyfe79/android/library/app/examples/recyclerview/component/TextMessageViewComponent.kt b/app/src/main/java/com/github/skyfe79/android/library/app/examples/recyclerview/component/TextMessageViewComponent.kt index 39a0818..d391266 100644 --- a/app/src/main/java/com/github/skyfe79/android/library/app/examples/recyclerview/component/TextMessageViewComponent.kt +++ b/app/src/main/java/com/github/skyfe79/android/library/app/examples/recyclerview/component/TextMessageViewComponent.kt @@ -3,19 +3,16 @@ package com.github.skyfe79.android.library.app.examples.recyclerview.component import android.content.Context import android.view.Gravity import android.view.View -import android.view.ViewGroup import android.widget.TextView -import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.ContextCompat import com.github.skyfe79.android.library.app.examples.recyclerview.model.BackgroundColorProvider import com.github.skyfe79.android.library.app.examples.recyclerview.model.TextMessageProvider import com.github.skyfe79.android.reactcomponentkit.collectionmodels.ItemModel import com.github.skyfe79.android.reactcomponentkit.component.ViewComponent import com.github.skyfe79.android.reactcomponentkit.component.component -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import com.github.skyfe79.android.reactcomponentkit.redux.State import org.jetbrains.anko.* -import org.jetbrains.anko.coroutines.experimental.bg class TextMessageViewComponent(token: Token): ViewComponent(token) { lateinit var textView: TextView diff --git a/reactcomponentkit/build.gradle b/reactcomponentkit/build.gradle index 8896769..8680114 100644 --- a/reactcomponentkit/build.gradle +++ b/reactcomponentkit/build.gradle @@ -17,7 +17,7 @@ publish { userOrg = 'skyfe79' groupId = 'com.github.skyfe79.android' artifactId = 'reactcomponentkit' - publishVersion = '1.2.8' + publishVersion = '2.0.0' desc = 'AndroidReactComponentKit = Component + MVVM + Redux for Android!!!' website = 'https://github.com/ReactComponentKit/AndroidReactComponentKit' } @@ -29,7 +29,7 @@ android { defaultConfig { minSdkVersion 16 targetSdkVersion 29 - versionCode 11 + versionCode 20 versionName "2.0.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -59,8 +59,8 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' - implementation "io.reactivex.rxjava2:rxjava:2.2.8" - implementation "io.reactivex.rxjava2:rxkotlin:2.3.0" + implementation "io.reactivex.rxjava2:rxjava:2.2.10" + implementation "io.reactivex.rxjava2:rxkotlin:2.4.0" implementation 'com.jakewharton.rxrelay2:rxrelay:2.1.0' implementation 'org.greenrobot:eventbus:3.1.1' diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt index 6e746ab..b81e3d6 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/RCK.kt @@ -1,6 +1,6 @@ package com.github.skyfe79.android.reactcomponentkit -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import com.github.skyfe79.android.reactcomponentkit.viewmodel.RCKViewModel import com.github.skyfe79.android.reactcomponentkit.redux.State import java.lang.ref.WeakReference diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/ReactComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/ReactComponent.kt index 4146f9e..728a5be 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/ReactComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/ReactComponent.kt @@ -1,6 +1,6 @@ package com.github.skyfe79.android.reactcomponentkit -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import com.github.skyfe79.android.reactcomponentkit.redux.State interface StateSubscriber { diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/CollectionViewAdapter.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/CollectionViewAdapter.kt index c6733db..50ac1a9 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/CollectionViewAdapter.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/CollectionViewAdapter.kt @@ -6,7 +6,7 @@ import com.github.skyfe79.android.reactcomponentkit.collectionmodels.ItemModel import com.github.skyfe79.android.reactcomponentkit.collectionmodels.RecyclerViewHelper import com.github.skyfe79.android.reactcomponentkit.collectionmodels.SectionModel import com.github.skyfe79.android.reactcomponentkit.component.ViewComponent -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import com.github.skyfe79.android.reactcomponentkit.recyclerview.CollectionViewCellViewHolder import com.github.skyfe79.android.reactcomponentkit.recyclerview.RecyclerViewCell import com.github.skyfe79.android.reactcomponentkit.recyclerview.RecyclerViewCellViewHolder diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/NestedCollectionViewComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/NestedCollectionViewComponent.kt index 1af32d3..fd159b5 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/NestedCollectionViewComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/collectionview/NestedCollectionViewComponent.kt @@ -5,7 +5,7 @@ import android.view.View import androidx.recyclerview.widget.RecyclerView import com.github.skyfe79.android.reactcomponentkit.R import com.github.skyfe79.android.reactcomponentkit.component.ViewComponent -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import com.github.skyfe79.android.reactcomponentkit.recyclerview.RecyclerViewAdapter import org.jetbrains.anko.* import kotlin.reflect.KClass diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/FragmentComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/FragmentComponent.kt index 66744c2..9110ebf 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/FragmentComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/FragmentComponent.kt @@ -5,7 +5,7 @@ import android.os.Bundle import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import com.github.skyfe79.android.reactcomponentkit.ReactComponent -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import com.github.skyfe79.android.reactcomponentkit.redux.State internal enum class FragmentComponentState { diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/IntentServiceComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/IntentServiceComponent.kt index 850aa3c..a2e6b25 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/IntentServiceComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/IntentServiceComponent.kt @@ -2,7 +2,7 @@ package com.github.skyfe79.android.reactcomponentkit.component import android.app.IntentService import com.github.skyfe79.android.reactcomponentkit.ReactComponent -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import com.github.skyfe79.android.reactcomponentkit.redux.State abstract class IntentServiceComponent(token: Token, name: String): IntentService(name), ReactComponent { diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/LayoutComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/LayoutComponent.kt index b0cf855..3378643 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/LayoutComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/LayoutComponent.kt @@ -1,7 +1,7 @@ package com.github.skyfe79.android.reactcomponentkit.component import com.github.skyfe79.android.reactcomponentkit.ReactComponent -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import com.github.skyfe79.android.reactcomponentkit.redux.State import org.jetbrains.anko.AnkoComponent diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ServiceComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ServiceComponent.kt index 4ea579e..8f75c89 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ServiceComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ServiceComponent.kt @@ -2,7 +2,7 @@ package com.github.skyfe79.android.reactcomponentkit.component import android.app.Service import com.github.skyfe79.android.reactcomponentkit.ReactComponent -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token abstract class ServiceComponent(token: Token): Service(), ReactComponent { override var token: Token = token diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt index b7e899b..a8158f0 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/component/ViewComponent.kt @@ -1,13 +1,12 @@ package com.github.skyfe79.android.reactcomponentkit.component import android.content.Context -import android.util.AttributeSet import android.view.View import android.view.ViewManager import com.github.skyfe79.android.reactcomponentkit.ReactComponent import com.github.skyfe79.android.reactcomponentkit.collectionmodels.ItemModel import com.github.skyfe79.android.reactcomponentkit.collectionview.SectionContent -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import com.github.skyfe79.android.reactcomponentkit.redux.State import org.jetbrains.anko.AnkoComponent import org.jetbrains.anko.AnkoContext diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/ActionDispatcher.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/ActionDispatcher.kt index 526e107..d07e94f 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/ActionDispatcher.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/dispatcher/ActionDispatcher.kt @@ -2,14 +2,16 @@ package com.github.skyfe79.android.reactcomponentkit.dispatcher import com.github.skyfe79.android.reactcomponentkit.eventbus.EventBus import com.github.skyfe79.android.reactcomponentkit.eventbus.EventType -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import com.github.skyfe79.android.reactcomponentkit.redux.Action import com.github.skyfe79.android.reactcomponentkit.redux.State +@Deprecated("Do not use ComponentNewStateEvent. It will be removed on the next version") sealed class ComponentNewStateEvent: EventType { data class On(val state: State): ComponentNewStateEvent() } +@Deprecated("Do not use ComponentDispatchEvent. It will be removed on the next version") sealed class ComponentDispatchEvent: EventType { data class Dispatch(val action: Action): ComponentDispatchEvent() } @@ -18,6 +20,7 @@ sealed class ComponentDispatchEvent: EventType { * Dispatch actions where to has same token * It is used among components which has root view models. */ +@Deprecated("Do not use ActionDispatcher. It will be removed on the next version") class ActionDispatcher(private val token: Token) { private val dispatchEventBus = EventBus(token) diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/eventbus/EventBus.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/eventbus/EventBus.kt index d671e29..19cdaf8 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/eventbus/EventBus.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/eventbus/EventBus.kt @@ -2,6 +2,7 @@ package com.github.skyfe79.android.reactcomponentkit.eventbus +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.lang.Exception @@ -17,6 +18,7 @@ internal data class Notification(val name: String, val sender: Any, val userInfo * - empty: ignore eventbus * - a valid token: post and receive events between eventbus which has same token */ +@Deprecated("Do not use EventBus. It will be removed on the next version") class EventBus(val token: Token? = null) { private companion object { diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewAdapter.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewAdapter.kt index 9a6ba0b..3d67b7f 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewAdapter.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewAdapter.kt @@ -5,7 +5,7 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import com.github.skyfe79.android.reactcomponentkit.collectionmodels.ItemModel import com.github.skyfe79.android.reactcomponentkit.component.ViewComponent -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import com.github.skyfe79.android.reactcomponentkit.recyclerview.sticky.StickyHeaders import org.jetbrains.anko.doAsync import org.jetbrains.anko.uiThread diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewCell.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewCell.kt index 1a3fee7..cabb190 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewCell.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/recyclerview/RecyclerViewCell.kt @@ -5,7 +5,7 @@ import android.view.ViewGroup import com.github.skyfe79.android.reactcomponentkit.collectionmodels.ItemModel import com.github.skyfe79.android.reactcomponentkit.collectionview.SectionContent import com.github.skyfe79.android.reactcomponentkit.component.ViewComponent -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token +import com.github.skyfe79.android.reactcomponentkit.viewmodel.Token import org.jetbrains.anko.AnkoContext diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RCKViewModel.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RCKViewModel.kt index 9386217..5bad4d1 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RCKViewModel.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/RCKViewModel.kt @@ -4,7 +4,6 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import com.github.skyfe79.android.reactcomponentkit.RCK import com.github.skyfe79.android.reactcomponentkit.StateSubscriber -import com.github.skyfe79.android.reactcomponentkit.eventbus.Token import com.github.skyfe79.android.reactcomponentkit.redux.* import com.github.skyfe79.android.reactcomponentkit.util.runOnUiThread import com.jakewharton.rxrelay2.BehaviorRelay @@ -18,7 +17,8 @@ import java.lang.ref.WeakReference import java.util.concurrent.locks.ReentrantLock abstract class RCKViewModel(application: Application): AndroidViewModel(application) { - val token: Token = Token() + val token: Token = + Token() private val rx_action: BehaviorRelay = BehaviorRelay.createDefault( VoidAction diff --git a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/eventbus/Token.kt b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/Token.kt similarity index 88% rename from reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/eventbus/Token.kt rename to reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/Token.kt index 1f4fb53..c711287 100644 --- a/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/eventbus/Token.kt +++ b/reactcomponentkit/src/main/java/com/github/skyfe79/android/reactcomponentkit/viewmodel/Token.kt @@ -1,4 +1,4 @@ -package com.github.skyfe79.android.reactcomponentkit.eventbus +package com.github.skyfe79.android.reactcomponentkit.viewmodel import android.os.Parcel import android.os.Parcelable @@ -21,7 +21,8 @@ data class Token(val token: String = UUID.randomUUID().toString()) : Parcelable @JvmField val CREATOR: Parcelable.Creator = object : Parcelable.Creator { - override fun createFromParcel(source: Parcel): Token = Token(source) + override fun createFromParcel(source: Parcel): Token = + Token(source) override fun newArray(size: Int): Array = arrayOfNulls(size) } }