diff options
author | Jakob Odersky <jakob@odersky.com> | 2018-05-06 13:56:16 -0700 |
---|---|---|
committer | Jakob Odersky <jakob@odersky.com> | 2018-05-08 23:02:39 -0700 |
commit | 8ecae787ff7124b008229d958c579c73649dd9e4 (patch) | |
tree | dad7bea34d9b7ea0f716a783f3b57de491ec959e /common/shared | |
download | scala-triad-8ecae787ff7124b008229d958c579c73649dd9e4.tar.gz scala-triad-8ecae787ff7124b008229d958c579c73649dd9e4.tar.bz2 scala-triad-8ecae787ff7124b008229d958c579c73649dd9e4.zip |
Initial commit
Diffstat (limited to 'common/shared')
-rw-r--r-- | common/shared/src/main/scala/ApiProtocol.scala | 12 | ||||
-rw-r--r-- | common/shared/src/main/scala/Message.scala | 39 | ||||
-rw-r--r-- | common/shared/src/main/scala/Templates.scala | 16 | ||||
-rw-r--r-- | common/shared/src/main/scala/TextTemplates.scala | 46 | ||||
-rw-r--r-- | common/shared/src/main/scala/http/Backend.scala | 8 | ||||
-rw-r--r-- | common/shared/src/main/scala/http/Request.scala | 9 | ||||
-rw-r--r-- | common/shared/src/main/scala/http/Response.scala | 6 |
7 files changed, 136 insertions, 0 deletions
diff --git a/common/shared/src/main/scala/ApiProtocol.scala b/common/shared/src/main/scala/ApiProtocol.scala new file mode 100644 index 0000000..5d0e00a --- /dev/null +++ b/common/shared/src/main/scala/ApiProtocol.scala @@ -0,0 +1,12 @@ +package triad + +import java.time.Instant +import spray.json.{DerivedJsonProtocol, JsNumber, JsValue, JsonFormat} + +object ApiProtocol extends DerivedJsonProtocol { + implicit val timestampFormat: JsonFormat[Instant] = new JsonFormat[Instant] { + def read(js: JsValue) = Instant.ofEpochMilli(js.convertTo[Long]) + def write(i: Instant) = JsNumber(i.toEpochMilli) + } + implicit val messageFormat = jsonFormat[Message] +} diff --git a/common/shared/src/main/scala/Message.scala b/common/shared/src/main/scala/Message.scala new file mode 100644 index 0000000..7ee36cb --- /dev/null +++ b/common/shared/src/main/scala/Message.scala @@ -0,0 +1,39 @@ +package triad + +import java.security.MessageDigest +import java.time.Instant + +case class Message(content: String, + author: String, + timestamp: Instant = Instant.now()) { + + lazy val id: String = { + val digest = MessageDigest.getInstance("SHA-256") + digest.update(content.getBytes) + digest.update(author.getBytes) + digest.update((timestamp.getEpochSecond & 0xff).toByte) + digest.update(((timestamp.getEpochSecond >> 8) & 0xff).toByte) + digest.update(((timestamp.getEpochSecond >> 16) & 0xff).toByte) + digest.update(((timestamp.getEpochSecond >> 24) & 0xff).toByte) + digest.update(((timestamp.getEpochSecond >> 32) & 0xff).toByte) + digest.update(((timestamp.getEpochSecond >> 40) & 0xff).toByte) + digest.update(((timestamp.getEpochSecond >> 48) & 0xff).toByte) + digest.update(((timestamp.getEpochSecond >> 56) & 0xff).toByte) + Message.bytesToHex(digest.digest()) + } + +} + +object Message { + private def bytesToHex(hash: Array[Byte]): String = { + val hexString = new StringBuffer(hash.length * 2) + var i = 0 + while (i < hash.length) { + val hex = Integer.toHexString(0xff & hash(i)) + if (hex.length == 1) hexString.append('0') + hexString.append(hex) + i += 1 + } + hexString.toString + } +} diff --git a/common/shared/src/main/scala/Templates.scala b/common/shared/src/main/scala/Templates.scala new file mode 100644 index 0000000..82e1dda --- /dev/null +++ b/common/shared/src/main/scala/Templates.scala @@ -0,0 +1,16 @@ +package triad + +class Templates[Builder, Output <: FragT, FragT]( + val bundle: scalatags.generic.Bundle[Builder, Output, FragT]) { + import bundle.all._ + + def message(msg: Message) = li( + div(`class` := "from")(msg.author), + div(`class` := "content")(msg.content) + ) + + def conversation(messages: Seq[Message]): Tag = ul(id := "conversation")( + for (msg <- messages.sortBy(_.timestamp)) yield message(msg) + ) + +} diff --git a/common/shared/src/main/scala/TextTemplates.scala b/common/shared/src/main/scala/TextTemplates.scala new file mode 100644 index 0000000..24a3b0e --- /dev/null +++ b/common/shared/src/main/scala/TextTemplates.scala @@ -0,0 +1,46 @@ +package triad + +object TextTemplates extends Templates(scalatags.Text) { + import bundle.all._ + + def scripts(js: Boolean = true) = + if (js) + Seq( + script(`type` := "text/javascript", + src := "/assets/ui/js/ui-fastopt.js"), + script(`type` := "text/javascript")( + raw( + """|document.addEventListener("DOMContentLoaded", function(event) { + | try { + | // root element that will contain the ScalaJS application + | var root = document.getElementById("conversation"); + | + | // clear any existing content + | while (root.firstChild) { + | root.removeChild(root.firstChild); + | } + | + | // run ScalaJS application + | console.info("Starting ScalaJS application...") + | triad.Main().main(root) + | } catch(ex) { + | // display warning message in case of exception + | //document.getElementById("scalajs-error").style.display = "block"; + | throw ex; + | } + |}); + |""".stripMargin + ) + ) + ) + else Seq.empty + + def page(messages: Seq[Message], js: Boolean = true) = html( + head(), + body( + conversation(messages), + scripts(js) + ) + ) + +} diff --git a/common/shared/src/main/scala/http/Backend.scala b/common/shared/src/main/scala/http/Backend.scala new file mode 100644 index 0000000..f3ce5f8 --- /dev/null +++ b/common/shared/src/main/scala/http/Backend.scala @@ -0,0 +1,8 @@ +package triad +package http + +import scala.concurrent.Future + +trait Backend { + def send(request: Request): Future[Response] +} diff --git a/common/shared/src/main/scala/http/Request.scala b/common/shared/src/main/scala/http/Request.scala new file mode 100644 index 0000000..ec7d28d --- /dev/null +++ b/common/shared/src/main/scala/http/Request.scala @@ -0,0 +1,9 @@ +package triad +package http + +case class Request( + method: String, + url: String, + headers: Map[String, String] = Map.empty, + body: Array[Byte] = Array.empty +) diff --git a/common/shared/src/main/scala/http/Response.scala b/common/shared/src/main/scala/http/Response.scala new file mode 100644 index 0000000..4ba2342 --- /dev/null +++ b/common/shared/src/main/scala/http/Response.scala @@ -0,0 +1,6 @@ +package triad +package http + +case class Response(statusCode: Int, + headers: Map[String, String], + body: Array[Byte]) |