Skip to content

Instantly share code, notes, and snippets.

@Tamriel
Last active April 26, 2024 07:29
Show Gist options
  • Save Tamriel/9447b18c4aa8b8a362d6dd839d00cc22 to your computer and use it in GitHub Desktop.
Save Tamriel/9447b18c4aa8b8a362d6dd839d00cc22 to your computer and use it in GitHub Desktop.
import buffer.Buffer
import org.scalajs.dom
import dom.document
import eu.timepit.crjdt.circe.RegNodeConflictResolver.LWW
import eu.timepit.crjdt.circe.syntax._
import eu.timepit.crjdt.core.{Operation, Replica}
import eu.timepit.crjdt.core.syntax._
import upickle.default._
import scala.scalajs.js
import scala.scalajs.js.JSApp
import scalatags.JsDom.all._
object Main extends JSApp {
val listName = "list"
def main(): Unit = {
var senderSeqNo = 16 // todo: restore from local crjdt
// create text field
val box = input(
`type` := "text",
placeholder := "This text field is synced."
).render
// init crjdt
val p0 = Replica.empty("p")
val list = doc.downField(listName)
var p1 = p0.applyCmd(list := `[]`)
// init server connection
// todo: save peer_id
val channelID = "a4f512ab6b5c6750bf7ea73486cf76a9"
val channelIDHex = Buffer.from(channelID, "hex")
val url = "ws://localhost:8080/events?peer_id=651e14bffe377c6d856a27704998da470156605b0c92035aa0cc3917eba5e0f7"
val socket = new dom.WebSocket(url)
socket.binaryType = "arraybuffer"
socket.onopen = { (e: dom.Event) =>
// connect to server
val subscribeToChannel = js.Dictionary(
"channelID" -> channelIDHex,
"startOffset" -> -1
)
val clientToServer = js.Dictionary(
"message" -> js.Dictionary(
"org.trvedata.trvedb.avro.SubscribeToChannel" -> subscribeToChannel
)
)
val buf = AvroSchemas.clientToServerType.toBuffer(clientToServer)
socket.send(buf)
// send message when user types
box.onkeydown = (e: dom.KeyboardEvent) => {
var pos = list.iter
for (i <- 1 to box.selectionStart) {
pos = pos.next
}
val opsBefore = p1.generatedOps
val cmd = e.key match {
case "Delete" => pos.delete
case "Backspace" => pos.delete
case _ => pos.insert(e.key) // todo: filter out all special keys, like in the ruby editor
}
p1 = p1.applyCmd(cmd)
val newOps = p1.generatedOps.diff(opsBefore)
senderSeqNo += 1
val sendMessage = js.Dictionary(
"channelID" -> channelIDHex,
"senderSeqNo" -> senderSeqNo,
"payload" -> Buffer.from(write(newOps))
)
val clientToServer = js.Dictionary(
"message" -> js.Dictionary(
"org.trvedata.trvedb.avro.SendMessage" -> sendMessage
)
)
socket.send(AvroSchemas.clientToServerType.toBuffer(clientToServer))
println(p1.document.toJson)
}
}
// change crjdt on message
socket.onmessage = { (e: dom.MessageEvent) =>
val buf = Buffer.from(e.data)
val serverToClientJsDict = AvroSchemas.serverToClientType.fromBuffer(buf)
println(serverToClientJsDict)
val messageJsDictOption = serverToClientJsDict.get("message")
if (messageJsDictOption.isDefined) {
val messageJsDict = messageJsDictOption.get
val sendMessageErrorDictOption = messageJsDict.get("org.trvedata.trvedb.avro.SendMessageError")
val receiveMessageDictOption = messageJsDict.get("org.trvedata.trvedb.avro.ReceiveMessage")
if (sendMessageErrorDictOption.isDefined) {
println(sendMessageErrorDictOption.get)
} else if (receiveMessageDictOption.isDefined) {
val payloadOption = receiveMessageDictOption.get.get("payload")
if (payloadOption.isDefined) {
// todo: maybe I can use types instead of Any
val payloadBuffer = payloadOption.get
val ops = read[Vector[Operation]](payloadBuffer.toString)
p1 = p1.applyRemoteOps(ops)
// convert crjdt to string
val array = p1.document.toJson.\\(listName)(0).asArray.get
var string = ""
for (char <- array) {
string += char.asString.get
}
box.value = string
// todo: test with fast typing, I guess something gets lost
}
}
}
}
// build GUI
document.body.appendChild(
div(
box
).render
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment