Rohan Sircar
4 years ago
19 changed files with 759 additions and 0 deletions
-
24.gitignore
-
1.scalafmt.conf
-
24UNLICENSE
-
61build.sbt
-
2project/build.properties
-
6project/plugin.sbt
-
5src/main/resources/application.conf
-
219src/main/scala/nova/monadic_sfx/SFXActors.scala
-
13src/main/scala/nova/monadic_sfx/Types.scala
-
84src/main/scala/nova/monadic_sfx/executors/GUIExecutor.scala
-
9src/main/scala/nova/monadic_sfx/executors/Schedulers.scala
-
10src/main/scala/nova/monadic_sfx/http/Backend.scala
-
27src/main/scala/nova/monadic_sfx/http/requests/DummyRequest.scala
-
8src/main/scala/nova/monadic_sfx/models/DummyModels.scala
-
79src/main/scala/nova/monadic_sfx/modules/MainModule.scala
-
39src/main/scala/nova/monadic_sfx/pages/HomePage.scala
-
57src/main/scala/nova/monadic_sfx/pages/LoginPage.scala
-
55src/main/scala/nova/monadic_sfx/ui/DefaultUI.scala
-
36src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala
@ -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 |
@ -0,0 +1 @@ |
|||||
|
version = "2.6.4" |
@ -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> |
@ -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 |
||||
|
) |
@ -0,0 +1,2 @@ |
|||||
|
sbt.version=1.3.10 |
||||
|
|
@ -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") |
@ -0,0 +1,5 @@ |
|||||
|
javafx-dispatcher { |
||||
|
type = "Dispatcher" |
||||
|
executor = "akka.dispatch.gui.JavaFXEventThreadExecutorServiceConfigurator" |
||||
|
throughput = 1 |
||||
|
} |
@ -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) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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] |
||||
|
} |
@ -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 |
||||
|
) |
@ -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())) |
||||
|
) |
||||
|
} |
||||
|
} |
@ -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] |
||||
|
) |
@ -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) |
||||
|
} |
||||
|
|
||||
|
} |
@ -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 |
||||
|
} |
@ -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 |
||||
|
} |
@ -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 |
||||
|
|
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
|
||||
|
} |
@ -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 |
||||
|
|
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue