Compare commits
No commits in common. "development" and "master" have entirely different histories.
developmen
...
master
@ -6,9 +6,6 @@
|
|||||||
<title>Outwatch App</title>
|
<title>Outwatch App</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/blk-design-system@1.0.2/assets/css/nucleo-icons.css" integrity="sha256-03+9B37/His+rzjhgA6Y1+ByU9DGN2ZPWjjA5CJJF2w=" crossorigin="anonymous">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/blk-design-system@1.0.2/assets/css/nucleo-icons.css" integrity="sha256-03+9B37/His+rzjhgA6Y1+ByU9DGN2ZPWjjA5CJJF2w=" crossorigin="anonymous">
|
||||||
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous"></script>
|
|
||||||
<script src="https://cdn.datatables.net/1.10.22/js/jquery.dataTables.min.js"></script> -->
|
|
||||||
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@sweetalert2/theme-dark@3/dark.css"> -->
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
var i = 0;
|
|
||||||
|
|
||||||
console.log("Starting worker")
|
|
||||||
onmessage = (ev) => {
|
|
||||||
console.log(`Worker received data ${ev.data}`)
|
|
||||||
const data = JSON.parse(ev.data)
|
|
||||||
postMessage(JSON.stringify({ data: data.data * 2 }))
|
|
||||||
}
|
|
||||||
|
|
||||||
// function timedCount() {
|
|
||||||
// i = i + 1;
|
|
||||||
// postMessage(JSON.stringify({ data: i }));
|
|
||||||
// setTimeout("timedCount()", 2000);
|
|
||||||
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// timedCount();
|
|
33
build.sbt
33
build.sbt
@ -33,30 +33,20 @@ libraryDependencies ++= Seq(
|
|||||||
// "io.circe" %%% "circe-config" % "0.8.0",
|
// "io.circe" %%% "circe-config" % "0.8.0",
|
||||||
"org.akka-js" %%% "shocon" % "1.0.0",
|
"org.akka-js" %%% "shocon" % "1.0.0",
|
||||||
"com.beachape" %%% "enumeratum-circe" % "1.6.1",
|
"com.beachape" %%% "enumeratum-circe" % "1.6.1",
|
||||||
"com.github.valskalla" %%% "odin-core" % "0.7.0+95-ab4381ae+20201227-1831-SNAPSHOT",
|
"com.github.valskalla" %%% "odin-core" % "0.7.0+95-ab4381ae+20201227-1831-SNAPSHOT"
|
||||||
"io.github.cquiroz" %%% "scala-java-time" % "2.1.0",
|
|
||||||
"io.github.cquiroz" %%% "scala-java-time-tzdb" % "2.1.0",
|
|
||||||
"com.lihaoyi" %%% "scalatags" % "0.9.2"
|
|
||||||
// "com.clovellytech" %%% "outwatch-router" % "0.0.9+7-5be0b1a2+20201227-2019-SNAPSHOT"
|
// "com.clovellytech" %%% "outwatch-router" % "0.0.9+7-5be0b1a2+20201227-2019-SNAPSHOT"
|
||||||
)
|
)
|
||||||
|
|
||||||
Compile / npmDependencies ++= Seq(
|
Compile / npmDependencies ++= Seq(
|
||||||
"jquery" -> "3.5.1",
|
"jquery" -> "3.3.1",
|
||||||
"@types/jquery" -> "3.5.5",
|
"popper.js" -> "1.16.1",
|
||||||
|
// // "@popperjs/core" -> "2.6.0",
|
||||||
"blk-design-system" -> "1.0.2",
|
"blk-design-system" -> "1.0.2",
|
||||||
"bootstrap" -> "4.5.3",
|
"bootstrap" -> "4.5.3",
|
||||||
"@types/chart.js" -> "2.9.11",
|
"@types/chart.js" -> "2.9.11",
|
||||||
"chart.js" -> "2.9.3",
|
"chart.js" -> "2.9.3",
|
||||||
"snabbdom" -> "git://github.com/outwatch/snabbdom.git#semver:0.7.5",
|
"snabbdom" -> "git://github.com/outwatch/snabbdom.git#semver:0.7.5",
|
||||||
"fuse.js" -> "6.4.3",
|
"fuse.js" -> "6.4.3"
|
||||||
"datatables.net-bs4" -> "1.10.23",
|
|
||||||
// "datatables.net-dt" -> "1.10.23",
|
|
||||||
"@types/datatables.net" -> "1.10.19",
|
|
||||||
"sweetalert2" -> "10.12.6",
|
|
||||||
"@sweetalert2/themes" -> "4.0.1",
|
|
||||||
"reconnecting-websocket" -> "4.4.0",
|
|
||||||
"paralleljs" -> "1.1.0",
|
|
||||||
"@types/paralleljs" -> "0.0.20"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Compile / npmDevDependencies ++= Seq(
|
Compile / npmDevDependencies ++= Seq(
|
||||||
@ -67,14 +57,8 @@ Compile / npmDevDependencies ++= Seq(
|
|||||||
"url-loader" -> "1.1.2"
|
"url-loader" -> "1.1.2"
|
||||||
)
|
)
|
||||||
|
|
||||||
stIgnore ++= List(
|
stIgnore ++= List("jquery", "blk-design-system", "bootstrap")
|
||||||
"datatables.net-bs4",
|
stIgnore ++= List("snabbdom")
|
||||||
"datatables.net-dt",
|
|
||||||
"blk-design-system",
|
|
||||||
"bootstrap",
|
|
||||||
"snabbdom",
|
|
||||||
"@sweetalert2/themes"
|
|
||||||
)
|
|
||||||
stStdlib := List("es6")
|
stStdlib := List("es6")
|
||||||
stUseScalaJsDom := false
|
stUseScalaJsDom := false
|
||||||
|
|
||||||
@ -126,9 +110,6 @@ webpackDevServerPort := 8080
|
|||||||
webpackConfigFile in fastOptJS := Some(
|
webpackConfigFile in fastOptJS := Some(
|
||||||
baseDirectory.value / "webpack.config.dev.js"
|
baseDirectory.value / "webpack.config.dev.js"
|
||||||
)
|
)
|
||||||
webpackConfigFile in fullOptJS := Some(
|
|
||||||
baseDirectory.value / "webpack.config.prod.js"
|
|
||||||
)
|
|
||||||
// webpackConfigFile in fullOptJS := Some(
|
// webpackConfigFile in fullOptJS := Some(
|
||||||
// baseDirectory.value / "webpack.config.js"
|
// baseDirectory.value / "webpack.config.js"
|
||||||
// )
|
// )
|
||||||
|
@ -25,7 +25,7 @@ object MonixWS {
|
|||||||
class MonixWS[F[_], S](val url: String)(implicit F: Sync[F], S: Show[S]) {
|
class MonixWS[F[_], S](val url: String)(implicit F: Sync[F], S: Show[S]) {
|
||||||
val ws = new org.scalajs.dom.WebSocket(url)
|
val ws = new org.scalajs.dom.WebSocket(url)
|
||||||
|
|
||||||
val source: Observable[MessageEvent] =
|
lazy val source: Observable[MessageEvent] =
|
||||||
Observable.create[MessageEvent](OverflowStrategy.Unbounded) { sub =>
|
Observable.create[MessageEvent](OverflowStrategy.Unbounded) { sub =>
|
||||||
ws.onmessage = (e: MessageEvent) => sub.onNext(e)
|
ws.onmessage = (e: MessageEvent) => sub.onNext(e)
|
||||||
ws.onerror =
|
ws.onerror =
|
||||||
@ -33,7 +33,7 @@ class MonixWS[F[_], S](val url: String)(implicit F: Sync[F], S: Show[S]) {
|
|||||||
Cancelable(() => ws.close())
|
Cancelable(() => ws.close())
|
||||||
}
|
}
|
||||||
|
|
||||||
val sink: F[Observer[S]] = {
|
lazy val sink: F[Observer[S]] = {
|
||||||
F.delay {
|
F.delay {
|
||||||
new Observer[S] {
|
new Observer[S] {
|
||||||
override def onNext(elem: S): Future[Ack] = {
|
override def onNext(elem: S): Future[Ack] = {
|
||||||
|
@ -20,7 +20,7 @@ object WebSocketF {
|
|||||||
class WebSocketF[F[_]](val url: String)(implicit F: Sync[F]) {
|
class WebSocketF[F[_]](val url: String)(implicit F: Sync[F]) {
|
||||||
val ws = new org.scalajs.dom.WebSocket(url)
|
val ws = new org.scalajs.dom.WebSocket(url)
|
||||||
|
|
||||||
val source: Observable[MessageEvent] =
|
lazy val source: Observable[MessageEvent] =
|
||||||
Observable.create[MessageEvent] { observer =>
|
Observable.create[MessageEvent] { observer =>
|
||||||
ws.onmessage = (e: MessageEvent) => observer.onNext(e)
|
ws.onmessage = (e: MessageEvent) => observer.onNext(e)
|
||||||
ws.onerror =
|
ws.onerror =
|
||||||
@ -28,7 +28,7 @@ class WebSocketF[F[_]](val url: String)(implicit F: Sync[F]) {
|
|||||||
Cancelable(() => ws.close())
|
Cancelable(() => ws.close())
|
||||||
}
|
}
|
||||||
|
|
||||||
val sink: F[Observer[String]] = {
|
lazy val sink: F[Observer[String]] = {
|
||||||
F.delay {
|
F.delay {
|
||||||
new Observer[String] {
|
new Observer[String] {
|
||||||
override def onNext(elem: String): Unit = ws.send(elem)
|
override def onNext(elem: String): Unit = ws.send(elem)
|
||||||
|
@ -1,187 +1,50 @@
|
|||||||
package outwatchapp
|
package outwatchapp
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
|
||||||
|
|
||||||
import colibri.ext.monix._
|
|
||||||
import com.softwaremill.macwire._
|
import com.softwaremill.macwire._
|
||||||
import io.odin.consoleLogger
|
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import monix.eval.Coeval
|
|
||||||
import monix.reactive.Observable
|
|
||||||
import monix.{eval => me}
|
|
||||||
import org.scalajs.dom.raw.Element
|
import org.scalajs.dom.raw.Element
|
||||||
import outwatch._
|
import outwatch._
|
||||||
import outwatch.dsl._
|
import outwatch.dsl._
|
||||||
import outwatch.router._
|
import outwatch.router._
|
||||||
import outwatchapp.components.ChartjsDemo
|
|
||||||
import outwatchapp.components.CounterDemo
|
import outwatchapp.components.CounterDemo
|
||||||
import outwatchapp.components.DatatablesDemo
|
|
||||||
import outwatchapp.components.FusejsDemo
|
|
||||||
import outwatchapp.components.RequestDemo
|
import outwatchapp.components.RequestDemo
|
||||||
|
import outwatchapp.components.todo.ChartjsDemo
|
||||||
import outwatchapp.pages.HomePage
|
import outwatchapp.pages.HomePage
|
||||||
import outwatchapp.ui.components.todo.TodoListStore
|
import nova.monadic_sfx.ui.components.todo.TodoListStore
|
||||||
import outwatchapp.util.IOUtils
|
|
||||||
import outwatchapp.util.reactive.WebSocket
|
|
||||||
import outwatchapp.util.reactive.WebWorker
|
|
||||||
import outwatchapp.util.reactive.WebsocketData
|
|
||||||
import outwatchapp.util.reactive.WorkerData
|
|
||||||
import outwatchapp.util.reactive.ReconnectingWebSocket
|
|
||||||
import cats.syntax.eq._
|
|
||||||
import monix.execution.Ack
|
|
||||||
import monix.reactive.Observer
|
|
||||||
|
|
||||||
class MainApp(el: Element)(implicit
|
class MainApp(el: Element)(implicit
|
||||||
backend: AppTypes.Backend,
|
backend: AppTypes.Backend,
|
||||||
store: RouterStore[Page]
|
store: RouterStore[Page]
|
||||||
) {
|
) {
|
||||||
|
|
||||||
import MainApp._
|
|
||||||
|
|
||||||
def run: Task[Unit] = for {
|
def run: Task[Unit] = for {
|
||||||
resolver <- resolver
|
resolver <- resolver
|
||||||
_ <- OutWatch.renderInto[Task](el, Router(resolver))
|
_ <- OutWatch.renderInto[Task](el, Router(resolver))
|
||||||
} yield ()
|
} yield ()
|
||||||
|
|
||||||
def producer(data: Long, ws: Observer[WebsocketData]): me.Task[Unit] =
|
|
||||||
me.Task.deferFuture(ws.onNext(WebsocketData(data))).flatMap {
|
|
||||||
case Ack.Continue => producer(data + 1, ws)
|
|
||||||
// .delayExecution(3.seconds)
|
|
||||||
case Ack.Stop => me.Task.unit
|
|
||||||
}
|
|
||||||
|
|
||||||
def resolver: Task[PartialFunction[Page, VDomModifier]] =
|
def resolver: Task[PartialFunction[Page, VDomModifier]] =
|
||||||
Task.deferAction(implicit s =>
|
Task.deferAction(implicit s =>
|
||||||
for {
|
for {
|
||||||
counterDemo <- CounterDemo()
|
counterDemo <- CounterDemo()
|
||||||
chartDemo <- ChartjsDemo()
|
chartDemo <- ChartjsDemo()
|
||||||
todoStore <- TodoListStore(consoleLogger[Task]())
|
todoStore <- TodoListStore()
|
||||||
requestDemo <- RequestDemo(todoStore)
|
requestDemo <- RequestDemo(todoStore)
|
||||||
demoWorker <- WebWorker[WorkerData]("/worker.js")
|
resolver: PartialFunction[Page, VDomModifier] = {
|
||||||
dtDemo <- DatatablesDemo()
|
case Page.Home => wire[HomePage].render
|
||||||
_ <- IOUtils
|
case Page.SomePage =>
|
||||||
.toIO(
|
|
||||||
Observable
|
|
||||||
.interval(1.second)
|
|
||||||
.take(2)
|
|
||||||
.doOnNextF(i => Coeval(println(s"Producer emitted $i")))
|
|
||||||
.doOnNextF(i =>
|
|
||||||
me.Task.deferFuture(demoWorker.onNext(WorkerData(i))).void
|
|
||||||
)
|
|
||||||
.completedL >>
|
|
||||||
me.Task.deferFuture(demoWorker.onNext(WorkerData(999)))
|
|
||||||
// .delayExecution(6.seconds)
|
|
||||||
)
|
|
||||||
.startAndForget
|
|
||||||
_ <-
|
|
||||||
ReconnectingWebSocket[WebsocketData]("ws://localhost:6789").flatMap {
|
|
||||||
case (source, sink) =>
|
|
||||||
IOUtils
|
|
||||||
.toIO(
|
|
||||||
me.Task.parZip2(
|
|
||||||
// Observable
|
|
||||||
// .interval(3.seconds)
|
|
||||||
// .doOnNext(_ => me.Task(println("Sending websocket data")))
|
|
||||||
// .doOnNext(i =>
|
|
||||||
// me.Task.deferFuture(
|
|
||||||
// ws.onNext(WebsocketData(i))
|
|
||||||
// ) >> me.Task.unit
|
|
||||||
// )
|
|
||||||
// .completedL,
|
|
||||||
producer(0, sink.value),
|
|
||||||
source.value
|
|
||||||
.doOnNext(data => me.Task(println(s"Received : $data")))
|
|
||||||
.completedL
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// .onErrorRestartIf {
|
|
||||||
// case e: Exception =>
|
|
||||||
// e.getMessage() === "Websocket closed"
|
|
||||||
// case _ => false
|
|
||||||
// }
|
|
||||||
// .onErrorRestartLoop(Backoff(3, 1.second)) { (err, state, retry) =>
|
|
||||||
// val Backoff(maxRetries, delay) = state
|
|
||||||
// if (maxRetries > 0)
|
|
||||||
// retry(Backoff(maxRetries - 1, delay * 2)).delayExecution(delay)
|
|
||||||
// else
|
|
||||||
// // No retries left, rethrow the error
|
|
||||||
// Task.raiseError(err)
|
|
||||||
// }
|
|
||||||
.startAndForget
|
|
||||||
// .start
|
|
||||||
// .startAndForget
|
|
||||||
// _ <- SweetAlertDemo.dumbNotif
|
|
||||||
// _ <- Task(println(s"Notif result: $notifRes"))
|
|
||||||
} yield {
|
|
||||||
case Page.Home => wire[HomePage].render
|
|
||||||
case Page.SomePage =>
|
|
||||||
div(
|
|
||||||
div(cls := "title", "SomePage"),
|
|
||||||
// RequestDemo(todoStore),
|
|
||||||
// Observable
|
|
||||||
// .interval(1.second)
|
|
||||||
// .doOnNextF(i => Coeval(println(s"Producer emitted $i")))
|
|
||||||
// .doOnNextF(i =>
|
|
||||||
// Coeval(demoWorker.onNext(WorkerData(i))) >> Coeval.unit
|
|
||||||
// )
|
|
||||||
// .take(2)
|
|
||||||
// .map(_ => div()),
|
|
||||||
demoWorker.map(_.toString).map(v => p(cls := "text-white", v)),
|
|
||||||
requestDemo,
|
|
||||||
div(cls := "slider")
|
|
||||||
)
|
|
||||||
case Page.UserHome(id) =>
|
|
||||||
div(
|
|
||||||
// cls := "text-white",
|
|
||||||
div(cls := "title", "UserHome"),
|
|
||||||
s"User id: $id",
|
|
||||||
div(FusejsDemo.y.map(_.item.toString).mkString(" ")),
|
|
||||||
// ExampleDataTable.value(
|
|
||||||
// onDomMount.asHtml.foreachSync(el =>
|
|
||||||
// Coeval(DatatablesDemo.init(el)) >> Coeval.unit
|
|
||||||
// )
|
|
||||||
// )
|
|
||||||
dtDemo
|
|
||||||
)
|
|
||||||
case Page.NotFound =>
|
|
||||||
Task(
|
|
||||||
div(
|
div(
|
||||||
cls := "page-header error-page header-filter",
|
div(cls := "title", "SomePage"),
|
||||||
div(
|
// RequestDemo(todoStore),
|
||||||
cls := "page-header-image"
|
requestDemo,
|
||||||
// style := js.Dictionary(
|
div(cls := "slider")
|
||||||
// "background-image" -> "url('../assets/img/braden-collum.jpg')"
|
|
||||||
// )
|
|
||||||
),
|
|
||||||
div(
|
|
||||||
cls := "container",
|
|
||||||
cls := "text-center",
|
|
||||||
div(
|
|
||||||
cls := "row",
|
|
||||||
div(
|
|
||||||
cls := "col-md-12",
|
|
||||||
h1(
|
|
||||||
fontSize := "12em",
|
|
||||||
color := "#fff",
|
|
||||||
letterSpacing := "14px",
|
|
||||||
fontWeight := 700,
|
|
||||||
cls := "title",
|
|
||||||
"404"
|
|
||||||
),
|
|
||||||
h2(cls := "description", "Page not found :("),
|
|
||||||
h4(
|
|
||||||
cls := "description",
|
|
||||||
"Ooooups! Looks like you got lost."
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
case Page.UserHome(id) =>
|
||||||
}
|
div(div(cls := "title", "UserHome"), s"User id: $id")
|
||||||
|
case Page.NotFound =>
|
||||||
|
div(
|
||||||
|
div(cls := "title", "NotFound"),
|
||||||
|
p(cls := "profile-description", "notfound")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} yield resolver
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
object MainApp {
|
|
||||||
final case class Backoff(maxRetries: Long, delay: FiniteDuration)
|
|
||||||
}
|
|
||||||
|
@ -1,20 +1,18 @@
|
|||||||
package outwatchapp
|
package outwatchapp
|
||||||
|
|
||||||
import scala.scalajs.js.annotation.JSImport
|
|
||||||
|
|
||||||
import cats.effect.ExitCode
|
import cats.effect.ExitCode
|
||||||
import monix.bio._
|
import monix.bio._
|
||||||
import org.scalajs.dom.document
|
|
||||||
import org.scalajs.dom.raw.Element
|
import org.scalajs.dom.raw.Element
|
||||||
import outwatch.router.AppRouter
|
|
||||||
import sttp.client.impl.monix.FetchMonixBackend
|
import sttp.client.impl.monix.FetchMonixBackend
|
||||||
|
import org.scalajs.dom.document
|
||||||
|
import scala.scalajs.js.annotation.JSImport
|
||||||
import scalajs.js
|
import scalajs.js
|
||||||
|
import outwatch.router.AppRouter
|
||||||
|
|
||||||
@JSImport("bootstrap/dist/css/bootstrap.min.css", JSImport.Namespace)
|
@JSImport("bootstrap/dist/css/bootstrap.min.css", JSImport.Namespace)
|
||||||
@js.native
|
@js.native
|
||||||
object BootstrapBundleCss extends js.Object
|
object BootstrapBundleCss extends js.Object
|
||||||
@JSImport("bootstrap/dist/js/bootstrap.bundle.min.js", JSImport.Namespace)
|
@JSImport("bootstrap", JSImport.Namespace)
|
||||||
@js.native
|
@js.native
|
||||||
object BootstrapBundleJs extends js.Object
|
object BootstrapBundleJs extends js.Object
|
||||||
|
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package outwatchapp
|
package outwatchapp
|
||||||
|
import colibri.ext.monix._
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
|
import monix.eval.Coeval
|
||||||
import outwatch._
|
import outwatch._
|
||||||
import outwatch.dsl._
|
import outwatch.dsl._
|
||||||
import outwatch.router.AppRouter
|
import outwatch.router.AppRouter
|
||||||
import outwatch.router._
|
import outwatch.router._
|
||||||
import outwatch.router.dsl._
|
import outwatch.router.dsl._
|
||||||
|
import outwatchapp.components.todo.FusejsDemo
|
||||||
|
|
||||||
import Page._
|
import Page._
|
||||||
|
|
||||||
@ -27,7 +30,7 @@ object Router {
|
|||||||
cls := "navbar-translate",
|
cls := "navbar-translate",
|
||||||
a(
|
a(
|
||||||
cls := "navbar-brand",
|
cls := "navbar-brand",
|
||||||
href := "#",
|
href := "https://demos.creative-tim.com/blk-design-system/index.html",
|
||||||
rel := "tooltip",
|
rel := "tooltip",
|
||||||
title := "",
|
title := "",
|
||||||
attr("data-placement") := "bottom",
|
attr("data-placement") := "bottom",
|
||||||
@ -101,6 +104,11 @@ object Router {
|
|||||||
),
|
),
|
||||||
div(
|
div(
|
||||||
cls := "container",
|
cls := "container",
|
||||||
|
Coeval(
|
||||||
|
println("Result = " + FusejsDemo.y.toString())
|
||||||
|
) >> Coeval(
|
||||||
|
FusejsDemo.y.foreach(o => println(o.item))
|
||||||
|
) >> Coeval(div()),
|
||||||
router.render(resolver),
|
router.render(resolver),
|
||||||
router.watch()
|
router.watch()
|
||||||
)
|
)
|
||||||
|
@ -15,7 +15,7 @@ object CounterDemo {
|
|||||||
def apply(): Task[HtmlVNode @@ CounterDemo] =
|
def apply(): Task[HtmlVNode @@ CounterDemo] =
|
||||||
Task.deferAction(implicit s =>
|
Task.deferAction(implicit s =>
|
||||||
Task(
|
Task(
|
||||||
div(p(cls := "text-white", "count: ", counter2))
|
div(p(cls := "profile-description", "count: ", counter2))
|
||||||
.taggedWith[CounterDemo]
|
.taggedWith[CounterDemo]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1,141 +0,0 @@
|
|||||||
package outwatchapp.components
|
|
||||||
|
|
||||||
import scala.annotation.unused
|
|
||||||
import scala.scalajs.js.annotation.JSExportAll
|
|
||||||
import scala.scalajs.js.annotation.JSImport
|
|
||||||
|
|
||||||
import colibri.ext.monix._
|
|
||||||
import monix.bio.Task
|
|
||||||
import monix.eval.Coeval
|
|
||||||
import monix.execution.Cancelable
|
|
||||||
import org.scalajs.dom.raw.HTMLElement
|
|
||||||
import outwatch.ManagedSubscriptions.managedElement
|
|
||||||
import typings.datatablesNet.DataTables.ColumnSettings
|
|
||||||
import typings.datatablesNet.DataTables.FunctionColumnRender
|
|
||||||
import typings.datatablesNet.DataTables.Settings
|
|
||||||
import typings.datatablesNet.datatablesNetRequire
|
|
||||||
import typings.jquery.JQuery_
|
|
||||||
import typings.jquery.{mod => $}
|
|
||||||
|
|
||||||
import scalajs.js
|
|
||||||
|
|
||||||
@js.native
|
|
||||||
@JSImport(
|
|
||||||
"datatables.net-bs4/css/dataTables.bootstrap4.min.css",
|
|
||||||
JSImport.Namespace
|
|
||||||
)
|
|
||||||
object DataTablesBs4Css extends js.Object
|
|
||||||
@js.native
|
|
||||||
@JSImport(
|
|
||||||
"datatables.net-bs4/js/dataTables.bootstrap4.min.js",
|
|
||||||
JSImport.Namespace
|
|
||||||
)
|
|
||||||
object DataTablesBs4Js extends js.Object
|
|
||||||
|
|
||||||
object DatatablesDemo {
|
|
||||||
implicit def isJQueryDT[T](x: JQuery_[T]): typings.datatablesNet.JQuery =
|
|
||||||
x.asInstanceOf[typings.datatablesNet.JQuery]
|
|
||||||
|
|
||||||
datatablesNetRequire
|
|
||||||
DataTablesBs4Css
|
|
||||||
DataTablesBs4Js
|
|
||||||
|
|
||||||
@unused
|
|
||||||
private def _test(tableElement: HTMLElement) = $(tableElement)
|
|
||||||
.DataTable(
|
|
||||||
Settings().setColumnsVarargs(
|
|
||||||
ColumnSettings().setData("name").setRender(nameRenderFn)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.destroy()
|
|
||||||
|
|
||||||
val nameRenderFn: FunctionColumnRender = (_data, tpe, row, d) => {
|
|
||||||
if (tpe.toString == "display") println("Matched")
|
|
||||||
else println("Did not match")
|
|
||||||
// this can be done with scalatags instead
|
|
||||||
s"""
|
|
||||||
<div>${_data.toString.toUpperCase}</div>
|
|
||||||
"""
|
|
||||||
// with scalatags
|
|
||||||
// import scalatags.Text.all._
|
|
||||||
// div(_data.toString.toUpperCase).render
|
|
||||||
}
|
|
||||||
|
|
||||||
@JSExportAll
|
|
||||||
case class TestData(
|
|
||||||
name: String,
|
|
||||||
position: String,
|
|
||||||
location: String,
|
|
||||||
id: String,
|
|
||||||
date: String,
|
|
||||||
salary: String
|
|
||||||
)
|
|
||||||
|
|
||||||
val dataset = js
|
|
||||||
.Array(
|
|
||||||
TestData(
|
|
||||||
"Tiger Nixon",
|
|
||||||
"System Architect",
|
|
||||||
"Edinburgh",
|
|
||||||
"5421",
|
|
||||||
"2011/04/25",
|
|
||||||
"$320,800"
|
|
||||||
),
|
|
||||||
TestData(
|
|
||||||
"Garrett Winters",
|
|
||||||
"Accountant",
|
|
||||||
"Tokyo",
|
|
||||||
"8422",
|
|
||||||
"2011/07/25",
|
|
||||||
"$170,750"
|
|
||||||
),
|
|
||||||
TestData(
|
|
||||||
"Ashton Cox",
|
|
||||||
"Junior Technical Author",
|
|
||||||
"San Francisco",
|
|
||||||
"1562",
|
|
||||||
"2009/01/12",
|
|
||||||
"$86,000"
|
|
||||||
),
|
|
||||||
TestData(
|
|
||||||
"Cedric Kelly",
|
|
||||||
"Senior Javascript Developer",
|
|
||||||
"Edinburgh",
|
|
||||||
"6224",
|
|
||||||
"2012/03/29",
|
|
||||||
"$433,060"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
// $(document).ready(_ => $(tableElement).DataTable())
|
|
||||||
|
|
||||||
def apply() = {
|
|
||||||
import outwatch.dsl._
|
|
||||||
Coeval(
|
|
||||||
div(
|
|
||||||
table(idAttr := "myTable")(managedElement { el =>
|
|
||||||
val dt = $(el).DataTable(
|
|
||||||
Settings()
|
|
||||||
.setColumnsVarargs(
|
|
||||||
ColumnSettings()
|
|
||||||
.setTitle("Name")
|
|
||||||
.setData("name")
|
|
||||||
.setRender(nameRenderFn),
|
|
||||||
ColumnSettings().setData("position"),
|
|
||||||
ColumnSettings().setData("location"),
|
|
||||||
ColumnSettings().setData("id"),
|
|
||||||
ColumnSettings().setData("date"),
|
|
||||||
ColumnSettings().setData("salary")
|
|
||||||
)
|
|
||||||
.setDom("Blfrtip")
|
|
||||||
.setData(dataset)
|
|
||||||
)
|
|
||||||
Cancelable { () =>
|
|
||||||
println("Destroying")
|
|
||||||
dt.destroy()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
).to[Task]
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,492 +0,0 @@
|
|||||||
package outwatchapp.components
|
|
||||||
|
|
||||||
import outwatch.dsl._
|
|
||||||
|
|
||||||
object Table {
|
|
||||||
val value = table(
|
|
||||||
idAttr := "myTable",
|
|
||||||
cls := "display",
|
|
||||||
cls := "table",
|
|
||||||
// style := "width: 100%",
|
|
||||||
width := "100%",
|
|
||||||
// style("width") := "100%",
|
|
||||||
thead(
|
|
||||||
tr(
|
|
||||||
th("Name"),
|
|
||||||
th("Position"),
|
|
||||||
th("Office"),
|
|
||||||
th("Age"),
|
|
||||||
th("Start date"),
|
|
||||||
th("Salary")
|
|
||||||
)
|
|
||||||
),
|
|
||||||
tbody(
|
|
||||||
tr(
|
|
||||||
td("Tiger Nixon"),
|
|
||||||
td("System Architect"),
|
|
||||||
td("Edinburgh"),
|
|
||||||
td("61"),
|
|
||||||
td("2011/04/25"),
|
|
||||||
td("$320,800")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Garrett Winters"),
|
|
||||||
td("Accountant"),
|
|
||||||
td("Tokyo"),
|
|
||||||
td("63"),
|
|
||||||
td("2011/07/25"),
|
|
||||||
td("$170,750")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Ashton Cox"),
|
|
||||||
td("Junior Technical Author"),
|
|
||||||
td("San Francisco"),
|
|
||||||
td("66"),
|
|
||||||
td("2009/01/12"),
|
|
||||||
td("$86,000")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Cedric Kelly"),
|
|
||||||
td("Senior Javascript Developer"),
|
|
||||||
td("Edinburgh"),
|
|
||||||
td("22"),
|
|
||||||
td("2012/03/29"),
|
|
||||||
td("$433,060")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Airi Satou"),
|
|
||||||
td("Accountant"),
|
|
||||||
td("Tokyo"),
|
|
||||||
td("33"),
|
|
||||||
td("2008/11/28"),
|
|
||||||
td("$162,700")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Brielle Williamson"),
|
|
||||||
td("Integration Specialist"),
|
|
||||||
td("New York"),
|
|
||||||
td("61"),
|
|
||||||
td("2012/12/02"),
|
|
||||||
td("$372,000")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Herrod Chandler"),
|
|
||||||
td("Sales Assistant"),
|
|
||||||
td("San Francisco"),
|
|
||||||
td("59"),
|
|
||||||
td("2012/08/06"),
|
|
||||||
td("$137,500")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Rhona Davidson"),
|
|
||||||
td("Integration Specialist"),
|
|
||||||
td("Tokyo"),
|
|
||||||
td("55"),
|
|
||||||
td("2010/10/14"),
|
|
||||||
td("$327,900")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Colleen Hurst"),
|
|
||||||
td("Javascript Developer"),
|
|
||||||
td("San Francisco"),
|
|
||||||
td("39"),
|
|
||||||
td("2009/09/15"),
|
|
||||||
td("$205,500")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Sonya Frost"),
|
|
||||||
td("Software Engineer"),
|
|
||||||
td("Edinburgh"),
|
|
||||||
td("23"),
|
|
||||||
td("2008/12/13"),
|
|
||||||
td("$103,600")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Jena Gaines"),
|
|
||||||
td("Office Manager"),
|
|
||||||
td("London"),
|
|
||||||
td("30"),
|
|
||||||
td("2008/12/19"),
|
|
||||||
td("$90,560")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Quinn Flynn"),
|
|
||||||
td("Support Lead"),
|
|
||||||
td("Edinburgh"),
|
|
||||||
td("22"),
|
|
||||||
td("2013/03/03"),
|
|
||||||
td("$342,000")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Charde Marshall"),
|
|
||||||
td("Regional Director"),
|
|
||||||
td("San Francisco"),
|
|
||||||
td("36"),
|
|
||||||
td("2008/10/16"),
|
|
||||||
td("$470,600")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Haley Kennedy"),
|
|
||||||
td("Senior Marketing Designer"),
|
|
||||||
td("London"),
|
|
||||||
td("43"),
|
|
||||||
td("2012/12/18"),
|
|
||||||
td("$313,500")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Tatyana Fitzpatrick"),
|
|
||||||
td("Regional Director"),
|
|
||||||
td("London"),
|
|
||||||
td("19"),
|
|
||||||
td("2010/03/17"),
|
|
||||||
td("$385,750")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Michael Silva"),
|
|
||||||
td("Marketing Designer"),
|
|
||||||
td("London"),
|
|
||||||
td("66"),
|
|
||||||
td("2012/11/27"),
|
|
||||||
td("$198,500")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Paul Byrd"),
|
|
||||||
td("Chief Financial Officer (CFO)"),
|
|
||||||
td("New York"),
|
|
||||||
td("64"),
|
|
||||||
td("2010/06/09"),
|
|
||||||
td("$725,000")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Gloria Little"),
|
|
||||||
td("Systems Administrator"),
|
|
||||||
td("New York"),
|
|
||||||
td("59"),
|
|
||||||
td("2009/04/10"),
|
|
||||||
td("$237,500")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Bradley Greer"),
|
|
||||||
td("Software Engineer"),
|
|
||||||
td("London"),
|
|
||||||
td("41"),
|
|
||||||
td("2012/10/13"),
|
|
||||||
td("$132,000")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Dai Rios"),
|
|
||||||
td("Personnel Lead"),
|
|
||||||
td("Edinburgh"),
|
|
||||||
td("35"),
|
|
||||||
td("2012/09/26"),
|
|
||||||
td("$217,500")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Jenette Caldwell"),
|
|
||||||
td("Development Lead"),
|
|
||||||
td("New York"),
|
|
||||||
td("30"),
|
|
||||||
td("2011/09/03"),
|
|
||||||
td("$345,000")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Yuri Berry"),
|
|
||||||
td("Chief Marketing Officer (CMO)"),
|
|
||||||
td("New York"),
|
|
||||||
td("40"),
|
|
||||||
td("2009/06/25"),
|
|
||||||
td("$675,000")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Caesar Vance"),
|
|
||||||
td("Pre-Sales Support"),
|
|
||||||
td("New York"),
|
|
||||||
td("21"),
|
|
||||||
td("2011/12/12"),
|
|
||||||
td("$106,450")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Doris Wilder"),
|
|
||||||
td("Sales Assistant"),
|
|
||||||
td("Sydney"),
|
|
||||||
td("23"),
|
|
||||||
td("2010/09/20"),
|
|
||||||
td("$85,600")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Angelica Ramos"),
|
|
||||||
td("Chief Executive Officer (CEO)"),
|
|
||||||
td("London"),
|
|
||||||
td("47"),
|
|
||||||
td("2009/10/09"),
|
|
||||||
td("$1,200,000")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Gavin Joyce"),
|
|
||||||
td("Developer"),
|
|
||||||
td("Edinburgh"),
|
|
||||||
td("42"),
|
|
||||||
td("2010/12/22"),
|
|
||||||
td("$92,575")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Jennifer Chang"),
|
|
||||||
td("Regional Director"),
|
|
||||||
td("Singapore"),
|
|
||||||
td("28"),
|
|
||||||
td("2010/11/14"),
|
|
||||||
td("$357,650")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Brenden Wagner"),
|
|
||||||
td("Software Engineer"),
|
|
||||||
td("San Francisco"),
|
|
||||||
td("28"),
|
|
||||||
td("2011/06/07"),
|
|
||||||
td("$206,850")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Fiona Green"),
|
|
||||||
td("Chief Operating Officer (COO)"),
|
|
||||||
td("San Francisco"),
|
|
||||||
td("48"),
|
|
||||||
td("2010/03/11"),
|
|
||||||
td("$850,000")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Shou Itou"),
|
|
||||||
td("Regional Marketing"),
|
|
||||||
td("Tokyo"),
|
|
||||||
td("20"),
|
|
||||||
td("2011/08/14"),
|
|
||||||
td("$163,000")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Michelle House"),
|
|
||||||
td("Integration Specialist"),
|
|
||||||
td("Sydney"),
|
|
||||||
td("37"),
|
|
||||||
td("2011/06/02"),
|
|
||||||
td("$95,400")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Suki Burks"),
|
|
||||||
td("Developer"),
|
|
||||||
td("London"),
|
|
||||||
td("53"),
|
|
||||||
td("2009/10/22"),
|
|
||||||
td("$114,500")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Prescott Bartlett"),
|
|
||||||
td("Technical Author"),
|
|
||||||
td("London"),
|
|
||||||
td("27"),
|
|
||||||
td("2011/05/07"),
|
|
||||||
td("$145,000")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Gavin Cortez"),
|
|
||||||
td("Team Leader"),
|
|
||||||
td("San Francisco"),
|
|
||||||
td("22"),
|
|
||||||
td("2008/10/26"),
|
|
||||||
td("$235,500")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Martena Mccray"),
|
|
||||||
td("Post-Sales support"),
|
|
||||||
td("Edinburgh"),
|
|
||||||
td("46"),
|
|
||||||
td("2011/03/09"),
|
|
||||||
td("$324,050")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Unity Butler"),
|
|
||||||
td("Marketing Designer"),
|
|
||||||
td("San Francisco"),
|
|
||||||
td("47"),
|
|
||||||
td("2009/12/09"),
|
|
||||||
td("$85,675")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Howard Hatfield"),
|
|
||||||
td("Office Manager"),
|
|
||||||
td("San Francisco"),
|
|
||||||
td("51"),
|
|
||||||
td("2008/12/16"),
|
|
||||||
td("$164,500")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Hope Fuentes"),
|
|
||||||
td("Secretary"),
|
|
||||||
td("San Francisco"),
|
|
||||||
td("41"),
|
|
||||||
td("2010/02/12"),
|
|
||||||
td("$109,850")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Vivian Harrell"),
|
|
||||||
td("Financial Controller"),
|
|
||||||
td("San Francisco"),
|
|
||||||
td("62"),
|
|
||||||
td("2009/02/14"),
|
|
||||||
td("$452,500")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Timothy Mooney"),
|
|
||||||
td("Office Manager"),
|
|
||||||
td("London"),
|
|
||||||
td("37"),
|
|
||||||
td("2008/12/11"),
|
|
||||||
td("$136,200")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Jackson Bradshaw"),
|
|
||||||
td("Director"),
|
|
||||||
td("New York"),
|
|
||||||
td("65"),
|
|
||||||
td("2008/09/26"),
|
|
||||||
td("$645,750")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Olivia Liang"),
|
|
||||||
td("Support Engineer"),
|
|
||||||
td("Singapore"),
|
|
||||||
td("64"),
|
|
||||||
td("2011/02/03"),
|
|
||||||
td("$234,500")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Bruno Nash"),
|
|
||||||
td("Software Engineer"),
|
|
||||||
td("London"),
|
|
||||||
td("38"),
|
|
||||||
td("2011/05/03"),
|
|
||||||
td("$163,500")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Sakura Yamamoto"),
|
|
||||||
td("Support Engineer"),
|
|
||||||
td("Tokyo"),
|
|
||||||
td("37"),
|
|
||||||
td("2009/08/19"),
|
|
||||||
td("$139,575")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Thor Walton"),
|
|
||||||
td("Developer"),
|
|
||||||
td("New York"),
|
|
||||||
td("61"),
|
|
||||||
td("2013/08/11"),
|
|
||||||
td("$98,540")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Finn Camacho"),
|
|
||||||
td("Support Engineer"),
|
|
||||||
td("San Francisco"),
|
|
||||||
td("47"),
|
|
||||||
td("2009/07/07"),
|
|
||||||
td("$87,500")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Serge Baldwin"),
|
|
||||||
td("Data Coordinator"),
|
|
||||||
td("Singapore"),
|
|
||||||
td("64"),
|
|
||||||
td("2012/04/09"),
|
|
||||||
td("$138,575")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Zenaida Frank"),
|
|
||||||
td("Software Engineer"),
|
|
||||||
td("New York"),
|
|
||||||
td("63"),
|
|
||||||
td("2010/01/04"),
|
|
||||||
td("$125,250")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Zorita Serrano"),
|
|
||||||
td("Software Engineer"),
|
|
||||||
td("San Francisco"),
|
|
||||||
td("56"),
|
|
||||||
td("2012/06/01"),
|
|
||||||
td("$115,000")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Jennifer Acosta"),
|
|
||||||
td("Junior Javascript Developer"),
|
|
||||||
td("Edinburgh"),
|
|
||||||
td("43"),
|
|
||||||
td("2013/02/01"),
|
|
||||||
td("$75,650")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Cara Stevens"),
|
|
||||||
td("Sales Assistant"),
|
|
||||||
td("New York"),
|
|
||||||
td("46"),
|
|
||||||
td("2011/12/06"),
|
|
||||||
td("$145,600")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Hermione Butler"),
|
|
||||||
td("Regional Director"),
|
|
||||||
td("London"),
|
|
||||||
td("47"),
|
|
||||||
td("2011/03/21"),
|
|
||||||
td("$356,250")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Lael Greer"),
|
|
||||||
td("Systems Administrator"),
|
|
||||||
td("London"),
|
|
||||||
td("21"),
|
|
||||||
td("2009/02/27"),
|
|
||||||
td("$103,500")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Jonas Alexander"),
|
|
||||||
td("Developer"),
|
|
||||||
td("San Francisco"),
|
|
||||||
td("30"),
|
|
||||||
td("2010/07/14"),
|
|
||||||
td("$86,500")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Shad Decker"),
|
|
||||||
td("Regional Director"),
|
|
||||||
td("Edinburgh"),
|
|
||||||
td("51"),
|
|
||||||
td("2008/11/13"),
|
|
||||||
td("$183,000")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Michael Bruce"),
|
|
||||||
td("Javascript Developer"),
|
|
||||||
td("Singapore"),
|
|
||||||
td("29"),
|
|
||||||
td("2011/06/27"),
|
|
||||||
td("$183,000")
|
|
||||||
),
|
|
||||||
tr(
|
|
||||||
td("Donna Snider"),
|
|
||||||
td("Customer Support"),
|
|
||||||
td("New York"),
|
|
||||||
td("27"),
|
|
||||||
td("2011/01/25"),
|
|
||||||
td("$112,000")
|
|
||||||
)
|
|
||||||
),
|
|
||||||
tfoot(
|
|
||||||
tr(
|
|
||||||
th("Name"),
|
|
||||||
th("Position"),
|
|
||||||
th("Office"),
|
|
||||||
th("Age"),
|
|
||||||
th("Start date"),
|
|
||||||
th("Salary")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
@ -5,15 +5,15 @@ import com.softwaremill.tagging._
|
|||||||
import monix.bio._
|
import monix.bio._
|
||||||
import monix.eval.Coeval
|
import monix.eval.Coeval
|
||||||
import monix.{eval => me}
|
import monix.{eval => me}
|
||||||
|
import nova.monadic_sfx.ui.components.todo.Todo
|
||||||
|
import nova.monadic_sfx.ui.components.todo.TodoListStore
|
||||||
import outwatch._
|
import outwatch._
|
||||||
import outwatch.dsl._
|
import outwatch.dsl._
|
||||||
import outwatch.reactive.handlers.monix._
|
import outwatch.reactive.handlers.monix._
|
||||||
import outwatchapp.AppTypes
|
import outwatchapp.AppTypes
|
||||||
import outwatchapp.implicits._
|
import outwatchapp.implicits._
|
||||||
import outwatchapp.ui.components.todo.Todo
|
|
||||||
import outwatchapp.ui.components.todo.TodoListStore
|
|
||||||
import outwatchapp.util.reactive.store.Store
|
|
||||||
import sttp.client._
|
import sttp.client._
|
||||||
|
import nova.monadic_sfx.util.reactive.store.Store
|
||||||
|
|
||||||
sealed trait RequestDemo
|
sealed trait RequestDemo
|
||||||
object RequestDemo {
|
object RequestDemo {
|
||||||
@ -57,14 +57,15 @@ object RequestDemo {
|
|||||||
cls := "form-control",
|
cls := "form-control",
|
||||||
placeholder := "0",
|
placeholder := "0",
|
||||||
onInput.value --> requestSub
|
onInput.value --> requestSub
|
||||||
)
|
),
|
||||||
),
|
|
||||||
div(
|
|
||||||
cls := "form-group",
|
|
||||||
label(
|
label(
|
||||||
color := "hsla(0,0%,100%,0.8)",
|
color := "hsla(0,0%,100%,0.8)",
|
||||||
"Enter content for todo"
|
"Enter content for todo"
|
||||||
),
|
),
|
||||||
|
small(cls := "form-text text-muted", "default is 0")
|
||||||
|
),
|
||||||
|
div(
|
||||||
|
cls := "form-group",
|
||||||
input(
|
input(
|
||||||
cls := "form-control",
|
cls := "form-control",
|
||||||
onInput.value --> todoContent
|
onInput.value --> todoContent
|
||||||
@ -77,7 +78,8 @@ object RequestDemo {
|
|||||||
Coeval(println("Clicked"))
|
Coeval(println("Clicked"))
|
||||||
),
|
),
|
||||||
onClick(
|
onClick(
|
||||||
todoContent.map(TodoListStore.Add)
|
todoContent
|
||||||
|
.map(TodoListStore.Add)
|
||||||
) --> todoStore.sink
|
) --> todoStore.sink
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -85,7 +87,7 @@ object RequestDemo {
|
|||||||
),
|
),
|
||||||
div(
|
div(
|
||||||
p(
|
p(
|
||||||
cls := "text-white",
|
cls := "profile-description",
|
||||||
requestSub
|
requestSub
|
||||||
.doOnNext(str => me.Task(println(str)))
|
.doOnNext(str => me.Task(println(str)))
|
||||||
.mapEval(request)
|
.mapEval(request)
|
||||||
|
@ -1,104 +0,0 @@
|
|||||||
package outwatchapp.components
|
|
||||||
import scala.scalajs.js.annotation.JSImport
|
|
||||||
|
|
||||||
import monix.bio.Task
|
|
||||||
import outwatchapp.implicits._
|
|
||||||
import typings.sweetalert2.mod.SweetAlertIcon
|
|
||||||
import typings.sweetalert2.mod.SweetAlertOptions
|
|
||||||
import typings.sweetalert2.mod.SweetAlertPosition
|
|
||||||
|
|
||||||
import scalajs.js
|
|
||||||
|
|
||||||
object SweetAlertDemo {
|
|
||||||
@JSImport(
|
|
||||||
"@sweetalert2/themes/borderless/borderless.min.css",
|
|
||||||
JSImport.Namespace
|
|
||||||
)
|
|
||||||
@js.native
|
|
||||||
object SweetAlertBorderlessTheme extends js.Object
|
|
||||||
@JSImport("@sweetalert2/themes/dark/dark.min.css", JSImport.Namespace)
|
|
||||||
@js.native
|
|
||||||
object SweetAlertDarkTheme extends js.Object
|
|
||||||
@JSImport("sweetalert2/dist/sweetalert2.js", JSImport.Namespace)
|
|
||||||
@js.native
|
|
||||||
object SweetAlertJs extends js.Object
|
|
||||||
|
|
||||||
SweetAlertDarkTheme
|
|
||||||
val Swal = SweetAlertJs.asInstanceOf[typings.sweetalert2.mod.default.type]
|
|
||||||
|
|
||||||
val loginPrompt = {
|
|
||||||
import scalatags.JsDom.all._
|
|
||||||
|
|
||||||
val nameInput = input(
|
|
||||||
tpe := "text",
|
|
||||||
`id` := "loginName",
|
|
||||||
cls := "swal-input",
|
|
||||||
placeholder := "Username"
|
|
||||||
).render
|
|
||||||
val passInput = input(
|
|
||||||
tpe := "password",
|
|
||||||
`id` := "loginPass",
|
|
||||||
cls := "swal-input",
|
|
||||||
placeholder := "Password"
|
|
||||||
).render
|
|
||||||
val html = div(nameInput, passInput).render.asST
|
|
||||||
|
|
||||||
def nameAndPass(a: js.Any) = (nameInput.value, passInput.value)
|
|
||||||
|
|
||||||
Swal.fireL(
|
|
||||||
SweetAlertOptions()
|
|
||||||
.setTitle("Login Form")
|
|
||||||
.setHtml(html)
|
|
||||||
.setFocusConfirm(false)
|
|
||||||
.setConfirmButtonText("Sign In")
|
|
||||||
.setPreConfirm(nameAndPass _)
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
val successPrompt =
|
|
||||||
Swal.fireL(
|
|
||||||
SweetAlertOptions()
|
|
||||||
.setPosition(SweetAlertPosition.`top-end`)
|
|
||||||
.setIcon(SweetAlertIcon.success)
|
|
||||||
.setTitle("Login Success")
|
|
||||||
.setShowConfirmButton(false)
|
|
||||||
.setTimer(1500)
|
|
||||||
) >> Task.unit
|
|
||||||
|
|
||||||
val failurePrompt =
|
|
||||||
Swal.fireL(
|
|
||||||
SweetAlertOptions()
|
|
||||||
.setPosition(SweetAlertPosition.`top-end`)
|
|
||||||
.setIcon(SweetAlertIcon.error)
|
|
||||||
.setTitle("Login Failure")
|
|
||||||
.setShowConfirmButton(false)
|
|
||||||
.setTimer(1500)
|
|
||||||
) >> Task.unit
|
|
||||||
|
|
||||||
val dumbNotif =
|
|
||||||
Swal
|
|
||||||
.fireL(
|
|
||||||
SweetAlertOptions()
|
|
||||||
.setTitle("Hello there")
|
|
||||||
.setText("Welcome to outwatch app")
|
|
||||||
// .setInput(SweetAlertInput.text)
|
|
||||||
// .setShowCancelButton(true)
|
|
||||||
.setIcon(SweetAlertIcon.info)
|
|
||||||
// .setToast(true)
|
|
||||||
// .setPreConfirm { res: String =>
|
|
||||||
// val username = Swal.getPopup().asOption
|
|
||||||
// username match {
|
|
||||||
// case Some(value) =>
|
|
||||||
// println(s"Got value: $value")
|
|
||||||
// 1
|
|
||||||
// case None =>
|
|
||||||
// println("Got none")
|
|
||||||
// 2
|
|
||||||
// }
|
|
||||||
// res
|
|
||||||
// }
|
|
||||||
) >> Task.unit
|
|
||||||
// .map(_.value.toOption)
|
|
||||||
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package outwatchapp.components
|
package outwatchapp.components.todo
|
||||||
import scala.scalajs.js.|
|
import scala.scalajs.js.|
|
||||||
import scala.util.Random
|
import scala.util.Random
|
||||||
|
|
||||||
@ -6,10 +6,9 @@ import colibri.ext.monix._
|
|||||||
import com.softwaremill.tagging._
|
import com.softwaremill.tagging._
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import monix.eval.Coeval
|
import monix.eval.Coeval
|
||||||
import monix.execution.Cancelable
|
|
||||||
import outwatch.HtmlVNode
|
import outwatch.HtmlVNode
|
||||||
import outwatch.ManagedSubscriptions.managedElement
|
|
||||||
import outwatch.dsl._
|
import outwatch.dsl._
|
||||||
|
import outwatch.reactive.handlers.monix._
|
||||||
import typings.chartJs.mod._
|
import typings.chartJs.mod._
|
||||||
import typings.chartJs.mod.{^ => Chart}
|
import typings.chartJs.mod.{^ => Chart}
|
||||||
import typings.moment.mod.Moment
|
import typings.moment.mod.Moment
|
||||||
@ -21,19 +20,29 @@ import scalajs.js
|
|||||||
sealed trait ChartjsDemo
|
sealed trait ChartjsDemo
|
||||||
object ChartjsDemo {
|
object ChartjsDemo {
|
||||||
val random = new Random()
|
val random = new Random()
|
||||||
def apply(): Task[HtmlVNode @@ ChartjsDemo] = Coeval(
|
def apply(): Task[HtmlVNode @@ ChartjsDemo] = Task.deferAction(implicit s =>
|
||||||
div(
|
for {
|
||||||
|
canvasH <- Handler.createF[Task, HTMLCanvasElement]
|
||||||
|
} yield div(
|
||||||
canvas(
|
canvas(
|
||||||
managedElement { el =>
|
onDomMount.asHtml.map(a =>
|
||||||
val chart = new Chart(
|
a.asInstanceOf[HTMLCanvasElement]
|
||||||
el.asInstanceOf[HTMLCanvasElement],
|
) --> canvasH
|
||||||
chartConfig(ChartType.bar, randomData(100, random.nextInt()))
|
),
|
||||||
|
div(
|
||||||
|
canvasH
|
||||||
|
.doOnNextF(el =>
|
||||||
|
Coeval(
|
||||||
|
new Chart(
|
||||||
|
el,
|
||||||
|
chartConfig(ChartType.bar, randomData(100, random.nextInt()))
|
||||||
|
)
|
||||||
|
) >> Coeval.unit
|
||||||
)
|
)
|
||||||
Cancelable(() => chart.destroy())
|
.map(_ => div())
|
||||||
}
|
|
||||||
)
|
)
|
||||||
).taggedWith[ChartjsDemo]
|
).taggedWith[ChartjsDemo]
|
||||||
).to[Task]
|
)
|
||||||
|
|
||||||
def randomData(
|
def randomData(
|
||||||
max: Int,
|
max: Int,
|
@ -1,6 +1,6 @@
|
|||||||
package outwatchapp.components
|
package outwatchapp.components.todo
|
||||||
|
|
||||||
import outwatchapp.ui.components.todo.Todo
|
import nova.monadic_sfx.ui.components.todo.Todo
|
||||||
import typings.fuseJs.mod.Fuse.IFuseOptions
|
import typings.fuseJs.mod.Fuse.IFuseOptions
|
||||||
import typings.fuseJs.mod._
|
import typings.fuseJs.mod._
|
||||||
import typings.fuseJs.mod.{default => FuseC}
|
import typings.fuseJs.mod.{default => FuseC}
|
@ -1,18 +1,15 @@
|
|||||||
package outwatchapp.ui.components.todo
|
package nova.monadic_sfx.ui.components.todo
|
||||||
|
|
||||||
import scala.scalajs.js.annotation.JSExportAll
|
import scala.scalajs.js.annotation.JSExportAll
|
||||||
|
|
||||||
import cats.kernel.Eq
|
import cats.kernel.Eq
|
||||||
import com.softwaremill.quicklens._
|
import com.softwaremill.quicklens._
|
||||||
import io.circe.generic.JsonCodec
|
import io.circe.generic.JsonCodec
|
||||||
import io.odin.Logger
|
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import outwatchapp.util.reactive.store.Middlewares.actionLoggerMiddleware
|
import nova.monadic_sfx.util.reactive.store.Reducer
|
||||||
import outwatchapp.util.reactive.store.Reducer
|
import nova.monadic_sfx.util.reactive.store.Store
|
||||||
import outwatchapp.util.reactive.store.Store
|
|
||||||
|
|
||||||
@JSExportAll
|
@JSExportAll
|
||||||
@JsonCodec
|
|
||||||
case class Todo(id: Int, content: String)
|
case class Todo(id: Int, content: String)
|
||||||
object Todo {
|
object Todo {
|
||||||
implicit val eqForTodo = Eq.fromUniversalEquals[Todo]
|
implicit val eqForTodo = Eq.fromUniversalEquals[Todo]
|
||||||
@ -40,16 +37,18 @@ object TodoListStore {
|
|||||||
implicit val eqForState = Eq.fromUniversalEquals[State]
|
implicit val eqForState = Eq.fromUniversalEquals[State]
|
||||||
}
|
}
|
||||||
|
|
||||||
def reducer(logger: Logger[Task])(
|
def reducer()(
|
||||||
state: State,
|
state: State,
|
||||||
action: Action
|
action: Action
|
||||||
): (State, Option[Task[Action]]) =
|
): (State, Option[Task[Action]]) =
|
||||||
action match {
|
action match {
|
||||||
case Init => (state, None)
|
case Init => (state, None)
|
||||||
case Add(content) =>
|
case Add(content) =>
|
||||||
|
println("hello")
|
||||||
val nextAction = Some(for {
|
val nextAction = Some(for {
|
||||||
// do some validation
|
// do some validation
|
||||||
// _ <- logger.debug(s"Received $content")
|
// _ <- logger.debug(s"Received $content")
|
||||||
|
_ <- Task(println(s"Received $content"))
|
||||||
res <- Task.pure(InternalAdd(content))
|
res <- Task.pure(InternalAdd(content))
|
||||||
} yield res)
|
} yield res)
|
||||||
(state, nextAction)
|
(state, nextAction)
|
||||||
@ -69,21 +68,19 @@ object TodoListStore {
|
|||||||
.using(_ :+ Todo(state.counter, content))
|
.using(_ :+ Todo(state.counter, content))
|
||||||
.modify(_.counter)
|
.modify(_.counter)
|
||||||
.using(_ + 1)
|
.using(_ + 1)
|
||||||
(nextState, Some(logger.debug(s"Received $content") >> Task.pure(End)))
|
(nextState, Some(Task.pure(End)))
|
||||||
case End => (state, None)
|
case End => (state, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
def apply(logger: Logger[Task]): Task[Store[Action, State]] =
|
def apply(): Task[Store[Action, State]] =
|
||||||
Task.deferAction(implicit s =>
|
Task.deferAction(implicit s =>
|
||||||
for {
|
for {
|
||||||
logMware <- actionLoggerMiddleware[Action, State](logger, "TodoStore")
|
|
||||||
store <-
|
store <-
|
||||||
Store
|
Store
|
||||||
.createL[Action, State](
|
.createL[Action, State](
|
||||||
Init,
|
Init,
|
||||||
State(Vector.empty[Todo], 0),
|
State(Vector.empty[Todo], 0),
|
||||||
Reducer.withOptionalEffects(reducer(logger) _),
|
Reducer.withOptionalEffects(reducer() _)
|
||||||
Seq(logMware)
|
|
||||||
)
|
)
|
||||||
} yield store
|
} yield store
|
||||||
)
|
)
|
||||||
|
@ -1,19 +1,11 @@
|
|||||||
package outwatchapp
|
package outwatchapp
|
||||||
|
|
||||||
import scala.util.Try
|
|
||||||
|
|
||||||
import colibri.LiftSink
|
import colibri.LiftSink
|
||||||
import colibri.LiftSource
|
import colibri.LiftSource
|
||||||
import colibri.ext.monix._
|
import colibri.ext.monix._
|
||||||
import monix.bio.Task
|
|
||||||
import monix.execution.Scheduler
|
import monix.execution.Scheduler
|
||||||
import monix.reactive.Observable
|
import monix.reactive.Observable
|
||||||
import monix.reactive.Observer
|
import monix.reactive.Observer
|
||||||
import org.scalajs.dom.raw.HTMLElement
|
|
||||||
import typings.sweetalert2.mod.SweetAlertOptions
|
|
||||||
import typings.sweetalert2.mod.{default => Swal}
|
|
||||||
|
|
||||||
import scalajs.js.|
|
|
||||||
|
|
||||||
package object implicits {
|
package object implicits {
|
||||||
// implicit def sinkForMyMonixProSub[A, M] =
|
// implicit def sinkForMyMonixProSub[A, M] =
|
||||||
@ -28,26 +20,10 @@ package object implicits {
|
|||||||
// )
|
// )
|
||||||
// }
|
// }
|
||||||
|
|
||||||
implicit class Ops[A](val sink: Observer[A]) extends AnyVal {
|
implicit class Ops[A](val sink: Observer[A]) {
|
||||||
@inline def liftSink[G[_]: LiftSink]: G[A] = LiftSink[G].lift(sink)
|
@inline def liftSink[G[_]: LiftSink]: G[A] = LiftSink[G].lift(sink)
|
||||||
}
|
}
|
||||||
implicit class Ops2[A](val source: Observable[A]) extends AnyVal {
|
implicit class Ops2[A](val source: Observable[A])(implicit s: Scheduler) {
|
||||||
@inline def liftSource[G[_]: LiftSource](implicit s: Scheduler): G[A] =
|
@inline def liftSource[G[_]: LiftSource]: G[A] = LiftSource[G].lift(source)
|
||||||
LiftSource[G].lift(source)
|
|
||||||
}
|
|
||||||
|
|
||||||
implicit class HTMLElementOps[T <: HTMLElement](private val el: T)
|
|
||||||
extends AnyVal {
|
|
||||||
def asST = el.asInstanceOf[typings.std.HTMLElement]
|
|
||||||
}
|
|
||||||
implicit class SweetAlertOps(private val sw: Swal.type) extends AnyVal {
|
|
||||||
def fireL[T](options: SweetAlertOptions[T, _]) =
|
|
||||||
Task.deferFuture(sw.fire(options).toFuture)
|
|
||||||
}
|
|
||||||
implicit class NullUnionOps[A, B](private val union: A | B) extends AnyVal {
|
|
||||||
@scala.inline
|
|
||||||
def asOption(implicit ev: B =:= scala.Null) = Try(
|
|
||||||
union.asInstanceOf[A]
|
|
||||||
).toOption
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,59 +1,36 @@
|
|||||||
package outwatchapp.pages
|
package outwatchapp.pages
|
||||||
import cats.syntax.eq._
|
|
||||||
import com.softwaremill.tagging._
|
import com.softwaremill.tagging._
|
||||||
import monix.bio.Task
|
|
||||||
import outwatch._
|
import outwatch._
|
||||||
import outwatch.dsl._
|
import outwatch.dsl._
|
||||||
import outwatchapp.components.ChartjsDemo
|
|
||||||
import outwatchapp.components.CounterDemo
|
import outwatchapp.components.CounterDemo
|
||||||
import outwatchapp.components.SweetAlertDemo
|
import outwatchapp.components.todo.ChartjsDemo
|
||||||
|
|
||||||
class HomePage(
|
class HomePage(
|
||||||
counterDemo: VNode @@ CounterDemo,
|
counterDemo: VNode @@ CounterDemo,
|
||||||
chartDemo: VNode @@ ChartjsDemo
|
chartDemo: VNode @@ ChartjsDemo
|
||||||
) {
|
) {
|
||||||
val loginDemo = for {
|
def render = div(
|
||||||
res <- SweetAlertDemo.loginPrompt.map(_.value.toOption)
|
div(cls := "title", "Home"),
|
||||||
_ <- Task(println(s"Got $res"))
|
div(
|
||||||
_ <-
|
cls := "card",
|
||||||
if (res === Some("foo" -> "bar")) SweetAlertDemo.successPrompt
|
|
||||||
else SweetAlertDemo.failurePrompt
|
|
||||||
} yield ()
|
|
||||||
|
|
||||||
def render = Task.deferAction(implicit s =>
|
|
||||||
Task(
|
|
||||||
div(
|
div(
|
||||||
div(cls := "title", "Home"),
|
cls := "card-body",
|
||||||
div(
|
counterDemo,
|
||||||
cls := "card",
|
chartDemo,
|
||||||
|
p(
|
||||||
|
cls := "profile-description",
|
||||||
div(
|
div(
|
||||||
cls := "card-body",
|
"hm",
|
||||||
counterDemo,
|
htmlTag("blockQuote")(
|
||||||
chartDemo,
|
cls := "blockquote",
|
||||||
div(
|
p(
|
||||||
cls := "text-center",
|
cls := "mb-0",
|
||||||
button(
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante."
|
||||||
cls := "btn btn-primary",
|
),
|
||||||
onClick.preventDefault.doAsync(loginDemo),
|
footer(
|
||||||
"Login"
|
cls := "blockquote-footer",
|
||||||
)
|
"Someone famous in ",
|
||||||
),
|
cite(title := "Source Title", "Source Title")
|
||||||
p(
|
|
||||||
cls := "text-white",
|
|
||||||
div(
|
|
||||||
"hm",
|
|
||||||
htmlTag("blockQuote")(
|
|
||||||
cls := "blockquote",
|
|
||||||
p(
|
|
||||||
cls := "mb-0",
|
|
||||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante."
|
|
||||||
),
|
|
||||||
footer(
|
|
||||||
cls := "blockquote-footer",
|
|
||||||
"Someone famous in ",
|
|
||||||
cite(title := "Source Title", "Source Title")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
package outwatchapp.util
|
|
||||||
|
|
||||||
import cats.arrow.FunctionK
|
|
||||||
import monix.bio.IO
|
|
||||||
|
|
||||||
object IOUtils {
|
|
||||||
def toIO[T](task: monix.eval.Task[T]) =
|
|
||||||
IO.deferAction(implicit s => IO.from(task))
|
|
||||||
|
|
||||||
def toTask[T](bio: monix.bio.IO[Throwable, T]) =
|
|
||||||
monix.eval.Task.deferAction(implicit s => bio.to[monix.eval.Task])
|
|
||||||
|
|
||||||
val ioTaskMapk =
|
|
||||||
new FunctionK[monix.eval.Task, monix.bio.Task] {
|
|
||||||
|
|
||||||
override def apply[A](
|
|
||||||
fa: monix.eval.Task[A]
|
|
||||||
): monix.bio.Task[A] = toIO(fa)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
package outwatchapp.util
|
|
||||||
import scala.concurrent.Future
|
|
||||||
|
|
||||||
import typings.paralleljs.ParallelOptions
|
|
||||||
import typings.paralleljs.mod.{^ => Parallel}
|
|
||||||
|
|
||||||
import scalajs.js
|
|
||||||
import scalajs.js.|
|
|
||||||
|
|
||||||
object ParallelDemo {
|
|
||||||
val p =
|
|
||||||
new Parallel(js.Array(1, 2, 3, 4, 5), ParallelOptions().setMaxWorkers(2))
|
|
||||||
// val pr = new js.Promise()
|
|
||||||
|
|
||||||
// p.map((_: Int) * 2)
|
|
||||||
|
|
||||||
def toFuture[A](p: Parallel[A]): Future[A] = {
|
|
||||||
val p2 = scala.concurrent.Promise[A]()
|
|
||||||
p.`then`(
|
|
||||||
{ (v: A) =>
|
|
||||||
p2.success(v)
|
|
||||||
(): Unit | js.Thenable[Unit]
|
|
||||||
},
|
|
||||||
{ (e: typings.std.Error) =>
|
|
||||||
p2.failure(e match {
|
|
||||||
case th: Throwable => th
|
|
||||||
case _ => js.JavaScriptException(e)
|
|
||||||
})
|
|
||||||
(): Unit | js.Thenable[Unit]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
p2.future
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package outwatchapp.util
|
|
||||||
|
|
||||||
// import typings.std.{SubtleCrypto => TS}
|
|
||||||
// import org.scalajs.dom.crypto.SubtleCrypto
|
|
||||||
import typings.std.global.Uint8Array
|
|
||||||
import typings.std.global.{SubtleCrypto => TS}
|
|
||||||
|
|
||||||
object SubtleCryptoTest {
|
|
||||||
val sc = new TS()
|
|
||||||
// Algorithm
|
|
||||||
// sc.deriveKey
|
|
||||||
// val n = WindowBase64
|
|
||||||
new Uint8Array(10)
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
// package outwatchapp.util
|
|
||||||
|
|
||||||
// import org.scalajs.dom
|
|
||||||
|
|
||||||
// import scala.scalajs.js
|
|
||||||
// import scala.scalajs.js.annotation.JSExport
|
|
||||||
// import org.scalajs.dom.webworkers.DedicatedWorkerGlobalScope
|
|
||||||
// import scala.scalajs.js.annotation.JSGlobalScope
|
|
||||||
// import scala.scalajs.js.annotation.JSExportTopLevel
|
|
||||||
|
|
||||||
// // @js.native
|
|
||||||
// // object WorkerGlobal extends js.GlobalScope {
|
|
||||||
// // def addEventListener(`type`: String, f: js.Function): Unit = js.native
|
|
||||||
// // def postMessage(data: js.Any): Unit = js.native
|
|
||||||
// // }
|
|
||||||
|
|
||||||
// @js.native
|
|
||||||
// @JSGlobalScope
|
|
||||||
// object WorkerGlobal extends DedicatedWorkerGlobalScope
|
|
||||||
|
|
||||||
// @JSExportTopLevel("WorkerMain")
|
|
||||||
// object WorkerMain {
|
|
||||||
// @JSExport
|
|
||||||
// def main(): Unit = {
|
|
||||||
// // WorkerGlobal.addEventListener("message", onMessage _)
|
|
||||||
// WorkerGlobal.onmessage = onMessage _
|
|
||||||
// WorkerGlobal.postMessage(s"Started")
|
|
||||||
// }
|
|
||||||
|
|
||||||
// val timeMessage = """Time.*""".r
|
|
||||||
// var count = 0
|
|
||||||
|
|
||||||
// def onMessage(msg: dom.MessageEvent) = {
|
|
||||||
// val s = msg.data.asInstanceOf[String]
|
|
||||||
// s match {
|
|
||||||
// case timeMessage() =>
|
|
||||||
// count += 1
|
|
||||||
// if (count % 600 == 0)
|
|
||||||
// WorkerGlobal.postMessage("60fps")
|
|
||||||
// case _ =>
|
|
||||||
// WorkerGlobal.postMessage(s"Received: $s")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
@ -1,77 +0,0 @@
|
|||||||
package outwatchapp.util.reactive
|
|
||||||
|
|
||||||
import io.circe.Decoder
|
|
||||||
import io.circe.Encoder
|
|
||||||
import io.circe.Printer
|
|
||||||
import io.circe.parser._
|
|
||||||
import io.circe.syntax._
|
|
||||||
import monix.execution.Ack
|
|
||||||
import monix.execution.Cancelable
|
|
||||||
import monix.execution.cancelables.SingleAssignCancelable
|
|
||||||
import monix.reactive.Observable
|
|
||||||
import monix.reactive.Observer
|
|
||||||
import monix.reactive.OverflowStrategy
|
|
||||||
import org.scalajs.dom.raw.MessageEvent
|
|
||||||
import org.scalajs.dom.webworkers.DedicatedWorkerGlobalScope
|
|
||||||
import scala.concurrent.Future
|
|
||||||
import outwatchapp.util.reactive.Exceptions.WrongTypeException
|
|
||||||
// @js.native
|
|
||||||
// @JSGlobalScope
|
|
||||||
// object WorkerGlobal extends DedicatedWorkerGlobalScope
|
|
||||||
|
|
||||||
// class DedicatedWorkerImpl[T: Encoder: Decoder](wg: DedicatedWorkerGlobalScope) {
|
|
||||||
// def run = Task.deferAction(implicit s =>
|
|
||||||
// for {
|
|
||||||
// _ <- Task.unit
|
|
||||||
// sub <- Task(ConcurrentSubject.publish[T])
|
|
||||||
// } yield ()
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
object DedicatedWorker {
|
|
||||||
def source[T: Decoder](wg: DedicatedWorkerGlobalScope) =
|
|
||||||
Observable.create[T](OverflowStrategy.DropOld(50)) { sub =>
|
|
||||||
val c = SingleAssignCancelable()
|
|
||||||
def onmessageFn(event: MessageEvent): Unit = {
|
|
||||||
event.data match {
|
|
||||||
case s: String =>
|
|
||||||
decode[T](s)
|
|
||||||
.map { res =>
|
|
||||||
if (sub.onNext(res) == Ack.Stop) c.cancel()
|
|
||||||
res
|
|
||||||
}
|
|
||||||
.left
|
|
||||||
.foreach(err =>
|
|
||||||
sub.onError(
|
|
||||||
new Exception(s"Failed to decode $s. Error was $err")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
case other =>
|
|
||||||
sub.onError(WrongTypeException(s"Received wrong type: $other"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wg.onmessage = onmessageFn _
|
|
||||||
|
|
||||||
c := Cancelable(() => wg.onmessage = _ => ())
|
|
||||||
}
|
|
||||||
|
|
||||||
def sink[T: Encoder](wg: DedicatedWorkerGlobalScope) =
|
|
||||||
new Observer[T] {
|
|
||||||
val printer = Printer.noSpaces
|
|
||||||
|
|
||||||
override def onNext(elem: T): Future[Ack] = {
|
|
||||||
// wg.onoffline
|
|
||||||
wg.postMessage(printer.print(elem.asJson))
|
|
||||||
Ack.Continue
|
|
||||||
}
|
|
||||||
|
|
||||||
override def onError(ex: Throwable): Unit = ex.printStackTrace()
|
|
||||||
|
|
||||||
override def onComplete(): Unit = println("Worker observer completed")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
def apply[T: Encoder: Decoder](
|
|
||||||
wg: DedicatedWorkerGlobalScope
|
|
||||||
): DedicatedWorker[T] = MonixProSubject.from(sink(wg), source(wg))
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
package outwatchapp.util.reactive
|
|
||||||
|
|
||||||
object Exceptions {
|
|
||||||
//cause: Option[Throwable]) extends Exception(message, cause.orNull)
|
|
||||||
sealed abstract class ReactiveException(val message: String)
|
|
||||||
extends Throwable(message)
|
|
||||||
final case class DecodeException(override val message: String)
|
|
||||||
extends ReactiveException(message)
|
|
||||||
final case class WrongTypeException(override val message: String)
|
|
||||||
extends ReactiveException(message)
|
|
||||||
final case class UseAfterClose(override val message: String)
|
|
||||||
extends ReactiveException(message)
|
|
||||||
final case class TerminatedException(override val message: String)
|
|
||||||
extends ReactiveException(message)
|
|
||||||
|
|
||||||
}
|
|
@ -1,165 +0,0 @@
|
|||||||
package outwatchapp.util.reactive
|
|
||||||
|
|
||||||
import scala.concurrent.Future
|
|
||||||
|
|
||||||
import io.circe.Decoder
|
|
||||||
import io.circe.Encoder
|
|
||||||
import io.circe.Printer
|
|
||||||
import io.circe.parser._
|
|
||||||
import io.circe.syntax._
|
|
||||||
import monix.bio.Task
|
|
||||||
import monix.execution.Ack
|
|
||||||
import monix.execution.Cancelable
|
|
||||||
import monix.execution.CancelablePromise
|
|
||||||
import monix.execution.cancelables.SingleAssignCancelable
|
|
||||||
import monix.reactive.Observable
|
|
||||||
import monix.reactive.Observer
|
|
||||||
import monix.reactive.OverflowStrategy
|
|
||||||
import typings.reconnectingWebsocket.eventsMod.CloseEvent
|
|
||||||
import typings.reconnectingWebsocket.eventsMod.ErrorEvent
|
|
||||||
import typings.reconnectingWebsocket.eventsMod.Event
|
|
||||||
import typings.reconnectingWebsocket.mod.{default => RW}
|
|
||||||
|
|
||||||
import scalajs.js
|
|
||||||
import typings.std.MessageEvent
|
|
||||||
import typings.reconnectingWebsocket.mod.Options
|
|
||||||
import outwatchapp.util.reactive.Exceptions.DecodeException
|
|
||||||
import outwatchapp.util.reactive.Exceptions.WrongTypeException
|
|
||||||
import monix.reactive.observers.Subscriber
|
|
||||||
import monix.reactive.observers.BufferedSubscriber
|
|
||||||
import outwatchapp.util.reactive.Exceptions.UseAfterClose
|
|
||||||
import monix.bio.IO
|
|
||||||
|
|
||||||
class ReconnectingWebSocketImpl[T: Encoder: Decoder](
|
|
||||||
ws: RW,
|
|
||||||
overflowStrategy: OverflowStrategy.Synchronous[T]
|
|
||||||
) {
|
|
||||||
val printer = Printer.noSpaces
|
|
||||||
|
|
||||||
/** @throws ReactiveException
|
|
||||||
*/
|
|
||||||
val source: Task[Observable[T]] =
|
|
||||||
Task.deferAction(implicit s =>
|
|
||||||
for {
|
|
||||||
obs <- Task(
|
|
||||||
Observable
|
|
||||||
.create[T](overflowStrategy) { sub =>
|
|
||||||
val c = SingleAssignCancelable()
|
|
||||||
val onmessage: js.Function1[MessageEvent[_], Unit] =
|
|
||||||
_.data match {
|
|
||||||
case s: String =>
|
|
||||||
decode[T](s)
|
|
||||||
.map { res =>
|
|
||||||
if (sub.onNext(res) == Ack.Stop) c.cancel()
|
|
||||||
res
|
|
||||||
}
|
|
||||||
.left
|
|
||||||
.foreach(err =>
|
|
||||||
sub.onError(
|
|
||||||
DecodeException(
|
|
||||||
s"Failed to decode $s. Error was $err"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
case other =>
|
|
||||||
sub.onError(
|
|
||||||
WrongTypeException(s"Received wrong type: $other")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val emptyMsgFn: js.Function1[MessageEvent[_], Unit] =
|
|
||||||
_ => println("message fn not initialized")
|
|
||||||
ws.onmessage = onmessage
|
|
||||||
|
|
||||||
val onclose: js.Function1[CloseEvent, Unit] = a => {
|
|
||||||
println(
|
|
||||||
s"Websocket closing - ${a.code} ${a.wasClean} ${a.reason}"
|
|
||||||
)
|
|
||||||
sub.onComplete()
|
|
||||||
}
|
|
||||||
ws.onclose = onclose
|
|
||||||
|
|
||||||
val onerror: js.Function1[ErrorEvent, Unit] = (e: Event) =>
|
|
||||||
sub.onError(new Exception(s"Error in WebSocket: $e"))
|
|
||||||
|
|
||||||
ws.onerror = onerror
|
|
||||||
|
|
||||||
c := Cancelable { () => ws.onmessage = emptyMsgFn }
|
|
||||||
}
|
|
||||||
.publish
|
|
||||||
.refCount
|
|
||||||
)
|
|
||||||
_ <- Task(obs.subscribe(Observer.empty))
|
|
||||||
} yield obs
|
|
||||||
)
|
|
||||||
|
|
||||||
val sink: Task[Observer[T]] =
|
|
||||||
Task.deferAction(implicit s =>
|
|
||||||
Task(
|
|
||||||
BufferedSubscriber(
|
|
||||||
new Subscriber[T] {
|
|
||||||
import monix.execution.FutureUtils
|
|
||||||
import scala.concurrent.duration._
|
|
||||||
override def scheduler = s
|
|
||||||
override def onNext(elem: T): Future[Ack] = {
|
|
||||||
val msg = printer.print(elem.asJson)
|
|
||||||
if (ws.readyState == 2 || ws.readyState == 3)
|
|
||||||
Ack.Stop
|
|
||||||
else {
|
|
||||||
FutureUtils
|
|
||||||
.delayedResult(2.second)(ws.send(msg))
|
|
||||||
.flatMap(_ => Ack.Continue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override def onError(ex: Throwable): Unit = s.reportFailure(ex)
|
|
||||||
override def onComplete(): Unit = println("CLOSING WEBSOCKET 2 ")
|
|
||||||
},
|
|
||||||
OverflowStrategy.Default
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
object ReconnectingWebSocket {
|
|
||||||
sealed trait Error
|
|
||||||
final case object Error extends Error
|
|
||||||
|
|
||||||
/** @throws ReactiveException
|
|
||||||
*/
|
|
||||||
final case class Source[T](value: Observable[T])
|
|
||||||
|
|
||||||
/** A buffered Observer
|
|
||||||
*
|
|
||||||
* @param value
|
|
||||||
*/
|
|
||||||
final case class Sink[T](value: Observer[T]) {
|
|
||||||
def send(t: T) =
|
|
||||||
IO.deferFuture(value.onNext(t)).flatMap {
|
|
||||||
case Ack.Continue => Task.unit
|
|
||||||
case Ack.Stop => IO.raiseError(UseAfterClose("Websocket was closed"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val defaultOptions = Options()
|
|
||||||
.setMinReconnectionDelay(5000)
|
|
||||||
.setMaxRetries(2)
|
|
||||||
.setMaxEnqueuedMessages(50)
|
|
||||||
|
|
||||||
def onopen[A](op: => A): js.Function1[Event, Unit] = _ => op
|
|
||||||
|
|
||||||
def apply[T <: Product: Encoder: Decoder](
|
|
||||||
path: String,
|
|
||||||
options: Options = defaultOptions,
|
|
||||||
overflowStrategy: OverflowStrategy.Synchronous[T] =
|
|
||||||
OverflowStrategy.DropOld(50)
|
|
||||||
) = for {
|
|
||||||
websocket <- Task(new RW(path, js.undefined, options))
|
|
||||||
p = CancelablePromise[Unit]()
|
|
||||||
_ <- Task(websocket.onopen = onopen(p.success(())))
|
|
||||||
_ <- Task.deferFuture(p.future)
|
|
||||||
_ <- Task(websocket.onopen = onopen(()))
|
|
||||||
impl = new ReconnectingWebSocketImpl[T](websocket, overflowStrategy)
|
|
||||||
source <- impl.source
|
|
||||||
sink <- impl.sink
|
|
||||||
// _ <- Task.deferAction(implicit s => Task(source.subscribe(sink)))
|
|
||||||
} yield Source(source) -> Sink(sink)
|
|
||||||
}
|
|
@ -1,132 +0,0 @@
|
|||||||
package outwatchapp.util.reactive
|
|
||||||
|
|
||||||
import scala.concurrent.Future
|
|
||||||
|
|
||||||
import io.circe.Decoder
|
|
||||||
import io.circe.Encoder
|
|
||||||
import io.circe.Printer
|
|
||||||
import io.circe.generic.JsonCodec
|
|
||||||
import io.circe.parser._
|
|
||||||
import io.circe.syntax._
|
|
||||||
import monix.bio.Task
|
|
||||||
import monix.execution.Ack
|
|
||||||
import monix.execution.Cancelable
|
|
||||||
import monix.execution.CancelablePromise
|
|
||||||
import monix.execution.cancelables.SingleAssignCancelable
|
|
||||||
import monix.reactive.Observable
|
|
||||||
import monix.reactive.Observer
|
|
||||||
import monix.reactive.OverflowStrategy
|
|
||||||
import org.scalajs.dom.raw.Event
|
|
||||||
import org.scalajs.dom.raw.MessageEvent
|
|
||||||
import org.scalajs.dom.{raw => sjsdr}
|
|
||||||
import outwatchapp.util.reactive.MonixProSubject
|
|
||||||
import monix.reactive.observers.Subscriber
|
|
||||||
import monix.execution.Scheduler
|
|
||||||
import monix.reactive.observers.BufferedSubscriber
|
|
||||||
import monix.execution.cancelables.CompositeCancelable
|
|
||||||
import scala.concurrent.duration._
|
|
||||||
|
|
||||||
class WebSocketImpl[T: Encoder: Decoder](
|
|
||||||
ws: sjsdr.WebSocket,
|
|
||||||
overflowStrategy: OverflowStrategy.Synchronous[T]
|
|
||||||
) {
|
|
||||||
val printer = Printer.noSpaces
|
|
||||||
|
|
||||||
val source: Task[Observable[T]] =
|
|
||||||
Task.deferAction(implicit s =>
|
|
||||||
for {
|
|
||||||
c <- Task(CompositeCancelable())
|
|
||||||
obs <- Task(
|
|
||||||
Observable
|
|
||||||
.create[T](overflowStrategy) { sub =>
|
|
||||||
// val c = SingleAssignCancelable()
|
|
||||||
ws.onmessage = (e: MessageEvent) =>
|
|
||||||
e.data match {
|
|
||||||
case s: String =>
|
|
||||||
decode[T](s)
|
|
||||||
.map { res =>
|
|
||||||
if (sub.onNext(res) == Ack.Stop) c.cancel()
|
|
||||||
res
|
|
||||||
}
|
|
||||||
.left
|
|
||||||
.foreach(err =>
|
|
||||||
sub.onError(
|
|
||||||
new Exception(s"Failed to decode $s. Error was $err")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
case other =>
|
|
||||||
sub.onError(new Exception(s"Received wrong type: $other"))
|
|
||||||
}
|
|
||||||
ws.onclose = a => {
|
|
||||||
println(
|
|
||||||
s"Websocket closing - ${a.code} ${a.wasClean} ${a.reason}"
|
|
||||||
)
|
|
||||||
sub.onComplete()
|
|
||||||
}
|
|
||||||
ws.onerror = (e: Event) => {
|
|
||||||
println("Error in websocket")
|
|
||||||
sub.onError(new Exception(s"Error in WebSocket: $e"))
|
|
||||||
}
|
|
||||||
c += Cancelable { () =>
|
|
||||||
println("Closing websocket")
|
|
||||||
ws.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.publish
|
|
||||||
.refCount
|
|
||||||
)
|
|
||||||
// empty subscription because otherwise ouwtatch kills the observable when
|
|
||||||
// the dom containing this observable is unmounted
|
|
||||||
_ <- Task(c += obs.subscribe(Observer.empty))
|
|
||||||
} yield obs
|
|
||||||
)
|
|
||||||
|
|
||||||
val sink: Task[Subscriber[T]] =
|
|
||||||
Task.deferAction(implicit s =>
|
|
||||||
Task(
|
|
||||||
BufferedSubscriber(
|
|
||||||
new Subscriber[T] {
|
|
||||||
override implicit def scheduler: Scheduler = s
|
|
||||||
|
|
||||||
override def onNext(elem: T): Future[Ack] = {
|
|
||||||
val msg = printer.print(elem.asJson)
|
|
||||||
if (ws.readyState == 2 || ws.readyState == 3)
|
|
||||||
// Future.failed(new Exception("Websocket closed"))
|
|
||||||
Future.successful(Ack.Stop)
|
|
||||||
else {
|
|
||||||
ws.send(msg)
|
|
||||||
Future.successful(Ack.Continue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override def onError(ex: Throwable): Unit = println(ex)
|
|
||||||
override def onComplete(): Unit = ()
|
|
||||||
},
|
|
||||||
OverflowStrategy.BackPressure(50)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
object WebSocket {
|
|
||||||
sealed trait Error
|
|
||||||
final case object Error extends Error
|
|
||||||
|
|
||||||
def apply[T <: Product: Encoder: Decoder](
|
|
||||||
path: String,
|
|
||||||
overflowStrategy: OverflowStrategy.Synchronous[T] =
|
|
||||||
OverflowStrategy.DropOld(50)
|
|
||||||
): Task[WebSocket[T]] = for {
|
|
||||||
websocket <- Task(new sjsdr.WebSocket(path))
|
|
||||||
p <- Task(CancelablePromise[Unit]())
|
|
||||||
// wait for websocket to open
|
|
||||||
_ <- Task(websocket.onopen = _ => p.success(()))
|
|
||||||
_ <- Task.deferFuture(p.future).timeout(10.seconds)
|
|
||||||
_ <- Task(websocket.onopen = _ => ())
|
|
||||||
impl = new WebSocketImpl[T](websocket, overflowStrategy)
|
|
||||||
source <- impl.source
|
|
||||||
sink <- impl.sink
|
|
||||||
} yield MonixProSubject.from(sink, source)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonCodec
|
|
||||||
case class WebsocketData(data: Long)
|
|
@ -1,121 +0,0 @@
|
|||||||
package outwatchapp.util.reactive
|
|
||||||
|
|
||||||
import scala.concurrent.Future
|
|
||||||
|
|
||||||
import io.circe.Decoder
|
|
||||||
import io.circe.Encoder
|
|
||||||
import io.circe.Printer
|
|
||||||
import io.circe.generic.JsonCodec
|
|
||||||
import io.circe.parser._
|
|
||||||
import io.circe.syntax._
|
|
||||||
import monix.bio.Task
|
|
||||||
import monix.execution.Ack
|
|
||||||
import monix.execution.Cancelable
|
|
||||||
import monix.execution.cancelables.SingleAssignCancelable
|
|
||||||
import monix.reactive.Observable
|
|
||||||
import monix.reactive.Observer
|
|
||||||
import monix.reactive.OverflowStrategy
|
|
||||||
import org.scalajs.dom.raw.Event
|
|
||||||
import org.scalajs.dom.raw.MessageEvent
|
|
||||||
import org.scalajs.dom.{raw => sjsdr}
|
|
||||||
import outwatchapp.util.reactive.MonixProSubject
|
|
||||||
import monix.execution.cancelables.CompositeCancelable
|
|
||||||
import monix.eval.Coeval
|
|
||||||
import monix.reactive.observers.Subscriber
|
|
||||||
import monix.execution.Scheduler
|
|
||||||
import monix.reactive.observers.BufferedSubscriber
|
|
||||||
import outwatchapp.util.reactive.Exceptions.DecodeException
|
|
||||||
import outwatchapp.util.reactive.Exceptions.WrongTypeException
|
|
||||||
import outwatchapp.util.reactive.Exceptions.TerminatedException
|
|
||||||
|
|
||||||
class WebWorkerImpl[T <: Product: Encoder: Decoder](
|
|
||||||
worker: sjsdr.Worker,
|
|
||||||
overflowStrategy: OverflowStrategy.Synchronous[T]
|
|
||||||
) {
|
|
||||||
|
|
||||||
val printer = Printer.noSpaces
|
|
||||||
|
|
||||||
val source: Task[Observable[T]] =
|
|
||||||
Task.deferAction(implicit s =>
|
|
||||||
for {
|
|
||||||
c <- Task(CompositeCancelable())
|
|
||||||
obs <- Task(
|
|
||||||
Observable
|
|
||||||
.create[T](overflowStrategy) { sub =>
|
|
||||||
// val c = SingleAssignCancelable()
|
|
||||||
worker.onmessage = (e: MessageEvent) =>
|
|
||||||
e.data match {
|
|
||||||
case s: String =>
|
|
||||||
decode[T](s)
|
|
||||||
.map { res =>
|
|
||||||
if (sub.onNext(res) == Ack.Stop) c.cancel()
|
|
||||||
res
|
|
||||||
}
|
|
||||||
.left
|
|
||||||
.foreach(err =>
|
|
||||||
sub.onError(
|
|
||||||
DecodeException(
|
|
||||||
s"Failed to decode $s. Error was $err"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
case other =>
|
|
||||||
sub.onError(
|
|
||||||
WrongTypeException(s"Received wrong type: $other")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
worker.onerror = (e: Event) =>
|
|
||||||
sub.onError(
|
|
||||||
TerminatedException(s"Worker terminated with error: $e")
|
|
||||||
)
|
|
||||||
c += Cancelable(() => worker.terminate())
|
|
||||||
}
|
|
||||||
.doOnSubscriptionCancelF(Coeval(println("Worker cancelled")))
|
|
||||||
.publish
|
|
||||||
.refCount
|
|
||||||
)
|
|
||||||
_ <- Task(c += obs.subscribe(Observer.empty))
|
|
||||||
} yield obs
|
|
||||||
)
|
|
||||||
|
|
||||||
val sink: Task[Observer[T]] =
|
|
||||||
Task.deferAction(implicit s =>
|
|
||||||
Task(
|
|
||||||
BufferedSubscriber(
|
|
||||||
new Subscriber[T] {
|
|
||||||
|
|
||||||
override implicit def scheduler: Scheduler = s
|
|
||||||
|
|
||||||
override def onNext(elem: T): Future[Ack] = {
|
|
||||||
val msg = printer.print(elem.asJson)
|
|
||||||
worker.postMessage(msg)
|
|
||||||
Ack.Continue
|
|
||||||
}
|
|
||||||
override def onError(ex: Throwable): Unit = s.reportFailure(ex)
|
|
||||||
override def onComplete(): Unit = ()
|
|
||||||
},
|
|
||||||
OverflowStrategy.Default
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
object WebWorker {
|
|
||||||
sealed trait Error
|
|
||||||
final case object Error extends Error
|
|
||||||
|
|
||||||
def apply[T <: Product: Encoder: Decoder](
|
|
||||||
path: String,
|
|
||||||
overflowStrategy: OverflowStrategy.Synchronous[T] =
|
|
||||||
OverflowStrategy.DropOld(50)
|
|
||||||
): Task[WebWorker[T]] = for {
|
|
||||||
worker <- Task(new sjsdr.Worker(path))
|
|
||||||
impl = new WebWorkerImpl[T](worker, overflowStrategy)
|
|
||||||
source <- impl.source
|
|
||||||
sink <- impl.sink
|
|
||||||
} yield MonixProSubject.from(sink, source)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonCodec
|
|
||||||
case class WorkerData(data: Long)
|
|
@ -1,12 +0,0 @@
|
|||||||
package outwatchapp.util
|
|
||||||
|
|
||||||
import monix.reactive.Observable
|
|
||||||
import monix.reactive.Observer
|
|
||||||
|
|
||||||
package object reactive {
|
|
||||||
type MonixProSubject[-I, +O] = Observable[O] with Observer[I]
|
|
||||||
type MonixSubject[A] = MonixProSubject[A, A]
|
|
||||||
type WebWorker[A] = MonixSubject[A]
|
|
||||||
type WebSocket[A] = MonixSubject[A]
|
|
||||||
type DedicatedWorker[A] = MonixSubject[A]
|
|
||||||
}
|
|
@ -1,18 +1,12 @@
|
|||||||
package outwatchapp.util.reactive.store
|
package nova.monadic_sfx.util.reactive.store
|
||||||
|
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
import io.circe.Encoder
|
|
||||||
import io.circe.Printer
|
import io.circe.Printer
|
||||||
import io.circe.generic.JsonCodec
|
import io.circe.generic.JsonCodec
|
||||||
import io.circe.syntax._
|
// object Middleware {
|
||||||
import io.odin.Logger
|
// def apply[A,M,T](ob: Observable[(A,M)], cb: (A,M) => T): Observable[(A,M)] = ob
|
||||||
import io.odin.LoggerMessage
|
// }
|
||||||
import io.odin.formatter.options.PositionFormat
|
|
||||||
import io.odin.formatter.options.ThrowableFormat
|
|
||||||
import io.odin.meta.Render
|
|
||||||
import monix.bio.Task
|
|
||||||
import monix.reactive.Observable
|
|
||||||
|
|
||||||
@JsonCodec
|
@JsonCodec
|
||||||
final case class StoreInfo[A](
|
final case class StoreInfo[A](
|
||||||
@ -23,42 +17,6 @@ final case class StoreInfo[A](
|
|||||||
|
|
||||||
object StoreInfo {
|
object StoreInfo {
|
||||||
val printer = Printer.noSpaces
|
val printer = Printer.noSpaces
|
||||||
implicit def render[T: Encoder]: Render[StoreInfo[T]] =
|
|
||||||
new Render[StoreInfo[T]] {
|
|
||||||
override def render(m: StoreInfo[T]): String = printer.print(m.asJson)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Middlewares {
|
object Middlewares {}
|
||||||
|
|
||||||
val format = create(ThrowableFormat.Default, PositionFormat.Full)
|
|
||||||
|
|
||||||
def create(
|
|
||||||
throwableFormat: ThrowableFormat,
|
|
||||||
positionFormat: PositionFormat
|
|
||||||
): io.odin.formatter.Formatter = (msg: LoggerMessage) => msg.message.value
|
|
||||||
|
|
||||||
def actionStateLoggerMiddleware[A, M](
|
|
||||||
logger: Logger[Task]
|
|
||||||
): Task[Middleware[A, M]] =
|
|
||||||
Task.deferAction(implicit s =>
|
|
||||||
Task((obs: Observable[(A, M)]) =>
|
|
||||||
obs.doOnNextF { case (a, m) =>
|
|
||||||
logger.debug(s"Received action $a with state $m")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def actionLoggerMiddleware[A: Encoder, M](
|
|
||||||
logger: Logger[Task],
|
|
||||||
name: String
|
|
||||||
): Task[Middleware[A, M]] =
|
|
||||||
Task.deferAction(implicit s =>
|
|
||||||
Task((obs: Observable[(A, M)]) =>
|
|
||||||
obs.doOnNextF { case (a, _) =>
|
|
||||||
Task(LocalDateTime.now())
|
|
||||||
.flatMap(curTime => logger.debug(StoreInfo(name, a, curTime)))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package outwatchapp.util.reactive
|
package nova.monadic_sfx.util.reactive.store
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package outwatchapp.util.reactive.store
|
package nova.monadic_sfx.util.reactive.store
|
||||||
|
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import monix.reactive.Observable
|
import monix.reactive.Observable
|
||||||
@ -6,7 +6,8 @@ import monix.reactive.ObservableLike
|
|||||||
|
|
||||||
object Reducer {
|
object Reducer {
|
||||||
|
|
||||||
/** Creates a Reducer which yields a new State, as-well as an Observable of Effects
|
/**
|
||||||
|
* Creates a Reducer which yields a new State, as-well as an Observable of Effects
|
||||||
* Effects are Actions which will be executed after the Action that caused them to occur.
|
* Effects are Actions which will be executed after the Action that caused them to occur.
|
||||||
* This is accomplished by subscribing to the Effects Observable within the stores scan loop.
|
* This is accomplished by subscribing to the Effects Observable within the stores scan loop.
|
||||||
*
|
*
|
||||||
@ -19,12 +20,14 @@ object Reducer {
|
|||||||
f: (M, A) => (M, F[A])
|
f: (M, A) => (M, F[A])
|
||||||
): Reducer[A, M] = (s: M, a: A) => f(s, a).map(ObservableLike[F].apply)
|
): Reducer[A, M] = (s: M, a: A) => f(s, a).map(ObservableLike[F].apply)
|
||||||
|
|
||||||
/** Creates a reducer which just transforms the state, without additional effects.
|
/**
|
||||||
|
* Creates a reducer which just transforms the state, without additional effects.
|
||||||
*/
|
*/
|
||||||
def apply[A, M](f: (M, A) => M): Reducer[A, M] =
|
def apply[A, M](f: (M, A) => M): Reducer[A, M] =
|
||||||
(s: M, a: A) => f(s, a) -> Observable.empty
|
(s: M, a: A) => f(s, a) -> Observable.empty
|
||||||
|
|
||||||
/** Creates a Reducer with an optional effect.
|
/**
|
||||||
|
* Creates a Reducer with an optional effect.
|
||||||
*/
|
*/
|
||||||
def withOptionalEffects[F[_]: ObservableLike, A, M](
|
def withOptionalEffects[F[_]: ObservableLike, A, M](
|
||||||
f: (M, A) => (M, Option[F[A]])
|
f: (M, A) => (M, Option[F[A]])
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package outwatchapp.util.reactive.store
|
package nova.monadic_sfx.util.reactive.store
|
||||||
|
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import monix.eval.Coeval
|
import monix.eval.Coeval
|
||||||
import monix.reactive.Observer
|
|
||||||
import monix.reactive.OverflowStrategy
|
import monix.reactive.OverflowStrategy
|
||||||
import monix.reactive.subjects.ConcurrentSubject
|
import monix.reactive.subjects.ConcurrentSubject
|
||||||
import outwatchapp.util.reactive.MonixProSubject
|
import monix.reactive.Observer
|
||||||
|
|
||||||
object Store {
|
object Store {
|
||||||
def createL[A, M](
|
def createL[A, M](
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package outwatchapp.util.reactive
|
package nova.monadic_sfx.util.reactive
|
||||||
|
|
||||||
import monix.reactive.Observable
|
import monix.reactive.Observable
|
||||||
|
import monix.reactive.Observer
|
||||||
|
|
||||||
package object store {
|
package object store {
|
||||||
|
type MonixProSubject[-I, +O] = Observable[O] with Observer[I]
|
||||||
type Middleware[A, M] = Observable[(A, M)] => Observable[(A, M)]
|
type Middleware[A, M] = Observable[(A, M)] => Observable[(A, M)]
|
||||||
type Store[A, M] = MonixProSubject[A, (A, M)]
|
type Store[A, M] = MonixProSubject[A, (A, M)]
|
||||||
|
|
||||||
/** A Function that applies an Action onto the Stores current state.
|
/**
|
||||||
|
* A Function that applies an Action onto the Stores current state.
|
||||||
* @param reducer The reducing function
|
* @param reducer The reducing function
|
||||||
* @tparam A The Action Type
|
* @tparam A The Action Type
|
||||||
* @tparam M The Model Type
|
* @tparam M The Model Type
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,25 +0,0 @@
|
|||||||
var webpack = require('webpack');
|
|
||||||
var merge = require('webpack-merge');
|
|
||||||
var generated = require('./scalajs.webpack.config');
|
|
||||||
var Path = require('path');
|
|
||||||
|
|
||||||
var local = {
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.css$/,
|
|
||||||
use: ['style-loader', 'css-loader']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.(ttf|eot|woff|png|glb|jpeg|jpg|mp4|jsn)$/,
|
|
||||||
use: 'file-loader'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.(eot)$/,
|
|
||||||
use: 'url-loader'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = merge(generated, local);
|
|
@ -1,10 +1,26 @@
|
|||||||
var webpack = require('webpack');
|
var webpack = require('webpack');
|
||||||
var merge = require('webpack-merge');
|
var merge = require('webpack-merge');
|
||||||
var common = require('./webpack.config.common.js');
|
var generated = require('./scalajs.webpack.config');
|
||||||
var Path = require('path');
|
var Path = require('path');
|
||||||
const rootDir = Path.resolve(__dirname, '../../../..');
|
const rootDir = Path.resolve(__dirname, '../../../..');
|
||||||
|
|
||||||
var local = {
|
var local = {
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: ['style-loader', 'css-loader']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(ttf|eot|woff|png|glb|jpeg|jpg|mp4|jsn)$/,
|
||||||
|
use: 'file-loader'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(eot)$/,
|
||||||
|
use: 'url-loader'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
devServer: {
|
devServer: {
|
||||||
contentBase: [
|
contentBase: [
|
||||||
Path.resolve(__dirname, 'dev'), // fastOptJS output
|
Path.resolve(__dirname, 'dev'), // fastOptJS output
|
||||||
@ -19,6 +35,12 @@ var local = {
|
|||||||
ignored: ["node_modules"]
|
ignored: ["node_modules"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.ProvidePlugin({
|
||||||
|
$: 'jquery/src/jquery',
|
||||||
|
jquery: 'jquery/src/jquery'
|
||||||
|
})
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = merge(common, local);
|
module.exports = merge(generated, local);
|
||||||
|
13
webpack.config.js
Normal file
13
webpack.config.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
var webpack = require('webpack');
|
||||||
|
|
||||||
|
module.exports = require('./scalajs.webpack.config');
|
||||||
|
|
||||||
|
module.exports.module = {
|
||||||
|
...module.exports.module,
|
||||||
|
rules: module.exports.module.rules.concat([
|
||||||
|
{
|
||||||
|
test: /\.css$/i,
|
||||||
|
use: ['style-loader', 'css-loader'],
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
};
|
@ -1,8 +0,0 @@
|
|||||||
var webpack = require('webpack');
|
|
||||||
var merge = require('webpack-merge');
|
|
||||||
var common = require('./webpack.config.common.js');
|
|
||||||
var Path = require('path');
|
|
||||||
|
|
||||||
var local = {};
|
|
||||||
|
|
||||||
module.exports = merge(common, local);
|
|
128
yarn.lock
128
yarn.lock
@ -2,11 +2,6 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
"@sweetalert2/themes@4.0.1":
|
|
||||||
version "4.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/@sweetalert2/themes/-/themes-4.0.1.tgz#ba07a40743fd439663341480c20360116c2e1f27"
|
|
||||||
integrity sha512-dE+SXd0ym2RUkDQT/rVyBIwe6QIKl1cfBF3133XdLeXVKUR+XEtkr1uVBK821uX5DRQfN9doJmVCd2jFdG/qXw==
|
|
||||||
|
|
||||||
"@types/chart.js@2.9.11":
|
"@types/chart.js@2.9.11":
|
||||||
version "2.9.11"
|
version "2.9.11"
|
||||||
resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.11.tgz#2b73fe59e78dfe31c2f8d6c6d0c169e98e65c16b"
|
resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.11.tgz#2b73fe59e78dfe31c2f8d6c6d0c169e98e65c16b"
|
||||||
@ -14,13 +9,6 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
moment "^2.10.2"
|
moment "^2.10.2"
|
||||||
|
|
||||||
"@types/datatables.net@1.10.19":
|
|
||||||
version "1.10.19"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/datatables.net/-/datatables.net-1.10.19.tgz#17c5f94433f761086131c6c8dc055a0e1099d1f9"
|
|
||||||
integrity sha512-WuzgytEmsIpVYZbkce+EvK1UqBI7/cwcC/WgYeAtXdq2zi+yWzJwMT5Yb6irAiOi52DBjeAEeRt3bYzFYvHWCQ==
|
|
||||||
dependencies:
|
|
||||||
"@types/jquery" "*"
|
|
||||||
|
|
||||||
"@types/glob@^7.1.1":
|
"@types/glob@^7.1.1":
|
||||||
version "7.1.3"
|
version "7.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
|
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
|
||||||
@ -29,13 +17,6 @@
|
|||||||
"@types/minimatch" "*"
|
"@types/minimatch" "*"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/jquery@*", "@types/jquery@3.5.5":
|
|
||||||
version "3.5.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.5.tgz#2c63f47c9c8d96693d272f5453602afd8338c903"
|
|
||||||
integrity sha512-6RXU9Xzpc6vxNrS6FPPapN1SxSHgQ336WC6Jj/N8q30OiaBZ00l1GBgeP7usjVZPivSkGUfL1z/WW6TX989M+w==
|
|
||||||
dependencies:
|
|
||||||
"@types/sizzle" "*"
|
|
||||||
|
|
||||||
"@types/json-schema@^7.0.6":
|
"@types/json-schema@^7.0.6":
|
||||||
version "7.0.6"
|
version "7.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
|
||||||
@ -47,19 +28,9 @@
|
|||||||
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
|
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
|
||||||
|
|
||||||
"@types/node@*":
|
"@types/node@*":
|
||||||
version "14.14.16"
|
version "14.14.10"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.16.tgz#3cc351f8d48101deadfed4c9e4f116048d437b4b"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.10.tgz#5958a82e41863cfc71f2307b3748e3491ba03785"
|
||||||
integrity sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==
|
integrity sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==
|
||||||
|
|
||||||
"@types/paralleljs@0.0.20":
|
|
||||||
version "0.0.20"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/paralleljs/-/paralleljs-0.0.20.tgz#5013416ccb05d2fec5e2bcf5331d37bd203abe44"
|
|
||||||
integrity sha512-+1y+7aNWEvsXfdubI9y5aUFYU7RiUgVqAo+Px9siGLPUq4RZodvrP2EdoCV+oPQdcgKuQnW+CuikWcUSZuAkhA==
|
|
||||||
|
|
||||||
"@types/sizzle@*":
|
|
||||||
version "2.3.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47"
|
|
||||||
integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==
|
|
||||||
|
|
||||||
"@webassemblyjs/ast@1.9.0":
|
"@webassemblyjs/ast@1.9.0":
|
||||||
version "1.9.0"
|
version "1.9.0"
|
||||||
@ -1039,21 +1010,6 @@ cyclist@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
|
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
|
||||||
integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
|
integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
|
||||||
|
|
||||||
datatables.net-bs4@1.10.23:
|
|
||||||
version "1.10.23"
|
|
||||||
resolved "https://registry.yarnpkg.com/datatables.net-bs4/-/datatables.net-bs4-1.10.23.tgz#bdd5d0dcb1bd68a7afe649a4424ba20647523b52"
|
|
||||||
integrity sha512-ChUB8t5t5uzPnJYTPXx2DOvnlm2shz8OadXrKoFavOadB308OuwHVxSldYq9+KGedCeiVxEjNqcaV4nFSXkRsw==
|
|
||||||
dependencies:
|
|
||||||
datatables.net "1.10.23"
|
|
||||||
jquery ">=1.7"
|
|
||||||
|
|
||||||
datatables.net@1.10.23:
|
|
||||||
version "1.10.23"
|
|
||||||
resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-1.10.23.tgz#59f7d7b12845183b1b379530d1385077e113ec01"
|
|
||||||
integrity sha512-we3tlNkzpxvgkKKlTxTMXPCt35untVXNg8zUYWpQyC1U5vJc+lT0+Zdc1ztK8d3lh5CfdnuFde2p8n3XwaGl3Q==
|
|
||||||
dependencies:
|
|
||||||
jquery ">=1.7"
|
|
||||||
|
|
||||||
debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
|
debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
|
||||||
version "2.6.9"
|
version "2.6.9"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||||
@ -1274,9 +1230,9 @@ enhanced-resolve@^4.1.0:
|
|||||||
tapable "^1.0.0"
|
tapable "^1.0.0"
|
||||||
|
|
||||||
errno@^0.1.3, errno@~0.1.7:
|
errno@^0.1.3, errno@~0.1.7:
|
||||||
version "0.1.8"
|
version "0.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
|
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
|
||||||
integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==
|
integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==
|
||||||
dependencies:
|
dependencies:
|
||||||
prr "~1.0.1"
|
prr "~1.0.1"
|
||||||
|
|
||||||
@ -1576,9 +1532,9 @@ flush-write-stream@^1.0.0:
|
|||||||
readable-stream "^2.3.6"
|
readable-stream "^2.3.6"
|
||||||
|
|
||||||
follow-redirects@^1.0.0:
|
follow-redirects@^1.0.0:
|
||||||
version "1.13.1"
|
version "1.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
|
||||||
integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==
|
integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==
|
||||||
|
|
||||||
for-in@^1.0.2:
|
for-in@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
@ -1659,9 +1615,9 @@ get-caller-file@^2.0.1:
|
|||||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||||
|
|
||||||
get-intrinsic@^1.0.0:
|
get-intrinsic@^1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.2.tgz#6820da226e50b24894e08859469dc68361545d49"
|
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.1.tgz#94a9768fcbdd0595a1c9273aacf4c89d075631be"
|
||||||
integrity sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==
|
integrity sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==
|
||||||
dependencies:
|
dependencies:
|
||||||
function-bind "^1.1.1"
|
function-bind "^1.1.1"
|
||||||
has "^1.0.3"
|
has "^1.0.3"
|
||||||
@ -1839,9 +1795,9 @@ hpack.js@^2.1.6:
|
|||||||
wbuf "^1.1.0"
|
wbuf "^1.1.0"
|
||||||
|
|
||||||
html-entities@^1.3.1:
|
html-entities@^1.3.1:
|
||||||
version "1.4.0"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc"
|
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44"
|
||||||
integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==
|
integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==
|
||||||
|
|
||||||
http-deceiver@^1.2.7:
|
http-deceiver@^1.2.7:
|
||||||
version "1.2.7"
|
version "1.2.7"
|
||||||
@ -1978,9 +1934,9 @@ inherits@2.0.3:
|
|||||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||||
|
|
||||||
ini@^1.3.4:
|
ini@^1.3.4:
|
||||||
version "1.3.8"
|
version "1.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
|
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
||||||
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
|
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
|
||||||
|
|
||||||
internal-ip@^4.3.0:
|
internal-ip@^4.3.0:
|
||||||
version "4.3.0"
|
version "4.3.0"
|
||||||
@ -2234,10 +2190,10 @@ isobject@^3.0.0, isobject@^3.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
||||||
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
|
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
|
||||||
|
|
||||||
jquery@3.5.1, jquery@>=1.7:
|
jquery@3.3.1:
|
||||||
version "3.5.1"
|
version "3.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5"
|
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
|
||||||
integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==
|
integrity sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==
|
||||||
|
|
||||||
json-parse-better-errors@^1.0.2:
|
json-parse-better-errors@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
@ -2499,11 +2455,16 @@ mime@1.6.0:
|
|||||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||||
|
|
||||||
mime@^2.0.3, mime@^2.4.4:
|
mime@^2.0.3:
|
||||||
version "2.4.7"
|
version "2.4.7"
|
||||||
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.7.tgz#962aed9be0ed19c91fd7dc2ece5d7f4e89a90d74"
|
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.7.tgz#962aed9be0ed19c91fd7dc2ece5d7f4e89a90d74"
|
||||||
integrity sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA==
|
integrity sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA==
|
||||||
|
|
||||||
|
mime@^2.4.4:
|
||||||
|
version "2.4.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1"
|
||||||
|
integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==
|
||||||
|
|
||||||
mimic-fn@^2.0.0:
|
mimic-fn@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||||
@ -2589,16 +2550,11 @@ ms@2.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
||||||
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
||||||
|
|
||||||
ms@2.1.2:
|
ms@2.1.2, ms@^2.1.1:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||||
|
|
||||||
ms@^2.1.1:
|
|
||||||
version "2.1.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
|
||||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
|
||||||
|
|
||||||
multicast-dns-service-types@^1.1.0:
|
multicast-dns-service-types@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901"
|
resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901"
|
||||||
@ -2880,11 +2836,6 @@ parallel-transform@^1.1.0:
|
|||||||
inherits "^2.0.3"
|
inherits "^2.0.3"
|
||||||
readable-stream "^2.1.5"
|
readable-stream "^2.1.5"
|
||||||
|
|
||||||
paralleljs@1.1.0:
|
|
||||||
version "1.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/paralleljs/-/paralleljs-1.1.0.tgz#d0cd5540fe4dd8e80fa94d3cb6a530510dbe6e70"
|
|
||||||
integrity sha512-5sYLAEzM+qMDYzQeVWyNdqINU4h+/Fhr0CrZs3+HCYPIhOWuA2BCzn74js2qzX+LKFwRQR6sSJBZhgT+WWhbfw==
|
|
||||||
|
|
||||||
parse-asn1@^5.0.0, parse-asn1@^5.1.5:
|
parse-asn1@^5.0.0, parse-asn1@^5.1.5:
|
||||||
version "5.1.6"
|
version "5.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4"
|
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4"
|
||||||
@ -2991,6 +2942,11 @@ pkg-dir@^3.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
find-up "^3.0.0"
|
find-up "^3.0.0"
|
||||||
|
|
||||||
|
popper.js@1.16.1:
|
||||||
|
version "1.16.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
|
||||||
|
integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
|
||||||
|
|
||||||
portfinder@^1.0.26:
|
portfinder@^1.0.26:
|
||||||
version "1.0.28"
|
version "1.0.28"
|
||||||
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778"
|
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778"
|
||||||
@ -3225,11 +3181,6 @@ readdirp@~3.5.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
picomatch "^2.2.1"
|
picomatch "^2.2.1"
|
||||||
|
|
||||||
reconnecting-websocket@4.4.0:
|
|
||||||
version "4.4.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz#3b0e5b96ef119e78a03135865b8bb0af1b948783"
|
|
||||||
integrity sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==
|
|
||||||
|
|
||||||
regex-not@^1.0.0, regex-not@^1.0.2:
|
regex-not@^1.0.0, regex-not@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
|
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
|
||||||
@ -3805,11 +3756,6 @@ supports-color@^6.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-flag "^3.0.0"
|
has-flag "^3.0.0"
|
||||||
|
|
||||||
sweetalert2@10.12.6:
|
|
||||||
version "10.12.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/sweetalert2/-/sweetalert2-10.12.6.tgz#249b95fecb28986a1aff82710a460ecb4717e2a9"
|
|
||||||
integrity sha512-kkPRpNqP0wF9tAVu1Ygp4pQx4pXY/pgseyW3dOXXRu0S6s7HKfHEmpBUIZrBylRfeJFPOsSEk6sALSwKzNZ9RQ==
|
|
||||||
|
|
||||||
tapable@^1.0.0, tapable@^1.1.3:
|
tapable@^1.0.0, tapable@^1.1.3:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
|
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
|
||||||
@ -4105,9 +4051,9 @@ webpack-cli@3.3.2:
|
|||||||
yargs "^12.0.5"
|
yargs "^12.0.5"
|
||||||
|
|
||||||
webpack-dev-middleware@^3.7.2:
|
webpack-dev-middleware@^3.7.2:
|
||||||
version "3.7.3"
|
version "3.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5"
|
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3"
|
||||||
integrity sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==
|
integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==
|
||||||
dependencies:
|
dependencies:
|
||||||
memory-fs "^0.4.1"
|
memory-fs "^0.4.1"
|
||||||
mime "^2.4.4"
|
mime "^2.4.4"
|
||||||
|
Loading…
Reference in New Issue
Block a user