Last active
June 1, 2018 08:49
-
-
Save zsoltk/add93eb49bf987c3724e0e9b07c17e47 to your computer and use it in GitHub Desktop.
News examples
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ReducerFeatureWithoutNews : ReducerFeature<Wish, State, Nothing>( | |
initialState = State(), | |
reducer = ReducerImpl() | |
) { | |
data class State( | |
val counter: Int = 0 | |
) | |
sealed class Wish { | |
object IncreaseCounter : Wish() | |
} | |
class ReducerImpl : Reducer<State, Wish> { | |
override fun invoke(state: State, effect: Wish): State = when (effect) { | |
IncreaseCounter -> state.copy( | |
counter = state.counter + 1 | |
) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ReducerFeatureWithNews : ReducerFeature<Wish, State, News>( | |
initialState = State(), | |
reducer = ReducerImpl(), | |
newsPublisher = NewsPublisherImpl() | |
) { | |
data class State( | |
val counter: Int = 0 | |
) | |
sealed class Wish { | |
object IncreaseCounter : Wish() | |
} | |
sealed class News { | |
object LoremIpsum : News() | |
object Unused : News() | |
} | |
class ReducerImpl : Reducer<State, Wish> { | |
override fun invoke(state: State, effect: Wish): State = when (effect) { | |
IncreaseCounter -> state.copy( | |
counter = state.counter + 1 | |
) | |
} | |
} | |
class NewsPublisherImpl : SimpleNewsPublisher<Wish, State, News>() { | |
override fun invoke(wish: Wish, state: State): News? = | |
when { | |
// this is dumb, but you get the idea | |
(wish === IncreaseCounter && state.counter == 101) -> LoremIpsum | |
else -> null | |
} | |
} | |
} | |
// --------------------------------------------- | |
object ClientCode { | |
class NewsListener(private val context: Context) : Consumer<News> { | |
override fun accept(news: News) { | |
// don't have to react on News.Unused if you don't want | |
when (news) { | |
is LoremIpsum -> { | |
Toast.makeText(context, "Lorem ipsum", Toast.LENGTH_SHORT).show() | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ActorReducerFeatureWithNews : DefaultFeature<Wish, Action, Effect, State, News>( | |
initialState = State(), | |
wishToAction = { Execute(it) }, | |
actor = ActorImpl(), | |
reducer = ReducerImpl(), | |
newsPublisher = NewsPublisherImpl() | |
) { | |
data class State( | |
val isLoading: Boolean = false, | |
val payload: Any? = null | |
) | |
sealed class Wish { | |
object LoadNewData : Wish() | |
} | |
sealed class News { | |
data class ErrorExecutingRequest(val throwable : Throwable) : News() | |
} | |
sealed class Action { | |
data class Execute(val wish: Wish) : Action() | |
} | |
sealed class Effect { | |
object StartedLoading : Effect() | |
data class ReceivedPayload(val payload: Any) : Effect() | |
data class FailedLoading(val throwable : Throwable) : Effect() | |
} | |
class ActorImpl : Actor<State, Action, Effect> { | |
private val service: Observable<Any> = TODO() | |
override fun invoke(state: State, action: Action): Observable<Effect> = when (action) { | |
is Execute -> when (action.wish) { | |
LoadNewData -> { | |
service | |
.observeOn(AndroidSchedulers.mainThread()) | |
.map { ReceivedPayload(it) as Effect } | |
.startWith(just(StartedLoading)) | |
.onErrorReturn { FailedLoading(it) } | |
} | |
} | |
} | |
} | |
class ReducerImpl : Reducer<State, Effect> { | |
override fun invoke(state: State, effect: Effect): State = when (effect) { | |
StartedLoading -> state.copy( | |
isLoading = true | |
) | |
is ReceivedPayload -> state.copy( | |
isLoading = false, | |
payload = effect.payload | |
) | |
is FailedLoading -> state.copy( | |
isLoading = false | |
) | |
} | |
} | |
class NewsPublisherImpl : NewsPublisher<Action, Effect, State, News> { | |
override fun invoke(action: Action, effect: Effect, state: State): News? = when (effect) { | |
is FailedLoading -> News.ErrorExecutingRequest(effect.throwable) | |
else -> null | |
} | |
} | |
} | |
// --------------------------------------------- | |
object ClientCode { | |
class NewsListener(private val context: Context) : Consumer<News> { | |
override fun accept(news: News) { | |
when (news) { | |
is News.ErrorExecutingRequest -> { | |
Toast.makeText(context, "Lorem ipsum", Toast.LENGTH_SHORT).show() | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DefaultFeatureWithNews : DefaultFeature<Wish, Action, Effect, State, News>( | |
initialState = State(), | |
wishToAction = { Execute(it) }, | |
actor = ActorImpl(), | |
reducer = ReducerImpl(), | |
postProcessor = PostProcessorImpl(), | |
newsPublisher = NewsPublisherImpl() | |
) { | |
data class State( | |
val isLoading: Boolean = false, | |
val payload: Any? = null, | |
internal val requestCounter: Int = 0 | |
) | |
sealed class Wish { | |
object LoadNewData : Wish() | |
} | |
sealed class News { | |
data class ErrorExecutingRequest(val throwable : Throwable) : News() | |
object CacheInvalidated : News() | |
} | |
sealed class Action { | |
data class Execute(val wish: Wish) : Action() | |
object InvalidateCache : Action() | |
} | |
sealed class Effect { | |
object StartedLoading : Effect() | |
data class ReceivedPayload(val payload: Any) : Effect() | |
data class FailedLoading(val throwable : Throwable) : Effect() | |
object CacheInvalidated : Effect() | |
} | |
class ActorImpl : Actor<State, Action, Effect> { | |
private val service: Observable<Any> = TODO() | |
override fun invoke(state: State, action: Action): Observable<Effect> = when (action) { | |
is Execute -> when (action.wish) { | |
LoadNewData -> { | |
service | |
.observeOn(AndroidSchedulers.mainThread()) | |
.map { ReceivedPayload(it) as Effect } | |
.startWith(just(StartedLoading)) | |
.onErrorReturn { FailedLoading(it) } | |
} | |
} | |
InvalidateCache -> { | |
Observable | |
.fromCallable { /* do the thing */ } | |
.map { CacheInvalidated } | |
} | |
} | |
} | |
class ReducerImpl : Reducer<State, Effect> { | |
override fun invoke(state: State, effect: Effect): State = when (effect) { | |
StartedLoading -> state.copy( | |
isLoading = true | |
) | |
is ReceivedPayload -> state.copy( | |
isLoading = false, | |
payload = effect.payload | |
) | |
is FailedLoading -> state.copy( | |
isLoading = false | |
) | |
is CacheInvalidated -> state.copy( | |
requestCounter = 0 | |
) | |
} | |
} | |
class PostProcessorImpl : PostProcessor<Action, Effect, State> { | |
override fun invoke(action: Action, effect: Effect, state: State): Action? = | |
when { | |
(state.requestCounter >= 3) -> InvalidateCache | |
else -> null | |
} | |
} | |
class NewsPublisherImpl : NewsPublisher<Action, Effect, State, News> { | |
override fun invoke(action: Action, effect: Effect, state: State): News? = when (effect) { | |
is FailedLoading -> News.ErrorExecutingRequest(effect.throwable) | |
is CacheInvalidated -> News.CacheInvalidated | |
else -> null | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Feature dependencies are injected, but if you really want, you can do the wiring somewhere else | |
class ReactingToNewsInFeature1( | |
feature1: ReducerFeatureWithNews, | |
feature2: ActorReducerFeatureWithNews | |
) : ReducerFeature<Wish, State, Nothing>( | |
initialState = State(), | |
reducer = ReducerImpl(), | |
bootstrapper = BootStrapperImpl(feature1, feature2) | |
) { | |
data class State( | |
val counter: Int = 0 | |
) | |
sealed class Wish { | |
object IncreaseCounter : Wish() | |
} | |
class BootStrapperImpl( | |
private val feature1: ReducerFeatureWithNews, | |
private val feature2: ActorReducerFeatureWithNews | |
) : Bootstrapper<Wish> { | |
// Again, no need to exhaust all possible News, just the ones we're interested in | |
override fun invoke(): Observable<Wish> = Observable.merge( | |
Observable.wrap(feature1.news).mapNotNull { | |
when (it) { | |
LoremIpsum -> IncreaseCounter | |
else -> null | |
} | |
}, | |
Observable.wrap(feature2.news).mapNotNull { | |
when (it) { | |
is ErrorExecutingRequest -> IncreaseCounter | |
else -> null | |
} | |
} | |
) | |
// Another simpler approach, if it's mapped to the same one wish: | |
fun invoke2(): Observable<Wish> = Observable.merge( | |
Observable.wrap(feature1.news).filter { it is LoremIpsum }, | |
Observable.wrap(feature2.news).filter { it is ErrorExecutingRequest } | |
).map { IncreaseCounter } | |
} | |
class ReducerImpl : Reducer<State, Wish> { | |
override fun invoke(state: State, effect: Wish): State = when (effect) { | |
IncreaseCounter -> state.copy( | |
counter = state.counter + 1 | |
) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment