/examples/cats/src/main/scala/CatsIOExample.scala

https://github.com/fomkin/korolev · Scala · 122 lines · 108 code · 11 blank · 3 comment · 12 complexity · 93c46848226a4f319f06617c584e4d16 MD5 · raw file

  1. import akka.actor.ActorSystem
  2. import akka.http.scaladsl.Http
  3. import cats.effect.IO
  4. import korolev.Context
  5. import korolev.akka.{AkkaHttpServerConfig, akkaHttpService}
  6. import korolev.cats.IOEffect
  7. import korolev.server.{KorolevServiceConfig, StateLoader}
  8. import korolev.state.javaSerialization._
  9. import scala.concurrent.ExecutionContext.Implicits.global
  10. object CatsIOExample extends App {
  11. private implicit val actorSystem: ActorSystem = ActorSystem()
  12. val applicationContext: Context[IO, State, Any] = {
  13. Context[IO, State, Any]
  14. }
  15. import applicationContext._
  16. import levsha.dsl._
  17. import html._
  18. // Handler to input
  19. private val inputId = elementId()
  20. private val editInputId = elementId()
  21. val config = KorolevServiceConfig[IO, State, Any](
  22. stateLoader = StateLoader.default(State()),
  23. document = state => {
  24. Html(
  25. body(
  26. div("Super TODO tracker"),
  27. div(height @= "250px", overflow @= "scroll",
  28. (state.todos zipWithIndex) map {
  29. case (todo, i) =>
  30. div(
  31. input(
  32. `type` := "checkbox",
  33. if (state.edit.nonEmpty) disabled else void,
  34. if (todo.done) checked else void,
  35. // Generate transition when clicking checkboxes
  36. event("click") { access =>
  37. access.transition { s =>
  38. val updated = s.todos.updated(i, s.todos(i).copy(done = !todo.done))
  39. s.copy(todos = updated)
  40. }
  41. }
  42. ),
  43. if (state.edit.contains(i)) {
  44. form(
  45. marginBottom @= "-10px",
  46. display @= "inline-block",
  47. input(
  48. editInputId,
  49. display @= "inline-block",
  50. `type` := "text",
  51. value := todo.text
  52. ),
  53. button(display @= "inline-block", "Save"),
  54. event("submit") { access =>
  55. access.valueOf(editInputId) flatMap { value =>
  56. access.transition { s =>
  57. val updatedTodo = s.todos(i).copy(text = value)
  58. val updatedTodos = s.todos.updated(i, updatedTodo)
  59. s.copy(todos = updatedTodos, edit = None)
  60. }
  61. }
  62. }
  63. )
  64. } else {
  65. span(
  66. if (todo.done) textDecoration @= "line-through" else void,
  67. todo.text,
  68. event("dblclick") { access =>
  69. access.transition(_.copy(edit = Some(i)))
  70. }
  71. )
  72. }
  73. )
  74. }
  75. ),
  76. form(
  77. // Generate AddTodo action when Add' button clicked
  78. event("submit") { access =>
  79. val prop = access.property(inputId)
  80. prop.get("value") flatMap { value =>
  81. prop.set("value", "") flatMap { _ =>
  82. val todo = State.Todo(value, done = false)
  83. access.transition(s => s.copy(todos = s.todos :+ todo))
  84. }
  85. }
  86. },
  87. input(
  88. if (state.edit.nonEmpty) disabled else void,
  89. inputId,
  90. `type` := "text",
  91. placeholder := "What should be done?"
  92. ),
  93. button(
  94. if (state.edit.nonEmpty) disabled else void,
  95. "Add todo"
  96. )
  97. )
  98. )
  99. )
  100. }
  101. )
  102. private val route = akkaHttpService(config).apply(AkkaHttpServerConfig())
  103. Http().bindAndHandle(route, "0.0.0.0", 8080)
  104. }
  105. case class State(
  106. todos: Vector[State.Todo] = (0 to 9).toVector.map(i => State.Todo(s"This is TODO #$i", done = false)),
  107. edit: Option[Int] = None
  108. )
  109. object State {
  110. case class Todo(text: String, done: Boolean)
  111. }