|
|
package nova.monadic_sfx.ui.components.todo
import scala.concurrent.duration.FiniteDuration
import cats.effect.concurrent.Deferred import io.odin.Logger import monix.bio.Task import monix.catnap.ConcurrentChannel import monix.catnap.ConsumerF import monix.execution.Scheduler import monix.reactive.Observer import nova.monadic_sfx.util.controls.FontIcon import nova.monadic_sfx.util.controls.IconLiteral import nova.monadic_sfx.util.controls.JFXListView import nova.monadic_sfx.util.reactive.store._ import scalafx.Includes._ import scalafx.beans.property.StringProperty import scalafx.collections.ObservableBuffer import scalafx.scene.control.ContextMenu import scalafx.scene.control.ListCell import scalafx.scene.control.MenuItem import scalafx.scene.control.SelectionMode import scalafx.scene.layout.HBox import scalafx.scene.text.Text
class TodoListViewOld( val listView: JFXListView[Todo] = TodoListViewOld.defaultListView, val lvObs: ObservableBuffer[Todo] = ObservableBuffer.empty ) { listView.items = lvObs }
object TodoListViewOld { def defaultListView = new JFXListView[Todo] { contextMenu = new ContextMenu { items ++= Seq( new MenuItem { text = "delete" }, new MenuItem { text = "edit" } ) } }
implicit class Operations[A](val sink: Observer[A]) extends AnyVal {}
def defaultListView2( store: MonixProSubject[ TodoListComponentOld.Command, (TodoListComponentOld.Command, Vector[Todo]) ] ): Task[JFXListView[Todo]] = Task.deferAction(implicit s => Task { val todos = store.map { case (_, items) => items }
val listView = new JFXListView[Todo] { lv => def selectedItems = lv.selectionModel().selectedItems.view // items = todos
// items <-- todos
// .map(ObservableBuffer.from(_))
cellFactory = _ => new ListCell[Todo] { val _text = StringProperty("") val _graphic = new HBox { children = Seq( new FontIcon { iconSize = 10 iconLiteral = IconLiteral.Gmi10k }, new Text { text <== _text } ) } item.onChange((_, _, todo) => { println("called") if (todo != null) { _text() = s"${todo.id} - ${todo.content}" graphic = _graphic } else { _text() = "" graphic = null } })
} selectionModel().selectionMode = SelectionMode.Multiple contextMenu = new ContextMenu { items ++= Seq( new MenuItem { text = "Add" onAction = _ => store.sink .onNext(TodoListComponentOld.Add(Todo(1, "blah3"))) }, new MenuItem { text = "Delete" // onAction = _ =>
// for {
// items <- Option(lv.selectionModel().selectedItems)
// _ <- Some(items.foreach(item => deleteSub.onNext(item)))
// } yield ()
onAction = _ => selectedItems .map(todo => TodoListComponentOld.Delete(todo.id)) .foreach(store.sink.onNext) }, new MenuItem { text = "Edit" // onAction = _ =>
// Option(lv.selectionModel().selectedItems).foreach(items =>
// items.foreach(item => editSub.onNext(item))
// )
} ) } }
listView } )
}
private[todo] class TodoListComponentImpure( todoListView: TodoListViewOld ) { def add(todo: Todo) = todoListView.lvObs += todo def find(id: Int) = todoListView.lvObs.find(_.id == id) def edit(id: Int, content: String) = find(id) .map(todo => todoListView.lvObs.replaceAll( todo, Todo(id, content) ) ) .getOrElse(false) }
class TodoListOps private ( props: TodoListOps.Props ) { import props._ // lazy val internal = new TodoListComponentImpure(todoListView)
// def add(todo: Todo) = Task(internal.add(todo))
def add(todo: Todo) = Task(todoListView.lvObs += todo).executeOn(fxScheduler) def find(id: Int) = Task(todoListView.lvObs.find(_.id == id)).executeOn(fxScheduler) def delete(id: Int) = (for { mbTodo <- find(id) _ <- logger.debug(mbTodo.toString()) res <- Task( mbTodo.map(todo => todoListView.lvObs.removeAll(todo)) ) _ <- logger.debug(todoListView.lvObs.toString()) } yield res.getOrElse(false)).executeOn(fxScheduler) def edit(id: Int, content: String) = (for { mbTodo <- find(id) res <- Task( mbTodo.map(todo => todoListView.lvObs.replaceAll( todo, Todo(id, content) ) ) ) } yield res.getOrElse(false)).executeOn(fxScheduler) }
object TodoListOps { class Props( val todoListView: TodoListViewOld, val fxScheduler: Scheduler, val logger: Logger[Task] ) { def create = Task(new TodoListOps(this)) } }
object TodoListComponentOld { sealed trait Complete object Complete extends Complete
sealed trait Command // sealed trait Tell extends Command
// sealed abstract class Ask extends Command
case class Add(todo: Todo) extends Command case class Find(id: Int, result: Deferred[Task, Option[Todo]]) extends Command case class Edit(id: Int, content: String) extends Command case class Delete(id: Int) extends Command // private case class FindInternal(id: Int, result: Deferred[Task, Todo])
// extends Ask
def reducer( state: Vector[Todo], action: TodoListComponentOld.Command ) = action match { case Add(todo) => state :+ todo // case Find(id, result) =>
case Edit(id, content) => state case Delete(id) => state.filterNot(_.id == id) case _ => state }
// val store =
// Store
// .createL[TodoListComponentOld.Command, Vector[Todo]](
// TodoListComponentOld.Delete(0),
// Vector.empty[Todo],
// (s: Vector[Todo], a: TodoListComponentOld.Command) =>
// reducer(s, a) -> Observable.empty
// )
class Props( val todoListView: TodoListViewOld, val fxScheduler: Scheduler, val channel: ConcurrentChannel[ Task, TodoListComponentOld.Complete, TodoListComponentOld.Command ], val logger: Logger[Task] ) {
def create = for { todoListOps <- new TodoListOps.Props(todoListView, fxScheduler, logger).create consumer = channel.consume.use(ref => todoConsumer(ref, todoListOps)) _ <- consumer.startAndForget } yield (new TodoListComponentOld(this))
private def todoConsumer( consumer: ConsumerF[Task, Complete, Command], ops: TodoListOps ): Task[Unit] = consumer.pull .flatMap { case Left(complete) => logger.info("Received `Complete` event") case Right(command) => logger.debug(s"Received command $command") >> (command match { // case t: Tell =>
// t match {
// case Add(todo) => ops.add(todo)
// case _ => Task.unit
// }
case Add(todo) => ops.add(todo) // case Find(id) =>
// for {
// p <- Deferred[Task, Todo]
// _ <- channel.push(FindInternal(id, p))
// res <- p.get
// } yield (res)
case Find(id, result) => for { mbTodo <- ops.find(id) } yield result.complete(mbTodo) // case _ => Task.unit
case Delete(id) => ops.delete(id)
case Edit(id, content) => ops.edit(id, content) }) } .flatMap(_ => todoConsumer(consumer, ops))
}
}
class TodoListComponentOld(props: TodoListComponentOld.Props) { import props._ import TodoListComponentOld._
def send(command: Command) = channel.push(command)
def ask[T]( commandBuilder: Deferred[Task, T] => Command )(implicit timeout: FiniteDuration) = for { p <- Deferred[Task, T] _ <- channel.push(commandBuilder(p)) res <- p.get.timeout(timeout) } yield res
def stop = channel.halt(Complete)
// import scala.concurrent.duration._
// val x = ask(FindInternal(0, _))(2.seconds)
}
// object TodoListComponent {
// sealed trait Complete
// object Complete extends Complete
// sealed trait Command
// class Props(
// val todoListView: TodoListView,
// val fxScheduler: Scheduler,
// val channel: ConcurrentChannel[
// Task,
// TodoListComponent.Complete,
// TodoListComponent.Command
// ]
// ) {
// def create = Task(new TodoListComponent(this))
// }
// }
// class TodoListComponent(props: TodoListComponent.Props) {
// import props._
// import TodoListComponent._
// def init =
// for {
// todoListOps <- new TodoListOps.Props(todoListView, fxScheduler).create
// consumer = channel.consume.use(ref => todoConsumer(ref, todoListOps))
// _ <- consumer.startAndForget
// } yield ()
// def send(command: Command) = channel.push(command)
// def todoConsumer(
// consumer: ConsumerF[Task, Complete, Command],
// ops: TodoListOps
// ) =
// consumer.pull.flatMap {
// case Left(value) => Task.unit
// case Right(value) => Task.unit
// }
// }
// def askHandler(
// channel: ConcurrentChannel[
// Task,
// TodoListComponent.Complete,
// TodoListComponent.Command
// ],
// consumer: ConsumerF[Task, Complete, Command],
// ops: TodoListOps
// ) =
// consumer.pull.flatMap {
// case Left(complete) => Task.unit
// case Right(command) =>
// command match {
// case a: Ask =>
// a match {
// case Find(id) =>
// for {
// p <- Deferred[Task, Todo]
// _ <- channel.push(FindInternal(id, p))
// res <- p.get
// } yield (res)
// case FindInternal(id, result) =>
// for {
// mb <- ops.find(id)
// } yield result.complete(mb.get)
// case _ => Task.unit
// }
// case _ => Task.unit
// }
// }
|