Support live navigation

This commit is contained in:
Paul-Henri Froidmont 2025-09-13 02:44:09 +02:00
parent 0b067aa7e1
commit d42472061b
Signed by: phfroidmont
GPG key ID: BE948AFD7E7873BE
5 changed files with 63 additions and 30 deletions

View file

@ -8,6 +8,10 @@ package object scalive extends HtmlTags with HtmlAttrs with ComplexHtmlKeys:
lazy val defer = htmlAttr("defer", codecs.BooleanAsAttrPresenceCodec)
object link:
def navigate(path: String, mods: Mod*): HtmlElement =
a(href := path, phx.link := "redirect", phx.linkState := "push", mods)
object phx:
private def phxAttr(suffix: String): HtmlAttr[String] =
new HtmlAttr(s"phx-$suffix", StringAsIsCodec)
@ -16,10 +20,12 @@ package object scalive extends HtmlTags with HtmlAttrs with ComplexHtmlKeys:
private def dataPhxAttr(suffix: String): HtmlAttr[String] =
dataAttr(s"phx-$suffix")
private[scalive] lazy val session = dataPhxAttr("session")
private[scalive] lazy val main = htmlAttr("data-phx-main", BooleanAsAttrPresenceCodec)
lazy val click = phxAttrJson("click")
def value(key: String) = phxAttr(s"value-$key")
private[scalive] lazy val session = dataPhxAttr("session")
private[scalive] lazy val main = htmlAttr("data-phx-main", BooleanAsAttrPresenceCodec)
private[scalive] lazy val link = dataPhxAttr("link")
private[scalive] lazy val linkState = dataPhxAttr("link-state")
lazy val click = phxAttrJson("click")
def value(key: String) = phxAttr(s"value-$key")
implicit def stringToMod(v: String): Mod = Mod.Content.Text(v)
implicit def htmlElementToMod(el: HtmlElement): Mod = Mod.Content.Tag(el)

View file

@ -16,7 +16,15 @@ final case class WebSocketMessage(
topic: String,
eventType: String,
payload: WebSocketMessage.Payload):
val meta = WebSocketMessage.Meta(joinRef, messageRef, topic, eventType)
val meta = WebSocketMessage.Meta(joinRef, messageRef, topic, eventType)
def okReply =
WebSocketMessage(
joinRef,
messageRef,
topic,
"phx_reply",
Payload.Reply("ok", LiveResponse.Empty)
)
object WebSocketMessage:
final case class Meta(
@ -33,6 +41,8 @@ object WebSocketMessage:
val payloadParsed = eventType match
case "heartbeat" => Right(Payload.Heartbeat)
case "phx_join" => payload.as[Payload.Join]
case "phx_leave" => Right(Payload.Leave)
case "phx_close" => Right(Payload.Close)
case "event" => payload.as[Payload.Event]
case s => Left(s"Unknown event type : $s")
@ -56,6 +66,8 @@ object WebSocketMessage:
m.payload match
case Payload.Heartbeat => Json.Obj.empty
case p: Payload.Join => p.toJsonAST.getOrElse(throw new IllegalArgumentException())
case Payload.Leave => Json.Obj.empty
case Payload.Close => Json.Obj.empty
case p: Payload.Reply => p.toJsonAST.getOrElse(throw new IllegalArgumentException())
case p: Payload.Event => p.toJsonAST.getOrElse(throw new IllegalArgumentException())
case p: Payload.Diff => p.toJsonAST.getOrElse(throw new IllegalArgumentException())
@ -65,11 +77,14 @@ object WebSocketMessage:
enum Payload:
case Heartbeat
case Join(
url: String,
url: Option[String],
redirect: Option[String],
// params: Map[String, String],
session: String,
static: Option[String],
sticky: Boolean)
case Leave
case Close
case Reply(status: String, response: LiveResponse)
case Diff(diff: scalive.Diff)
case Event(`type`: Payload.EventType, event: String, value: Map[String, String])