You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
3.2 KiB
124 lines
3.2 KiB
package nova.monadic_sfx.util.reactive.store
|
|
|
|
import monix.bio.Task
|
|
import monix.eval.Coeval
|
|
import monix.reactive.Observer
|
|
import monix.reactive.OverflowStrategy
|
|
import monix.reactive.subjects.ConcurrentSubject
|
|
import cats.effect.Resource
|
|
import monix.{eval => me}
|
|
import monix.catnap.ConcurrentQueue
|
|
import nova.monadic_sfx.util.IOUtils
|
|
import monix.reactive.Observable
|
|
import io.odin.Logger
|
|
import nova.monadic_sfx.implicits._
|
|
|
|
object Store {
|
|
def createL[A, M](
|
|
initialAction: A,
|
|
initialState: M,
|
|
reducer: Reducer[A, M],
|
|
middlewares: Seq[Middleware[A, M]] = Seq.empty,
|
|
overflowStrategy: OverflowStrategy.Synchronous[A] =
|
|
OverflowStrategy.DropOld(50)
|
|
): Task[Store[A, M]] =
|
|
Task.deferAction { implicit s =>
|
|
Task {
|
|
val subject = ConcurrentSubject.publish[A](overflowStrategy)
|
|
|
|
val fold: ((A, M), A) => Coeval[(A, M)] = {
|
|
case ((_, state), action) =>
|
|
Coeval {
|
|
val (newState, effects) = reducer(state, action)
|
|
|
|
effects.subscribe(subject.onNext _)
|
|
|
|
action -> newState
|
|
}
|
|
}
|
|
|
|
val obs = subject
|
|
.scanEval0F[Coeval, (A, M)](
|
|
Coeval.pure(initialAction -> initialState)
|
|
)(fold)
|
|
|
|
val res = middlewares
|
|
.foldLeft(obs) {
|
|
case (obs, middleware) => middleware(obs)
|
|
}
|
|
.doOnNextF(i => Coeval(println(s"Emitted item 1: $i")))
|
|
.behavior(initialAction -> initialState)
|
|
.refCount
|
|
|
|
res.subscribe(Observer.empty)
|
|
|
|
// .doOnNextF(i => Coeval(println(s"Emitted item 2: $i")))
|
|
|
|
MonixProSubject.from(
|
|
subject,
|
|
res
|
|
)
|
|
}
|
|
}
|
|
|
|
// : Resource[Task, Store[A, M]]
|
|
def backpressured[A, M](
|
|
initialAction: A,
|
|
initialState: M,
|
|
reducer: Reducer[A, M],
|
|
logger: Logger[Task],
|
|
middlewares: Seq[Middleware[A, M]] = Seq.empty
|
|
) = {
|
|
|
|
for {
|
|
queue <- ConcurrentQueue[Task].bounded[A](10)
|
|
source <- Task.deferAction(implicit s =>
|
|
Task {
|
|
val fold: ((A, M), A) => Task[(A, M)] = {
|
|
case ((_, state), action) =>
|
|
for {
|
|
_ <- Task.unit
|
|
(newState, effects) = reducer(state, action)
|
|
_ <-
|
|
effects
|
|
.doOnNextF(queue.offer)
|
|
.completedL
|
|
.toIO
|
|
// .start
|
|
|
|
} yield action -> newState
|
|
}
|
|
|
|
val obs = Observable
|
|
.repeatEvalF(queue.poll)
|
|
.scanEval0F[Task, (A, M)](
|
|
Task.pure(initialAction -> initialState)
|
|
)(fold)
|
|
|
|
val res =
|
|
// middlewares
|
|
// .foldLeft(obs) {
|
|
// case (obs, middleware) => middleware(obs)
|
|
// }
|
|
obs
|
|
.doOnNextF(i => logger.debug(s"Emitted item 1: $i"))
|
|
.behavior(initialAction -> initialState)
|
|
.refCount
|
|
|
|
// res.subscribe(Observer.empty)
|
|
|
|
// .doOnNextF(i => Coeval(println(s"Emitted item 2: $i")))
|
|
|
|
res
|
|
}
|
|
)
|
|
} yield new MyStore(Sink2.concurrentQueue(queue), source)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
final class MyStore[A, M](
|
|
val sink: Sink2[A],
|
|
val source: Observable[(A, M)]
|
|
)
|