First commit
This commit is contained in:
parent
56dac9a58a
commit
e3abe03456
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# sbt specific
|
||||||
|
.cache/
|
||||||
|
.history/
|
||||||
|
.lib/
|
||||||
|
dist/*
|
||||||
|
target/
|
||||||
|
lib_managed/
|
||||||
|
src_managed/
|
||||||
|
project/boot/
|
||||||
|
project/plugins/project/
|
||||||
|
metals.sbt
|
||||||
|
.metals
|
||||||
|
.bloop
|
||||||
|
|
||||||
|
# Scala-IDE specific
|
||||||
|
.scala_dependencies
|
||||||
|
.worksheet
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
.vscode
|
||||||
|
/project/project
|
1
.scalafmt.conf
Normal file
1
.scalafmt.conf
Normal file
@ -0,0 +1 @@
|
|||||||
|
version = "2.6.4"
|
24
UNLICENSE
Normal file
24
UNLICENSE
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
For more information, please refer to <https://unlicense.org>
|
61
build.sbt
Normal file
61
build.sbt
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Name of the project
|
||||||
|
name := "ScalaFX Hello World"
|
||||||
|
|
||||||
|
// Project version
|
||||||
|
version := "14-R19"
|
||||||
|
|
||||||
|
// Version of Scala used by the project
|
||||||
|
scalaVersion := "2.13.3"
|
||||||
|
|
||||||
|
// Add dependency on ScalaFX library
|
||||||
|
libraryDependencies += "org.scalafx" %% "scalafx" % "14-R19"
|
||||||
|
resolvers += Resolver.sonatypeRepo("snapshots")
|
||||||
|
|
||||||
|
enablePlugins(JavaFxPlugin)
|
||||||
|
|
||||||
|
libraryDependencies ++= Seq(
|
||||||
|
"org.typelevel" %% "cats-core" % "2.1.1",
|
||||||
|
"org.typelevel" %% "cats-effect" % "2.1.4",
|
||||||
|
"io.monix" %% "monix" % "3.2.2",
|
||||||
|
"io.monix" %% "monix-bio" % "1.0.0",
|
||||||
|
"io.circe" %% "circe-core" % "0.13.0",
|
||||||
|
"io.circe" %% "circe-generic" % "0.13.0",
|
||||||
|
"com.softwaremill.sttp.client" %% "core" % "2.2.5",
|
||||||
|
"com.softwaremill.sttp.client" %% "monix" % "2.2.5",
|
||||||
|
"com.softwaremill.sttp.client" %% "circe" % "2.2.5",
|
||||||
|
"com.softwaremill.sttp.client" %% "async-http-client-backend-monix" % "2.2.5",
|
||||||
|
"com.github.valskalla" %% "odin-monix" % "0.8.1",
|
||||||
|
"com.typesafe.akka" %% "akka-actor-typed" % "2.6.8",
|
||||||
|
"com.softwaremill.macwire" %% "util" % "2.3.7",
|
||||||
|
"com.softwaremill.macwire" %% "macros" % "2.3.6" % "provided",
|
||||||
|
"com.softwaremill.macwire" %% "macrosakka" % "2.3.6" % "provided",
|
||||||
|
"com.github.valskalla" %% "odin-slf4j" % "0.8.1"
|
||||||
|
)
|
||||||
|
|
||||||
|
scalacOptions ++= Seq(
|
||||||
|
"-unchecked",
|
||||||
|
"-deprecation",
|
||||||
|
"-Xcheckinit",
|
||||||
|
"-encoding",
|
||||||
|
"utf8",
|
||||||
|
"-feature",
|
||||||
|
"-Ywarn-unused:imports"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fork a new JVM for 'run' and 'test:run', to avoid JavaFX double initialization problems
|
||||||
|
fork := true
|
||||||
|
|
||||||
|
// Determine OS version of JavaFX binaries
|
||||||
|
lazy val osName = System.getProperty("os.name") match {
|
||||||
|
case n if n.startsWith("Linux") => "linux"
|
||||||
|
case n if n.startsWith("Mac") => "mac"
|
||||||
|
case n if n.startsWith("Windows") => "win"
|
||||||
|
case _ => throw new Exception("Unknown platform!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add JavaFX dependencies
|
||||||
|
lazy val javaFXModules =
|
||||||
|
Seq("base", "controls", "fxml", "graphics", "media", "swing", "web")
|
||||||
|
libraryDependencies ++= javaFXModules.map(m =>
|
||||||
|
"org.openjfx" % s"javafx-$m" % "14.0.1" classifier osName
|
||||||
|
)
|
2
project/build.properties
Normal file
2
project/build.properties
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
sbt.version=1.3.10
|
||||||
|
|
6
project/plugin.sbt
Normal file
6
project/plugin.sbt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
scalacOptions ++= Seq("-unchecked", "-deprecation")
|
||||||
|
|
||||||
|
// [https://github.com/sbt/sbteclipse]
|
||||||
|
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.4")
|
||||||
|
// addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.10")
|
||||||
|
addSbtPlugin("com.quadstingray" % "sbt-javafx" % "1.5.2")
|
5
src/main/resources/application.conf
Normal file
5
src/main/resources/application.conf
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
javafx-dispatcher {
|
||||||
|
type = "Dispatcher"
|
||||||
|
executor = "akka.dispatch.gui.JavaFXEventThreadExecutorServiceConfigurator"
|
||||||
|
throughput = 1
|
||||||
|
}
|
219
src/main/scala/nova/monadic_sfx/SFXActors.scala
Normal file
219
src/main/scala/nova/monadic_sfx/SFXActors.scala
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
object ScalaFXHelloWorld extends JFXApp with MainModule {
|
||||||
|
|
||||||
|
val logger = consoleLogger().withAsyncUnsafe()
|
||||||
|
|
||||||
|
lazy val schedulers: Schedulers = new Schedulers()
|
||||||
|
|
||||||
|
implicit lazy val defaultScheduler: Scheduler = schedulers.fx
|
||||||
|
|
||||||
|
lazy val backendTask = AsyncHttpClientMonixBackend()
|
||||||
|
lazy val actorSystemTask = Task {
|
||||||
|
classic.ActorSystem(
|
||||||
|
name = "FXActorSystem"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy val application = for {
|
||||||
|
_ <- logger.info("Starting application..")
|
||||||
|
backend <- backendTask
|
||||||
|
actorSystem <- actorSystemTask
|
||||||
|
// to spawn child actors
|
||||||
|
// _ <- Task { actorSystem.spawn() }
|
||||||
|
appStage <- Task { makePrimaryStage(backend, actorSystem) }
|
||||||
|
// splash screen
|
||||||
|
_ <- Task {
|
||||||
|
// this stage refers to implicit jfx stage
|
||||||
|
// makes this impure, but I can't think of a better way right now
|
||||||
|
stage = appStage
|
||||||
|
}
|
||||||
|
// wait 2 seconds before showing home screen
|
||||||
|
d <- deps
|
||||||
|
fib1 <- d.send().start
|
||||||
|
_ <- Task.sleep(2.seconds)
|
||||||
|
_ <- Task {
|
||||||
|
// appStage.maximized = true
|
||||||
|
appStage.height = 800
|
||||||
|
appStage.width = 800
|
||||||
|
appStage
|
||||||
|
.scene()
|
||||||
|
.setRoot(
|
||||||
|
LoginPage(appStage, backend, actorSystem)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// _ <- fib1.join
|
||||||
|
} yield ()
|
||||||
|
application.runToFuture
|
||||||
|
.onComplete(res =>
|
||||||
|
res match {
|
||||||
|
case Failure(exception) => {
|
||||||
|
println("Application start failed. Reason -")
|
||||||
|
exception.printStackTrace()
|
||||||
|
}
|
||||||
|
case Success(value) => println("Application started Successfully")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Task
|
||||||
|
// .suspend {
|
||||||
|
// val program = Task {
|
||||||
|
// stage = new PrimaryStage {
|
||||||
|
// // initStyle(StageStyle.Unified)
|
||||||
|
// title = "ScalaFX Hello World"
|
||||||
|
// scene = defaultUI.scene
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// val backendResource = AsyncHttpClientMonixBackend
|
||||||
|
// .resource()
|
||||||
|
// .use { implicit backend =>
|
||||||
|
// Task
|
||||||
|
// .suspend(
|
||||||
|
// (for {
|
||||||
|
// req <-
|
||||||
|
// basicRequest
|
||||||
|
// .get(uri"https://httpbin.org/get")
|
||||||
|
// .response(asJson[HttpBinResponse])
|
||||||
|
// .send()
|
||||||
|
// } yield println(req)) >>
|
||||||
|
// Task(println(Thread.currentThread().getName()))
|
||||||
|
// )
|
||||||
|
// // .delayExecution(1.second)
|
||||||
|
// }
|
||||||
|
// .executeOn(Scheduler.global)
|
||||||
|
// val akkaResource = Resource
|
||||||
|
// .make(Task {
|
||||||
|
// classic.ActorSystem(
|
||||||
|
// name = "FXActorSystem"
|
||||||
|
// )
|
||||||
|
// })(sys => Task(println("Shutting down actor system")) >> 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.unit
|
||||||
|
// }
|
||||||
|
// .delayExecution(1.second)
|
||||||
|
// backendResource.start >> akkaResource.start >>
|
||||||
|
// program.to[Task].asyncBoundary >>
|
||||||
|
// Task(println(Thread.currentThread().getName()))
|
||||||
|
// .executeOn(Scheduler.global)
|
||||||
|
// Task.parZip3(
|
||||||
|
// program.to[Task].executeOn(defaultScheduler),
|
||||||
|
// // backendResource,
|
||||||
|
// // dummyRequester.send(),
|
||||||
|
// // akkaResource,
|
||||||
|
// Task(println(Thread.currentThread().getName()))
|
||||||
|
// .executeOn(schedulers.cpu)
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// .runToFuture
|
||||||
|
// .onComplete(res =>
|
||||||
|
// res match {
|
||||||
|
// case Failure(exception) => {
|
||||||
|
// println("Application start failed. Reason -")
|
||||||
|
// exception.printStackTrace()
|
||||||
|
// }
|
||||||
|
// case Success(value) => println("Application started Successfully")
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
|
||||||
|
// new TaskApp {
|
||||||
|
// override protected def scheduler: Scheduler =
|
||||||
|
// JFXExecutionContexts.javaFxScheduler
|
||||||
|
// override def run(args: List[String]): Task[ExitCode] =
|
||||||
|
// Task.suspend {
|
||||||
|
// Task {
|
||||||
|
// AsyncHttpClientMonixBackend().flatMap(implicit backend => {
|
||||||
|
// val req = RequestPayload("").asJson
|
||||||
|
// basicRequest.get(uri"").body(req).send()
|
||||||
|
// })
|
||||||
|
// } >>
|
||||||
|
// program.to[Task].executeOn(JFXExecutionContexts.javaFxScheduler) >>
|
||||||
|
// Task(println(Thread.currentThread().getName()))
|
||||||
|
// .executeOn(Scheduler.global) >>
|
||||||
|
// // Task.unit.asyncBoundary >>
|
||||||
|
// Task.pure(ExitCode.Success)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Task.sleep(3.seconds).flatMap { _ =>
|
||||||
|
//
|
||||||
|
// (for {
|
||||||
|
// req <-
|
||||||
|
// basicRequest
|
||||||
|
// .get(uri"https://httpbin.org/get")
|
||||||
|
// .response(asJson[HttpBinResponse])
|
||||||
|
// .send()
|
||||||
|
// } yield println(req)) >> Task(
|
||||||
|
// println(Thread.currentThread().getName())
|
||||||
|
// ) >>
|
||||||
|
// backend.close()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
def test(stage: Stage) = {
|
||||||
|
stage.scene().setRoot(new FlowPane())
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
src/main/scala/nova/monadic_sfx/Types.scala
Normal file
13
src/main/scala/nova/monadic_sfx/Types.scala
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package nova.monadic_sfx
|
||||||
|
|
||||||
|
import monix.eval.Task
|
||||||
|
import sttp.client.SttpBackend
|
||||||
|
import monix.reactive.Observable
|
||||||
|
import sttp.client.asynchttpclient.WebSocketHandler
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
trait AppTypes {}
|
||||||
|
object AppTypes {
|
||||||
|
type HttpBackend =
|
||||||
|
SttpBackend[Task, Observable[ByteBuffer], WebSocketHandler]
|
||||||
|
}
|
84
src/main/scala/nova/monadic_sfx/executors/GUIExecutor.scala
Normal file
84
src/main/scala/nova/monadic_sfx/executors/GUIExecutor.scala
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package nova.monadic_sfx.executors
|
||||||
|
|
||||||
|
import akka.dispatch.{
|
||||||
|
DispatcherPrerequisites,
|
||||||
|
ExecutorServiceFactory,
|
||||||
|
ExecutorServiceConfigurator
|
||||||
|
}
|
||||||
|
import com.typesafe.config.Config
|
||||||
|
import java.util.concurrent.{
|
||||||
|
ExecutorService,
|
||||||
|
AbstractExecutorService,
|
||||||
|
ThreadFactory,
|
||||||
|
TimeUnit
|
||||||
|
}
|
||||||
|
import java.util.Collections
|
||||||
|
import javax.swing.SwingUtilities
|
||||||
|
import javafx.application.Platform
|
||||||
|
import monix.execution.Scheduler
|
||||||
|
import scala.concurrent.ExecutionContext
|
||||||
|
import java.util.concurrent.Executor
|
||||||
|
|
||||||
|
// First we wrap invokeLater/runLater as an ExecutorService
|
||||||
|
abstract class GUIExecutorService extends AbstractExecutorService {
|
||||||
|
def execute(command: Runnable): Unit
|
||||||
|
|
||||||
|
def shutdown(): Unit = ()
|
||||||
|
|
||||||
|
def shutdownNow() = Collections.emptyList[Runnable]
|
||||||
|
|
||||||
|
def isShutdown = false
|
||||||
|
|
||||||
|
def isTerminated = false
|
||||||
|
|
||||||
|
def awaitTermination(l: Long, timeUnit: TimeUnit) = true
|
||||||
|
}
|
||||||
|
|
||||||
|
object JavaFXExecutorService extends GUIExecutorService {
|
||||||
|
override def execute(command: Runnable) = Platform.runLater(command)
|
||||||
|
}
|
||||||
|
|
||||||
|
object SwingExecutorService extends GUIExecutorService {
|
||||||
|
override def execute(command: Runnable) = SwingUtilities.invokeLater(command)
|
||||||
|
}
|
||||||
|
|
||||||
|
class JavaFXEventThreadExecutorServiceConfigurator(
|
||||||
|
config: Config,
|
||||||
|
prerequisites: DispatcherPrerequisites
|
||||||
|
) extends ExecutorServiceConfigurator(config, prerequisites) {
|
||||||
|
private val f = new ExecutorServiceFactory {
|
||||||
|
def createExecutorService: ExecutorService = JavaFXExecutorService
|
||||||
|
}
|
||||||
|
|
||||||
|
def createExecutorServiceFactory(
|
||||||
|
id: String,
|
||||||
|
threadFactory: ThreadFactory
|
||||||
|
): ExecutorServiceFactory = f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then we create an ExecutorServiceConfigurator so that Akka can use our SwingExecutorService for the dispatchers
|
||||||
|
class SwingEventThreadExecutorServiceConfigurator(
|
||||||
|
config: Config,
|
||||||
|
prerequisites: DispatcherPrerequisites
|
||||||
|
) extends ExecutorServiceConfigurator(config, prerequisites) {
|
||||||
|
private val f = new ExecutorServiceFactory {
|
||||||
|
def createExecutorService: ExecutorService = SwingExecutorService
|
||||||
|
}
|
||||||
|
|
||||||
|
def createExecutorServiceFactory(
|
||||||
|
id: String,
|
||||||
|
threadFactory: ThreadFactory
|
||||||
|
): ExecutorServiceFactory = f
|
||||||
|
}
|
||||||
|
|
||||||
|
object JFXExecutionContexts {
|
||||||
|
val javaFxExecutionContext: ExecutionContext =
|
||||||
|
ExecutionContext.fromExecutor(new Executor {
|
||||||
|
def execute(command: Runnable): Unit = {
|
||||||
|
Platform.runLater(command)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
val fxScheduler =
|
||||||
|
Scheduler(javaFxExecutionContext)
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package nova.monadic_sfx.executors
|
||||||
|
|
||||||
|
import monix.execution.Scheduler
|
||||||
|
|
||||||
|
class Schedulers(
|
||||||
|
val blockingIO: Scheduler = Scheduler.io(),
|
||||||
|
val cpu: Scheduler = Scheduler.global,
|
||||||
|
val fx: Scheduler = JFXExecutionContexts.fxScheduler
|
||||||
|
)
|
10
src/main/scala/nova/monadic_sfx/http/Backend.scala
Normal file
10
src/main/scala/nova/monadic_sfx/http/Backend.scala
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
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 }
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package nova.monadic_sfx.http.requests
|
||||||
|
|
||||||
|
import nova.monadic_sfx.AppTypes
|
||||||
|
|
||||||
|
import nova.monadic_sfx.AppTypes.HttpBackend
|
||||||
|
import monix.eval.Task
|
||||||
|
import sttp.client._
|
||||||
|
import sttp.client.circe._
|
||||||
|
import io.circe.generic.auto._
|
||||||
|
import nova.monadic_sfx.models._
|
||||||
|
|
||||||
|
class DummyRequest(backend: HttpBackend) extends AppTypes {
|
||||||
|
private implicit val _backend = backend
|
||||||
|
def send() = {
|
||||||
|
Task
|
||||||
|
.suspend(
|
||||||
|
(for {
|
||||||
|
req <-
|
||||||
|
basicRequest
|
||||||
|
.get(uri"https://httpbin.org/get")
|
||||||
|
.response(asJson[HttpBinResponse])
|
||||||
|
.send()
|
||||||
|
} yield println(req)) >>
|
||||||
|
Task(println(Thread.currentThread().getName()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
8
src/main/scala/nova/monadic_sfx/models/DummyModels.scala
Normal file
8
src/main/scala/nova/monadic_sfx/models/DummyModels.scala
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package nova.monadic_sfx.models
|
||||||
|
|
||||||
|
case class RequestPayload(data: String)
|
||||||
|
final case class HttpBinResponse(
|
||||||
|
url: String,
|
||||||
|
origin: String,
|
||||||
|
headers: Map[String, String]
|
||||||
|
)
|
79
src/main/scala/nova/monadic_sfx/modules/MainModule.scala
Normal file
79
src/main/scala/nova/monadic_sfx/modules/MainModule.scala
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
39
src/main/scala/nova/monadic_sfx/pages/HomePage.scala
Normal file
39
src/main/scala/nova/monadic_sfx/pages/HomePage.scala
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
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
|
||||||
|
}
|
57
src/main/scala/nova/monadic_sfx/pages/LoginPage.scala
Normal file
57
src/main/scala/nova/monadic_sfx/pages/LoginPage.scala
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
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 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
|
||||||
|
) {
|
||||||
|
|
||||||
|
//pure function callbacks, but with side effects still
|
||||||
|
private def onLogout(stage: PrimaryStage) = {
|
||||||
|
println("logging out")
|
||||||
|
stage.scene().setRoot(render)
|
||||||
|
}
|
||||||
|
private def onLogin(stage: PrimaryStage) = {
|
||||||
|
println("logging in")
|
||||||
|
stage
|
||||||
|
.scene()
|
||||||
|
.setRoot(HomePage(backend, system, () => onLogout(appStage)))
|
||||||
|
}
|
||||||
|
private lazy val root = new VBox {
|
||||||
|
children = Seq(
|
||||||
|
new TextField {
|
||||||
|
text = "username"
|
||||||
|
editable = true
|
||||||
|
},
|
||||||
|
new TextField {
|
||||||
|
text = "password"
|
||||||
|
},
|
||||||
|
new Button {
|
||||||
|
text = "Login"
|
||||||
|
onAction = () => onLogin(appStage)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
def render: Parent = root
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object LoginPage {
|
||||||
|
def apply(
|
||||||
|
appStage: PrimaryStage,
|
||||||
|
backend: AppTypes.HttpBackend,
|
||||||
|
system: akka.actor.ActorSystem
|
||||||
|
) = new LoginPage(appStage, backend, system).render
|
||||||
|
}
|
55
src/main/scala/nova/monadic_sfx/ui/DefaultUI.scala
Normal file
55
src/main/scala/nova/monadic_sfx/ui/DefaultUI.scala
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package nova.monadic_sfx.ui
|
||||||
|
|
||||||
|
import scalafx.geometry.Insets
|
||||||
|
import scalafx.scene.Scene
|
||||||
|
import scalafx.scene.effect.DropShadow
|
||||||
|
import scalafx.scene.layout.HBox
|
||||||
|
import scalafx.scene.paint.Color._
|
||||||
|
import scalafx.scene.paint._
|
||||||
|
import scalafx.scene.text.Text
|
||||||
|
import monix.eval.Coeval
|
||||||
|
|
||||||
|
class DefaultUI {
|
||||||
|
val scene =
|
||||||
|
new Scene {
|
||||||
|
fill = Color.rgb(38, 38, 38)
|
||||||
|
content = new HBox {
|
||||||
|
padding = Insets(50, 80, 50, 80)
|
||||||
|
children = Seq(
|
||||||
|
new Text {
|
||||||
|
text = "Scala"
|
||||||
|
style = "-fx-font: normal bold 100pt sans-serif"
|
||||||
|
fill = new LinearGradient(endX = 0, stops = Stops(Red, DarkRed))
|
||||||
|
},
|
||||||
|
new Text {
|
||||||
|
text = "FX"
|
||||||
|
style = "-fx-font: italic bold 100pt sans-serif"
|
||||||
|
fill = new LinearGradient(
|
||||||
|
endX = 0,
|
||||||
|
stops = Stops(White, DarkGray)
|
||||||
|
)
|
||||||
|
effect = new DropShadow {
|
||||||
|
color = DarkGray
|
||||||
|
radius = 15
|
||||||
|
spread = 0.25
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// val program = Coeval
|
||||||
|
// .suspend {
|
||||||
|
// Coeval(println("hello")) >>
|
||||||
|
// Coeval(println(Thread.currentThread().getName())) >>
|
||||||
|
// Coeval {
|
||||||
|
// stage = new PrimaryStage {
|
||||||
|
// // initStyle(StageStyle.Unified)
|
||||||
|
// title = "ScalaFX Hello World"
|
||||||
|
// scene = scn
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
36
src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala
Normal file
36
src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package org.slf4j.impl
|
||||||
|
|
||||||
|
import cats.effect.{ContextShift, Clock, Effect, IO, Timer}
|
||||||
|
import io.odin._
|
||||||
|
import io.odin.slf4j.OdinLoggerBinder
|
||||||
|
|
||||||
|
import scala.concurrent.ExecutionContext
|
||||||
|
import _root_.monix.execution.Scheduler
|
||||||
|
|
||||||
|
//effect type should be specified inbefore
|
||||||
|
//log line will be recorded right after the call with no suspension
|
||||||
|
class StaticLoggerBinder extends OdinLoggerBinder[IO] {
|
||||||
|
|
||||||
|
val ec: ExecutionContext = Scheduler.global
|
||||||
|
implicit val timer: Timer[IO] = IO.timer(ec)
|
||||||
|
implicit val clock: Clock[IO] = timer.clock
|
||||||
|
implicit val cs: ContextShift[IO] = IO.contextShift(ec)
|
||||||
|
implicit val F: Effect[IO] = IO.ioEffect
|
||||||
|
|
||||||
|
val loggers: PartialFunction[String, Logger[IO]] = {
|
||||||
|
case "some.external.package.SpecificClass" =>
|
||||||
|
consoleLogger[IO](minLevel = Level.Warn) //disable noisy external logs
|
||||||
|
case "org.asynchttpclient.netty.channel.DefaultChannelPool" =>
|
||||||
|
consoleLogger[IO](minLevel = Level.Warn)
|
||||||
|
case _ => //if wildcard case isn't provided, default logger is no-op
|
||||||
|
consoleLogger[IO]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object StaticLoggerBinder extends StaticLoggerBinder {
|
||||||
|
|
||||||
|
var REQUESTED_API_VERSION: String = "1.7"
|
||||||
|
|
||||||
|
def getSingleton: StaticLoggerBinder = this
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user