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.
 
 
 

128 lines
3.3 KiB

package nova.monadic_sfx.ui.components.router
import scala.concurrent.duration._
import io.circe.Codec
import io.circe.Decoder
import io.circe.Encoder
import io.circe.generic.JsonCodec
import io.circe.generic.semiauto._
import io.odin.Logger
import monix.bio.Task
import monix.eval.Coeval
import monix.reactive.Observable
import nova.monadic_sfx.util.IOUtils
import nova.monadic_sfx.util.MutHistory
import nova.monadic_sfx.util.controls.JFXSpinner
import nova.monadic_sfx.util.reactive.store.Middlewares
import nova.monadic_sfx.util.reactive.store.Reducer
import nova.monadic_sfx.util.reactive.store.Store
import scalafx.scene.Parent
object FXRouter {
final case class State[P](page: P)
sealed abstract class Action[+T]
final case class Replace[T](page: T) extends Action[T]
final case class HistoryEvent[T](page: T) extends Action[T]
final case object Forward extends Action[Nothing]
final case object Backward extends Action[Nothing]
object Action {
implicit def codec[T: Encoder: Decoder]: Codec[Action[T]] = deriveCodec
}
type FXStore[P] = Store[Action[P], State[P]]
}
class FXRouter[P](history: MutHistory[P])(implicit
E: Encoder[P],
D: Decoder[P]
) {
import FXRouter._
def store(initialPage: P, logger: Logger[Task]): Task[FXStore[P]] =
Task.deferAction(implicit s =>
for {
mw <- Middlewares.actionLoggerMiddleware[Action[P], State[P]](
logger,
"RouterStore"
)
store <- Store.createL[Action[P], State[P]](
Replace(initialPage),
State(initialPage),
Reducer.withOptionalEffects[Task, Action[P], State[P]](reducer _),
Seq(mw)
)
} yield store
)
def reducer(
state: State[P],
action: Action[P]
): (State[P], Option[Task[Action[P]]]) =
action match {
// case Init => (state, None)
case Replace(p) =>
history.push(p)
(state.copy(page = p), None)
case HistoryEvent(p) =>
(state.copy(page = p), None)
case Forward => (state, None)
case Backward => (state, None)
}
def render(
resolver: P => Task[Parent],
transitionDelay: FiniteDuration = 500.millis
)(implicit store: FXStore[P]) =
store
.flatMap {
case (_, FXRouter.State(p)) =>
Observable.from(Coeval(new JFXSpinner)) ++ Observable.from(
IOUtils.toTask(
Task
.racePair(
Task.sleep(transitionDelay),
resolver(p)
)
.flatMap {
case Left(_ -> fib) => fib.join
case Right(fib -> res) => fib.join >> Task.pure(res)
}
)
)
}
def link(
page: P,
store: FXStore[P]
) = {
store.onNext(Replace(page))
}
}
@JsonCodec
sealed trait Page
object Page {
final case object Home extends Page
final case class UserHome(id: Int) extends Page
final case object Todo extends Page
}
// case class State()
// object RouterStore {
// sealed trait Action
// case object Init extends Action
// def reducer(state: State, action: Action) =
// action match {
// case Init => state
// }
// def apply() =
// Store.createL[Action, State](Init, State(), Reducer(reducer _), Seq.empty)
// }