Skip to content

Instantly share code, notes, and snippets.

@yurique
Created September 4, 2019 06:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yurique/69f9c245959bf1ae20d1cbc3df3f1cd9 to your computer and use it in GitHub Desktop.
Save yurique/69f9c245959bf1ae20d1cbc3df3f1cd9 to your computer and use it in GitHub Desktop.
package app.tulz.http.ws
import com.raquo.laminar.api.L._
import io.circe.{Decoder, Encoder}
import io.circe.parser._
import io.circe.syntax._
import org.scalajs.dom
import org.scalajs.dom.raw.{ErrorEvent, WebSocket}
import cats.syntax.option._
import scala.util.Try
sealed trait WebSocketEvent[+T]
object WebSocketEvent {
case object Connected extends WebSocketEvent[Nothing]
case object Closed extends WebSocketEvent[Nothing]
final case class Error(message: String) extends WebSocketEvent[Nothing]
final case class Message[T](message: T) extends WebSocketEvent[T]
}
object JsonWebSocket {
def forUrl[Receive: Decoder, Send: Encoder](url: String)(implicit owner: Owner): (EventStream[WebSocketEvent[Receive]], Observer[Send], () => Unit) = {
val receiveBus = new EventBus[WebSocketEvent[Receive]]()
val sendBus = new EventBus[Send]
Try {
println(s"connecting to ws: $url")
val ws = new WebSocket(url)
var subscription: Option[Subscription] = Option.empty
ws.onopen = { event
receiveBus.writer.onNext(WebSocketEvent.Connected)
subscription = sendBus.events.foreach(e => ws.send(e.asJson.noSpaces)).some
}
ws.onerror = { event
receiveBus.writer.onNext(WebSocketEvent.Error(event.asInstanceOf[ErrorEvent].message))
}
ws.onmessage = { event
decode[Receive](event.data.toString) match {
case Right(message) =>
receiveBus.writer.onNext(WebSocketEvent.Message(message))
case Left(error) =>
receiveBus.writer.onNext(WebSocketEvent.Error(error.getMessage))
}
}
ws.onclose = { event
receiveBus.writer.onNext(WebSocketEvent.Closed)
subscription.foreach(_.kill())
subscription = None
}
(
receiveBus.events,
sendBus.writer,
() => {
receiveBus.writer.onNext(WebSocketEvent.Closed)
ws.close()
}
)
}.fold({ error =>
println(error)
receiveBus.writer.onNext(WebSocketEvent.Error(error.getMessage))
(
receiveBus.events,
sendBus.writer,
() => ()
)
}, identity)
}
def forPath[Receive: Decoder, Send: Encoder](path: String)(implicit owner: Owner): (EventStream[WebSocketEvent[Receive]], Observer[Send], () => Unit) = {
val wsProtocol = if (dom.document.location.protocol == "https:") "wss" else "ws"
forUrl(s"$wsProtocol://${dom.document.location.host}$path")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment