mirror of
https://github.com/phfroidmont/scalive.git
synced 2025-12-25 05:26:59 +01:00
Rough websocket responses
This commit is contained in:
parent
fadef26425
commit
ccdd22b61a
3 changed files with 70 additions and 16 deletions
|
|
@ -52,5 +52,5 @@ object example extends ScalaCommon:
|
||||||
}
|
}
|
||||||
|
|
||||||
object js extends TypeScriptModule:
|
object js extends TypeScriptModule:
|
||||||
def mainFileName = "app.ts"
|
def mainFileName = "app.js"
|
||||||
def npmDeps = Seq("phoenix@1.7.21", "phoenix_live_view@1.1.8")
|
def npmDeps = Seq("phoenix@1.7.21", "phoenix_live_view@1.1.8")
|
||||||
|
|
|
||||||
|
|
@ -26,3 +26,10 @@ final case class Socket[Cmd](lv: LiveView[Cmd]):
|
||||||
println(DiffBuilder.build(element, trackUpdates = clientInitialized).toJsonPretty)
|
println(DiffBuilder.build(element, trackUpdates = clientInitialized).toJsonPretty)
|
||||||
clientInitialized = true
|
clientInitialized = true
|
||||||
element.setAllUnchanged()
|
element.setAllUnchanged()
|
||||||
|
|
||||||
|
def diff: Diff =
|
||||||
|
element.syncAll()
|
||||||
|
val diff = DiffBuilder.build(element, trackUpdates = clientInitialized)
|
||||||
|
clientInitialized = true
|
||||||
|
element.setAllUnchanged()
|
||||||
|
diff
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package scalive
|
package scalive
|
||||||
|
|
||||||
|
import scalive.SocketMessage.LiveResponse
|
||||||
|
import scalive.SocketMessage.Payload
|
||||||
import zio.*
|
import zio.*
|
||||||
import zio.http.*
|
import zio.http.*
|
||||||
import zio.http.ChannelEvent.Read
|
import zio.http.ChannelEvent.Read
|
||||||
|
|
@ -22,17 +24,41 @@ class LiveRouter(rootLayout: HtmlElement => HtmlElement, liveRoutes: List[LiveRo
|
||||||
|
|
||||||
private val socketApp: WebSocketApp[Any] =
|
private val socketApp: WebSocketApp[Any] =
|
||||||
Handler.webSocket { channel =>
|
Handler.webSocket { channel =>
|
||||||
channel.receiveAll {
|
channel
|
||||||
case Read(WebSocketFrame.Text(content)) =>
|
.receiveAll {
|
||||||
for
|
case Read(WebSocketFrame.Text(content)) =>
|
||||||
_ <-
|
for
|
||||||
content.fromJson[SocketMessage].fold(m => ZIO.logError(m), m => ZIO.log(m.toString))
|
message <- ZIO
|
||||||
_ <- channel.send(Read(WebSocketFrame.text("bar")))
|
.fromEither(content.fromJson[SocketMessage])
|
||||||
yield ()
|
.mapError(new IllegalArgumentException(_))
|
||||||
case _ => ZIO.unit
|
reply <- handleMessage(message)
|
||||||
}
|
_ <- channel.send(Read(WebSocketFrame.text(reply.toJson)))
|
||||||
|
yield ()
|
||||||
|
case _ => ZIO.unit
|
||||||
|
}.tapErrorCause(ZIO.logErrorCause(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def handleMessage(message: SocketMessage): Task[SocketMessage] =
|
||||||
|
val reply = message.payload match
|
||||||
|
case Payload.Heartbeat => ZIO.succeed(Payload.Reply("ok", LiveResponse.Empty))
|
||||||
|
case Payload.Join(url, session, static, sticky) =>
|
||||||
|
// TODO very rough handling
|
||||||
|
ZIO
|
||||||
|
.fromEither(URL.decode(url)).map(url =>
|
||||||
|
val req = Request(url = url)
|
||||||
|
liveRoutes
|
||||||
|
.collectFirst { route =>
|
||||||
|
val pathParams = route.path.decode(req.path).getOrElse(???)
|
||||||
|
val lv = route.liveviewBuilder(pathParams, req)
|
||||||
|
val s = Socket(lv)
|
||||||
|
Payload.Reply("ok", LiveResponse.InitDiff(s.diff))
|
||||||
|
|
||||||
|
}.getOrElse(???)
|
||||||
|
)
|
||||||
|
case Payload.Reply(_, _) => ZIO.die(new IllegalArgumentException())
|
||||||
|
|
||||||
|
reply.map(SocketMessage(message.joinRef, message.messageRef, message.topic, "phx_reply", _))
|
||||||
|
|
||||||
val routes: Routes[Any, Response] =
|
val routes: Routes[Any, Response] =
|
||||||
Routes.fromIterable(
|
Routes.fromIterable(
|
||||||
liveRoutes
|
liveRoutes
|
||||||
|
|
@ -41,6 +67,7 @@ class LiveRouter(rootLayout: HtmlElement => HtmlElement, liveRoutes: List[LiveRo
|
||||||
Method.GET / "live" / "websocket" -> handler(socketApp.toResponse)
|
Method.GET / "live" / "websocket" -> handler(socketApp.toResponse)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
end LiveRouter
|
||||||
|
|
||||||
final case class SocketMessage(
|
final case class SocketMessage(
|
||||||
// Live session ID, auto increment defined by the client on join
|
// Live session ID, auto increment defined by the client on join
|
||||||
|
|
@ -58,8 +85,9 @@ object SocketMessage:
|
||||||
Chunk(joinRef, Json.Str(messageRef), Json.Str(topic), Json.Str(eventType), payload)
|
Chunk(joinRef, Json.Str(messageRef), Json.Str(topic), Json.Str(eventType), payload)
|
||||||
) =>
|
) =>
|
||||||
val payloadParsed = eventType match
|
val payloadParsed = eventType match
|
||||||
case "phx_join" => payload.as[Payload.Join]
|
case "heartbeat" => Right(Payload.Heartbeat)
|
||||||
case s => Left(s"Unknown event type : $s")
|
case "phx_join" => payload.as[Payload.Join]
|
||||||
|
case s => Left(s"Unknown event type : $s")
|
||||||
|
|
||||||
payloadParsed.map(
|
payloadParsed.map(
|
||||||
SocketMessage(
|
SocketMessage(
|
||||||
|
|
@ -74,22 +102,41 @@ object SocketMessage:
|
||||||
},
|
},
|
||||||
m =>
|
m =>
|
||||||
Json.Arr(
|
Json.Arr(
|
||||||
m.joinRef.map(Json.Num(_)).getOrElse(Json.Null),
|
m.joinRef.map(ref => Json.Str(ref.toString)).getOrElse(Json.Null),
|
||||||
Json.Num(m.messageRef),
|
Json.Str(m.messageRef.toString),
|
||||||
Json.Str(m.topic),
|
Json.Str(m.topic),
|
||||||
Json.Str(m.eventType),
|
Json.Str(m.eventType),
|
||||||
m.payload.match
|
m.payload.match
|
||||||
case p: Payload.Join => p.toJsonAST.getOrElse(throw new IllegalArgumentException())
|
case Payload.Heartbeat => Json.Obj.empty
|
||||||
|
case p: Payload.Join => p.toJsonAST.getOrElse(throw new IllegalArgumentException())
|
||||||
|
case p: Payload.Reply => p.toJsonAST.getOrElse(throw new IllegalArgumentException())
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
enum Payload:
|
enum Payload:
|
||||||
|
case Heartbeat
|
||||||
case Join(
|
case Join(
|
||||||
url: String,
|
url: String,
|
||||||
// params: Map[String, String],
|
// params: Map[String, String],
|
||||||
session: String,
|
session: String,
|
||||||
static: Option[String],
|
static: Option[String],
|
||||||
sticky: Boolean)
|
sticky: Boolean)
|
||||||
|
case Reply(status: String, response: LiveResponse)
|
||||||
object Payload:
|
object Payload:
|
||||||
given JsonCodec[Payload.Join] = JsonCodec.derived
|
given JsonCodec[Payload.Join] = JsonCodec.derived
|
||||||
|
given JsonEncoder[Payload.Reply] = JsonEncoder.derived
|
||||||
|
|
||||||
|
enum LiveResponse:
|
||||||
|
case Empty
|
||||||
|
case InitDiff(rendered: scalive.Diff)
|
||||||
|
object LiveResponse:
|
||||||
|
given JsonEncoder[LiveResponse] =
|
||||||
|
JsonEncoder[Json].contramap {
|
||||||
|
case Empty => Json.Obj.empty
|
||||||
|
case InitDiff(rendered) =>
|
||||||
|
Json.Obj(
|
||||||
|
"liveview_version" -> Json.Str("1.1.8"),
|
||||||
|
"rendered" -> rendered.toJsonAST.getOrElse(throw new IllegalArgumentException())
|
||||||
|
)
|
||||||
|
}
|
||||||
end SocketMessage
|
end SocketMessage
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue