Modularized code
Moved all resource creation code to modules (thin cake pattern) Cleanup Added test actor running on fx thread
This commit is contained in:
parent
02c4e2f2f3
commit
b6823aebfa
@ -1,5 +1,5 @@
|
|||||||
javafx-dispatcher {
|
javafx-dispatcher {
|
||||||
type = "Dispatcher"
|
type = "Dispatcher"
|
||||||
executor = "akka.dispatch.gui.JavaFXEventThreadExecutorServiceConfigurator"
|
executor = "nova.monadic_sfx.executors.JavaFXEventThreadExecutorServiceConfigurator"
|
||||||
throughput = 1
|
throughput = 1
|
||||||
}
|
}
|
@ -1,60 +1,22 @@
|
|||||||
package nova.monadic_sfx
|
package nova.monadic_sfx
|
||||||
|
|
||||||
import scalafx.application.JFXApp
|
|
||||||
import scalafx.application.JFXApp.PrimaryStage
|
|
||||||
import monix.eval.Task
|
import monix.eval.Task
|
||||||
import monix.execution.Scheduler
|
|
||||||
// import sttp.client.asynchttpclient.monix.AsyncHttpClientMonixBackend
|
// import sttp.client.asynchttpclient.monix.AsyncHttpClientMonixBackend
|
||||||
// import sttp.client._
|
// import sttp.client._
|
||||||
// import sttp.client.circe._
|
// import sttp.client.circe._
|
||||||
// import io.circe.generic.auto._
|
// import io.circe.generic.auto._
|
||||||
import scala.util.Failure
|
|
||||||
import scala.util.Success
|
|
||||||
import akka.{actor => classic}
|
|
||||||
import nova.monadic_sfx.executors._
|
import nova.monadic_sfx.executors._
|
||||||
import cats.effect.Resource
|
import cats.effect.Resource
|
||||||
import nova.monadic_sfx.models._
|
|
||||||
import nova.monadic_sfx.ui.DefaultUI
|
|
||||||
import nova.monadic_sfx.http.Backend
|
|
||||||
import nova.monadic_sfx.modules.MainModule
|
|
||||||
import scalafx.stage.Stage
|
|
||||||
import scalafx.scene.layout.FlowPane
|
|
||||||
import nova.monadic_sfx.pages.LoginPage
|
|
||||||
import scala.concurrent.duration._
|
|
||||||
import sttp.client.asynchttpclient.monix.AsyncHttpClientMonixBackend
|
import sttp.client.asynchttpclient.monix.AsyncHttpClientMonixBackend
|
||||||
import scalafx.Includes._
|
|
||||||
import scala.concurrent.Await
|
|
||||||
import akka.actor.typed.scaladsl.adapter._
|
|
||||||
import scalafx.application.Platform
|
|
||||||
import io.odin.syntax._
|
import io.odin.syntax._
|
||||||
// import io.odin._
|
|
||||||
import io.odin.monix._
|
import io.odin.monix._
|
||||||
import nova.monadic_sfx.pages.HomePage
|
|
||||||
import monix.execution.Callback
|
|
||||||
import monix.eval.TaskApp
|
import monix.eval.TaskApp
|
||||||
import cats.effect.ExitCode
|
import cats.effect.ExitCode
|
||||||
import scalafx.scene.layout.HBox
|
|
||||||
import scalafx.scene.Scene
|
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import cats.effect.Clock
|
|
||||||
import cats.effect.Sync
|
|
||||||
|
|
||||||
object Main extends TaskApp {
|
object Main extends MainModule with TaskApp {
|
||||||
|
|
||||||
// lazy val schedulers: Schedulers = new Schedulers()
|
|
||||||
// override implicit def scheduler: Scheduler = schedulers.fx
|
|
||||||
override def run(args: List[String]): Task[ExitCode] = {
|
override def run(args: List[String]): Task[ExitCode] = {
|
||||||
// val logger = consoleLogger().withAsyncUnsafe()
|
|
||||||
|
|
||||||
// lazy val backendTask = AsyncHttpClientMonixBackend()
|
|
||||||
// lazy val actorSystemTask = Task {
|
|
||||||
// classic.ActorSystem(
|
|
||||||
// name = "FXActorSystem"
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
// implicit lazy val FSync = Sync[Task]
|
|
||||||
// implicit lazy val FClock = Clock[Task]
|
|
||||||
// val startTime = Task.clock
|
// val startTime = Task.clock
|
||||||
// .monotonic(scala.concurrent.duration.MILLISECONDS)
|
// .monotonic(scala.concurrent.duration.MILLISECONDS)
|
||||||
// .map(Duration.fromNanos(_))
|
// .map(Duration.fromNanos(_))
|
||||||
@ -62,108 +24,13 @@ object Main extends TaskApp {
|
|||||||
// clock <- Resource.liftF(Task(Task.clock))
|
// clock <- Resource.liftF(Task(Task.clock))
|
||||||
logger <- consoleLogger().withAsync()
|
logger <- consoleLogger().withAsync()
|
||||||
backend <- AsyncHttpClientMonixBackend.resource()
|
backend <- AsyncHttpClientMonixBackend.resource()
|
||||||
actorSystem <-
|
actorSystem <- actorResource(logger)
|
||||||
Resource.make(logger.info("Creating Actor System") >> Task {
|
reqs <- Resource.liftF(Task(requesters(backend, actorSystem)))
|
||||||
classic.ActorSystem(
|
schedulers <- Resource.liftF(Task(new Schedulers()))
|
||||||
name = "FXActorSystem"
|
fxApp <- fxAppResource(logger, backend, actorSystem, reqs, schedulers)
|
||||||
)
|
|
||||||
})(sys =>
|
|
||||||
logger.info("Shutting down actor system") >> Task.fromFuture(
|
|
||||||
sys.terminate()
|
|
||||||
) >> logger.info("Actor System terminated")
|
|
||||||
)
|
|
||||||
// _ <- Resource.liftF(logger.info(Thread.currentThread().getName()))
|
|
||||||
fxApp <- Resource.make(logger.info("Creating FX Application") >> Task {
|
|
||||||
// val appStage = makePrimaryStage(backend, actorSystem)
|
|
||||||
// stage = appStage
|
|
||||||
// val stage2 = new PrimaryStage {
|
|
||||||
// scene = new Scene(new HBox())
|
|
||||||
// }
|
|
||||||
val app: JFXApp = new JFXApp {
|
|
||||||
lazy val schedulers: Schedulers = new Schedulers()
|
|
||||||
|
|
||||||
implicit lazy val defaultScheduler: Scheduler = schedulers.fx
|
|
||||||
|
|
||||||
val application =
|
|
||||||
for {
|
|
||||||
appStage <- logger.info("Inside JFX application stage") >> Task(
|
|
||||||
makePrimaryStage(backend, actorSystem)
|
|
||||||
)
|
|
||||||
_ <- Task { stage = appStage }
|
|
||||||
_ <- Task.sleep(2.seconds)
|
|
||||||
loginScene <- LoginPage(appStage, backend, actorSystem)
|
|
||||||
_ <- Task {
|
|
||||||
// appStage.maximized = true
|
|
||||||
appStage.height = 800
|
|
||||||
appStage.width = 800
|
|
||||||
appStage
|
|
||||||
.scene()
|
|
||||||
.setRoot(
|
|
||||||
loginScene
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} yield ()
|
|
||||||
application.timed.runAsync(
|
|
||||||
new Callback[Throwable, (FiniteDuration, Unit)] {
|
|
||||||
|
|
||||||
override def onSuccess(value: (FiniteDuration, Unit)): Unit = {
|
|
||||||
val (duration, _) = value
|
|
||||||
println(
|
|
||||||
s"Application started successfully in ${duration.toSeconds} seconds"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override def onError(e: Throwable): Unit = {
|
|
||||||
println("Application start failed. Reason -")
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
override def stopApp() = {
|
|
||||||
Platform.exit()
|
|
||||||
// System.exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
app
|
|
||||||
})(app => logger.info("Stopping FX Application") >> Task(app.stopApp()))
|
|
||||||
// _ <- Resource.liftF(Task.unit.executeOn(defaultScheduler))
|
|
||||||
_ <- Resource.liftF(logger.info(Thread.currentThread().getName()))
|
|
||||||
} yield (fxApp)
|
} yield (fxApp)
|
||||||
// >> logger.info("test")
|
|
||||||
appResource
|
appResource
|
||||||
.use(fxApp => Task(fxApp.main(args.toArray)))
|
.use(fxApp => Task(fxApp.main(args.toArray)))
|
||||||
.as(ExitCode.Success)
|
.as(ExitCode.Success)
|
||||||
}
|
}
|
||||||
|
|
||||||
def makePrimaryStage(
|
|
||||||
backend: AppTypes.HttpBackend,
|
|
||||||
actorSystem: classic.ActorSystem
|
|
||||||
) = {
|
|
||||||
new PrimaryStage {
|
|
||||||
scene = new DefaultUI().scene
|
|
||||||
// onCloseRequest = () => {
|
|
||||||
// val f2 = actorSystem.terminate()
|
|
||||||
// val f1 = backend.close().runToFuture
|
|
||||||
|
|
||||||
// println("Closing backend")
|
|
||||||
// Await.result(f1, 3.seconds)
|
|
||||||
// println("Closing actor system")
|
|
||||||
// println(Thread.currentThread().getName())
|
|
||||||
// Await.result(f2, 3.seconds)
|
|
||||||
// println("Actor system closed")
|
|
||||||
// Platform.exit()
|
|
||||||
// System.exit(0)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// class MyFxApp extends javafx.application.Application {
|
|
||||||
|
|
||||||
// override def start(stage: javafx.stage.Stage): Unit = {
|
|
||||||
// stage.show()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
7
src/main/scala/nova/monadic_sfx/MainModule.scala
Normal file
7
src/main/scala/nova/monadic_sfx/MainModule.scala
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package nova.monadic_sfx
|
||||||
|
|
||||||
|
import nova.monadic_sfx.actors.ActorModule
|
||||||
|
import nova.monadic_sfx.ui.UiModule
|
||||||
|
import nova.monadic_sfx.http.HttpModule
|
||||||
|
|
||||||
|
trait MainModule extends ActorModule with UiModule with HttpModule
|
49
src/main/scala/nova/monadic_sfx/actors/ActorModule.scala
Normal file
49
src/main/scala/nova/monadic_sfx/actors/ActorModule.scala
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package nova.monadic_sfx.actors
|
||||||
|
|
||||||
|
import io.odin.Logger
|
||||||
|
import monix.eval.Task
|
||||||
|
import cats.effect.Resource
|
||||||
|
import akka.actor._
|
||||||
|
import akka.actor.typed.scaladsl.adapter._
|
||||||
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
|
import com.softwaremill.macwire._
|
||||||
|
import akka.actor.typed.Behavior
|
||||||
|
import akka.actor.typed.DispatcherSelector
|
||||||
|
|
||||||
|
trait ActorModule {
|
||||||
|
def actorResource(logger: Logger[Task]): Resource[Task, ActorSystem] =
|
||||||
|
Resource.make(logger.info("Creating Actor System") >> Task {
|
||||||
|
ActorSystem(
|
||||||
|
name = "FXActorSystem"
|
||||||
|
)
|
||||||
|
})(sys =>
|
||||||
|
logger.info("Shutting down actor system") >> Task.fromFuture(
|
||||||
|
sys.terminate()
|
||||||
|
) >> logger.info("Actor System terminated")
|
||||||
|
)
|
||||||
|
|
||||||
|
def testActor(
|
||||||
|
system: ActorSystem
|
||||||
|
): akka.actor.typed.ActorRef[Counter.Command] = {
|
||||||
|
val behaviour: Behavior[Counter.Command] =
|
||||||
|
Behaviors.setup(context => wire[Counter])
|
||||||
|
system.spawn(
|
||||||
|
behaviour,
|
||||||
|
"CounterActor",
|
||||||
|
DispatcherSelector.fromConfig("javafx-dispatcher")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def testActorL(
|
||||||
|
system: ActorSystem
|
||||||
|
): Task[akka.actor.typed.ActorRef[Counter.Command]] =
|
||||||
|
Task {
|
||||||
|
val behaviour: Behavior[Counter.Command] =
|
||||||
|
Behaviors.setup(context => wire[Counter])
|
||||||
|
system.spawn(
|
||||||
|
behaviour,
|
||||||
|
"CounterActor",
|
||||||
|
DispatcherSelector.fromConfig("javafx-dispatcher")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
34
src/main/scala/nova/monadic_sfx/actors/TestActor.scala
Normal file
34
src/main/scala/nova/monadic_sfx/actors/TestActor.scala
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package nova.monadic_sfx.actors
|
||||||
|
|
||||||
|
import akka.actor.typed._
|
||||||
|
import akka.actor.typed.scaladsl._
|
||||||
|
|
||||||
|
object Counter {
|
||||||
|
sealed trait Command
|
||||||
|
case object Increment extends Command
|
||||||
|
final case class GetValue(replyTo: ActorRef[Value]) extends Command
|
||||||
|
final case class Value(n: Int)
|
||||||
|
|
||||||
|
def apply(): Behavior[Command] = {
|
||||||
|
Behaviors.setup(context => new Counter(context))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Counter(context: ActorContext[Counter.Command])
|
||||||
|
extends AbstractBehavior[Counter.Command](context) {
|
||||||
|
import Counter._
|
||||||
|
|
||||||
|
private var n = 0
|
||||||
|
|
||||||
|
override def onMessage(msg: Command): Behavior[Counter.Command] = {
|
||||||
|
msg match {
|
||||||
|
case Increment =>
|
||||||
|
n += 1
|
||||||
|
context.log.debug("Incremented counter to [{}]", n)
|
||||||
|
this
|
||||||
|
case GetValue(replyTo) =>
|
||||||
|
replyTo ! Value(n)
|
||||||
|
Behaviors.stopped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package nova.monadic_sfx.executors
|
||||||
|
|
||||||
|
trait ExecutorsModule {
|
||||||
|
lazy val schedulers = new Schedulers()
|
||||||
|
}
|
@ -20,7 +20,7 @@ import scala.concurrent.ExecutionContext
|
|||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
|
|
||||||
// First we wrap invokeLater/runLater as an ExecutorService
|
// First we wrap invokeLater/runLater as an ExecutorService
|
||||||
abstract class GUIExecutorService extends AbstractExecutorService {
|
trait GUIExecutorService extends AbstractExecutorService {
|
||||||
def execute(command: Runnable): Unit
|
def execute(command: Runnable): Unit
|
||||||
|
|
||||||
def shutdown(): Unit = ()
|
def shutdown(): Unit = ()
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
package nova.monadic_sfx.http
|
|
||||||
|
|
||||||
import sttp.client.asynchttpclient.monix.AsyncHttpClientMonixBackend
|
|
||||||
import nova.monadic_sfx.AppTypes
|
|
||||||
|
|
||||||
object Backend {
|
|
||||||
val backend = AsyncHttpClientMonixBackend
|
|
||||||
.resource()
|
|
||||||
def apply() = { backend }
|
|
||||||
}
|
|
28
src/main/scala/nova/monadic_sfx/http/HttpModule.scala
Normal file
28
src/main/scala/nova/monadic_sfx/http/HttpModule.scala
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package nova.monadic_sfx.http
|
||||||
|
|
||||||
|
import nova.monadic_sfx.http.requests.DummyRequest
|
||||||
|
import nova.monadic_sfx.AppTypes
|
||||||
|
|
||||||
|
trait HttpModule {
|
||||||
|
def requesters(
|
||||||
|
backend: AppTypes.HttpBackend,
|
||||||
|
system: akka.actor.ActorSystem
|
||||||
|
): Requesters = {
|
||||||
|
import com.softwaremill.macwire._
|
||||||
|
val dummyRequester = wire[DummyRequest]
|
||||||
|
wire[Requesters]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Requesters(val dummyRequester: DummyRequest)
|
||||||
|
|
||||||
|
// object Requesters {
|
||||||
|
// def apply(
|
||||||
|
// backend: AppTypes.HttpBackend,
|
||||||
|
// system: akka.actor.ActorSystem
|
||||||
|
// ): Requesters = {
|
||||||
|
// import com.softwaremill.macwire._
|
||||||
|
// val dummyRequester = wire[DummyRequest]
|
||||||
|
// wire[Requesters]
|
||||||
|
// }
|
||||||
|
// }
|
@ -8,20 +8,36 @@ import sttp.client._
|
|||||||
import sttp.client.circe._
|
import sttp.client.circe._
|
||||||
import io.circe.generic.auto._
|
import io.circe.generic.auto._
|
||||||
import nova.monadic_sfx.models._
|
import nova.monadic_sfx.models._
|
||||||
|
import cats.data.EitherT
|
||||||
|
|
||||||
class DummyRequest(backend: HttpBackend) extends AppTypes {
|
class DummyRequest(backend: HttpBackend) extends AppTypes {
|
||||||
private implicit val _backend = backend
|
private implicit val _backend = backend
|
||||||
def send() = {
|
def send() = {
|
||||||
Task
|
Task
|
||||||
.suspend(
|
.suspend {
|
||||||
(for {
|
for {
|
||||||
req <-
|
req <-
|
||||||
basicRequest
|
basicRequest
|
||||||
.get(uri"https://httpbin.org/get")
|
.get(uri"https://httpbin.org/get")
|
||||||
.response(asJson[HttpBinResponse])
|
.response(asJson[HttpBinResponse])
|
||||||
.send()
|
.send()
|
||||||
} yield println(req)) >>
|
} yield (req)
|
||||||
Task(println(Thread.currentThread().getName()))
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def test() = {
|
||||||
|
for {
|
||||||
|
res <- send()
|
||||||
|
res3 <- Task { res.body }
|
||||||
|
res2 <- Task {
|
||||||
|
res3.fold(
|
||||||
|
err => {
|
||||||
|
err.toString()
|
||||||
|
},
|
||||||
|
value => value.toString()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} yield (res3)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
package nova.monadic_sfx.modules
|
|
||||||
|
|
||||||
import nova.monadic_sfx.executors.Schedulers
|
|
||||||
import monix.execution.Scheduler
|
|
||||||
import nova.monadic_sfx.http.requests.DummyRequest
|
|
||||||
import monix.eval.Task
|
|
||||||
import akka.{actor => classic}
|
|
||||||
import cats.effect.Resource
|
|
||||||
import nova.monadic_sfx.AppTypes
|
|
||||||
|
|
||||||
trait MainModule {
|
|
||||||
import com.softwaremill.macwire._
|
|
||||||
// val schedulers: Schedulers = new Schedulers()
|
|
||||||
|
|
||||||
// implicit val defaultScheduler: Scheduler = schedulers.fx
|
|
||||||
|
|
||||||
// val program =
|
|
||||||
// for {
|
|
||||||
// backend <- Backend()
|
|
||||||
// fxActorSystem <-
|
|
||||||
// Resource
|
|
||||||
// .make(Task {
|
|
||||||
// classic.ActorSystem(
|
|
||||||
// name = "FXActorSystem"
|
|
||||||
// )
|
|
||||||
// })(sys => Task(sys.terminate()))
|
|
||||||
|
|
||||||
// } yield {
|
|
||||||
// val dummyRequester = wire[DummyRequest]
|
|
||||||
// dummyRequester.send()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// val program = Backend().use(backend =>
|
|
||||||
// Resource
|
|
||||||
// .make(Task {
|
|
||||||
// classic.ActorSystem(
|
|
||||||
// name = "FXActorSystem"
|
|
||||||
// )
|
|
||||||
// })(sys => Task(sys.terminate()))
|
|
||||||
// .use { implicit system =>
|
|
||||||
// // system.spa
|
|
||||||
// // system.typed
|
|
||||||
// // val javaFxActor = system.actorOf(
|
|
||||||
// // Props[JavaFxActor]().withDispatcher("javafx-dispatcher"),
|
|
||||||
// // "javaFxActor"
|
|
||||||
// // )
|
|
||||||
// // val swingActor = system.actorOf(
|
|
||||||
// // Props[SwingActor]().withDispatcher("swing-dispatcher"),
|
|
||||||
// // "swingActor"
|
|
||||||
// // )
|
|
||||||
// Task(new DummyRequest(backend)) >>
|
|
||||||
// Task.unit
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
def schedulers: Schedulers
|
|
||||||
|
|
||||||
def defaultScheduler: Scheduler
|
|
||||||
|
|
||||||
def backendTask: Task[AppTypes.HttpBackend]
|
|
||||||
def actorSystemTask: Task[classic.ActorSystem]
|
|
||||||
|
|
||||||
def deps =
|
|
||||||
for {
|
|
||||||
backend <- backendTask
|
|
||||||
actorSystem <- actorSystemTask
|
|
||||||
dummyRequesterTask <- Task {
|
|
||||||
wireDeps(backend, actorSystem)
|
|
||||||
}
|
|
||||||
} yield dummyRequesterTask
|
|
||||||
|
|
||||||
def wireDeps(
|
|
||||||
backend: AppTypes.HttpBackend,
|
|
||||||
system: classic.ActorSystem
|
|
||||||
): DummyRequest = {
|
|
||||||
wire[DummyRequest]
|
|
||||||
// new DummyRequest(backend)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package nova.monadic_sfx.pages
|
|
||||||
import nova.monadic_sfx.AppTypes
|
|
||||||
import scalafx.scene.control.TextField
|
|
||||||
import scalafx.scene.control._
|
|
||||||
import scalafx.scene.layout.VBox
|
|
||||||
import scalafx.scene.Node
|
|
||||||
import scalafx.Includes._
|
|
||||||
import scalafx.scene.layout.HBox
|
|
||||||
import scalafx.scene.text.Text
|
|
||||||
import scalafx.scene.Parent
|
|
||||||
import scalafx.application.JFXApp.PrimaryStage
|
|
||||||
|
|
||||||
class HomePage(
|
|
||||||
backend: AppTypes.HttpBackend,
|
|
||||||
system: akka.actor.ActorSystem,
|
|
||||||
onLogout: () => Unit
|
|
||||||
) {
|
|
||||||
private lazy val root = new HBox {
|
|
||||||
children = List(
|
|
||||||
new Text {
|
|
||||||
text = "hello"
|
|
||||||
},
|
|
||||||
new Button {
|
|
||||||
text = "logout"
|
|
||||||
onAction = () => onLogout()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
def render = root
|
|
||||||
}
|
|
||||||
|
|
||||||
object HomePage {
|
|
||||||
def apply(
|
|
||||||
backend: AppTypes.HttpBackend,
|
|
||||||
system: akka.actor.ActorSystem,
|
|
||||||
onLogout: () => Unit
|
|
||||||
): Parent =
|
|
||||||
new HomePage(backend, system, onLogout).render
|
|
||||||
}
|
|
@ -1,102 +0,0 @@
|
|||||||
package nova.monadic_sfx.pages
|
|
||||||
import nova.monadic_sfx.AppTypes
|
|
||||||
import scalafx.scene.control.TextField
|
|
||||||
import scalafx.scene.control._
|
|
||||||
import scalafx.scene.layout.VBox
|
|
||||||
import scalafx.scene.Node
|
|
||||||
import scalafx.Includes._
|
|
||||||
import scalafx.scene.Parent
|
|
||||||
import scalafx.application.JFXApp.PrimaryStage
|
|
||||||
import nova.monadic_sfx.http.requests.DummyRequest
|
|
||||||
import monix.eval.Task
|
|
||||||
import monix.execution.Scheduler
|
|
||||||
// import io.odin.syntax._
|
|
||||||
// import _root_.monix.eval.Task
|
|
||||||
// import io.odin.monix._
|
|
||||||
// import javafx.beans.property.ObjectProperty
|
|
||||||
// import javafx.event.{ActionEvent, EventHandler}
|
|
||||||
class LoginPage(
|
|
||||||
appStage: PrimaryStage,
|
|
||||||
backend: AppTypes.HttpBackend,
|
|
||||||
system: akka.actor.ActorSystem
|
|
||||||
) {
|
|
||||||
|
|
||||||
val dummyRequester = new DummyRequest(backend)
|
|
||||||
|
|
||||||
//pure function callbacks, but with side effects still
|
|
||||||
private def onLogout(stage: PrimaryStage) =
|
|
||||||
for {
|
|
||||||
_ <- Task { println("logging out") }
|
|
||||||
root <- render
|
|
||||||
_ <- Task(stage.scene().setRoot(root))
|
|
||||||
} yield ()
|
|
||||||
|
|
||||||
private def onLogin(stage: PrimaryStage) =
|
|
||||||
Task.deferAction { implicit Scheduler =>
|
|
||||||
for {
|
|
||||||
_ <- Task(println("logging in"))
|
|
||||||
root <- Task {
|
|
||||||
stage
|
|
||||||
.scene()
|
|
||||||
.setRoot(
|
|
||||||
HomePage(
|
|
||||||
backend,
|
|
||||||
system,
|
|
||||||
() => runFxTask(onLogout(appStage))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} yield ()
|
|
||||||
}
|
|
||||||
|
|
||||||
private lazy val root = Task.deferAction(implicit scheduler =>
|
|
||||||
Task {
|
|
||||||
new VBox {
|
|
||||||
children = Seq(
|
|
||||||
new Label {
|
|
||||||
text = "username"
|
|
||||||
},
|
|
||||||
new TextField(),
|
|
||||||
new Label {
|
|
||||||
text = "password"
|
|
||||||
},
|
|
||||||
new TextField(),
|
|
||||||
new Button {
|
|
||||||
text = "Login"
|
|
||||||
onAction = () =>
|
|
||||||
runFxTask {
|
|
||||||
Task
|
|
||||||
.parZip2(
|
|
||||||
dummyRequester
|
|
||||||
.send(),
|
|
||||||
// .executeOn(Scheduler.global)
|
|
||||||
onLogin(appStage)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def render: Task[Parent] = root
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implicitly runs monix task as fire and forget. \
|
|
||||||
* For use in ScalaFX callbacks.
|
|
||||||
*
|
|
||||||
* @param task
|
|
||||||
* @param s
|
|
||||||
*/
|
|
||||||
def runFxTask[T](task: => Task[T])(implicit s: Scheduler) = {
|
|
||||||
task.runAsyncAndForget
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
object LoginPage {
|
|
||||||
def apply(
|
|
||||||
appStage: PrimaryStage,
|
|
||||||
backend: AppTypes.HttpBackend,
|
|
||||||
system: akka.actor.ActorSystem
|
|
||||||
) = new LoginPage(appStage, backend, system).render
|
|
||||||
}
|
|
90
src/main/scala/nova/monadic_sfx/ui/MyFxApp.scala
Normal file
90
src/main/scala/nova/monadic_sfx/ui/MyFxApp.scala
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package nova.monadic_sfx.ui
|
||||||
|
|
||||||
|
import scalafx.application.JFXApp
|
||||||
|
import nova.monadic_sfx.executors.Schedulers
|
||||||
|
import monix.execution.Scheduler
|
||||||
|
import monix.eval.Task
|
||||||
|
import nova.monadic_sfx.screens.LoginScreen
|
||||||
|
import nova.monadic_sfx.AppTypes
|
||||||
|
import scalafx.application.Platform
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
import io.odin.Logger
|
||||||
|
import monix.execution.Callback
|
||||||
|
import com.softwaremill.macwire._
|
||||||
|
import nova.monadic_sfx.http.Requesters
|
||||||
|
|
||||||
|
import akka.actor._
|
||||||
|
import akka.actor.typed.Behavior
|
||||||
|
import akka.actor.typed.scaladsl.adapter._
|
||||||
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
|
import nova.monadic_sfx.actors.Counter
|
||||||
|
import akka.actor.typed.DispatcherSelector
|
||||||
|
|
||||||
|
class MyFxApp(
|
||||||
|
logger: Logger[Task],
|
||||||
|
backend: AppTypes.HttpBackend,
|
||||||
|
actorSystem: akka.actor.ActorSystem,
|
||||||
|
requesters: Requesters,
|
||||||
|
schedulers: Schedulers
|
||||||
|
) extends JFXApp {
|
||||||
|
|
||||||
|
implicit lazy val defaultScheduler: Scheduler = schedulers.fx
|
||||||
|
|
||||||
|
lazy val application =
|
||||||
|
for {
|
||||||
|
appStage <- Task(
|
||||||
|
UiModule.makePrimaryStage(backend, actorSystem)
|
||||||
|
)
|
||||||
|
// _ <- Task {
|
||||||
|
// val counterActor = testActor(actorSystem)
|
||||||
|
// counterActor ! (Counter.Increment)
|
||||||
|
// }
|
||||||
|
_ <- Task { stage = appStage }
|
||||||
|
_ <- Task.sleep(2.seconds)
|
||||||
|
loginScene <- wire[LoginScreen].render
|
||||||
|
_ <- Task {
|
||||||
|
// appStage.maximized = true
|
||||||
|
appStage.height = 800
|
||||||
|
appStage.width = 800
|
||||||
|
appStage
|
||||||
|
.scene()
|
||||||
|
.setRoot(
|
||||||
|
loginScene
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} yield ()
|
||||||
|
|
||||||
|
def testActor(
|
||||||
|
system: ActorSystem
|
||||||
|
): akka.actor.typed.ActorRef[Counter.Command] = {
|
||||||
|
val behaviour: Behavior[Counter.Command] =
|
||||||
|
Behaviors.setup(context => wire[Counter])
|
||||||
|
system.spawn(
|
||||||
|
behaviour,
|
||||||
|
"CounterActor",
|
||||||
|
DispatcherSelector.fromConfig("javafx-dispatcher")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
application.timed.runAsync(
|
||||||
|
new Callback[Throwable, (FiniteDuration, Unit)] {
|
||||||
|
|
||||||
|
override def onSuccess(value: (FiniteDuration, Unit)): Unit = {
|
||||||
|
val (duration, _) = value
|
||||||
|
println(
|
||||||
|
s"Application started successfully in ${duration.toSeconds} seconds"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def onError(e: Throwable): Unit = {
|
||||||
|
println("Application start failed. Reason -")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
override def stopApp() = {
|
||||||
|
Platform.exit()
|
||||||
|
}
|
||||||
|
}
|
37
src/main/scala/nova/monadic_sfx/ui/UiModule.scala
Normal file
37
src/main/scala/nova/monadic_sfx/ui/UiModule.scala
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package nova.monadic_sfx.ui
|
||||||
|
|
||||||
|
import scalafx.application.JFXApp
|
||||||
|
import monix.eval.Task
|
||||||
|
import nova.monadic_sfx.AppTypes
|
||||||
|
import scalafx.application.JFXApp.PrimaryStage
|
||||||
|
import io.odin.Logger
|
||||||
|
import cats.effect.Resource
|
||||||
|
import com.softwaremill.macwire._
|
||||||
|
import nova.monadic_sfx.http.Requesters
|
||||||
|
import nova.monadic_sfx.executors.Schedulers
|
||||||
|
|
||||||
|
trait UiModule {
|
||||||
|
def fxAppResource(
|
||||||
|
logger: Logger[Task],
|
||||||
|
backend: AppTypes.HttpBackend,
|
||||||
|
actorSystem: akka.actor.ActorSystem,
|
||||||
|
requesters: Requesters,
|
||||||
|
schedulers: Schedulers
|
||||||
|
): Resource[Task, JFXApp] =
|
||||||
|
Resource.make(logger.info("Creating FX Application") >> Task {
|
||||||
|
val app: JFXApp = wire[MyFxApp]
|
||||||
|
app
|
||||||
|
})(app => logger.info("Stopping FX Application") >> Task(app.stopApp()))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object UiModule {
|
||||||
|
def makePrimaryStage(
|
||||||
|
backend: AppTypes.HttpBackend,
|
||||||
|
actorSystem: akka.actor.ActorSystem
|
||||||
|
) = {
|
||||||
|
new PrimaryStage {
|
||||||
|
scene = new DefaultUI().scene
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
src/main/scala/nova/monadic_sfx/ui/screens/HomeScreen.scala
Normal file
45
src/main/scala/nova/monadic_sfx/ui/screens/HomeScreen.scala
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package nova.monadic_sfx.screens
|
||||||
|
import nova.monadic_sfx.AppTypes
|
||||||
|
import scalafx.scene.control.TextField
|
||||||
|
import scalafx.scene.control._
|
||||||
|
import scalafx.scene.layout.VBox
|
||||||
|
import scalafx.scene.Node
|
||||||
|
import scalafx.Includes._
|
||||||
|
import scalafx.scene.layout.HBox
|
||||||
|
import scalafx.scene.text.Text
|
||||||
|
import scalafx.scene.Parent
|
||||||
|
import scalafx.application.JFXApp.PrimaryStage
|
||||||
|
import monix.eval.Task
|
||||||
|
import nova.monadic_sfx.util.Action
|
||||||
|
|
||||||
|
class HomeScreen(
|
||||||
|
backend: AppTypes.HttpBackend,
|
||||||
|
system: akka.actor.ActorSystem,
|
||||||
|
onLogout: () => Task[Unit]
|
||||||
|
) {
|
||||||
|
private lazy val root = Task.deferAction { implicit s =>
|
||||||
|
Task {
|
||||||
|
new HBox {
|
||||||
|
children = List(
|
||||||
|
new Text {
|
||||||
|
text = "hello"
|
||||||
|
},
|
||||||
|
new Button {
|
||||||
|
text = "logout"
|
||||||
|
onAction = () => Action.asyncT(onLogout())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def render = root
|
||||||
|
}
|
||||||
|
|
||||||
|
object HomeScreen {
|
||||||
|
def apply(
|
||||||
|
backend: AppTypes.HttpBackend,
|
||||||
|
system: akka.actor.ActorSystem,
|
||||||
|
onLogout: () => Task[Unit]
|
||||||
|
): Task[Parent] =
|
||||||
|
new HomeScreen(backend, system, onLogout).render
|
||||||
|
}
|
108
src/main/scala/nova/monadic_sfx/ui/screens/LoginScreen.scala
Normal file
108
src/main/scala/nova/monadic_sfx/ui/screens/LoginScreen.scala
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package nova.monadic_sfx.screens
|
||||||
|
import nova.monadic_sfx.AppTypes
|
||||||
|
import scalafx.scene.control.TextField
|
||||||
|
import scalafx.scene.control._
|
||||||
|
import scalafx.scene.layout.VBox
|
||||||
|
import scalafx.scene.Node
|
||||||
|
import scalafx.Includes._
|
||||||
|
import scalafx.scene.Parent
|
||||||
|
import scalafx.application.JFXApp.PrimaryStage
|
||||||
|
import nova.monadic_sfx.http.requests.DummyRequest
|
||||||
|
import monix.eval.Task
|
||||||
|
import monix.execution.Scheduler
|
||||||
|
import cats.effect.Effect
|
||||||
|
import cats.effect.implicits._
|
||||||
|
import nova.monadic_sfx.util.Action
|
||||||
|
import io.odin.Logger
|
||||||
|
import nova.monadic_sfx.http.Requesters
|
||||||
|
import sttp.client.Response
|
||||||
|
import nova.monadic_sfx.models.HttpBinResponse
|
||||||
|
import sttp.client.ResponseError
|
||||||
|
import nova.monadic_sfx.executors.Schedulers
|
||||||
|
import nova.monadic_sfx.ui.screens.Screen
|
||||||
|
// import io.odin.syntax._
|
||||||
|
// import _root_.monix.eval.Task
|
||||||
|
// import io.odin.monix._
|
||||||
|
// import javafx.beans.property.ObjectProperty
|
||||||
|
// import javafx.event.{ActionEvent, EventHandler}
|
||||||
|
class LoginScreen(
|
||||||
|
override protected val appStage: PrimaryStage,
|
||||||
|
logger: Logger[Task],
|
||||||
|
backend: AppTypes.HttpBackend,
|
||||||
|
system: akka.actor.ActorSystem,
|
||||||
|
requesters: Requesters,
|
||||||
|
schedulers: Schedulers
|
||||||
|
) extends Screen {
|
||||||
|
val dummyRequester: DummyRequest = requesters.dummyRequester
|
||||||
|
//pure function callbacks, but with side effects still
|
||||||
|
|
||||||
|
// lazy val hs = {
|
||||||
|
// import com.softwaremill.macwire._
|
||||||
|
// lazy val action = () => onLogout(appStage)
|
||||||
|
// wire[HomeScreen]
|
||||||
|
// }
|
||||||
|
|
||||||
|
private def onLogout(stage: PrimaryStage) =
|
||||||
|
for {
|
||||||
|
_ <- logger.info("Logging out")
|
||||||
|
root <- render
|
||||||
|
_ <- changeRootL(root)
|
||||||
|
} yield ()
|
||||||
|
|
||||||
|
private def onLogin(stage: PrimaryStage) =
|
||||||
|
for {
|
||||||
|
_ <- logger.info("Logging in")
|
||||||
|
homeScreen <- HomeScreen(
|
||||||
|
backend,
|
||||||
|
system,
|
||||||
|
() => onLogout(appStage)
|
||||||
|
)
|
||||||
|
_ <- Task { stage.scene().setRoot(homeScreen) }
|
||||||
|
} yield ()
|
||||||
|
|
||||||
|
private lazy val root = Task.deferAction(implicit scheduler =>
|
||||||
|
Task {
|
||||||
|
new VBox {
|
||||||
|
children = Seq(
|
||||||
|
new Label {
|
||||||
|
text = "username"
|
||||||
|
},
|
||||||
|
new TextField(),
|
||||||
|
new Label {
|
||||||
|
text = "password"
|
||||||
|
},
|
||||||
|
new TextField(),
|
||||||
|
new Button {
|
||||||
|
text = "Login"
|
||||||
|
onAction = () =>
|
||||||
|
Action.asyncT {
|
||||||
|
Task
|
||||||
|
.parSequence(
|
||||||
|
List(
|
||||||
|
testRequest,
|
||||||
|
// .executeOn(Scheduler.global)
|
||||||
|
onLogin(appStage)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
def render: Task[Parent] = root
|
||||||
|
|
||||||
|
val testRequest = for {
|
||||||
|
res <- dummyRequester.send()
|
||||||
|
_ <- logger.info(res.body.toString())
|
||||||
|
} yield ()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// object LoginScreen {
|
||||||
|
// def apply(
|
||||||
|
// appStage: PrimaryStage,
|
||||||
|
// backend: AppTypes.HttpBackend,
|
||||||
|
// system: akka.actor.ActorSystem
|
||||||
|
// ) = new LoginScreen(appStage, backend, system).render
|
||||||
|
// }
|
13
src/main/scala/nova/monadic_sfx/ui/screens/Screen.scala
Normal file
13
src/main/scala/nova/monadic_sfx/ui/screens/Screen.scala
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package nova.monadic_sfx.ui.screens
|
||||||
|
|
||||||
|
import scalafx.application.JFXApp.PrimaryStage
|
||||||
|
import scalafx.scene.Parent
|
||||||
|
import monix.eval.Task
|
||||||
|
|
||||||
|
trait Screen {
|
||||||
|
protected def appStage: PrimaryStage
|
||||||
|
def changeRoot(root: Parent): Unit = {
|
||||||
|
appStage.scene().setRoot(root)
|
||||||
|
}
|
||||||
|
def changeRootL(root: Parent): Task[Unit] = Task(changeRoot(root))
|
||||||
|
}
|
23
src/main/scala/nova/monadic_sfx/util/Action.scala
Normal file
23
src/main/scala/nova/monadic_sfx/util/Action.scala
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package nova.monadic_sfx.util
|
||||||
|
|
||||||
|
import monix.eval.Task
|
||||||
|
import monix.execution.Scheduler
|
||||||
|
import cats.effect.Effect
|
||||||
|
import cats.effect.implicits._
|
||||||
|
|
||||||
|
object Action {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implicitly runs monix task as fire and forget. \
|
||||||
|
* For use in ScalaFX callbacks.
|
||||||
|
*
|
||||||
|
* @param task
|
||||||
|
* @param s
|
||||||
|
*/
|
||||||
|
def asyncT[T](task: => Task[T])(implicit s: Scheduler): Unit = {
|
||||||
|
task.runAsyncAndForget
|
||||||
|
}
|
||||||
|
|
||||||
|
def asyncF[F[_]: Effect, T](cb: => F[T]): Unit =
|
||||||
|
cb.toIO.unsafeRunAsyncAndForget()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user