DEV: new api to iterate through tracked topics

DEV: new api to iterate through tracked topics

topicTrackingState.forEachTracked(topic,isNew,isUnread) can be used to iterate through tracking state efficiently.

This is handy for extension looking at subsets of tags and categories.

diff --git a/app/assets/javascripts/discourse/app/models/topic-tracking-state.js b/app/assets/javascripts/discourse/app/models/topic-tracking-state.js
index 7a0c537..bd59690 100644
--- a/app/assets/javascripts/discourse/app/models/topic-tracking-state.js
+++ b/app/assets/javascripts/discourse/app/models/topic-tracking-state.js
@@ -441,6 +441,18 @@ const TopicTrackingState = EmberObject.extend({
     return this.countCategoryByState("unread", categoryId, tagId);
   },
 
+  forEachTracked(fn) {
+    Object.values(this.states).forEach(topic => {
+      if (topic.archetype !== "private_message" && !topic.deleted) {
+        let newTopic = isNew(topic);
+        let unreadTopic = isUnread(topic);
+        if (newTopic || unreadTopic) {
+          fn(topic, newTopic, unreadTopic);
+        }
+      }
+    });
+  },
+
   countTags(tags) {
     let counts = {};
 
@@ -448,26 +460,18 @@ const TopicTrackingState = EmberObject.extend({
       counts[tag] = { unreadCount: 0, newCount: 0 };
     });
 
-    Object.values(this.states).forEach(topic => {
-      if (
-        topic.archetype !== "private_message" &&
-        !topic.deleted &&
-        topic.tags
-      ) {
-        let newTopic = isNew(topic);
-        let unreadTopic = isUnread(topic);
-        if (newTopic || unreadTopic) {
-          tags.forEach(tag => {
-            if (topic.tags.indexOf(tag) > -1) {
-              if (unreadTopic) {
-                counts[tag].unreadCount++;
-              }
-              if (newTopic) {
-                counts[tag].newCount++;
-              }
+    this.forEachTracked((topic, newTopic, unreadTopic) => {
+      if (topic.tags) {
+        tags.forEach(tag => {
+          if (topic.tags.indexOf(tag) > -1) {
+            if (unreadTopic) {
+              counts[tag].unreadCount++;
             }
-          });
-        }
+            if (newTopic) {
+              counts[tag].newCount++;
+            }
+          }
+        });
       }
     });
 
diff --git a/test/javascripts/models/topic-tracking-state-test.js b/test/javascripts/models/topic-tracking-state-test.js
index 80dfa59..f759fa9 100644
--- a/test/javascripts/models/topic-tracking-state-test.js
+++ b/test/javascripts/models/topic-tracking-state-test.js
@@ -64,6 +64,81 @@ QUnit.test("tag counts", function(assert) {
   assert.equal(states["unread"].newCount, 0, "unread counts");
 });
 
+QUnit.test("forEachTracked", function(assert) {
+  const state = TopicTrackingState.create();
+
+  state.loadStates([
+    {
+      topic_id: 1,
+      last_read_post_number: null,
+      tags: ["foo", "new"]
+    },
+    {
+      topic_id: 2,
+      last_read_post_number: null,
+      tags: ["new"]
+    },
+    {
+      topic_id: 3,
+      last_read_post_number: null,
+      tags: ["random"]
+    },
+    {
+      topic_id: 4,
+      last_read_post_number: 1,
+      highest_post_number: 7,
+      category_id: 7,
+      tags: ["unread"],
+      notification_level: NotificationLevels.TRACKING
+    },
+    {
+      topic_id: 5,
+      last_read_post_number: 1,
+      highest_post_number: 7,
+      tags: ["bar", "unread"],
+      category_id: 7,
+      notification_level: NotificationLevels.TRACKING
+    },
+    {
+      topic_id: 6,
+      last_read_post_number: 1,
+      highest_post_number: 7,
+      tags: null,
+      notification_level: NotificationLevels.TRACKING
+    }
+  ]);
+
+  let randomUnread = 0,
+    randomNew = 0,
+    sevenUnread = 0,
+    sevenNew = 0;
+
+  state.forEachTracked((topic, isNew, isUnread) => {
+    if (topic.category_id === 7) {
+      if (isNew) {
+        sevenNew += 1;
+      }
+      if (isUnread) {
+        sevenUnread += 1;
+      }
+    }
+
+    if (topic.tags && topic.tags.indexOf("random") > -1) {
+      if (isNew) {
+        randomNew += 1;
+      }
+      if (isUnread) {
+        randomUnread += 1;
+      }
+    }
+  });
+
+  assert.equal(randomNew, 1, "random new");
+  assert.equal(randomUnread, 0, "random unread");
+  assert.equal(sevenNew, 0, "seven unread");
+  assert.equal(sevenUnread, 2, "seven unread");
+});
+
 QUnit.test("sync", function(assert) {
   const state = TopicTrackingState.create();
   state.states["t111"] = { last_read_post_number: null };

GitHub sha: eb18f91c

2 Likes