From 32c195c2a11e8fcce4be03d0db50da7987e9d8e0 Mon Sep 17 00:00:00 2001
From: Moritz Langenstein <ml5717@ic.ac.uk>
Date: Thu, 10 Oct 2019 15:47:49 +0100
Subject: [PATCH] (ml5717) (alh1717) (gc4117) Synchronisation of line adding
 using CRDT Map and Array

---
 package-lock.json     |  23 ++++-----
 package.json          |   3 +-
 public/index.html     |  53 +++------------------
 public/js/.gitkeep    |   0
 src/app.js            | 107 ++++++++++++++++++++++++++++++++++++++----
 src/y-webrtc/index.js |  16 +++----
 6 files changed, 126 insertions(+), 76 deletions(-)
 create mode 100644 public/js/.gitkeep

diff --git a/package-lock.json b/package-lock.json
index c2a7af6..d1bb9ed 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1547,11 +1547,6 @@
       "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
       "dev": true
     },
-    "fast-diff": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
-      "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w=="
-    },
     "fast-json-stable-stringify": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
@@ -4669,6 +4664,11 @@
       "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
       "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
     },
+    "uuid": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
+      "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
+    },
     "v8-compile-cache": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz",
@@ -4929,19 +4929,16 @@
       "resolved": "https://registry.npmjs.org/y-array/-/y-array-10.1.4.tgz",
       "integrity": "sha1-4TGlsDDW3LyhAmjUKT7PRL2uezQ="
     },
+    "y-map": {
+      "version": "10.1.3",
+      "resolved": "https://registry.npmjs.org/y-map/-/y-map-10.1.3.tgz",
+      "integrity": "sha1-oVgCztusNp5Qa5b2je+PCi6DYZY="
+    },
     "y-memory": {
       "version": "8.0.9",
       "resolved": "https://registry.npmjs.org/y-memory/-/y-memory-8.0.9.tgz",
       "integrity": "sha512-OrcReh6DgZhz5R7JGXqAH53T0Ygw24qcxKj4jN9w2DIi2eIiKFCD5Y6apBTTNxiw2FaVP15F+M8phRRIMXFGBQ=="
     },
-    "y-text": {
-      "version": "9.5.1",
-      "resolved": "https://registry.npmjs.org/y-text/-/y-text-9.5.1.tgz",
-      "integrity": "sha512-uwNLY4LeLLR7Cu4xfvh9ZY4gmOYyjii4irDHYRuhPDs1XFcm6krVaALeggA42LevTQ+k5q1eHpdv5jnJha8r6g==",
-      "requires": {
-        "fast-diff": "^1.1.1"
-      }
-    },
     "y18n": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
diff --git a/package.json b/package.json
index df286f3..c717ce0 100644
--- a/package.json
+++ b/package.json
@@ -11,9 +11,10 @@
     "http-server": "^0.11.1",
     "peer": "git+https://github.com/peers/peerjs-server.git",
     "peerjs": "^1.1.0",
+    "uuid": "^3.3.3",
     "y-array": "^10.1.4",
+    "y-map": "^10.1.3",
     "y-memory": "^8.0.9",
-    "y-text": "^9.5.1",
     "yjs": "^12.3.3"
   },
   "devDependencies": {
diff --git a/public/index.html b/public/index.html
index 338342d..4f47abd 100644
--- a/public/index.html
+++ b/public/index.html
@@ -16,52 +16,13 @@
       <ul id="connected-peers"></ul>
     </div>
 
-    <textarea style="width: 100%; height: 500px" id="textfield"></textarea>
-    <script src="js/app.js"></script>
-
-    <svg id="whiteboard" width="100%" height="100%"></svg>
-    <script>
-      var whiteboard = document.getElementById("whiteboard")
-
-      var painting = false
-      var path
-
-      whiteboard.onmousedown = function(e) {
-        painting = true
-
-        var mouse = {
-          x: e.offsetX,
-          y: e.offsetY,
-        }
-
-        path = document.createElementNS("http://www.w3.org/2000/svg", "path")
+    <svg
+      id="whiteboard"
+      width="100%"
+      height="100%"
+      style="position: fixed"
+    ></svg>
 
-        path.setAttribute("d", "M" + mouse.x + " " + mouse.y)
-        path.setAttribute("stroke", "blue")
-        path.setAttribute("stroke-width", 3)
-        path.setAttribute("fill", "none")
-        path.setAttribute("pointer-events", "none")
-
-        whiteboard.appendChild(path)
-      }
-
-      whiteboard.onmouseup = function(e) {
-        painting = false
-      }
-
-      whiteboard.onmousemove = function(e) {
-        var mouse = {
-          x: e.offsetX,
-          y: e.offsetY,
-        }
-
-        if (painting) {
-          path.setAttribute(
-            "d",
-            path.getAttribute("d") + " L" + mouse.x + " " + mouse.y,
-          )
-        }
-      }
-    </script>
+    <script src="js/app.js"></script>
   </body>
 </html>
diff --git a/public/js/.gitkeep b/public/js/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/src/app.js b/src/app.js
index 978f335..9d426d0 100644
--- a/src/app.js
+++ b/src/app.js
@@ -1,25 +1,25 @@
 const Y = require("yjs")
 require("y-memory")(Y)
+require("y-map")(Y)
 require("y-array")(Y)
-require("y-text")(Y)
 require("./y-webrtc")(Y)
 
+const uuidv4 = require("uuid/v4")
+
 Y({
   db: {
-    name: "memory"
+    name: "memory",
   },
   connector: {
     name: "webrtc",
     host: "localhost",
     port: 3000,
-    path: "/api"
+    path: "/api",
   },
   share: {
-    textfield: "Text"
-  }
-}).then(y => {
-  y.share.textfield.bind(document.getElementById("textfield"))
-
+    drawing: "Map",
+  },
+}).then((y) => {
   const userIDElem = document.getElementById("user-id")
   const peerIDElem = document.getElementById("peer-id")
   const peerButton = document.getElementById("peer-connect")
@@ -58,4 +58,95 @@ Y({
 
     peerIDElem.value = ""
   }
+
+  const whiteboard = document.getElementById("whiteboard")
+
+  var painting = false
+  var paths = new Map()
+  var pathID
+
+  function createOrUpdatePath(uid, points) {
+    var path = paths.get(uid)
+
+    if (path === undefined) {
+      path = document.createElementNS("http://www.w3.org/2000/svg", "path")
+
+      path.setAttribute("stroke", "blue")
+      path.setAttribute("stroke-width", 3)
+      path.setAttribute("fill", "none")
+      path.setAttribute("pointer-events", "none")
+
+      whiteboard.appendChild(path)
+
+      paths.set(uid, path)
+    }
+
+    points = points.toArray().filter((point) => point !== undefined)
+
+    if (points.length <= 0) {
+      path.removeAttribute("d")
+
+      return path
+    }
+
+    var pathString = "M" + points[0][0] + " " + points[0][1]
+
+    for (var i = 1; i < points.length; i++) {
+      pathString += " L" + points[i][0] + " " + points[i][1]
+    }
+
+    path.setAttribute("d", pathString)
+
+    return path
+  }
+
+  whiteboard.onmousedown = function(e) {
+    painting = true
+
+    const mouse = {
+      x: e.offsetX,
+      y: e.offsetY,
+    }
+
+    pathID = uuidv4()
+
+    const sharedPath = y.share.drawing.set(pathID, Y.Array)
+    sharedPath.push([[mouse.x, mouse.y]])
+  }
+
+  whiteboard.onmouseup = function() {
+    painting = false
+  }
+
+  whiteboard.onmousemove = function(e) {
+    const mouse = {
+      x: e.offsetX,
+      y: e.offsetY,
+    }
+
+    if (painting) {
+      const sharedPath = y.share.drawing.get(pathID)
+      sharedPath.push([[mouse.x, mouse.y]])
+    }
+  }
+
+  y.share.drawing.observe(function(lineEvent) {
+    const lineID = lineEvent.name
+
+    switch (lineEvent.type) {
+      case "add":
+        createOrUpdatePath(lineID, lineEvent.value)
+
+        lineEvent.value.observe(function(pointEvent) {
+          switch (pointEvent.type) {
+            case "insert":
+              console.log(pointEvent)
+              createOrUpdatePath(lineID, pointEvent.object)
+              break
+          }
+        })
+
+        break
+    }
+  })
 })
diff --git a/src/y-webrtc/index.js b/src/y-webrtc/index.js
index 0d2f040..17e0eba 100644
--- a/src/y-webrtc/index.js
+++ b/src/y-webrtc/index.js
@@ -2,7 +2,7 @@
 "use strict"
 
 var {
-  peerjs: { Peer }
+  peerjs: { Peer },
 } = require("peerjs")
 
 function extend(Y) {
@@ -23,7 +23,7 @@ function extend(Y) {
       var peer = new Peer({
         host: this.webrtcOptions.host,
         port: this.webrtcOptions.port,
-        path: this.webrtcOptions.path
+        path: this.webrtcOptions.path,
       })
 
       this.peer = peer
@@ -31,7 +31,7 @@ function extend(Y) {
       this.peers = new Map()
 
       peer.on("open", function(id) {
-        console.log("My peer ID is: " + id)
+        //console.log("My peer ID is: " + id)
 
         for (var f of self.userEventListeners) {
           f({ action: "userID", id: id })
@@ -53,20 +53,20 @@ function extend(Y) {
       var self = this
 
       dataConnection.on("open", function() {
-        console.log("Connected to peer " + dataConnection.peer)
+        //console.log("Connected to peer " + dataConnection.peer)
 
         self.peers.set(dataConnection.peer, dataConnection)
         self.userJoined(dataConnection.peer, "master")
       })
 
       dataConnection.on("data", function(data) {
-        console.log("Message from peer " + dataConnection.peer + ":", data)
+        //console.log("Message from peer " + dataConnection.peer + ":", data)
 
         self.receiveMessage(dataConnection.peer, data)
       })
 
       dataConnection.on("close", function() {
-        console.log("Disconnected from peer " + dataConnection.peer)
+        //console.log("Disconnected from peer " + dataConnection.peer)
 
         self.peers.delete(dataConnection.peer)
         self.userLeft(dataConnection.peer)
@@ -87,7 +87,7 @@ function extend(Y) {
     }
 
     send(uid, message) {
-      console.log("Sending message", message, "to " + uid)
+      //console.log("Sending message", message, "to " + uid)
 
       var self = this
 
@@ -109,7 +109,7 @@ function extend(Y) {
     }
 
     broadcast(message) {
-      console.log("Broadcasting message", message)
+      //console.log("Broadcasting message", message)
 
       for (const uid of this.peers.keys()) {
         this.send(uid, message)
-- 
GitLab