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,
diff --git a/src/contentScripts/profile.js b/src/contentScripts/profile.js
deleted file mode 100644
index 3f7a3fa..0000000
--- a/src/contentScripts/profile.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import {getOptions} from '../common/optionsUtils.js';
-
-import {injectPreviousPostsLinksUnifiedProfile} from './utilsCommon/unifiedProfiles.js';
-
-getOptions('history').then(options => {
- if (options?.history)
- injectPreviousPostsLinksUnifiedProfile(/* isCommunityConsole = */ false);
-});
diff --git a/src/contentScripts/publicProfile.js b/src/contentScripts/publicProfile.js
new file mode 100644
index 0000000..fec0c85
--- /dev/null
+++ b/src/contentScripts/publicProfile.js
@@ -0,0 +1,43 @@
+import {getOptions} from '../common/optionsUtils.js';
+
+import PerForumStatsSection from './communityConsole/utils/PerForumStatsSection.js';
+import {correctArrayKeys} from './utilsCommon/protojs.js';
+import {injectPreviousPostsLinksUnifiedProfile} from './utilsCommon/unifiedProfiles.js';
+
+const profileViewRegex = /var view ?= ?(.+\]);/;
+
+getOptions(['history', 'extrainfo']).then(options => {
+ if (options?.history)
+ injectPreviousPostsLinksUnifiedProfile(/* isCommunityConsole = */ false);
+
+ if (options?.extrainfo) {
+ try {
+ // Find chart
+ const chart = document.querySelector(
+ 'sc-tailwind-user_profile-user-profile ' +
+ '.scTailwindUser_profileUserprofilesection ' +
+ 'sc-tailwind-shared-activity-chart');
+ if (!chart) throw new Error('Couldn\'t find existing chart.');
+
+ // Extract profile JSON information
+ const scripts = document.querySelectorAll('script');
+ let profileView = null;
+ for (let i = 0; i < scripts.length; ++i) {
+ const matches = scripts[i].textContent.match(profileViewRegex);
+ if (matches?.[1]) {
+ profileView = JSON.parse(matches[1]);
+ break;
+ }
+ }
+ const profileViewC = {'1': correctArrayKeys(profileView)};
+ console.log(profileViewC);
+ if (!profileView) throw new Error('Could not find user view data.');
+ new PerForumStatsSection(
+ chart?.parentNode, profileViewC,
+ document.documentElement?.lang ?? 'en',
+ /* isCommunityConsole = */ false);
+ } catch (err) {
+ console.error('Error while injecting extra info: ', err);
+ }
+ }
+});
diff --git a/src/contentScripts/publicProfileStart.js b/src/contentScripts/publicProfileStart.js
new file mode 100644
index 0000000..f5a3a2f
--- /dev/null
+++ b/src/contentScripts/publicProfileStart.js
@@ -0,0 +1,7 @@
+import {injectScript} from '../common/contentScriptsUtils.js';
+import {getOptions} from '../common/optionsUtils.js';
+
+getOptions('extrainfo').then(options => {
+ if (options?.extrainfo)
+ injectScript(chrome.runtime.getURL('extraInfoInject.bundle.js'));
+});
diff --git a/src/contentScripts/utilsCommon/protojs.js b/src/contentScripts/utilsCommon/protojs.js
new file mode 100644
index 0000000..026559b
--- /dev/null
+++ b/src/contentScripts/utilsCommon/protojs.js
@@ -0,0 +1,13 @@
+// Function which converts a protobuf array into an array which can be accessed
+// as if it was a protobuf object (with the same keys). If the input is not an
+// array it returns itself (since it is called recursively).
+export function correctArrayKeys(input) {
+ if (!Array.isArray(input)) return input;
+
+ let object = [];
+ for (let i = 0; i < input.length; ++i) {
+ if (input[i] === null) continue;
+ object[i + 1] = correctArrayKeys(input[i]);
+ }
+ return object;
+}