Compare commits
No commits in common. "development" and "master" have entirely different histories.
developmen
...
master
@ -6,9 +6,6 @@
|
||||
<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">
|
||||
<!-- <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>
|
||||
|
||||
|
@ -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",
|
||||
"org.akka-js" %%% "shocon" % "1.0.0",
|
||||
"com.beachape" %%% "enumeratum-circe" % "1.6.1",
|
||||
"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.github.valskalla" %%% "odin-core" % "0.7.0+95-ab4381ae+20201227-1831-SNAPSHOT"
|
||||
// "com.clovellytech" %%% "outwatch-router" % "0.0.9+7-5be0b1a2+20201227-2019-SNAPSHOT"
|
||||
)
|
||||
|
||||
Compile / npmDependencies ++= Seq(
|
||||
"jquery" -> "3.5.1",
|
||||
"@types/jquery" -> "3.5.5",
|
||||
"jquery" -> "3.3.1",
|
||||
"popper.js" -> "1.16.1",
|
||||
// // "@popperjs/core" -> "2.6.0",
|
||||
"blk-design-system" -> "1.0.2",
|
||||
"bootstrap" -> "4.5.3",
|
||||
"@types/chart.js" -> "2.9.11",
|
||||
"chart.js" -> "2.9.3",
|
||||
"snabbdom" -> "git://github.com/outwatch/snabbdom.git#semver:0.7.5",
|
||||
"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"
|
||||
"fuse.js" -> "6.4.3"
|
||||
)
|
||||
|
||||
Compile / npmDevDependencies ++= Seq(
|
||||
@ -67,14 +57,8 @@ Compile / npmDevDependencies ++= Seq(
|
||||
"url-loader" -> "1.1.2"
|
||||
)
|
||||
|
||||
stIgnore ++= List(
|
||||
"datatables.net-bs4",
|
||||
"datatables.net-dt",
|
||||
"blk-design-system",
|
||||
"bootstrap",
|
||||
"snabbdom",
|
||||
"@sweetalert2/themes"
|
||||
)
|
||||
stIgnore ++= List("jquery", "blk-design-system", "bootstrap")
|
||||
stIgnore ++= List("snabbdom")
|
||||
stStdlib := List("es6")
|
||||
stUseScalaJsDom := false
|
||||
|
||||
@ -126,9 +110,6 @@ webpackDevServerPort := 8080
|
||||
webpackConfigFile in fastOptJS := Some(
|
||||
baseDirectory.value / "webpack.config.dev.js"
|
||||
)
|
||||
webpackConfigFile in fullOptJS := Some(
|
||||
baseDirectory.value / "webpack.config.prod.js"
|
||||
)
|
||||
// webpackConfigFile in fullOptJS := Some(
|
||||
// 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]) {
|
||||
val ws = new org.scalajs.dom.WebSocket(url)
|
||||
|
||||
val source: Observable[MessageEvent] =
|
||||
lazy val source: Observable[MessageEvent] =
|
||||
Observable.create[MessageEvent](OverflowStrategy.Unbounded) { sub =>
|
||||
ws.onmessage = (e: MessageEvent) => sub.onNext(e)
|
||||
ws.onerror =
|
||||
@ -33,7 +33,7 @@ class MonixWS[F[_], S](val url: String)(implicit F: Sync[F], S: Show[S]) {
|
||||
Cancelable(() => ws.close())
|
||||
}
|
||||
|
||||
val sink: F[Observer[S]] = {
|
||||
lazy val sink: F[Observer[S]] = {
|
||||
F.delay {
|
||||
new Observer[S] {
|
||||
override def onNext(elem: S): Future[Ack] = {
|
||||
|
@ -20,7 +20,7 @@ object WebSocketF {
|
||||
class WebSocketF[F[_]](val url: String)(implicit F: Sync[F]) {
|
||||
val ws = new org.scalajs.dom.WebSocket(url)
|
||||
|
||||
val source: Observable[MessageEvent] =
|
||||
lazy val source: Observable[MessageEvent] =
|
||||
Observable.create[MessageEvent] { observer =>
|
||||
ws.onmessage = (e: MessageEvent) => observer.onNext(e)
|
||||
ws.onerror =
|
||||
@ -28,7 +28,7 @@ class WebSocketF[F[_]](val url: String)(implicit F: Sync[F]) {
|
||||
Cancelable(() => ws.close())
|
||||
}
|
||||
|
||||
val sink: F[Observer[String]] = {
|
||||
lazy val sink: F[Observer[String]] = {
|
||||
F.delay {
|
||||
new Observer[String] {
|
||||
override def onNext(elem: String): Unit = ws.send(elem)
|
||||
|
@ -1,187 +1,50 @@
|
||||
package outwatchapp
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import colibri.ext.monix._
|
||||
import com.softwaremill.macwire._
|
||||
import io.odin.consoleLogger
|
||||
import monix.bio.Task
|
||||
import monix.eval.Coeval
|
||||
import monix.reactive.Observable
|
||||
import monix.{eval => me}
|
||||
import org.scalajs.dom.raw.Element
|
||||
import outwatch._
|
||||
import outwatch.dsl._
|
||||
import outwatch.router._
|
||||
import outwatchapp.components.ChartjsDemo
|
||||
import outwatchapp.components.CounterDemo
|
||||
import outwatchapp.components.DatatablesDemo
|
||||
import outwatchapp.components.FusejsDemo
|
||||
import outwatchapp.components.RequestDemo
|
||||
import outwatchapp.components.todo.ChartjsDemo
|
||||
import outwatchapp.pages.HomePage
|
||||
import outwatchapp.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
|
||||
import nova.monadic_sfx.ui.components.todo.TodoListStore
|
||||
|
||||
class MainApp(el: Element)(implicit
|
||||
backend: AppTypes.Backend,
|
||||
store: RouterStore[Page]
|
||||
) {
|
||||
|
||||
import MainApp._
|
||||
|
||||
def run: Task[Unit] = for {
|
||||
resolver <- resolver
|
||||
_ <- OutWatch.renderInto[Task](el, Router(resolver))
|
||||
} 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]] =
|
||||
Task.deferAction(implicit s =>
|
||||
for {
|
||||
counterDemo <- CounterDemo()
|
||||
chartDemo <- ChartjsDemo()
|
||||
todoStore <- TodoListStore(consoleLogger[Task]())
|
||||
todoStore <- TodoListStore()
|
||||
requestDemo <- RequestDemo(todoStore)
|
||||
demoWorker <- WebWorker[WorkerData]("/worker.js")
|
||||
dtDemo <- DatatablesDemo()
|
||||
_ <- IOUtils
|
||||
.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 {
|
||||
resolver: PartialFunction[Page, VDomModifier] = {
|
||||
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
|
||||
)
|
||||
div(div(cls := "title", "UserHome"), s"User id: $id")
|
||||
case Page.NotFound =>
|
||||
Task(
|
||||
div(
|
||||
cls := "page-header error-page header-filter",
|
||||
div(
|
||||
cls := "page-header-image"
|
||||
// style := js.Dictionary(
|
||||
// "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."
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
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
|
||||
|
||||
import scala.scalajs.js.annotation.JSImport
|
||||
|
||||
import cats.effect.ExitCode
|
||||
import monix.bio._
|
||||
import org.scalajs.dom.document
|
||||
import org.scalajs.dom.raw.Element
|
||||
import outwatch.router.AppRouter
|
||||
import sttp.client.impl.monix.FetchMonixBackend
|
||||
|
||||
import org.scalajs.dom.document
|
||||
import scala.scalajs.js.annotation.JSImport
|
||||
import scalajs.js
|
||||
import outwatch.router.AppRouter
|
||||
|
||||
@JSImport("bootstrap/dist/css/bootstrap.min.css", JSImport.Namespace)
|
||||
@js.native
|
||||
object BootstrapBundleCss extends js.Object
|
||||
@JSImport("bootstrap/dist/js/bootstrap.bundle.min.js", JSImport.Namespace)
|
||||
@JSImport("bootstrap", JSImport.Namespace)
|
||||
@js.native
|
||||
object BootstrapBundleJs extends js.Object
|
||||
|
||||
|
@ -1,10 +1,13 @@
|
||||
package outwatchapp
|
||||
import colibri.ext.monix._
|
||||
import monix.bio.Task
|
||||
import monix.eval.Coeval
|
||||
import outwatch._
|
||||
import outwatch.dsl._
|
||||
import outwatch.router.AppRouter
|
||||
import outwatch.router._
|
||||
import outwatch.router.dsl._
|
||||
import outwatchapp.components.todo.FusejsDemo
|
||||
|
||||
import Page._
|
||||
|
||||
@ -27,7 +30,7 @@ object Router {
|
||||
cls := "navbar-translate",
|
||||
a(
|
||||
cls := "navbar-brand",
|
||||
href := "#",
|
||||
href := "https://demos.creative-tim.com/blk-design-system/index.html",
|
||||
rel := "tooltip",
|
||||
title := "",
|
||||
attr("data-placement") := "bottom",
|
||||
@ -101,6 +104,11 @@ object Router {
|
||||
),
|
||||
div(
|
||||
cls := "container",
|
||||
Coeval(
|
||||
println("Result = " + FusejsDemo.y.toString())
|
||||
) >> Coeval(
|
||||
FusejsDemo.y.foreach(o => println(o.item))
|
||||
) >> Coeval(div()),
|
||||
router.render(resolver),
|
||||
router.watch()
|
||||
)
|
||||
|
@ -15,7 +15,7 @@ object CounterDemo {
|
||||
def apply(): Task[HtmlVNode @@ CounterDemo] =
|
||||
Task.deferAction(implicit s =>
|
||||
Task(
|
||||
div(p(cls := "text-white", "count: ", counter2))
|
||||
div(p(cls := "profile-description", "count: ", counter2))
|
||||
.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.eval.Coeval
|
||||
import monix.{eval => me}
|
||||
import nova.monadic_sfx.ui.components.todo.Todo
|
||||
import nova.monadic_sfx.ui.components.todo.TodoListStore
|
||||
import outwatch._
|
||||
import outwatch.dsl._
|
||||
import outwatch.reactive.handlers.monix._
|
||||
import outwatchapp.AppTypes
|
||||
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 nova.monadic_sfx.util.reactive.store.Store
|
||||
|
||||
sealed trait RequestDemo
|
||||
object RequestDemo {
|
||||
@ -57,14 +57,15 @@ object RequestDemo {
|
||||
cls := "form-control",
|
||||
placeholder := "0",
|
||||
onInput.value --> requestSub
|
||||
)
|
||||
),
|
||||
div(
|
||||
cls := "form-group",
|
||||
label(
|
||||
color := "hsla(0,0%,100%,0.8)",
|
||||
"Enter content for todo"
|
||||
),
|
||||
small(cls := "form-text text-muted", "default is 0")
|
||||
),
|
||||
div(
|
||||
cls := "form-group",
|
||||
input(
|
||||
cls := "form-control",
|
||||
onInput.value --> todoContent
|
||||
@ -77,7 +78,8 @@ object RequestDemo {
|
||||
Coeval(println("Clicked"))
|
||||
),
|
||||
onClick(
|
||||
todoContent.map(TodoListStore.Add)
|
||||
todoContent
|
||||
.map(TodoListStore.Add)
|
||||
) --> todoStore.sink
|
||||
)
|
||||
)
|
||||
@ -85,7 +87,7 @@ object RequestDemo {
|
||||
),
|
||||
div(
|
||||
p(
|
||||
cls := "text-white",
|
||||
cls := "profile-description",
|
||||
requestSub
|
||||
.doOnNext(str => me.Task(println(str)))
|
||||
.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.util.Random
|
||||
|
||||
@ -6,10 +6,9 @@ import colibri.ext.monix._
|
||||
import com.softwaremill.tagging._
|
||||
import monix.bio.Task
|
||||
import monix.eval.Coeval
|
||||
import monix.execution.Cancelable
|
||||
import outwatch.HtmlVNode
|
||||
import outwatch.ManagedSubscriptions.managedElement
|
||||
import outwatch.dsl._
|
||||
import outwatch.reactive.handlers.monix._
|
||||
import typings.chartJs.mod._
|
||||
import typings.chartJs.mod.{^ => Chart}
|
||||
import typings.moment.mod.Moment
|
||||
@ -21,19 +20,29 @@ import scalajs.js
|
||||
sealed trait ChartjsDemo
|
||||
object ChartjsDemo {
|
||||
val random = new Random()
|
||||
def apply(): Task[HtmlVNode @@ ChartjsDemo] = Coeval(
|
||||
div(
|
||||
def apply(): Task[HtmlVNode @@ ChartjsDemo] = Task.deferAction(implicit s =>
|
||||
for {
|
||||
canvasH <- Handler.createF[Task, HTMLCanvasElement]
|
||||
} yield div(
|
||||
canvas(
|
||||
managedElement { el =>
|
||||
val chart = new Chart(
|
||||
el.asInstanceOf[HTMLCanvasElement],
|
||||
onDomMount.asHtml.map(a =>
|
||||
a.asInstanceOf[HTMLCanvasElement]
|
||||
) --> canvasH
|
||||
),
|
||||
div(
|
||||
canvasH
|
||||
.doOnNextF(el =>
|
||||
Coeval(
|
||||
new Chart(
|
||||
el,
|
||||
chartConfig(ChartType.bar, randomData(100, random.nextInt()))
|
||||
)
|
||||
Cancelable(() => chart.destroy())
|
||||
}
|
||||
) >> Coeval.unit
|
||||
)
|
||||
.map(_ => div())
|
||||
)
|
||||
).taggedWith[ChartjsDemo]
|
||||
).to[Task]
|
||||
)
|
||||
|
||||
def randomData(
|
||||
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._
|
||||
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 cats.kernel.Eq
|
||||
import com.softwaremill.quicklens._
|
||||
import io.circe.generic.JsonCodec
|
||||
import io.odin.Logger
|
||||
import monix.bio.Task
|
||||
import outwatchapp.util.reactive.store.Middlewares.actionLoggerMiddleware
|
||||
import outwatchapp.util.reactive.store.Reducer
|
||||
import outwatchapp.util.reactive.store.Store
|
||||
import nova.monadic_sfx.util.reactive.store.Reducer
|
||||
import nova.monadic_sfx.util.reactive.store.Store
|
||||
|
||||
@JSExportAll
|
||||
@JsonCodec
|
||||
case class Todo(id: Int, content: String)
|
||||
object Todo {
|
||||
implicit val eqForTodo = Eq.fromUniversalEquals[Todo]
|
||||
@ -40,16 +37,18 @@ object TodoListStore {
|
||||
implicit val eqForState = Eq.fromUniversalEquals[State]
|
||||
}
|
||||
|
||||
def reducer(logger: Logger[Task])(
|
||||
def reducer()(
|
||||
state: State,
|
||||
action: Action
|
||||
): (State, Option[Task[Action]]) =
|
||||
action match {
|
||||
case Init => (state, None)
|
||||
case Add(content) =>
|
||||
println("hello")
|
||||
val nextAction = Some(for {
|
||||
// do some validation
|
||||
// _ <- logger.debug(s"Received $content")
|
||||
_ <- Task(println(s"Received $content"))
|
||||
res <- Task.pure(InternalAdd(content))
|
||||
} yield res)
|
||||
(state, nextAction)
|
||||
@ -69,21 +68,19 @@ object TodoListStore {
|
||||
.using(_ :+ Todo(state.counter, content))
|
||||
.modify(_.counter)
|
||||
.using(_ + 1)
|
||||
(nextState, Some(logger.debug(s"Received $content") >> Task.pure(End)))
|
||||
(nextState, Some(Task.pure(End)))
|
||||
case End => (state, None)
|
||||
}
|
||||
|
||||
def apply(logger: Logger[Task]): Task[Store[Action, State]] =
|
||||
def apply(): Task[Store[Action, State]] =
|
||||
Task.deferAction(implicit s =>
|
||||
for {
|
||||
logMware <- actionLoggerMiddleware[Action, State](logger, "TodoStore")
|
||||
store <-
|
||||
Store
|
||||
.createL[Action, State](
|
||||
Init,
|
||||
State(Vector.empty[Todo], 0),
|
||||
Reducer.withOptionalEffects(reducer(logger) _),
|
||||
Seq(logMware)
|
||||
Reducer.withOptionalEffects(reducer() _)
|
||||
)
|
||||
} yield store
|
||||
)
|
||||
|
@ -1,19 +1,11 @@
|
||||
package outwatchapp
|
||||
|
||||
import scala.util.Try
|
||||
|
||||
import colibri.LiftSink
|
||||
import colibri.LiftSource
|
||||
import colibri.ext.monix._
|
||||
import monix.bio.Task
|
||||
import monix.execution.Scheduler
|
||||
import monix.reactive.Observable
|
||||
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 {
|
||||
// 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)
|
||||
}
|
||||
implicit class Ops2[A](val source: Observable[A]) extends AnyVal {
|
||||
@inline def liftSource[G[_]: LiftSource](implicit s: Scheduler): G[A] =
|
||||
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
|
||||
implicit class Ops2[A](val source: Observable[A])(implicit s: Scheduler) {
|
||||
@inline def liftSource[G[_]: LiftSource]: G[A] = LiftSource[G].lift(source)
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,15 @@
|
||||
package outwatchapp.pages
|
||||
import cats.syntax.eq._
|
||||
import com.softwaremill.tagging._
|
||||
import monix.bio.Task
|
||||
import outwatch._
|
||||
import outwatch.dsl._
|
||||
import outwatchapp.components.ChartjsDemo
|
||||
import outwatchapp.components.CounterDemo
|
||||
import outwatchapp.components.SweetAlertDemo
|
||||
import outwatchapp.components.todo.ChartjsDemo
|
||||
|
||||
class HomePage(
|
||||
counterDemo: VNode @@ CounterDemo,
|
||||
chartDemo: VNode @@ ChartjsDemo
|
||||
) {
|
||||
val loginDemo = for {
|
||||
res <- SweetAlertDemo.loginPrompt.map(_.value.toOption)
|
||||
_ <- Task(println(s"Got $res"))
|
||||
_ <-
|
||||
if (res === Some("foo" -> "bar")) SweetAlertDemo.successPrompt
|
||||
else SweetAlertDemo.failurePrompt
|
||||
} yield ()
|
||||
|
||||
def render = Task.deferAction(implicit s =>
|
||||
Task(
|
||||
div(
|
||||
def render = div(
|
||||
div(cls := "title", "Home"),
|
||||
div(
|
||||
cls := "card",
|
||||
@ -30,16 +17,8 @@ class HomePage(
|
||||
cls := "card-body",
|
||||
counterDemo,
|
||||
chartDemo,
|
||||
div(
|
||||
cls := "text-center",
|
||||
button(
|
||||
cls := "btn btn-primary",
|
||||
onClick.preventDefault.doAsync(loginDemo),
|
||||
"Login"
|
||||
)
|
||||
),
|
||||
p(
|
||||
cls := "text-white",
|
||||
cls := "profile-description",
|
||||
div(
|
||||
"hm",
|
||||
htmlTag("blockQuote")(
|
||||
@ -59,6 +38,4 @@ class HomePage(
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -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 io.circe.Encoder
|
||||
import io.circe.Printer
|
||||
import io.circe.generic.JsonCodec
|
||||
import io.circe.syntax._
|
||||
import io.odin.Logger
|
||||
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
|
||||
// object Middleware {
|
||||
// def apply[A,M,T](ob: Observable[(A,M)], cb: (A,M) => T): Observable[(A,M)] = ob
|
||||
// }
|
||||
|
||||
@JsonCodec
|
||||
final case class StoreInfo[A](
|
||||
@ -23,42 +17,6 @@ final case class StoreInfo[A](
|
||||
|
||||
object StoreInfo {
|
||||
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 {
|
||||
|
||||
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)))
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
object Middlewares {}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package outwatchapp.util.reactive
|
||||
package nova.monadic_sfx.util.reactive.store
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
@ -1,4 +1,4 @@
|
||||
package outwatchapp.util.reactive.store
|
||||
package nova.monadic_sfx.util.reactive.store
|
||||
|
||||
import cats.implicits._
|
||||
import monix.reactive.Observable
|
||||
@ -6,7 +6,8 @@ import monix.reactive.ObservableLike
|
||||
|
||||
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.
|
||||
* 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])
|
||||
): 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] =
|
||||
(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](
|
||||
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.eval.Coeval
|
||||
import monix.reactive.Observer
|
||||
import monix.reactive.OverflowStrategy
|
||||
import monix.reactive.subjects.ConcurrentSubject
|
||||
import outwatchapp.util.reactive.MonixProSubject
|
||||
import monix.reactive.Observer
|
||||
|
||||
object Store {
|
||||
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.Observer
|
||||
|
||||
package object store {
|
||||
type MonixProSubject[-I, +O] = Observable[O] with Observer[I]
|
||||
type Middleware[A, M] = Observable[(A, M)] => Observable[(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
|
||||
* @tparam A The Action 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 merge = require('webpack-merge');
|
||||
var common = require('./webpack.config.common.js');
|
||||
var generated = require('./scalajs.webpack.config');
|
||||
var Path = require('path');
|
||||
const rootDir = Path.resolve(__dirname, '../../../..');
|
||||
|
||||
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: {
|
||||
contentBase: [
|
||||
Path.resolve(__dirname, 'dev'), // fastOptJS output
|
||||
@ -19,6 +35,12 @@ var local = {
|
||||
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
|
||||
|
||||
|
||||
"@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":
|
||||
version "2.9.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.11.tgz#2b73fe59e78dfe31c2f8d6c6d0c169e98e65c16b"
|
||||
@ -14,13 +9,6 @@
|
||||
dependencies:
|
||||
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":
|
||||
version "7.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
|
||||
@ -29,13 +17,6 @@
|
||||
"@types/minimatch" "*"
|
||||
"@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":
|
||||
version "7.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
|
||||
@ -47,19 +28,9 @@
|
||||
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
|
||||
|
||||
"@types/node@*":
|
||||
version "14.14.16"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.16.tgz#3cc351f8d48101deadfed4c9e4f116048d437b4b"
|
||||
integrity sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==
|
||||
|
||||
"@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==
|
||||
version "14.14.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.10.tgz#5958a82e41863cfc71f2307b3748e3491ba03785"
|
||||
integrity sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==
|
||||
|
||||
"@webassemblyjs/ast@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"
|
||||
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:
|
||||
version "2.6.9"
|
||||
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"
|
||||
|
||||
errno@^0.1.3, errno@~0.1.7:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
|
||||
integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
|
||||
integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==
|
||||
dependencies:
|
||||
prr "~1.0.1"
|
||||
|
||||
@ -1576,9 +1532,9 @@ flush-write-stream@^1.0.0:
|
||||
readable-stream "^2.3.6"
|
||||
|
||||
follow-redirects@^1.0.0:
|
||||
version "1.13.1"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
|
||||
integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==
|
||||
version "1.13.0"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
|
||||
integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==
|
||||
|
||||
for-in@^1.0.2:
|
||||
version "1.0.2"
|
||||
@ -1659,9 +1615,9 @@ get-caller-file@^2.0.1:
|
||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||
|
||||
get-intrinsic@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.2.tgz#6820da226e50b24894e08859469dc68361545d49"
|
||||
integrity sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.1.tgz#94a9768fcbdd0595a1c9273aacf4c89d075631be"
|
||||
integrity sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
has "^1.0.3"
|
||||
@ -1839,9 +1795,9 @@ hpack.js@^2.1.6:
|
||||
wbuf "^1.1.0"
|
||||
|
||||
html-entities@^1.3.1:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc"
|
||||
integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44"
|
||||
integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==
|
||||
|
||||
http-deceiver@^1.2.7:
|
||||
version "1.2.7"
|
||||
@ -1978,9 +1934,9 @@ inherits@2.0.3:
|
||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||
|
||||
ini@^1.3.4:
|
||||
version "1.3.8"
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
|
||||
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
||||
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
|
||||
|
||||
internal-ip@^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"
|
||||
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
|
||||
|
||||
jquery@3.5.1, jquery@>=1.7:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5"
|
||||
integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==
|
||||
jquery@3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
|
||||
integrity sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==
|
||||
|
||||
json-parse-better-errors@^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"
|
||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||
|
||||
mime@^2.0.3, mime@^2.4.4:
|
||||
mime@^2.0.3:
|
||||
version "2.4.7"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.7.tgz#962aed9be0ed19c91fd7dc2ece5d7f4e89a90d74"
|
||||
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:
|
||||
version "2.1.0"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
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:
|
||||
version "1.1.0"
|
||||
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"
|
||||
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:
|
||||
version "5.1.6"
|
||||
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4"
|
||||
@ -2991,6 +2942,11 @@ pkg-dir@^3.0.0:
|
||||
dependencies:
|
||||
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:
|
||||
version "1.0.28"
|
||||
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778"
|
||||
@ -3225,11 +3181,6 @@ readdirp@~3.5.0:
|
||||
dependencies:
|
||||
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:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
|
||||
@ -3805,11 +3756,6 @@ supports-color@^6.1.0:
|
||||
dependencies:
|
||||
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:
|
||||
version "1.1.3"
|
||||
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"
|
||||
|
||||
webpack-dev-middleware@^3.7.2:
|
||||
version "3.7.3"
|
||||
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5"
|
||||
integrity sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3"
|
||||
integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==
|
||||
dependencies:
|
||||
memory-fs "^0.4.1"
|
||||
mime "^2.4.4"
|
||||
|
Loading…
Reference in New Issue
Block a user