You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
3.4 KiB

  1. package outwatchapp.util.reactive
  2. import scala.concurrent.Future
  3. import io.circe.Decoder
  4. import io.circe.Encoder
  5. import io.circe.Printer
  6. import io.circe.generic.JsonCodec
  7. import io.circe.parser._
  8. import io.circe.syntax._
  9. import monix.bio.Task
  10. import monix.execution.Ack
  11. import monix.execution.Cancelable
  12. import monix.reactive.Observable
  13. import monix.reactive.Observer
  14. import monix.reactive.OverflowStrategy
  15. import outwatchapp.util.reactive.MonixProSubject
  16. import org.scalajs.dom.raw.Event
  17. import org.scalajs.dom.raw.MessageEvent
  18. import org.scalajs.dom.{raw => sjsdr}
  19. class WebWorkerImpl[T <: Product: Encoder: Decoder](
  20. worker: sjsdr.Worker,
  21. overflowStrategy: OverflowStrategy.Synchronous[T]
  22. ) {
  23. // private def parseFn(data: T) = {
  24. // data match {
  25. // case _: AnyRef => parseRef(data)
  26. // case s: String =>
  27. // case other =>
  28. // println(other)
  29. // Left(WebWorker.Error)
  30. // }
  31. // }
  32. // private def parseRef(data: T): Either[WebWorker.Error, T] = {
  33. // data match {
  34. // case s: String => decode[T](s).leftMap(_ => WebWorker.Error)
  35. // case a: Int =>
  36. // println(a)
  37. // Left(WebWorker.Error)
  38. // case other =>
  39. // println(other)
  40. // Left(WebWorker.Error)
  41. // }
  42. // }
  43. // private def parsePrimitive(data: T): Either[WebWorker.Error, T] = {
  44. // data match {
  45. // case s: String => decode[T](s).leftMap(_ => WebWorker.Error)
  46. // case a: Int =>
  47. // println(a)
  48. // Left(WebWorker.Error)
  49. // case other =>
  50. // println(other)
  51. // Left(WebWorker.Error)
  52. // }
  53. // }
  54. // println(s"Got data $a")
  55. // .map(sub.onNext).left.foreach(println)
  56. val printer = Printer.noSpaces
  57. lazy val source: Task[Observable[T]] =
  58. Task.deferAction(implicit s =>
  59. for {
  60. obs <- Task(
  61. Observable
  62. .create[T](overflowStrategy) { sub =>
  63. worker.onmessage = (e: MessageEvent) =>
  64. e.data match {
  65. case s: String =>
  66. decode[T](s).map(sub.onNext).left.foreach(println)
  67. case other => println(other)
  68. }
  69. worker.onerror = (e: Event) =>
  70. sub.onError(new Exception(s"Error in WebSocket: $e"))
  71. Cancelable(() => worker.terminate())
  72. }
  73. .publish
  74. .refCount
  75. )
  76. _ <- Task(obs.subscribe(Observer.empty))
  77. } yield obs
  78. )
  79. lazy val sink: Task[Observer[T]] =
  80. Task(
  81. new Observer[T] {
  82. override def onNext(elem: T): Future[Ack] = {
  83. val msg = printer.print(elem.asJson)
  84. worker.postMessage(msg)
  85. Future.successful(Ack.Continue)
  86. }
  87. override def onError(ex: Throwable): Unit = println(ex)
  88. override def onComplete(): Unit = ()
  89. }
  90. )
  91. }
  92. object WebWorker {
  93. sealed trait Error
  94. final case object Error extends Error
  95. // val w = new sjsdr.Worker("/worker.js").asInstanceOf[Worker]
  96. // type MonixWebWorker[T] = MonixProSubject[T, T]
  97. def apply[T <: Product: Encoder: Decoder](
  98. path: String,
  99. overflowStrategy: OverflowStrategy.Synchronous[T] =
  100. OverflowStrategy.DropOld(50)
  101. ): Task[WebWorker[T]] = for {
  102. worker <- Task(new sjsdr.Worker(path))
  103. impl = new WebWorkerImpl[T](worker, overflowStrategy)
  104. source <- impl.source
  105. sink <- impl.sink
  106. } yield MonixProSubject.from(sink, source)
  107. }
  108. @JsonCodec
  109. case class WorkerData(data: Long)