Add per-forum stats to TW profiles

Fixed: twpowertools:102

Change-Id: Ie947c05346a5e430f08b3f3c4fbd13c2c8d744da
diff --git a/src/contentScripts/communityConsole/extraInfo.js b/src/contentScripts/communityConsole/extraInfo.js
index 35572da..af5ac7f 100644
--- a/src/contentScripts/communityConsole/extraInfo.js
+++ b/src/contentScripts/communityConsole/extraInfo.js
@@ -939,7 +939,8 @@
         })
         .then(profile => {
           new PerForumStatsSection(
-              chart?.parentNode, profile.body, this.displayLanguage);
+              chart?.parentNode, profile.body, this.displayLanguage,
+              /* isCommunityConsole = */ true);
         })
         .catch(err => {
           console.error(
diff --git a/src/contentScripts/communityConsole/main.js b/src/contentScripts/communityConsole/main.js
index 8c67bb5..f43a25a 100644
--- a/src/contentScripts/communityConsole/main.js
+++ b/src/contentScripts/communityConsole/main.js
@@ -279,4 +279,5 @@
   injectStylesheet(chrome.runtime.getURL('css/autorefresh_list.css'));
   // Extra info
   injectStylesheet(chrome.runtime.getURL('css/extrainfo.css'));
+  injectStylesheet(chrome.runtime.getURL('css/extrainfo_perforumstats.css'));
 });
diff --git a/src/contentScripts/communityConsole/utils/PerForumStatsSection.js b/src/contentScripts/communityConsole/utils/PerForumStatsSection.js
index 153cfb1..7ca0f38 100644
--- a/src/contentScripts/communityConsole/utils/PerForumStatsSection.js
+++ b/src/contentScripts/communityConsole/utils/PerForumStatsSection.js
@@ -24,8 +24,9 @@
 };
 
 export default class PerForumStatsSection {
-  constructor(existingChartSection, profile, locale) {
+  constructor(existingChartSection, profile, locale, isCommunityConsole) {
     this.locale = locale;
+    this.isCommunityConsole = isCommunityConsole;
     this.parseAndSetData(profile);
     this.buildDOM(existingChartSection);
     if (this.data.length) this.injectChart(this.data[0]?.id);
@@ -42,7 +43,7 @@
 
     this.data = [];
     for (const id of intersectionForumIDs) {
-      const fui = forumUserInfos.find(ui => ui[1] === id)?.[2];
+      const fui = forumUserInfos.find(ui => ui?.[1] === id)?.[2];
       const numMessages = kDataKeys.reduce((prevVal, key) => {
         if (!fui?.[key[0]]) return prevVal;
         return prevVal + fui[key[0]].reduce((prevVal, userActivity) => {
@@ -51,7 +52,7 @@
       }, /* initialValue = */ 0);
       this.data.push({
         id,
-        forumTitle: forumTitles.find(t => t[1] === id)?.[2],
+        forumTitle: forumTitles.find(t => t?.[1] === id)?.[2],
         forumUserInfo: fui,
         numMessages,
       });
@@ -78,9 +79,23 @@
     let title = document.createElement('h2');
     title.classList.add('scTailwindSharedActivitycharttitle');
 
-    const [badge, badgeTooltip] = createExtBadge();
+    let badge, badgeTooltip;
+    if (this.isCommunityConsole) {
+      [badge, badgeTooltip] = createExtBadge();
+    } else {
+      badge = document.createElement('span');
+      badge.classList.add('TWPT-badge');
+
+      var badgeImg = document.createElement('img');
+      badgeImg.src =
+          'https://fonts.gstatic.com/s/i/materialicons/repeat/v6/24px.svg';
+
+      badge.appendChild(badgeImg);
+    }
+
     let titleText = document.createElement('span');
-    titleText.textContent = chrome.i18n.getMessage('inject_perforumstats_heading');
+    titleText.textContent =
+        chrome.i18n.getMessage('inject_perforumstats_heading');
 
     title.append(badge, titleText);
 
@@ -93,7 +108,7 @@
     root.append(title, selector, chartEl);
     section.append(root);
     existingChartSection.after(section);
-    new MDCTooltip(badgeTooltip);
+    if (this.isCommunityConsole) new MDCTooltip(badgeTooltip);
   }
 
   getAplosData(forumId) {
@@ -101,10 +116,15 @@
     for (const [key, name, color] of kDataKeys) {
       let rawData = this.data.find(f => f.id === forumId)?.forumUserInfo?.[key];
       let data;
-      if (!rawData)
+      if (!rawData) {
         data = [];
-      else
-        data = rawData.map(m => JSON.stringify(Object.values(m)));
+      } else {
+        // We're filtering empty strings since in the public forum there a lose
+        // conversion takes place and the first element of the array is always
+        // null, which breaks the Aplos graph rendering.
+        data =
+            rawData.map(m => JSON.stringify(Object.values(m))).filter(m => !!m);
+      }
       aplosData.push({
         color,
         data,