extraInfo: show extra info in the canned responses list
Bug: twpowertools:93
Change-Id: I8d81391cb931f2096c704b50ff4f3cc9037b8c6e
diff --git a/src/contentScripts/communityConsole/extraInfo.js b/src/contentScripts/communityConsole/extraInfo.js
index c572523..266592a 100644
--- a/src/contentScripts/communityConsole/extraInfo.js
+++ b/src/contentScripts/communityConsole/extraInfo.js
@@ -2,10 +2,12 @@
import {waitFor} from 'poll-until-promise';
import {isOptionEnabled} from '../../common/optionsUtils.js';
+import {createPlainTooltip} from '../../common/tooltip.js';
import {createExtBadge} from './utils/common.js';
const kViewUnifiedUserResponseEvent = 'TWPT_ViewUnifiedUserResponse';
+const kListCannedResponsesResponse = 'TWPT_ListCannedResponsesResponse';
const kAbuseCategories = [
['1', 'Account'],
@@ -147,6 +149,11 @@
id: -1,
timestamp: 0,
};
+ this.lastCRsList = {
+ body: {},
+ id: -1,
+ duplicateNames: new Set(),
+ };
this.setUpHandlers();
}
@@ -160,6 +167,27 @@
timestamp: Date.now(),
};
});
+ window.addEventListener(kListCannedResponsesResponse, e => {
+ if (e.detail.id < this.lastCRsList.id) return;
+
+ // Look if there are duplicate names
+ const crs = e.detail.body?.['1'] ?? [];
+ const names = crs.map(cr => cr?.['7']).slice().sort();
+ let duplicateNames = new Set();
+ for (let i = 1; i < names.length; i++)
+ if (names[i - 1] == names[i]) duplicateNames.add(names[i]);
+
+ this.lastCRsList = {
+ body: e.detail.body,
+ id: e.detail.id,
+ duplicateNames,
+ };
+ });
+ }
+
+ // Whether the feature is enabled
+ isEnabled() {
+ return isOptionEnabled('extrainfo');
}
// Add a pretty component which contains |info| to |node|.
@@ -173,8 +201,7 @@
let badgeCell = document.createElement('div');
badgeCell.classList.add('TWPT-extrainfo-badge-cell');
- let badge, badgeTooltip;
- [badge, badgeTooltip] = createExtBadge();
+ const [badge, badgeTooltip] = createExtBadge();
badgeCell.append(badge);
let infoCell = document.createElement('div');
@@ -250,8 +277,79 @@
}
injectAtProfileIfEnabled(card) {
- isOptionEnabled('extrainfo').then(isEnabled => {
+ this.isEnabled().then(isEnabled => {
if (isEnabled) return this.injectAtProfile(card);
});
}
+
+ // Canned responses (CRs) functionality
+
+ getCRName(tags, isExpanded) {
+ if (!isExpanded)
+ return tags.parentNode?.querySelector?.('.text .name')?.textContent;
+
+ // https://www.youtube.com/watch?v=Z6_ZNW1DACE
+ return tags.parentNode?.parentNode?.parentNode?.parentNode?.parentNode
+ ?.parentNode?.parentNode?.querySelector?.('.text .name')
+ ?.textContent;
+ }
+
+ // Inject usage stats in the |tags| component of a CR
+ injectAtCR(tags, isExpanded) {
+ waitFor(() => {
+ if (this.lastCRsList.id != -1) return Promise.resolve(this.lastCRsList);
+ return Promise.reject('Didn\'t receive canned responses list');
+ }, {
+ interval: 500,
+ timeout: 15 * 1000,
+ }).then(crs => {
+ let name = this.getCRName(tags, isExpanded);
+
+ // If another CR has the same name, there's no easy way to distinguish
+ // them, so don't show the usage stats.
+ if (crs.duplicateNames.has(name)) return;
+
+ for (const cr of (crs.body?.['1'] ?? [])) {
+ if (cr['7'] == name) {
+ let tag = document.createElement('material-chip');
+ tag.classList.add('TWPT-tag');
+
+ let container = document.createElement('div');
+ container.classList.add('TWPT-chip-content-container');
+
+ let content = document.createElement('div');
+ content.classList.add('TWPT-content');
+
+ const [badge, badgeTooltip] = createExtBadge();
+
+ let label = document.createElement('span');
+ label.textContent = 'Used ' + (cr['8'] ?? '0') + ' times';
+
+ content.append(badge, label);
+ container.append(content);
+ tag.append(container);
+ tags.append(tag);
+
+ new MDCTooltip(badgeTooltip);
+
+ if (cr['9']) {
+ const lastUsedTime = Math.floor(parseInt(cr['9']) / 1e3);
+ let date = (new Date(lastUsedTime)).toLocaleString();
+ createPlainTooltip(label, 'Last used: ' + date);
+ }
+
+ break;
+ }
+ }
+ });
+ }
+
+ injectAtCRIfEnabled(tags, isExpanded) {
+ // If the tag has already been injected, exit.
+ if (tags.querySelector('.TWPT-tag')) return;
+
+ this.isEnabled().then(isEnabled => {
+ if (isEnabled) return this.injectAtCR(tags, isExpanded);
+ });
+ }
}
diff --git a/src/contentScripts/communityConsole/main.js b/src/contentScripts/communityConsole/main.js
index a534f38..73ed360 100644
--- a/src/contentScripts/communityConsole/main.js
+++ b/src/contentScripts/communityConsole/main.js
@@ -44,6 +44,10 @@
// Unified profile iframe
'iframe',
+
+ // Canned response tags or toolbelt (for the extra info feature)
+ '.tags',
+ '.toolbelt',
];
function handleCandidateNode(node) {
@@ -149,6 +153,15 @@
unifiedProfilesFix.checkIframe(node)) {
unifiedProfilesFix.fixIframe(node);
}
+
+ // Show additional details in the canned responses view.
+ if (node.matches('ec-canned-response-row .tags')) {
+ window.TWPTExtraInfo.injectAtCRIfEnabled(node, /* isExpanded = */ false);
+ }
+ if (node.matches('ec-canned-response-row .main .toolbelt')) {
+ const tags = node.parentNode?.querySelector?.('.tags');
+ if (tags) window.TWPTExtraInfo.injectAtCRIfEnabled(tags, /* isExpanded = */ true);
+ }
}
}