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.

197 lines
5.5 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
  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. import nova.monadic_sfx.util.reactive.store.MyStore
  23. object FXRouter {
  24. final case class State[P](page: P, history: History[P])
  25. object State {
  26. implicit def eqForAction[T] = Eq.fromUniversalEquals[State[T]]
  27. }
  28. sealed abstract class Action[+T]
  29. final case class Replace[T](page: T) extends Action[T]
  30. final case class HistoryEvent[T](page: T) extends Action[T]
  31. final case object Forward extends Action[Nothing]
  32. final case object Backward extends Action[Nothing]
  33. object Action {
  34. implicit def codec[T: Encoder: Decoder]: Codec[Action[T]] = deriveCodec
  35. implicit def eqForAction[T] = Eq.fromUniversalEquals[Action[T]]
  36. }
  37. type FXStore[P] = Store[Action[P], State[P]]
  38. type FXStore2[P] = MyStore[Action[P], State[P]]
  39. }
  40. final class FXRouter[P]()(implicit E: Encoder[P], D: Decoder[P]) {
  41. import FXRouter._
  42. def store(initialPage: P, logger: Logger[Task]): Task[FXStore[P]] =
  43. Task.deferAction(implicit s =>
  44. for {
  45. mw <- Middlewares.actionLoggerMiddleware[Action[P], State[P]](
  46. logger,
  47. "RouterStore"
  48. )
  49. store <- Store.createL[Action[P], State[P]](
  50. Replace(initialPage),
  51. State(initialPage, History(initialPage)),
  52. Reducer.withOptionalEffects[Task, Action[P], State[P]](reducer _),
  53. Seq(mw)
  54. // Seq(classOf[HistoryEvent[P]])
  55. )
  56. } yield store
  57. )
  58. def store2(initialPage: P, logger: Logger[Task]) =
  59. Task.deferAction(implicit s =>
  60. for {
  61. mw <- Middlewares.actionLoggerMiddleware[Action[P], State[P]](
  62. logger,
  63. "RouterStore"
  64. )
  65. store <- Store.backpressured[Action[P], State[P]](
  66. Replace(initialPage),
  67. State(initialPage, History(initialPage)),
  68. Reducer.withOptionalEffects[Task, Action[P], State[P]](reducer _),
  69. logger,
  70. Seq(mw)
  71. // Seq(classOf[HistoryEvent[P]])
  72. )
  73. } yield store
  74. )
  75. def reducer(
  76. state: State[P],
  77. action: Action[P]
  78. ): (State[P], Option[Task[Action[P]]]) =
  79. action match {
  80. // case Init => (state, None)
  81. case Replace(p) =>
  82. (state.copy(page = p, history = state.history :+ p), None)
  83. case HistoryEvent(p) =>
  84. (state.copy(page = p), None)
  85. case Forward =>
  86. val s1 = state.modify(_.history).using(_.forward)
  87. val s2 = s1.modify(_.page).setTo(s1.history.current)
  88. s2 -> Some(Task.pure(HistoryEvent(s2.history.current)))
  89. case Backward =>
  90. val s1 = state.modify(_.history).using(_.backward)
  91. val s2 = s1.modify(_.page).setTo(s1.history.current)
  92. s2 -> Some(Task.pure(HistoryEvent(s2.history.current)))
  93. }
  94. def render(
  95. resolver: P => Parent,
  96. transitionDelay: FiniteDuration = 500.millis
  97. )(implicit store: FXStore[P]) =
  98. store
  99. .filter {
  100. case (a, _) => a =!= FXRouter.Forward
  101. }
  102. .filter {
  103. case (a, _) => a =!= FXRouter.Backward
  104. }
  105. .distinctUntilChanged
  106. .flatMap {
  107. case (_, FXRouter.State(p, _)) =>
  108. Observable.from(Coeval(new JFXSpinner)) ++ Observable.from(
  109. IOUtils.toTask(
  110. Task
  111. .racePair(
  112. Task.sleep(transitionDelay),
  113. Task.pure(resolver(p))
  114. )
  115. .flatMap {
  116. case Left(_ -> fib) => fib.join
  117. case Right(fib -> res) => fib.join >> Task.pure(res)
  118. }
  119. )
  120. )
  121. }
  122. def render2(
  123. resolver: P => Parent,
  124. transitionDelay: FiniteDuration = 500.millis
  125. )(implicit store: FXStore2[P]) =
  126. store.source
  127. .filter {
  128. case (a, _) => a =!= FXRouter.Forward
  129. }
  130. .filter {
  131. case (a, _) => a =!= FXRouter.Backward
  132. }
  133. .distinctUntilChanged
  134. .flatMap {
  135. case (_, FXRouter.State(p, _)) =>
  136. Observable.from(Coeval(new JFXSpinner)) ++ Observable.from(
  137. IOUtils.toTask(
  138. Task
  139. .racePair(
  140. Task.sleep(transitionDelay),
  141. Task(resolver(p))
  142. )
  143. .flatMap {
  144. case Left(_ -> fib) => fib.join
  145. case Right(fib -> res) => fib.join >> Task.pure(res)
  146. }
  147. )
  148. )
  149. }
  150. def link(
  151. page: P,
  152. store: FXStore[P]
  153. ) = {
  154. store.onNext(Replace(page))
  155. }
  156. }
  157. @JsonCodec
  158. sealed trait Page
  159. object Page {
  160. final case object Home extends Page
  161. final case object UserHome extends Page
  162. final case object Todo extends Page
  163. implicit val eq = Eq.fromUniversalEquals[Page]
  164. }
  165. // case class State()
  166. // object RouterStore {
  167. // sealed trait Action
  168. // case object Init extends Action
  169. // def reducer(state: State, action: Action) =
  170. // action match {
  171. // case Init => state
  172. // }
  173. // def apply() =
  174. // Store.createL[Action, State](Init, State(), Reducer(reducer _), Seq.empty)
  175. // }