diff --git a/src/main/scala/nova/monadic_sfx/Main.scala b/src/main/scala/nova/monadic_sfx/Main.scala index 39c6f02..b3b50c8 100644 --- a/src/main/scala/nova/monadic_sfx/Main.scala +++ b/src/main/scala/nova/monadic_sfx/Main.scala @@ -12,8 +12,8 @@ import com.softwaremill.macwire._ import io.odin._ import io.odin.syntax._ import nova.monadic_sfx.executors._ -import nova.monadic_sfx.util.IOUtils._ -import sttp.client.httpclient.monix.HttpClientMonixBackend +// import nova.monadic_sfx.util.IOUtils._ +// import sttp.client.httpclient.monix.HttpClientMonixBackend object Main extends MainModule with BIOApp { def appResource(startTime: Long) = diff --git a/src/main/scala/nova/monadic_sfx/MainApp.scala b/src/main/scala/nova/monadic_sfx/MainApp.scala index 4b2eedb..4b03f8d 100644 --- a/src/main/scala/nova/monadic_sfx/MainApp.scala +++ b/src/main/scala/nova/monadic_sfx/MainApp.scala @@ -7,6 +7,7 @@ import nova.monadic_sfx.executors.Schedulers import nova.monadic_sfx.implicits.JFXButton import nova.monadic_sfx.implicits.JavaFXMonixObservables._ import nova.monadic_sfx.ui.MyFxApp +import nova.monadic_sfx.ui.components.todo.TodoListStore import nova.monadic_sfx.ui.components.todo.TodoListView import org.gerweck.scalafx.util._ import scalafx.Includes._ @@ -21,7 +22,6 @@ import scalafx.scene.control.TableView import scalafx.scene.layout.HBox import scalafx.scene.paint.Color import scalafx.scene.shape.Rectangle -import nova.monadic_sfx.ui.components.todo.TodoListStore class MainApp( // spawnProtocol: ActorSystem[SpawnProtocol.Command], @@ -131,8 +131,8 @@ class MainApp( def createTodoComponent: Task[Unit] = for { store <- TodoListStore(logger) - lv <- TodoListView(store) - _ <- Task(_scene.getChildren += lv) + rootNode <- TodoListView(store) + _ <- Task(_scene.getChildren += rootNode) } yield () } diff --git a/src/main/scala/nova/monadic_sfx/implicits/ActionObservable.scala b/src/main/scala/nova/monadic_sfx/implicits/ActionObservable.scala index cb33d3b..ced90c7 100644 --- a/src/main/scala/nova/monadic_sfx/implicits/ActionObservable.scala +++ b/src/main/scala/nova/monadic_sfx/implicits/ActionObservable.scala @@ -1,13 +1,13 @@ package nova.monadic_sfx.implicits +import monix.execution.Scheduler import monix.reactive.Observable import monix.reactive.Observer import monix.{eval => me} -import monix.execution.Scheduler -class ActionObservableExecuter[T]( - delegate: Observable[T] -) { +class ActionObservableExecutor[T]( + private val delegate: Observable[T] +) extends AnyVal { def -->(sub: Observer[T])(implicit s: Scheduler) = delegate .doOnNext(el => me.Task(sub.onNext(el))) @@ -15,23 +15,69 @@ class ActionObservableExecuter[T]( } class ActionObservableBuilder[A]( - observableAction: Observable[A] -) { - def useLazy[T](v: => T) = - new ActionObservableExecuter[T](observableAction.mapEval(_ => me.Task(v))) + private val observableAction: Observable[A] +) extends AnyVal { + def useLazyEval[T](v: => me.Task[T]) = + new ActionObservableExecutor[T](observableAction.mapEval(_ => v)) - def use[T](cb: A => T) = - new ActionObservableExecuter[T]( - observableAction.mapEval(ae => me.Task(cb(ae))) + def useEval[T](cb: A => me.Task[T]) = + new ActionObservableExecutor[T]( + observableAction.mapEval(cb) ) - def useIterable[T](cb: A => collection.immutable.Iterable[T]) = - new ActionObservableExecuter[T]( + def useIterableEval[T](cb: A => collection.immutable.Iterable[T]) = + new ActionObservableExecutor[T]( observableAction.flatMap(a => Observable.suspend(Observable.fromIterable(cb(a))) ) ) - def map[B](cb: A => B) = - new ActionObservableBuilder(observableAction.mapEval(v => me.Task(cb(v)))) + def doOnNext(cb: A => me.Task[Unit]): ActionObservableBuilder[A] = + new ActionObservableBuilder(observableAction.doOnNext(cb)) + + def mapEval[B](cb: A => me.Task[B]) = + new ActionObservableBuilder(observableAction.mapEval(cb)) + + def underlying = observableAction + + // def publish[B](f: Observable[A] => Observable[B]) = + // new ActionObservableBuilder(observableAction.publishSelector(f)) + + def useEval2[B, C](f: A => me.Task[B], g: A => me.Task[C]) = + new ActionObservableExecutor[(B, C)]( + observableAction.publishSelector(conn => + conn + .mapEval(f) + .switchMap(b => + conn.mapEval(a => + for { + c <- g(a) + } yield (b, c) + ) + ) + ) + ) + + def bifurcate[B, C]( + f: ActionObservableBuilder[A] => B, + g: ActionObservableBuilder[A] => C + )(implicit s: Scheduler) = + observableAction + .publishSelector(conn => + Observable( + Observable.unit.doOnNext(_ => + me.Task(f(new ActionObservableBuilder[A](conn))) >> me.Task.unit + ), + Observable.unit.doOnNext(_ => + me.Task(g(new ActionObservableBuilder[A](conn))) >> me.Task.unit + ) + ).merge + ) + .subscribe() + + // def useEval2[B,C](a1: ActionObservableBuilder[A] => B, a2: ActionObservableBuilder[A] => C) = observableAction.publishSelector(conn => + // new ActionObservableBuilder[] + // ) } + +// class MappedActionExecutor(actionMap: Map[]) diff --git a/src/main/scala/nova/monadic_sfx/implicits/JFXButton.scala b/src/main/scala/nova/monadic_sfx/implicits/JFXButton.scala index fc6d562..0f42006 100644 --- a/src/main/scala/nova/monadic_sfx/implicits/JFXButton.scala +++ b/src/main/scala/nova/monadic_sfx/implicits/JFXButton.scala @@ -14,9 +14,6 @@ object JFXButton { if (v != null) v.delegate else null } -// extends ButtonBase(delegate) -// with SFXDelegate[jfoenixc.JFXButton] - class JFXButton( override val delegate: jfoenixc.JFXButton = new jfoenixc.JFXButton ) extends Button(delegate) { diff --git a/src/main/scala/nova/monadic_sfx/implicits/JavaFxMonixObservables.scala b/src/main/scala/nova/monadic_sfx/implicits/JavaFxMonixObservables.scala index dc72d82..02b8adc 100644 --- a/src/main/scala/nova/monadic_sfx/implicits/JavaFxMonixObservables.scala +++ b/src/main/scala/nova/monadic_sfx/implicits/JavaFxMonixObservables.scala @@ -2,6 +2,8 @@ package nova.monadic_sfx.implicits import javafx.beans.property.ObjectProperty import javafx.collections.ObservableList +import javafx.event.ActionEvent +import javafx.event.EventHandler import javafx.scene.{input => jfxsi} import javafx.{event => jfxe} import monix.bio.Task @@ -13,18 +15,15 @@ import monix.reactive.Observer import monix.reactive.OverflowStrategy import monix.tail.Iterant import monix.{eval => me} +import org.gerweck.scalafx.util._ import scalafx.Includes._ import scalafx.beans.property.Property +import scalafx.beans.property.ReadOnlyProperty import scalafx.beans.value.ObservableValue import scalafx.collections.ObservableBuffer import scalafx.scene.Scene import scalafx.scene.control.ButtonBase -import scalafx.beans.property.ReadOnlyProperty -import javafx.event.ActionEvent -import javafx.event.EventHandler -import javafx.scene.{control => jfxsc} import scalafx.scene.control.MenuItem -import org.gerweck.scalafx.util._ object JavaFXMonixObservables { @@ -80,8 +79,8 @@ object JavaFXMonixObservables { op.onNext(prop.value) } - def -->(op: Property[T, J]) = { - op() = prop.value + def ==>(op: Property[T, J]) = { + op <== prop } def <--(obs: Observable[T])(implicit s: Scheduler) = { @@ -90,13 +89,12 @@ object JavaFXMonixObservables { def asOption = prop.map(Option(_)) - def observableChange[J1 >: J]() - : Observable[(ObservableValue[T, J], J1, J1)] = { + def observableChange[J1 >: J](): Observable[J1] = { import monix.execution.cancelables.SingleAssignCancelable Observable.create(OverflowStrategy.Unbounded) { sub => val c = SingleAssignCancelable() - val canc = prop.onChange((a, b, c) => sub.onNext((a, b, c))) + val canc = prop.onChange((a, b, c) => sub.onNext(c)) c := Cancelable(() => canc.cancel()) c @@ -107,18 +105,9 @@ object JavaFXMonixObservables { implicit final class BindObs2[A](private val prop: ObjectProperty[A]) extends AnyVal { - // def -->(sub: Var[A]) = - // prop.onChange((a, b, c) => sub := c) - def -->(sub: Observer[A]) = prop.onChange((a, b, c) => if (c != null) sub.onNext(c)) - // def -->[J1 >: A, T]( - // op: Observable[J1] => me.Task[T] - // )(implicit s: Scheduler) = { - // op(prop.observableChange().map(_._3)).runToFuture - // } - def -->(op: Property[A, A]) = { prop.onChange((a, b, c) => if (c != null) op() = c) } @@ -147,21 +136,16 @@ object JavaFXMonixObservables { op.onNext(prop.value) } - def -->(op: Property[T, J]) = { + def ==>(op: Property[T, J]) = { op <== prop } - // def <--(obs: Observable[T])(implicit s: Scheduler) = { - // obs.doOnNext(v => me.Task(prop.value = v)).subscribe() - // } - - def observableChange[J1 >: J]() - : Observable[(ObservableValue[T, J], J1, J1)] = { + def observableChange[J1 >: J](): Observable[J1] = { import monix.execution.cancelables.SingleAssignCancelable Observable.create(OverflowStrategy.Unbounded) { sub => val c = SingleAssignCancelable() - val canc = prop.onChange((a, b, c) => sub.onNext((a, b, c))) + val canc = prop.onChange((a, b, c) => sub.onNext(c)) c := Cancelable(() => canc.cancel()) c @@ -227,17 +211,6 @@ object JavaFXMonixObservables { implicit final class OnActionObservable( private val button: ButtonBase ) extends AnyVal { - // def -->[T]( - // op: Observable[jfxe.ActionEvent] => me.Task[T] - // )(implicit s: Scheduler) = { - // op(button.observableAction()).runToFuture - // } - - // def -->( - // sub: ConcurrentSubject[jfxe.ActionEvent, jfxe.ActionEvent] - // ) = { - // button.onAction = value => sub.onNext(value) - // } def observableAction(): Observable[jfxe.ActionEvent] = { import monix.execution.cancelables.SingleAssignCancelable @@ -262,19 +235,8 @@ object JavaFXMonixObservables { } implicit final class MenuItemActionObservable( - private val et: MenuItem + private val item: MenuItem ) extends AnyVal { - // def -->[T]( - // op: Observable[jfxe.ActionEvent] => me.Task[T] - // )(implicit s: Scheduler) = { - // op(button.observableAction()).runToFuture - // } - - // def -->( - // sub: ConcurrentSubject[jfxe.ActionEvent, jfxe.ActionEvent] - // ) = { - // button.onAction = value => sub.onNext(value) - // } def observableAction(): Observable[jfxe.ActionEvent] = { import monix.execution.cancelables.SingleAssignCancelable @@ -286,9 +248,9 @@ object JavaFXMonixObservables { } } - et.onAction = l + item.onAction = l c := Cancelable(() => - et.removeEventHandler( + item.removeEventHandler( jfxe.ActionEvent.ACTION, l ) diff --git a/src/main/scala/nova/monadic_sfx/implicits/MenuItem.scala b/src/main/scala/nova/monadic_sfx/implicits/MenuItem.scala index 839c65d..577ce75 100644 --- a/src/main/scala/nova/monadic_sfx/implicits/MenuItem.scala +++ b/src/main/scala/nova/monadic_sfx/implicits/MenuItem.scala @@ -1,8 +1,9 @@ package nova.monadic_sfx.implicits -import JavaFXMonixObservables._ import scalafx.scene.{control => sfxc} +import JavaFXMonixObservables._ + class MenuItem extends sfxc.MenuItem { def obsAction = new ActionObservableBuilder(this.observableAction()) diff --git a/src/main/scala/nova/monadic_sfx/ui/components/todo/TodoListComponentOld.scala b/src/main/scala/nova/monadic_sfx/ui/components/todo/TodoListComponentOld.scala index ab5667f..7b7f2e6 100644 --- a/src/main/scala/nova/monadic_sfx/ui/components/todo/TodoListComponentOld.scala +++ b/src/main/scala/nova/monadic_sfx/ui/components/todo/TodoListComponentOld.scala @@ -14,6 +14,7 @@ import nova.monadic_sfx.implicits.FontIcon import nova.monadic_sfx.implicits.IconLiteral import nova.monadic_sfx.implicits.JFXListView import nova.monadic_sfx.implicits.JavaFXMonixObservables._ +import nova.monadic_sfx.util.reactive._ import scalafx.Includes._ import scalafx.beans.property.StringProperty import scalafx.collections.ObservableBuffer @@ -23,7 +24,6 @@ import scalafx.scene.control.MenuItem import scalafx.scene.control.SelectionMode import scalafx.scene.layout.HBox import scalafx.scene.text.Text -import nova.monadic_sfx.util.reactive._ class TodoListViewOld( val listView: JFXListView[Todo] = TodoListViewOld.defaultListView, diff --git a/src/main/scala/nova/monadic_sfx/ui/components/todo/TodoListStore.scala b/src/main/scala/nova/monadic_sfx/ui/components/todo/TodoListStore.scala index 23b9675..b424e1f 100644 --- a/src/main/scala/nova/monadic_sfx/ui/components/todo/TodoListStore.scala +++ b/src/main/scala/nova/monadic_sfx/ui/components/todo/TodoListStore.scala @@ -1,14 +1,12 @@ package nova.monadic_sfx.ui.components.todo -import nova.monadic_sfx.util.reactive.Store -import nova.monadic_sfx.util.reactive.Reducer -import nova.monadic_sfx.util.reactive.Middlewares.actionLoggerMiddleware +import com.softwaremill.quicklens._ +import io.circe.generic.JsonCodec import io.odin.Logger import monix.bio.Task -import io.odin._ -import io.odin.syntax._ -import io.circe.generic.JsonCodec -import com.softwaremill.quicklens._ +import nova.monadic_sfx.util.reactive.Middlewares.actionLoggerMiddleware +import nova.monadic_sfx.util.reactive.Reducer +import nova.monadic_sfx.util.reactive.Store case class Todo(id: Int, content: String) @@ -34,28 +32,15 @@ object TodoListStore { todos = state.todos :+ Todo(state.counter, content), counter = state.counter + 1 ) - case Edit(id, content) => + case Edit(_id, content) => + val condition: Todo => Boolean = _.id == _id state - .modify(_.todos.eachWhere(_.id == id)) + .modify(_.todos.eachWhere(condition)) .using(_.copy(content = content)) case Delete(id) => state.copy(state.todos.filterNot(_.id == id)) } - def updateTodo(id: Int, content: String, todos: Vector[Todo]) = - todos.view.zipWithIndex - .find { - case (todo, index) => todo.id == id - } - .map { - case (todo, index) => todo.copy(content = content) -> index - } - .map { - case (todo, index) => todos.updated(index, todo) - } - - val middlewareLogger = consoleLogger[Task]().withAsync() - def apply(logger: Logger[Task]) = Store .createL[Command, State]( diff --git a/src/main/scala/nova/monadic_sfx/ui/controller/TodoController.scala b/src/main/scala/nova/monadic_sfx/ui/controller/TodoController.scala index 7caaf9b..70cd159 100644 --- a/src/main/scala/nova/monadic_sfx/ui/controller/TodoController.scala +++ b/src/main/scala/nova/monadic_sfx/ui/controller/TodoController.scala @@ -12,11 +12,11 @@ import nova.monadic_sfx.implicits.JFXButton import nova.monadic_sfx.implicits.JFXListView import nova.monadic_sfx.implicits.JFXTextArea import nova.monadic_sfx.implicits.JFXTextField +import nova.monadic_sfx.ui.components.todo.TodoListComponentOld import scalafx.collections.ObservableBuffer import scalafx.scene.control.Label import scalafx.scene.layout.HBox import scalafx.scene.paint.Color -import nova.monadic_sfx.ui.components.todo.TodoListComponentOld class TodoController(todoListComponent: TodoListComponentOld) { import AnimFX._ diff --git a/src/main/scala/nova/monadic_sfx/util/reactive/Middlewares.scala b/src/main/scala/nova/monadic_sfx/util/reactive/Middlewares.scala index 6c1a482..0df6a30 100644 --- a/src/main/scala/nova/monadic_sfx/util/reactive/Middlewares.scala +++ b/src/main/scala/nova/monadic_sfx/util/reactive/Middlewares.scala @@ -1,8 +1,8 @@ package nova.monadic_sfx.util.reactive import io.odin.Logger -import monix.reactive.Observable import monix.bio.Task +import monix.reactive.Observable import nova.monadic_sfx.util.IOUtils._ // object Middleware { diff --git a/src/main/scala/nova/monadic_sfx/util/reactive/MonixProSubject.scala b/src/main/scala/nova/monadic_sfx/util/reactive/MonixProSubject.scala index 6627235..4f47370 100644 --- a/src/main/scala/nova/monadic_sfx/util/reactive/MonixProSubject.scala +++ b/src/main/scala/nova/monadic_sfx/util/reactive/MonixProSubject.scala @@ -1,11 +1,12 @@ package nova.monadic_sfx.util.reactive +import scala.concurrent.Future + +import monix.execution.Ack import monix.execution.Cancelable import monix.reactive.Observable import monix.reactive.Observer -import scala.concurrent.Future import monix.reactive.observers.Subscriber -import monix.execution.Ack object MonixProSubject { def from[I, O]( diff --git a/src/main/scala/nova/monadic_sfx/util/reactive/Reducer.scala b/src/main/scala/nova/monadic_sfx/util/reactive/Reducer.scala index 73eb18c..8a78a73 100644 --- a/src/main/scala/nova/monadic_sfx/util/reactive/Reducer.scala +++ b/src/main/scala/nova/monadic_sfx/util/reactive/Reducer.scala @@ -1,8 +1,8 @@ package nova.monadic_sfx.util.reactive -import monix.reactive.ObservableLike import cats.implicits._ import monix.reactive.Observable +import monix.reactive.ObservableLike object Reducer { diff --git a/src/main/scala/nova/monadic_sfx/util/reactive/Store.scala b/src/main/scala/nova/monadic_sfx/util/reactive/Store.scala index 1a34b04..963a576 100644 --- a/src/main/scala/nova/monadic_sfx/util/reactive/Store.scala +++ b/src/main/scala/nova/monadic_sfx/util/reactive/Store.scala @@ -1,11 +1,11 @@ package nova.monadic_sfx.util.reactive +import cats.effect.Sync +import monix.bio.Task +import monix.execution.Scheduler +import monix.reactive.Observable import monix.reactive.OverflowStrategy import monix.reactive.subjects.ConcurrentSubject -import monix.reactive.Observable -import monix.bio.Task -import cats.effect.Sync -import monix.execution.Scheduler object Store { def createL[A, M](