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.

147 lines
4.1 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. package nova.monadic_sfx.ui.components.router
  2. import scala.concurrent.duration._
  3. import cats.kernel.Eq
  4. import cats.syntax.eq._
  5. import com.softwaremill.quicklens._
  6. import io.circe.Codec
  7. import io.circe.Decoder
  8. import io.circe.Encoder
  9. import io.circe.generic.JsonCodec
  10. import io.circe.generic.semiauto._
  11. import io.odin.Logger
  12. import monix.bio.Task
  13. import monix.eval.Coeval
  14. import monix.reactive.Observable
  15. import nova.monadic_sfx.util.History
  16. import nova.monadic_sfx.util.IOUtils
  17. import nova.monadic_sfx.util.controls.JFXSpinner
  18. import nova.monadic_sfx.util.reactive.store.Middlewares
  19. import nova.monadic_sfx.util.reactive.store.Reducer
  20. import nova.monadic_sfx.util.reactive.store.Store
  21. import scalafx.scene.Parent
  22. object FXRouter {
  23. final case class State[P](page: P, history: History[P])
  24. object State {
  25. implicit def eqForAction[T] = Eq.fromUniversalEquals[State[T]]
  26. }
  27. sealed abstract class Action[+T]
  28. final case class Replace[T](page: T) extends Action[T]
  29. final case class HistoryEvent[T](page: T) extends Action[T]
  30. final case object Forward extends Action[Nothing]
  31. final case object Backward extends Action[Nothing]
  32. object Action {
  33. implicit def codec[T: Encoder: Decoder]: Codec[Action[T]] = deriveCodec
  34. implicit def eqForAction[T] = Eq.fromUniversalEquals[Action[T]]
  35. }
  36. type FXStore[P] = Store[Action[P], State[P]]
  37. }
  38. class FXRouter[P]()(implicit E: Encoder[P], D: Decoder[P]) {
  39. import FXRouter._
  40. def store(initialPage: P, logger: Logger[Task]): Task[FXStore[P]] =
  41. Task.deferAction(implicit s =>
  42. for {
  43. mw <- Middlewares.actionLoggerMiddleware[Action[P], State[P]](
  44. logger,
  45. "RouterStore"
  46. )
  47. store <- Store.createL[Action[P], State[P]](
  48. Replace(initialPage),
  49. State(initialPage, History(initialPage)),
  50. Reducer.withOptionalEffects[Task, Action[P], State[P]](reducer _),
  51. Seq(mw)
  52. // Seq(classOf[HistoryEvent[P]])
  53. )
  54. } yield store
  55. )
  56. def reducer(
  57. state: State[P],
  58. action: Action[P]
  59. ): (State[P], Option[Task[Action[P]]]) =
  60. action match {
  61. // case Init => (state, None)
  62. case Replace(p) =>
  63. (state.copy(page = p, history = state.history :+ p), None)
  64. case HistoryEvent(p) =>
  65. (state.copy(page = p), None)
  66. case Forward =>
  67. val s1 = state.modify(_.history).using(_.forward)
  68. val s2 = s1.modify(_.page).setTo(s1.history.current)
  69. s2 -> Some(Task.pure(HistoryEvent(s2.history.current)))
  70. case Backward =>
  71. val s1 = state.modify(_.history).using(_.backward)
  72. val s2 = s1.modify(_.page).setTo(s1.history.current)
  73. s2 -> Some(Task.pure(HistoryEvent(s2.history.current)))
  74. }
  75. def render(
  76. resolver: P => Parent,
  77. transitionDelay: FiniteDuration = 500.millis
  78. )(implicit store: FXStore[P]) =
  79. store
  80. .filter {
  81. case (a, _) => a =!= FXRouter.Forward
  82. }
  83. .filter {
  84. case (a, _) => a =!= FXRouter.Backward
  85. }
  86. .distinctUntilChanged
  87. .flatMap {
  88. case (_, FXRouter.State(p, _)) =>
  89. Observable.from(Coeval(new JFXSpinner)) ++ Observable.from(
  90. IOUtils.toTask(
  91. Task
  92. .racePair(
  93. Task.sleep(transitionDelay),
  94. Task.pure(resolver(p))
  95. )
  96. .flatMap {
  97. case Left(_ -> fib) => fib.join
  98. case Right(fib -> res) => fib.join >> Task.pure(res)
  99. }
  100. )
  101. )
  102. }
  103. def link(
  104. page: P,
  105. store: FXStore[P]
  106. ) = {
  107. store.onNext(Replace(page))
  108. }
  109. }
  110. @JsonCodec
  111. sealed trait Page
  112. object Page {
  113. final case object Home extends Page
  114. final case object UserHome extends Page
  115. final case object Todo extends Page
  116. implicit val eqForPage = Eq.fromUniversalEquals[Page]
  117. }
  118. // case class State()
  119. // object RouterStore {
  120. // sealed trait Action
  121. // case object Init extends Action
  122. // def reducer(state: State, action: Action) =
  123. // action match {
  124. // case Init => state
  125. // }
  126. // def apply() =
  127. // Store.createL[Action, State](Init, State(), Reducer(reducer _), Seq.empty)
  128. // }