Skip to content
Snippets Groups Projects
room.js 4.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • import uuidv4 from "uuid/v4"
    import yArray from "y-array"
    import yMap from "y-map"
    
    import yUnion, { Union } from "./y-union.js"
    
    import yMemory from "y-memory"
    import Y from "yjs"
    
    
    import yP2PMesh from "./y-p2p-mesh.js"
    
    
    yMemory(Y)
    
    Y.Struct.Union = Union
    yUnion(Y)
    
    yMap(Y)
    yArray(Y)
    
    import { spreadErasureIntervals, flattenErasureIntervals } from "./erasure.js"
    
    class Room extends EventTarget {
      constructor(name) {
        super()
        this.name = name
        this._y = null
        this.ownID = null
    
        this.erasureIntervals = {}
    
    Giovanni Caruso's avatar
    Giovanni Caruso committed
      addPath([x, y, w, colour]) {
    
        const id = uuidv4()
    
        this.shared.strokePoints.set(id, Y.Array).push([[x, y, w, colour]])
    
        this.shared.eraseIntervals.set(id, Y.Union)
    
    
        return id
      }
    
    
    Giovanni Caruso's avatar
    Giovanni Caruso committed
      extendPath(id, [x, y, w, colour]) {
    
        this.shared.strokePoints.get(id).push([[x, y, w, colour]])
    
      extendErasureIntervals(pathID, pointID, newIntervals) {
    
        this.shared.eraseIntervals
          .get(pathID)
          .merge(flattenErasureIntervals({ [pointID]: newIntervals }))
    
    Nayeem Rahman's avatar
    Nayeem Rahman committed
        const paths = new Map()
    
        for (const id of this.shared.strokePoints.keys()) {
    
          paths.set(id, this._generatePath(id))
        }
    
        return paths
      }
    
    
      get shared() {
        return this._y.share
      }
    
    
        const points = this.shared.strokePoints.get(id)
    
        if (!points) return []
    
        return points.toArray()
    
      _generateRemovedIntervals(id) {
    
        const intervals = this.shared.eraseIntervals.get(id)
    
        if (!intervals) return []
    
        return spreadErasureIntervals(intervals.get())
    
      inviteUser(id) {
        this._y.connector.connectToPeer(id)
      }
    
    
      async _initialise(connection) {
    
        this._y = await Y({
          db: {
            name: "memory",
          },
          connector: {
    
            url: "/",
            room: this.name,
    
            handshake: {
              initial: 100,
              interval: 500,
            },
            heartbeat: {
              interval: 500,
              minimum: 1000,
              timeout: 10000,
            },
    
            onUserEvent: (event) => {
    
              if (event.action == "userConnection") {
    
                const { id, quality } = event
    
                  new CustomEvent("userConnection", { detail: { id, quality } }),
    
                )
              } else if (event.action == "userID") {
    
                const { user: id } = event
    
                this.ownID = id
                this.dispatchEvent(new CustomEvent("allocateOwnID", { detail: id }))
              } else if (event.action == "userJoined") {
                const { user: id } = event
                this.dispatchEvent(new CustomEvent("userJoin", { detail: id }))
              } else if (event.action == "userLeft") {
                const { user: id } = event
                this.dispatchEvent(new CustomEvent("userLeave", { detail: id }))
    
              } else if (event.action === "peerSyncedWithUs") {
                const { user: id } = event
                this.dispatchEvent(
                  new CustomEvent("peerSyncedWithUs", { detail: id }),
                )
              } else if (event.action === "waitingForSyncStep") {
                const { user: id } = event
                this.dispatchEvent(
                  new CustomEvent("waitingForSyncStep", { detail: id }),
                )
              } else if (event.action === "weSyncedWithPeer") {
                const { user: id } = event
                this.dispatchEvent(
                  new CustomEvent("weSyncedWithPeer", { detail: id }),
                )
    
            strokePoints: "Map",
            eraseIntervals: "Map",
    
    
        const dispatchRemovedIntervalsEvent = (lineEvent) => {
          const id = lineEvent.name
          const intervals = this._generateRemovedIntervals(id)
          const points = this._generatePath(id)
          const detail = { id, intervals, points }
          this.dispatchEvent(
            new CustomEvent("removedIntervalsChange", {
              detail,
            }),
          )
        }
    
        const dispatchPathUpdateEvent = (lineEvent) => {
          const id = lineEvent.name
          const points = this._generatePath(id)
          const detail = { id, points }
          this.dispatchEvent(new CustomEvent("addOrUpdatePath", { detail }))
        }
    
    
        this.shared.strokePoints.observe((lineEvent) => {
    
          if (lineEvent.type == "add") {
    
            dispatchPathUpdateEvent(lineEvent)
    
    
            lineEvent.value.observe((pointEvent) => {
              if (pointEvent.type == "insert") {
    
                dispatchPathUpdateEvent(lineEvent)
    
        this.shared.eraseIntervals.observe((lineEvent) => {
    
          if (lineEvent.type == "add") {
            dispatchRemovedIntervalsEvent(lineEvent)
    
            lineEvent.value.observe(() => {
              dispatchRemovedIntervalsEvent(lineEvent)
            })
          }
    
    export const connect = async (roomName, connection) => {
    
      const room = new Room(roomName)
    
      await room._initialise(connection)
    
      return room
    }