Skip to content

Commit

Permalink
Reactive redux updates in detail view.
Browse files Browse the repository at this point in the history
  • Loading branch information
pardom committed Aug 26, 2016
1 parent 6563169 commit a4a99dd
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 91 deletions.
42 changes: 27 additions & 15 deletions android/src/main/kotlin/clean/news/ui/item/detail/ItemDetailView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ import clean.news.presentation.model.item.ItemDetailViewModel.Action
import clean.news.ui.item.detail.ItemDetailKey.ItemDetailComponent
import com.jakewharton.rxbinding.support.v7.widget.itemClicks
import com.jakewharton.rxbinding.support.v7.widget.navigationClicks
import redux.Store
import redux.Store.Subscription
import com.jakewharton.rxbinding.widget.text
import redux.asObservable
import rx.subscriptions.CompositeSubscription
import javax.inject.Inject

class ItemDetailView : RelativeLayout, Store.Subscriber {
class ItemDetailView : RelativeLayout {
@Inject
lateinit var model: ItemDetailViewModel

Expand All @@ -37,7 +37,6 @@ class ItemDetailView : RelativeLayout, Store.Subscriber {
private val adapter: ItemDetailAdapter

private val subscriptions = CompositeSubscription()
private lateinit var subscription: Subscription

@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : super(context, attrs, defStyle) {
Expand Down Expand Up @@ -65,13 +64,34 @@ class ItemDetailView : RelativeLayout, Store.Subscriber {
.subscribe { model.dispatch(Action.Share()) }
.addTo(subscriptions)

subscription = model.subscribe(this)
onStateChanged()
val modelChanges = model.asObservable().share()

modelChanges
.startWith(model.getState())
.map { it.item.title }
.filter { it != null }
.cast(String::class.java)
.distinctUntilChanged()
.subscribe(titleTextView.text())
.addTo(subscriptions)

modelChanges
.startWith(model.getState())
.map { it.children }
.distinctUntilChanged()
.subscribe { adapter.setItems(it) }
.addTo(subscriptions)

modelChanges
.startWith(model.getState())
.map { it.loading }
.distinctUntilChanged()
.subscribe { adapter.setLoading(it) }
.addTo(subscriptions)
}

override fun onDetachedFromWindow() {
subscriptions.unsubscribe()
subscription.unsubscribe()
super.onDetachedFromWindow()
}

Expand All @@ -90,14 +110,6 @@ class ItemDetailView : RelativeLayout, Store.Subscriber {
adapter.setCollapsedIds(savedState.collapsedIds)
}

override fun onStateChanged() {
val state = model.getState()
toolbar.title = state.item.title
titleTextView.text = state.item.title
adapter.setItems(state.children)
adapter.setLoading(state.loading)
}

class SavedState : BaseSavedState {
var collapsedCount = 0
var collapsedIds = LongArray(0)
Expand Down
8 changes: 4 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ ext {
okHttpVersion = '3.2.0'
paperParcelVersion = '1.0.0-beta8'
phraseVersion = '1.1.0'
reduxKotlinVersion = '1.0.0'
retrofitVersion = '2.0.0'
rxAndroidVersion = '1.1.0'
rxBindingVersion = '0.4.0'
Expand Down Expand Up @@ -81,6 +82,9 @@ ext {
paperParcel = "com.github.grandstaish.paperparcel:paperparcel-kotlin:$paperParcelVersion"
paperParcelCompiler = "com.github.grandstaish.paperparcel:compiler:$paperParcelVersion"
phrase = "com.squareup.phrase:phrase:$phraseVersion"
reduxKotlin = "com.github.pardom:redux-kotlin:$reduxKotlinVersion"
reduxObservableKotlin = "com.github.pardom:redux-observable-kotlin:-SNAPSHOT"
reduxRxJavaKotlin = "com.github.pardom:redux-rxjava-kotlin:-SNAPSHOT"
retrofit = "com.squareup.retrofit2:retrofit:$retrofitVersion"
retrofitAdapterRxJava = "com.squareup.retrofit2:adapter-rxjava:$retrofitVersion"
retrofitConverterMoshi = "com.squareup.retrofit2:converter-moshi:$retrofitVersion"
Expand All @@ -91,10 +95,6 @@ ext {
rxBindingAppCompatV7 = "com.jakewharton.rxbinding:rxbinding-appcompat-v7-kotlin:$rxBindingVersion"
rxJava = "io.reactivex:rxjava:$rxJavaVersion"

reduxKotlin = "com.github.pardom:redux-kotlin:-SNAPSHOT"
reduxLoggerKotlin = "com.github.pardom:redux-logger-kotlin:-SNAPSHOT"
reduxObservableKotlin = "com.github.pardom:redux-observable-kotlin:-SNAPSHOT"

// Testing

assertJ = "org.assertj:assertj-core:$assertJVersion"
Expand Down
7 changes: 3 additions & 4 deletions presentation/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ dependencies {

compile javaxInject
compile kotlinStdLib
compile reduxKotlin
compile reduxObservableKotlin
compile reduxRxJavaKotlin
compile rxJava

compile(reduxKotlin) { changing = true }
compile(reduxLoggerKotlin) { changing = true }
compile(reduxObservableKotlin) { changing = true }
}

configurations.all {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ import redux.Dispatcher
import redux.Middleware
import redux.Reducer
import redux.Store
import redux.logger.Logger
import redux.logger.Logger.Event
import redux.logger.LoggerMiddleware
import redux.observable.Epic
import redux.observable.EpicMiddleware
import rx.Observable
Expand All @@ -33,13 +30,19 @@ class ItemDetailViewModel @Inject constructor(
private val getChildren: GetChildren,
private val item: Item) : StoreModel<State>() {

init {
dispatch(Action.GetChildren(item))
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// State

data class State(
val item: Item,
val children: List<Item>,
val loading: Boolean)

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Actions

sealed class Action {
Expand All @@ -50,66 +53,57 @@ class ItemDetailViewModel @Inject constructor(
class Share() : Action()
}

override fun createStore(): Store<State> {
// Reducer
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Reducer

val reducer = object : Reducer<State> {
override fun reduce(state: State, action: Any): State {
return when (action) {
is ShowChildren -> state.copy(children = action.children)
else -> state
}
}
fun reducer() = Reducer { state: State, action: Any ->
when (action) {
is ShowChildren -> state.copy(children = action.children)
else -> state
}
}

// Middleware

val logger = object : Logger<State> {
override fun log(event: Event, action: Any, state: State) {
}
}
val epic = object : Epic<State> {
override fun map(actions: Observable<out Any>, store: Store<State>): Observable<out Any> {
return actions.ofType(Action.GetChildren::class.java)
.flatMap {
getChildren.execute(GetChildren.Request(it.item))
.observeOn(observeScheduler)
}
.map { Action.ShowChildren(it.items) }
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Epic

val loggerMiddleware = LoggerMiddleware.create(logger)
val epicMiddleware = EpicMiddleware.create(epic)
val navigationMiddleware = object : Middleware<State> {
override fun dispatch(store: Store<State>, action: Any, next: Dispatcher): Any {
when (action) {
is GoBack -> navService.goBack()
is GoToUrl -> navService.goTo(navFactory.url(item))
is Share -> navService.goTo(navFactory.shareDetail(item))
fun epic() = Epic { actions: Observable<out Any>, store: Store<State> ->
actions.ofType(Action.GetChildren::class.java)
.flatMap {
getChildren.execute(GetChildren.Request(it.item))
.observeOn(observeScheduler)
}
return action
}
.map { Action.ShowChildren(it.items) }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Nav

fun navigationMiddleware() = Middleware { store: Store<State>, action: Any, next: Dispatcher ->
val result = next.dispatch(action)
when (action) {
is GoBack -> navService.goBack()
is GoToUrl -> navService.goTo(navFactory.url(item))
is Share -> navService.goTo(navFactory.shareDetail(item))
}
result
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Overrides

override fun createStore(): Store<State> {
return Store.create(
reducer,
reducer(),
State(
item,
listOf(item),
false
),
Middleware.apply(
loggerMiddleware,
epicMiddleware,
navigationMiddleware
navigationMiddleware(),
EpicMiddleware.create(epic())
)
)
}

init {
dispatch(Action.GetChildren(item))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ import redux.Dispatcher
import redux.Middleware
import redux.Reducer
import redux.Store
import redux.logger.Logger
import redux.logger.Logger.Event
import redux.logger.LoggerMiddleware
import redux.observable.Epic
import redux.observable.EpicMiddleware
import rx.Observable
Expand Down Expand Up @@ -58,10 +55,6 @@ class ItemListViewModel @Inject constructor(

// Middleware

val logger = object : Logger<State> {
override fun log(event: Event, action: Any, state: State) {
}
}
val epic = object : Epic<State> {
override fun map(actions: Observable<out Any>, store: Store<State>): Observable<out Any> {
return actions.ofType(Action.GetItems::class.java)
Expand All @@ -73,7 +66,6 @@ class ItemListViewModel @Inject constructor(
}
}

val loggerMiddleware = LoggerMiddleware.create(logger)
val epicMiddleware = EpicMiddleware.create(epic)
val navigationMiddleware = object : Middleware<State> {
override fun dispatch(store: Store<State>, action: Any, next: Dispatcher): Any {
Expand All @@ -93,7 +85,6 @@ class ItemListViewModel @Inject constructor(
false
),
Middleware.apply(
loggerMiddleware,
epicMiddleware,
navigationMiddleware
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ import redux.Dispatcher
import redux.Middleware
import redux.Reducer
import redux.Store
import redux.logger.Logger
import redux.logger.Logger.Event
import redux.logger.LoggerMiddleware
import javax.inject.Inject

class ItemUrlViewModel @Inject constructor(
Expand Down Expand Up @@ -45,12 +42,6 @@ class ItemUrlViewModel @Inject constructor(

// Middleware

val logger = object : Logger<State> {
override fun log(event: Event, action: Any, state: State) {
}
}

val loggerMiddleware = LoggerMiddleware.create(logger)
val navigationMiddleware = object : Middleware<State> {
override fun dispatch(store: Store<State>, action: Any, next: Dispatcher): Any {
when (action) {
Expand All @@ -68,10 +59,7 @@ class ItemUrlViewModel @Inject constructor(
return Store.create(
reducer,
State(item),
Middleware.apply(
loggerMiddleware,
navigationMiddleware
)
Middleware.apply(navigationMiddleware)
)
}

Expand Down

0 comments on commit a4a99dd

Please sign in to comment.