Created
February 21, 2024 15:12
-
-
Save andimiller/59084d0bb2ba1d705e1cc81917a6a10d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.andimiller.examples.htmx | |
package chat | |
import cats.implicits._ | |
import cats.effect._ | |
import org.http4s.{FormDataDecoder, HttpRoutes} | |
import org.http4s.dsl.Http4sDsl | |
import HTML._ | |
import org.http4s.FormDataDecoder.{field, formEntityDecoder} | |
import java.time.LocalDateTime | |
import scala.xml.Elem | |
case class Message(time: LocalDateTime, name: String, message: String) | |
class Routes[F[_]: Async](history: Ref[F, Vector[Message]]) extends Http4sDsl[F] { | |
def header(title: String) = | |
<head> | |
<title>{title}</title> | |
<script src="https://unpkg.com/[email protected]"></script> | |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css" /> | |
</head> | |
case class NameForm(name: String) | |
implicit val mapper: FormDataDecoder[NameForm] = field[String]("name").map(NameForm) | |
case class MessageForm(name: String, message: String) | |
implicit val msgMapper: FormDataDecoder[MessageForm] = | |
( | |
field[String]("name"), | |
field[String]("message") | |
).mapN(MessageForm) | |
def chatLog: F[Elem] = history.get.map { msgs => | |
<table> | |
<tbody> | |
{ | |
msgs.map { msg => | |
<tr> | |
<td>{msg.time.toString}</td> | |
<td>{msg.name}</td> | |
<td>{msg.message}</td> | |
</tr> | |
} | |
} | |
</tbody> | |
</table> | |
} | |
def routes: HttpRoutes[F] = HttpRoutes.of[F] { | |
case GET -> Root => | |
Ok( | |
<html> | |
{header("Chat Demo")} | |
<body> | |
<header> | |
<h1>Chat Demo</h1> | |
</header> | |
<main> | |
<div id="chatlog" hx-get="/chatlog" hx-trigger="every 1s"> | |
</div> | |
<div id="input"> | |
<input | |
name="name" | |
placeholder="Enter your name to chat" | |
hx-post="/name" | |
hx-target="#input" | |
></input> | |
</div> | |
</main> | |
</body> | |
</html> | |
) | |
case req @ POST -> Root / "name" => | |
req.as[NameForm].flatMap { nameForm => | |
Ok( | |
<form | |
hx-post="/message" | |
hx-target="#chatlog" | |
> | |
<fieldset role="group"> | |
<input name="name" value={nameForm.name} readonly=""> | |
</input> | |
<input | |
name="message" | |
type="text" | |
placeholder="Enter your message to chat" | |
hx-sync="closest" | |
hx-trigger="submit" | |
width="20%" | |
></input> | |
<button>Send</button> | |
</fieldset> | |
</form> | |
) | |
} | |
case req @ POST -> Root / "message" => | |
req.as[MessageForm].flatMap { msgForm => | |
history.update { prev => | |
prev.appended( | |
Message(LocalDateTime.now(), msgForm.name, msgForm.message) | |
) | |
} *> chatLog.flatMap(Ok(_)) | |
} | |
case GET -> Root / "chatlog" => | |
chatLog.flatMap(Ok(_)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment