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 {
|
||||
type = "Dispatcher"
|
||||
executor = "akka.dispatch.gui.JavaFXEventThreadExecutorServiceConfigurator"
|
||||
executor = "nova.monadic_sfx.executors.JavaFXEventThreadExecutorServiceConfigurator"
|
||||
throughput = 1
|
||||
}
|
@ -1,60 +1,22 @@
|
||||
package nova.monadic_sfx
|
||||
|
||||
import scalafx.application.JFXApp
|
||||
import scalafx.application.JFXApp.PrimaryStage
|
||||
import monix.eval.Task
|
||||
import monix.execution.Scheduler
|
||||
// import sttp.client.asynchttpclient.monix.AsyncHttpClientMonixBackend
|
||||
// import sttp.client._
|
||||
// import sttp.client.circe._
|
||||
// import io.circe.generic.auto._
|
||||
import scala.util.Failure
|
||||
import scala.util.Success
|
||||
import akka.{actor => classic}
|
||||
import nova.monadic_sfx.executors._
|
||||
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 scalafx.Includes._
|
||||
import scala.concurrent.Await
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
import scalafx.application.Platform
|
||||
import io.odin.syntax._
|
||||
// import io.odin._
|
||||
import io.odin.monix._
|
||||
import nova.monadic_sfx.pages.HomePage
|
||||
import monix.execution.Callback
|
||||
import monix.eval.TaskApp
|
||||
import cats.effect.ExitCode
|
||||
import scalafx.scene.layout.HBox
|
||||
import scalafx.scene.Scene
|
||||
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] = {
|
||||
// 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
|
||||
// .monotonic(scala.concurrent.duration.MILLISECONDS)
|
||||
// .map(Duration.fromNanos(_))
|
||||
@ -62,108 +24,13 @@ object Main extends TaskApp {
|
||||
// clock <- Resource.liftF(Task(Task.clock))
|
||||
logger <- consoleLogger().withAsync()
|
||||
backend <- AsyncHttpClientMonixBackend.resource()
|
||||
actorSystem <-
|
||||
Resource.make(logger.info("Creating Actor System") >> Task {
|
||||
classic.ActorSystem(
|
||||
name = "FXActorSystem"
|
||||
)
|
||||
})(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()))
|
||||
actorSystem <- actorResource(logger)
|
||||
reqs <- Resource.liftF(Task(requesters(backend, actorSystem)))
|
||||
schedulers <- Resource.liftF(Task(new Schedulers()))
|
||||
fxApp <- fxAppResource(logger, backend, actorSystem, reqs, schedulers)
|
||||
} yield (fxApp)
|
||||
// >> logger.info("test")
|
||||
appResource
|
||||
.use(fxApp => Task(fxApp.main(args.toArray)))
|
||||
.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
|
||||
|
||||
// First we wrap invokeLater/runLater as an ExecutorService
|
||||
abstract class GUIExecutorService extends AbstractExecutorService {
|
||||
trait GUIExecutorService extends AbstractExecutorService {
|
||||
def execute(command: Runnable): 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 io.circe.generic.auto._
|
||||
import nova.monadic_sfx.models._
|
||||
import cats.data.EitherT
|
||||
|
||||
class DummyRequest(backend: HttpBackend) extends AppTypes {
|
||||
private implicit val _backend = backend
|
||||
def send() = {
|
||||
Task
|
||||
.suspend(
|
||||
(for {
|
||||
.suspend {
|
||||
for {
|
||||
req <-
|
||||
basicRequest
|
||||
.get(uri"https://httpbin.org/get")
|
||||
.response(asJson[HttpBinResponse])
|
||||
.send()
|
||||
} yield println(req)) >>
|
||||
Task(println(Thread.currentThread().getName()))
|
||||
)
|
||||
} yield (req)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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