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.

115 lines
2.9 KiB

  1. package nova.monadic_sfx.ui.components.router
  2. import io.circe.Codec
  3. import io.circe.Decoder
  4. import io.circe.Encoder
  5. import io.circe.generic.JsonCodec
  6. import io.circe.generic.semiauto._
  7. import io.odin.Logger
  8. import monix.bio.Task
  9. import nova.monadic_sfx.util.IOUtils
  10. import nova.monadic_sfx.util.reactive.store.Middlewares
  11. import nova.monadic_sfx.util.reactive.store.Reducer
  12. import nova.monadic_sfx.util.reactive.store.Store
  13. import scalafx.scene.Parent
  14. import monix.reactive.Observable
  15. import nova.monadic_sfx.util.controls.JFXSpinner
  16. import scala.concurrent.duration._
  17. import monix.eval.Coeval
  18. object FXRouter {
  19. final case class State[P](page: P)
  20. sealed abstract class Action[+T]
  21. final case class Replace[T](page: T) extends Action[T]
  22. object Action {
  23. implicit def codec[T: Encoder: Decoder]: Codec[Action[T]] = deriveCodec
  24. }
  25. type FXStore[P] = Store[Action[P], State[P]]
  26. }
  27. class FXRouter[P]()(implicit E: Encoder[P], D: Decoder[P]) {
  28. import FXRouter._
  29. def store(initialPage: P, logger: Logger[Task]): Task[FXStore[P]] =
  30. Task.deferAction(implicit s =>
  31. for {
  32. mw <- Middlewares.actionLoggerMiddleware[Action[P], State[P]](
  33. logger,
  34. "RouterStore"
  35. )
  36. store <- Store.createL[Action[P], State[P]](
  37. Replace(initialPage),
  38. State(initialPage),
  39. Reducer.withOptionalEffects[Task, Action[P], State[P]](reducer _),
  40. Seq(mw)
  41. )
  42. } yield store
  43. )
  44. def reducer(
  45. state: State[P],
  46. action: Action[P]
  47. ): (State[P], Option[Task[Action[P]]]) =
  48. action match {
  49. // case Init => (state, None)
  50. case Replace(p) =>
  51. (state.copy(page = p), None)
  52. }
  53. def render(
  54. resolver: P => Task[Parent],
  55. transitionDelay: FiniteDuration = 500.millis
  56. )(implicit store: FXStore[P]) =
  57. store
  58. .flatMap {
  59. case (_, FXRouter.State(p)) =>
  60. Observable.from(Coeval(new JFXSpinner)) ++ Observable.from(
  61. IOUtils.toTask(
  62. Task
  63. .racePair(
  64. Task.sleep(transitionDelay),
  65. resolver(p)
  66. )
  67. .flatMap {
  68. case Left(_ -> fib) => fib.join
  69. case Right(fib -> res) => fib.join >> Task.pure(res)
  70. }
  71. )
  72. )
  73. }
  74. def link(
  75. page: P,
  76. store: FXStore[P]
  77. ) = {
  78. store.onNext(Replace(page))
  79. }
  80. }
  81. @JsonCodec
  82. sealed trait Page
  83. object Page {
  84. final case object Home extends Page
  85. final case class UserHome(id: Int) extends Page
  86. final case object Todo extends Page
  87. }
  88. // case class State()
  89. // object RouterStore {
  90. // sealed trait Action
  91. // case object Init extends Action
  92. // def reducer(state: State, action: Action) =
  93. // action match {
  94. // case Init => state
  95. // }
  96. // def apply() =
  97. // Store.createL[Action, State](Init, State(), Reducer(reducer _), Seq.empty)
  98. // }