Added WebSocket observable class

This commit is contained in:
Rohan Sircar 2020-12-30 13:51:45 +05:30
parent 537e1abc29
commit 937a104615

View File

@ -0,0 +1,84 @@
package outwatchapp.util.reactive
import org.scalajs.dom.{raw => sjsdr}
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.reactive.Observable
import monix.reactive.Observer
import monix.reactive.OverflowStrategy
import outwatchapp.util.reactive.MonixProSubject
import org.scalajs.dom.raw.Event
import org.scalajs.dom.raw.MessageEvent
import scala.concurrent.Future
class WebSocketImpl[T: Encoder: Decoder](
ws: sjsdr.WebSocket,
overflowStrategy: OverflowStrategy.Synchronous[T]
) {
val printer = Printer.noSpaces
lazy val source: Task[Observable[T]] =
Task.deferAction(implicit s =>
for {
obs <- Task(
Observable
.create[T](overflowStrategy) { sub =>
ws.onmessage = (e: MessageEvent) =>
e.data match {
case s: String =>
decode[T](s).map(sub.onNext).left.foreach(println)
case other => println(other)
}
ws.onerror = (e: Event) =>
sub.onError(new Exception(s"Error in WebSocket: $e"))
Cancelable(() => ws.close())
}
.publish
.refCount
)
_ <- Task(obs.subscribe(Observer.empty))
} yield obs
)
lazy val sink: Task[Observer[T]] =
Task(
new Observer[T] {
override def onNext(elem: T): Future[Ack] = {
val msg = printer.print(elem.asJson)
ws.send(msg)
Future.successful(Ack.Continue)
}
override def onError(ex: Throwable): Unit = println(ex)
override def onComplete(): Unit = ()
}
)
}
object WebSocket {
sealed trait Error
final case object Error extends Error
// val w = new sjsdr.Worker("/worker.js").asInstanceOf[Worker]
// type MonixWebWorker[T] = MonixProSubject[T, T]
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))
impl = new WebSocketImpl[T](websocket, overflowStrategy)
source <- impl.source
sink <- impl.sink
} yield MonixProSubject.from(sink, source)
}
@JsonCodec
case class WebsocketData(data: Long)