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.

102 lines
3.0 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
  1. package nova.monadic_sfx.ui.components.todo
  2. import cats.kernel.Eq
  3. import com.softwaremill.quicklens._
  4. import io.circe.generic.JsonCodec
  5. import io.odin.Logger
  6. import monix.bio.Task
  7. import nova.monadic_sfx.util.reactive.store.Middlewares.actionLoggerMiddleware
  8. import nova.monadic_sfx.util.reactive.store.Reducer
  9. import nova.monadic_sfx.util.reactive.store.Store
  10. import nova.monadic_sfx.util.reactive.store.MyStore
  11. case class Todo(id: Int, content: String)
  12. object Todo {
  13. implicit val eqForTodo = Eq.fromUniversalEquals[Todo]
  14. }
  15. object TodoListStore {
  16. @JsonCodec
  17. sealed trait Action
  18. case object Init extends Action
  19. case class Add(content: String) extends Action
  20. case class Edit(id: Int, content: String) extends Action
  21. case class Delete(id: Int) extends Action
  22. private case class InternalAdd(content: String) extends Action
  23. private case object End extends Action
  24. object Action {
  25. implicit val eqForAction = Eq.fromUniversalEquals[Action]
  26. }
  27. case class State(todos: Vector[Todo], counter: Int)
  28. object State {
  29. implicit val eqForState = Eq.fromUniversalEquals[State]
  30. }
  31. def reducer(logger: Logger[Task])(
  32. state: State,
  33. action: Action
  34. ): (State, Option[Task[Action]]) =
  35. action match {
  36. case Init => (state, None)
  37. case Add(content) =>
  38. val nextAction = Some(for {
  39. // do some validation
  40. // _ <- logger.debug(s"Received $content")
  41. res <- Task.pure(InternalAdd(content))
  42. } yield res)
  43. (state, nextAction)
  44. case Edit(id, content) =>
  45. val condition: Todo => Boolean = _.id == id
  46. val nextState = state
  47. .modify(_.todos.eachWhere(condition))
  48. .using(_.copy(content = content))
  49. (nextState, None)
  50. case Delete(id) =>
  51. (state.copy(state.todos.filterNot(_.id == id)), None)
  52. case InternalAdd(content) =>
  53. val nextState =
  54. state
  55. .modify(_.todos)
  56. .using(_ :+ Todo(state.counter, content))
  57. .modify(_.counter)
  58. .using(_ + 1)
  59. (nextState, Some(logger.debug(s"Received $content") >> Task.pure(End)))
  60. case End => (state, None)
  61. }
  62. def apply(logger: Logger[Task]): Task[MyStore[Action, State]] =
  63. Task.deferAction(implicit s =>
  64. for {
  65. logMware <- actionLoggerMiddleware[Action, State](logger, "TodoStore")
  66. store <-
  67. Store
  68. .backpressured[Action, State](
  69. Init,
  70. State(Vector.empty[Todo], 0),
  71. Reducer.withOptionalEffects(reducer(logger) _),
  72. logger,
  73. Seq(logMware)
  74. )
  75. } yield store
  76. )
  77. }
  78. // Task.deferAction(implicit s =>
  79. // Store
  80. // .createJsonL[Action, State](
  81. // Init,
  82. // State(Vector.empty[Todo], 0),
  83. // Reducer.withOptionalEffects(reducer(logger) _),
  84. // "TodoStore",
  85. // logger
  86. // // Seq(
  87. // // actionLoggerMiddleware(logger, "TodoStore")
  88. // // // actionLoggerMiddleware(logger, "TodoStore2")
  89. // // )
  90. // )
  91. // )