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.
100 lines
2.9 KiB
100 lines
2.9 KiB
package nova.monadic_sfx.ui.components.todo
|
|
|
|
import cats.kernel.Eq
|
|
import com.softwaremill.quicklens._
|
|
import io.circe.generic.JsonCodec
|
|
import io.odin.Logger
|
|
import monix.bio.Task
|
|
import nova.monadic_sfx.util.reactive.store.Middlewares.actionLoggerMiddleware
|
|
import nova.monadic_sfx.util.reactive.store.Reducer
|
|
import nova.monadic_sfx.util.reactive.store.Store
|
|
|
|
case class Todo(id: Int, content: String)
|
|
object Todo {
|
|
implicit val eqForTodo = Eq.fromUniversalEquals[Todo]
|
|
}
|
|
|
|
object TodoListStore {
|
|
|
|
@JsonCodec
|
|
sealed trait Action
|
|
case object Init extends Action
|
|
case class Add(content: String) extends Action
|
|
case class Edit(id: Int, content: String) extends Action
|
|
case class Delete(id: Int) extends Action
|
|
|
|
private case class InternalAdd(content: String) extends Action
|
|
private case object End extends Action
|
|
object Action {
|
|
implicit val eqForAction = Eq.fromUniversalEquals[Action]
|
|
|
|
}
|
|
|
|
case class State(todos: Vector[Todo], counter: Int)
|
|
object State {
|
|
implicit val eqForState = Eq.fromUniversalEquals[State]
|
|
}
|
|
|
|
def reducer(logger: Logger[Task])(
|
|
state: State,
|
|
action: Action
|
|
): (State, Option[Task[Action]]) =
|
|
action match {
|
|
case Init => (state, None)
|
|
case Add(content) =>
|
|
val nextAction = Some(for {
|
|
// do some validation
|
|
// _ <- logger.debug(s"Received $content")
|
|
res <- Task.pure(InternalAdd(content))
|
|
} yield res)
|
|
(state, nextAction)
|
|
case Edit(id, content) =>
|
|
val condition: Todo => Boolean = _.id == id
|
|
val nextState = state
|
|
.modify(_.todos.eachWhere(condition))
|
|
.using(_.copy(content = content))
|
|
(nextState, None)
|
|
case Delete(id) =>
|
|
(state.copy(state.todos.filterNot(_.id == id)), None)
|
|
|
|
case InternalAdd(content) =>
|
|
val nextState =
|
|
state
|
|
.modify(_.todos)
|
|
.using(_ :+ Todo(state.counter, content))
|
|
.modify(_.counter)
|
|
.using(_ + 1)
|
|
(nextState, Some(logger.debug(s"Received $content") >> Task.pure(End)))
|
|
case End => (state, None)
|
|
}
|
|
|
|
def apply(logger: Logger[Task]): Task[Store[Action, State]] =
|
|
Task.deferAction(implicit s =>
|
|
for {
|
|
logMware <- actionLoggerMiddleware[Action, State](logger, "TodoStore")
|
|
store <-
|
|
Store
|
|
.createL[Action, State](
|
|
Init,
|
|
State(Vector.empty[Todo], 0),
|
|
Reducer.withOptionalEffects(reducer(logger) _),
|
|
Seq(logMware)
|
|
)
|
|
} yield store
|
|
)
|
|
}
|
|
|
|
// Task.deferAction(implicit s =>
|
|
// Store
|
|
// .createJsonL[Action, State](
|
|
// Init,
|
|
// State(Vector.empty[Todo], 0),
|
|
// Reducer.withOptionalEffects(reducer(logger) _),
|
|
// "TodoStore",
|
|
// logger
|
|
// // Seq(
|
|
// // actionLoggerMiddleware(logger, "TodoStore")
|
|
// // // actionLoggerMiddleware(logger, "TodoStore2")
|
|
// // )
|
|
// )
|
|
// )
|