Rohan Sircar
3 years ago
25 changed files with 25211 additions and 155 deletions
-
3assets/app/index.html
-
26build.sbt
-
4src/main/scala/outwatch/util/MonixWebSocket.scala
-
4src/main/scala/outwatch/util/WebSocket2.scala
-
143src/main/scala/outwatchapp/MainApp.scala
-
0src/main/scala/outwatchapp/components/Chartjs.scala
-
33src/main/scala/outwatchapp/components/ChartjsDemo.scala
-
141src/main/scala/outwatchapp/components/DatatablesDemo.scala
-
492src/main/scala/outwatchapp/components/ExampleDataTable.scala
-
2src/main/scala/outwatchapp/components/Fusejs.scala
-
6src/main/scala/outwatchapp/components/RequestDemo.scala
-
104src/main/scala/outwatchapp/components/SweetAlertDemo.scala
-
30src/main/scala/outwatchapp/implicits/package.scala
-
65src/main/scala/outwatchapp/pages/HomePage.scala
-
34src/main/scala/outwatchapp/util/ParallelDemo.scala
-
14src/main/scala/outwatchapp/util/SubtleCryptoTest.scala
-
44src/main/scala/outwatchapp/util/WorkerTest.scala
-
77src/main/scala/outwatchapp/util/reactive/DedicatedWorker.scala
-
16src/main/scala/outwatchapp/util/reactive/Exceptions.scala
-
165src/main/scala/outwatchapp/util/reactive/ReconnectingWebSocket.scala
-
94src/main/scala/outwatchapp/util/reactive/WebSocket.scala
-
111src/main/scala/outwatchapp/util/reactive/WebWorker.scala
-
1src/main/scala/outwatchapp/util/reactive/package.scala
-
23690src/main/scala/outwatchapp/util/reactive/store/test.css
-
67yarn.lock
@ -0,0 +1,141 @@ |
|||
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] |
|||
} |
|||
} |
@ -0,0 +1,492 @@ |
|||
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") |
|||
) |
|||
) |
|||
) |
|||
} |
@ -1,4 +1,4 @@ |
|||
package outwatchapp.components.todo |
|||
package outwatchapp.components |
|||
|
|||
import outwatchapp.ui.components.todo.Todo |
|||
import typings.fuseJs.mod.Fuse.IFuseOptions |
@ -0,0 +1,104 @@ |
|||
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) |
|||
|
|||
} |
@ -0,0 +1,34 @@ |
|||
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 |
|||
} |
|||
} |
@ -0,0 +1,14 @@ |
|||
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) |
|||
} |
@ -0,0 +1,44 @@ |
|||
// 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") |
|||
// } |
|||
// } |
|||
// } |
@ -0,0 +1,77 @@ |
|||
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)) |
|||
} |
@ -0,0 +1,16 @@ |
|||
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) |
|||
|
|||
} |
@ -0,0 +1,165 @@ |
|||
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) |
|||
} |
23690
src/main/scala/outwatchapp/util/reactive/store/test.css
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue