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.
 
 
 

375 lines
11 KiB

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
// }
// }