Add experiment to show per-forum activity in profiles
This is an experiment for now, since there might be performance
improvements and UI changes in the future.
Bug: twpowertools:92
Change-Id: Ief2a423d41a6b7179bb935c7a2a246678a4a4d0a
diff --git a/src/contentScripts/communityConsole/utils/PerForumStatsSection.js b/src/contentScripts/communityConsole/utils/PerForumStatsSection.js
new file mode 100644
index 0000000..aaa553c
--- /dev/null
+++ b/src/contentScripts/communityConsole/utils/PerForumStatsSection.js
@@ -0,0 +1,172 @@
+// Each entry includes the following information in order:
+// - ID
+// - Name (for the label in the legend)
+// - Codename
+// - Color (for the label in the legend)
+const kDataKeys = [
+ [4, 'Recommended', 'recommended', '#34A853'],
+ [6, 'Replies (not recommended)', 'replies', '#DADCE0'],
+ [5, 'Questions', 'questions', '#77909D'],
+];
+const kRoles = {
+ 1: 'bronze',
+ 2: 'silver',
+ 3: 'gold',
+ 4: 'platinum',
+ 5: 'diamond',
+ 10: 'community_manager',
+ 20: 'community_specialist',
+ 100: 'google_employee',
+ 30: 'alumnus',
+};
+
+export default class PerForumStatsSection {
+ constructor(existingChartSection, profile, locale) {
+ if (typeof window.sc_renderProfileActivityChart !== 'function') {
+ console.error(
+ 'PerForumStatsSection: window.sc_renderProfileActivityChart is not available.');
+ return;
+ }
+ this.locale = locale;
+ this.parseAndSetData(profile);
+ this.buildDOM(existingChartSection);
+ if (this.data.length) this.injectChart(this.data[0]?.id);
+ }
+
+ parseAndSetData(profile) {
+ const forumUserInfos = profile?.[1]?.[7] ?? [];
+ const forumTitles = profile?.[1]?.[8] ?? [];
+
+ const forumUserInfoIDs = forumUserInfos.map(ui => ui[1]);
+ const forumTitleIDs = forumTitles.map(t => t[1]);
+ const intersectionForumIDs =
+ forumUserInfoIDs.filter(id => forumTitleIDs.includes(id));
+
+ this.data = [];
+ for (const id of intersectionForumIDs) {
+ 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) => {
+ return prevVal + (userActivity?.[3] ?? 0);
+ }, /* initialValue = */ 0);
+ }, /* initialValue = */ 0);
+ this.data.push({
+ id,
+ forumTitle: forumTitles.find(t => t[1] === id)?.[2],
+ forumUserInfo: fui,
+ numMessages,
+ });
+ }
+ this.data.sort((a, b) => {
+ // First sort by number of messages
+ if (b.numMessages > a.numMessages) return 1;
+ if (b.numMessages < a.numMessages) return -1;
+ // Then sort by name
+ return a.forumTitle.localeCompare(
+ b.forumTitle, 'en', {sensitivity: 'base'});
+ });
+ }
+
+ buildDOM(existingChartSection) {
+ let section = document.createElement('div');
+ section.classList.add('scTailwindUser_profileUserprofilesection');
+
+ let root = document.createElement('div');
+ root.classList.add(
+ 'scTailwindSharedActivitychartroot',
+ 'TWPT-scTailwindSharedActivitychartroot');
+
+ let title = document.createElement('h2');
+ title.classList.add('scTailwindSharedActivitycharttitle');
+ title.textContent = 'Per-forum activity';
+
+ let selector = this.createForumSelector();
+
+ let chartEl = document.createElement('div');
+ chartEl.classList.add('scTailwindSharedActivitychartchart');
+
+ root.append(title, selector, chartEl);
+ section.append(root);
+ existingChartSection.after(section);
+
+ this.chartEl = chartEl;
+ }
+
+ getAplosData(forumId) {
+ let aplosData = [];
+ for (const [key, label, name, color] of kDataKeys) {
+ let rawData = this.data.find(f => f.id === forumId)?.forumUserInfo?.[key];
+ let data;
+ if (!rawData)
+ data = [];
+ else
+ data = rawData.map(m => JSON.stringify(Object.values(m)));
+ aplosData.push({
+ color,
+ data,
+ label,
+ name,
+ });
+ }
+ return aplosData;
+ }
+
+ createForumSelector() {
+ let div = document.createElement('div');
+ div.classList.add('TWPT-select-container');
+
+ let select = document.createElement('select');
+ let noPostsGroup;
+ let noPostsGroupFlag = false;
+ for (const forumData of this.data) {
+ const hasPosted = forumData.numMessages > 0;
+
+ if (!hasPosted && !noPostsGroupFlag) {
+ noPostsGroup = document.createElement('optgroup');
+ noPostsGroup.label = 'Not posted to within the last 12 months';
+ noPostsGroupFlag = true;
+ }
+
+ let additionalLabelsArray = [];
+ if (hasPosted)
+ additionalLabelsArray.push(forumData.numMessages + ' messages');
+ let role = forumData.forumUserInfo?.[1]?.[3] ?? 0;
+ if (role) additionalLabelsArray.push(kRoles[role]);
+ let additionalLabels = '';
+ if (additionalLabelsArray.length > 0)
+ additionalLabels = ' (' + additionalLabelsArray.join(', ') + ')';
+
+ let option = document.createElement('option');
+ option.textContent = forumData.forumTitle + additionalLabels;
+ option.value = forumData.id;
+ if (hasPosted)
+ select.append(option);
+ else
+ noPostsGroup.append(option);
+ }
+ if (noPostsGroupFlag) select.append(noPostsGroup);
+ select.addEventListener('change', e => {
+ let forumId = e.target.value;
+ this.injectChart(forumId);
+ });
+
+ div.append(select);
+ return div;
+ }
+
+ injectChart(forumId) {
+ this.chartEl.replaceChildren();
+
+ let data = this.getAplosData(forumId);
+ let metadata = {
+ activities: [],
+ finalMonth: undefined,
+ locale: this.locale,
+ shouldDisableTransitions: true,
+ };
+ let chartTitle = 'User activity chart';
+ let chart = window.sc_renderProfileActivityChart(
+ this.chartEl, data, metadata, chartTitle);
+ }
+}
diff --git a/src/contentScripts/communityConsole/utils/common.js b/src/contentScripts/communityConsole/utils/common.js
index 68fb736..40bc1fe 100644
--- a/src/contentScripts/communityConsole/utils/common.js
+++ b/src/contentScripts/communityConsole/utils/common.js
@@ -72,3 +72,10 @@
node.parentNode?.querySelector('[debugid="' + debugid + '"]') === null &&
node.parentNode?.parentNode?.tagName == 'EC-BULK-ACTIONS';
}
+
+// Returns the display language set by the user.
+export function getDisplayLanguage() {
+ var startup =
+ JSON.parse(document.querySelector('html').getAttribute('data-startup'));
+ return startup?.[1]?.[1]?.[3]?.[6] ?? 'en';
+}