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.

84 lines
2.4 KiB

  1. package outwatchapp.util.reactive
  2. import org.scalajs.dom.{raw => sjsdr}
  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 scala.concurrent.Future
  19. class WebSocketImpl[T: Encoder: Decoder](
  20. ws: sjsdr.WebSocket,
  21. overflowStrategy: OverflowStrategy.Synchronous[T]
  22. ) {
  23. val printer = Printer.noSpaces
  24. lazy val source: Task[Observable[T]] =
  25. Task.deferAction(implicit s =>
  26. for {
  27. obs <- Task(
  28. Observable
  29. .create[T](overflowStrategy) { sub =>
  30. ws.onmessage = (e: MessageEvent) =>
  31. e.data match {
  32. case s: String =>
  33. decode[T](s).map(sub.onNext).left.foreach(println)
  34. case other => println(other)
  35. }
  36. ws.onerror = (e: Event) =>
  37. sub.onError(new Exception(s"Error in WebSocket: $e"))
  38. Cancelable(() => ws.close())
  39. }
  40. .publish
  41. .refCount
  42. )
  43. _ <- Task(obs.subscribe(Observer.empty))
  44. } yield obs
  45. )
  46. lazy val sink: Task[Observer[T]] =
  47. Task(
  48. new Observer[T] {
  49. override def onNext(elem: T): Future[Ack] = {
  50. val msg = printer.print(elem.asJson)
  51. ws.send(msg)
  52. Future.successful(Ack.Continue)
  53. }
  54. override def onError(ex: Throwable): Unit = println(ex)
  55. override def onComplete(): Unit = ()
  56. }
  57. )
  58. }
  59. object WebSocket {
  60. sealed trait Error
  61. final case object Error extends Error
  62. // val w = new sjsdr.Worker("/worker.js").asInstanceOf[Worker]
  63. // type MonixWebWorker[T] = MonixProSubject[T, T]
  64. def apply[T <: Product: Encoder: Decoder](
  65. path: String,
  66. overflowStrategy: OverflowStrategy.Synchronous[T] =
  67. OverflowStrategy.DropOld(50)
  68. ): Task[WebSocket[T]] = for {
  69. websocket <- Task(new sjsdr.WebSocket(path))
  70. impl = new WebSocketImpl[T](websocket, overflowStrategy)
  71. source <- impl.source
  72. sink <- impl.sink
  73. } yield MonixProSubject.from(sink, source)
  74. }
  75. @JsonCodec
  76. case class WebsocketData(data: Long)