From 48eb81d93b6674c28ab4e5067b018106c3150d02 Mon Sep 17 00:00:00 2001
From: netpro2k <netpro2k@gmail.com>
Date: Wed, 20 Jun 2018 19:16:05 -0700
Subject: [PATCH] Fix counter not correctly handling entity deletion

---
 src/components/networked-counter.js | 143 ++++++++++------------------
 1 file changed, 51 insertions(+), 92 deletions(-)

diff --git a/src/components/networked-counter.js b/src/components/networked-counter.js
index 9b9b5f8be..b26c49d1d 100644
--- a/src/components/networked-counter.js
+++ b/src/components/networked-counter.js
@@ -11,126 +11,85 @@ AFRAME.registerComponent("networked-counter", {
     release_event: { type: "string", default: "grab-end" }
   },
 
-  init: function() {
-    this.count = 0;
-    this.queue = {};
-    this.timeouts = {};
+  init() {
+    this.registeredEls = new Map();
   },
 
-  remove: function() {
-    for (const id in this.queue) {
-      if (this.queue.hasOwnProperty(id)) {
-        const item = this.queue[id];
-        item.el.removeEventListener(this.data.grab_event, item.onGrabHandler);
-        item.el.removeEventListener(this.data.release_event, item.onReleaseHandler);
-      }
-    }
-
-    for (const id in this.timeouts) {
-      this._removeTimeout(id);
-    }
+  remove() {
+    this.registeredEls.forEach(({ onGrabHandler, onReleaseHandler, timeout }, el) => {
+      el.removeEventListener(this.data.grab_event, onGrabHandler);
+      el.removeEventListener(this.data.release_event, onReleaseHandler);
+      clearTimeout(timeout);
+    });
+    this.registeredEls.clear();
   },
 
-  register: function(networkedEl) {
-    if (this.data.max <= 0) {
-      return;
-    }
-
-    const id = NAF.utils.getNetworkId(networkedEl);
-    if (id && this.queue.hasOwnProperty(id)) {
-      return;
-    }
+  register(el) {
+    if (this.data.max <= 0 || this.registeredEls.has(el)) return;
 
-    const now = Date.now();
-    const grabEventListener = this._onGrabbed.bind(this, id);
-    const releaseEventListener = this._onReleased.bind(this, id);
+    const grabEventListener = this._onGrabbed.bind(this, el);
+    const releaseEventListener = this._onReleased.bind(this, el);
 
-    this.queue[id] = {
-      ts: now,
-      el: networkedEl,
+    this.registeredEls.set(el, {
+      ts: Date.now(),
       onGrabHandler: grabEventListener,
       onReleaseHandler: releaseEventListener
-    };
+    });
 
-    networkedEl.addEventListener(this.data.grab_event, grabEventListener);
-    networkedEl.addEventListener(this.data.release_event, releaseEventListener);
+    el.addEventListener(this.data.grab_event, grabEventListener);
+    el.addEventListener(this.data.release_event, releaseEventListener);
 
-    this.count++;
-
-    if (!this._isCurrentlyGrabbed(id)) {
-      this._addTimeout(id);
+    if (!el.is("grabbed")) {
+      this._startTimer(el);
     }
 
     this._destroyOldest();
   },
 
-  deregister: function(networkedEl) {
-    const id = NAF.utils.getNetworkId(networkedEl);
-    if (id && this.queue.hasOwnProperty(id)) {
-      const item = this.queue[id];
-      networkedEl.removeEventListener(this.data.grab_event, item.onGrabHandler);
-      networkedEl.removeEventListener(this.data.release_event, item.onReleaseHandler);
-
-      delete this.queue[id];
-
-      this._removeTimeout(id);
-      delete this.timeouts[id];
-
-      this.count--;
+  deregister(el) {
+    if (this.registeredEls.has(el)) {
+      const { onGrabHandler, onReleaseHandler, timeout } = this.registeredEls.get(el);
+      el.removeEventListener(this.data.grab_event, onGrabHandler);
+      el.removeEventListener(this.data.release_event, onReleaseHandler);
+      clearTimeout(timeout);
+      this.registeredEls.delete(el);
     }
   },
 
-  _onGrabbed: function(id) {
-    this._removeTimeout(id);
+  _onGrabbed(el) {
+    clearTimeout(this.registeredEls.get(el).timeout);
   },
 
-  _onReleased: function(id) {
-    this._removeTimeout(id);
-    this._addTimeout(id);
-    this.queue[id].ts = Date.now();
+  _onReleased(el) {
+    this._startTimer(el);
+    this.registeredEls.get(el).ts = Date.now();
   },
 
-  _destroyOldest: function() {
-    if (this.count > this.data.max) {
-      let oldest = null,
-        ts = Number.MAX_VALUE;
-      for (const id in this.queue) {
-        if (this.queue.hasOwnProperty(id)) {
-          if (this.queue[id].ts < ts && !this._isCurrentlyGrabbed(id)) {
-            oldest = this.queue[id];
-            ts = this.queue[id].ts;
-          }
+  _destroyOldest() {
+    if (this.registeredEls.size > this.data.max) {
+      let oldestEl = null,
+        minTs = Number.MAX_VALUE;
+      this.registeredEls.forEach(({ ts }, el) => {
+        if (ts < minTs && !el.is("grabbed")) {
+          oldestEl = el;
+          minTs = ts;
         }
-      }
-      if (ts > 0) {
-        this.deregister(oldest.el);
-        this._destroy(oldest.el);
-      }
+      });
+      this._destroy(oldestEl);
     }
   },
 
-  _isCurrentlyGrabbed: function(id) {
-    const networkedEl = this.queue[id].el;
-    return networkedEl.is("grabbed");
-  },
-
-  _addTimeout: function(id) {
+  _startTimer(el) {
     if (!this.data.ttl) return;
-    const timeout = this.data.ttl * 1000;
-    this.timeouts[id] = setTimeout(() => {
-      const el = this.queue[id].el;
-      this.deregister(el);
-      this._destroy(el);
-    }, timeout);
-  },
-
-  _removeTimeout: function(id) {
-    if (this.timeouts.hasOwnProperty(id)) {
-      clearTimeout(this.timeouts[id]);
-    }
+    clearTimeout(this.registeredEls.get(el).timeout);
+    this.registeredEls.get(el).timeout = setTimeout(() => {
+      this._detroy(el);
+    }, this.data.ttl * 1000);
   },
 
-  _destroy: function(networkedEl) {
-    networkedEl.parentNode.removeChild(networkedEl);
+  _destroy(el) {
+    // networekd-interactable's remvoe will also call deregister, but it will happen async so we do it here as well.
+    this.deregister(el);
+    el.parentNode.removeChild(el);
   }
 });
-- 
GitLab