refactor: migrate extra info feature to the new architecture
Bug: twpowertools:176
Change-Id: I379216066b973fe76f000ab9581053c1f0da569e
diff --git a/src/contentScripts/communityConsole/extraInfo/consts.js b/src/contentScripts/communityConsole/extraInfo/consts.js
deleted file mode 100644
index 236cf2e..0000000
--- a/src/contentScripts/communityConsole/extraInfo/consts.js
+++ /dev/null
@@ -1,198 +0,0 @@
-export const kViewUnifiedUserResponseEvent = 'TWPT_ViewUnifiedUserResponse';
-export const kViewThreadResponse = 'TWPT_ViewThreadResponse';
-export const kViewForumRequest = 'TWPT_ViewForumRequest';
-export const kViewForumResponse = 'TWPT_ViewForumResponse';
-
-// Used to match each category with the corresponding string.
-export const kAbuseCategories = [
- ['1', 'account'],
- ['2', 'displayname'],
- ['3', 'avatar'],
-];
-export const kAbuseViolationCategories = {
- 0: 'NO_VIOLATION',
- 1: 'COMMUNITY_POLICY_VIOLATION',
- 2: 'LEGAL_VIOLATION',
- 3: 'CSAI_VIOLATION',
- 4: 'OTHER_VIOLATION',
-};
-export const kAbuseViolationCategoriesI18n = {
- 0: 'noviolation',
- 1: 'communitypolicy',
- 2: 'legal',
- 3: 'csai',
- 4: 'other',
-};
-
-// The following array will appear in the interface as is (without being
-// translated).
-export const kAbuseViolationTypes = {
- 0: 'UNSPECIFIED',
- 23: 'ACCOUNT_DISABLED',
- 55: 'ACCOUNT_HAS_SERVICES_DISABLED',
- 35: 'ACCOUNT_HIJACKED',
- 96: 'ACCOUNT_LEAKED_CREDENTIALS',
- 92: 'ACCOUNT_NOT_SUPPORTED',
- 81: 'ARTISTIC_NUDITY',
- 66: 'BAD_BEHAVIOR_PATTERN',
- 78: 'BAD_ENGAGEMENT_BEHAVIOR_PATTERN',
- 79: 'BORDERLINE_HARASSMENT',
- 80: 'BORDERLINE_HATE_SPEECH',
- 38: 'BOTNET',
- 32: 'BRANDING_VIOLATION',
- 100: 'CAPITALIZING_TRAGIC_EVENTS',
- 105: 'CLOAKING',
- 49: 'COIN_MINING',
- 7: 'COMMERCIAL_CONTENT',
- 97: 'COPPA_REGULATED',
- 57: 'COPYRIGHT_CIRCUMVENTION',
- 8: 'COPYRIGHTED_CONTENT',
- 58: 'COURT_ORDER',
- 51: 'CSAI',
- 94: 'CSAI_INSPECT',
- 52: 'CSAI_CARTOON_HUMOR',
- 53: 'CSAI_SOLICITATION',
- 108: 'CSAI_NON_APPARENT',
- 67: 'DANGEROUS',
- 37: 'DATA_SCRAPING',
- 86: 'DECEPTIVE_OAUTH_IMPLEMENTATION',
- 46: 'DEFAMATORY_CONTENT',
- 36: 'DELINQUENT_BILLING',
- 30: 'DISRUPTION_ATTEMPT',
- 112: 'DOMESTIC_INTERFERENCE',
- 22: 'DOS',
- 9: 'DUPLICATE_CONTENT',
- 68: 'DUPLICATE_LOCAL_PAGE',
- 121: 'NON_QUALIFYING_ORGANIZATION',
- 115: 'EGREGIOUS_INTERACTION_WITH_MINOR',
- 83: 'ENGAGEMENT_COLLUSION',
- 41: 'EXPLOIT_ATTACKS',
- 65: 'FAKE_USER',
- 2: 'FRAUD',
- 21: 'FREE_TRIAL_VIOLATION',
- 43: 'GIBBERISH',
- 101: 'FOREIGN_INTERFERENCE',
- 59: 'GOVERNMENT_ORDER',
- 10: 'GRAPHICAL_VIOLENCE',
- 11: 'HARASSMENT',
- 12: 'HATE_SPEECH',
- 90: 'IDENTICAL_PRODUCT_NAME',
- 60: 'ILLEGAL_DRUGS',
- 13: 'IMPERSONATION',
- 69: 'IMPERSONATION_WITH_PII',
- 116: 'INAPPROPRIATE_INTERACTION_WITH_MINOR',
- 45: 'INAPPROPRIATE_CONTENT_SPEECH',
- 106: 'INTENTIONAL_THWARTING',
- 27: 'INTRUSION_ATTEMPT',
- 87: 'INVALID_API_USAGE',
- 14: 'INVALID_CONTENT',
- 20: 'INVALID_GCE_USAGE',
- 120: 'INVALID_STORAGE_USAGE',
- 15: 'INVALID_IMAGE_QUALITY',
- 88: 'INVALID_API_PRIVACY_POLICY_DISCLOSURE',
- 54: 'INVALID_USAGE_OF_IP_PROXYING',
- 99: 'KEYWORD_STUFFING',
- 61: 'LEGAL_COUNTERFEIT',
- 62: 'LEGAL_EXPORT',
- 63: 'LEGAL_PRIVACY',
- 33: 'LEGAL_REVIEW',
- 91: 'LEGAL_PROTECTED',
- 70: 'LOW_QUALITY_CONTENT',
- 93: 'LOW_REPUTATION_PHONE_NUMBER',
- 6: 'MALICIOUS_SOFTWARE',
- 40: 'MALWARE',
- 113: 'MISLEADING',
- 114: 'MISREP_OF_ID',
- 89: 'MEMBER_OF_ABUSIVE_GCE_NETWORK',
- 84: 'NON_CONSENSUAL_EXPLICIT_IMAGERY',
- 1: 'NONE',
- 102: 'OFF_TOPIC',
- 31: 'OPEN_PROXY',
- 28: 'PAYMENT_FRAUD',
- 16: 'PEDOPHILIA',
- 71: 'PERSONAL_INFORMATION_CONTENT',
- 25: 'PHISHING',
- 34: 'POLICY_REVIEW',
- 17: 'PORNOGRAPHY',
- 29: 'QUOTA_CIRCUMVENTION',
- 72: 'QUOTA_EXCEEDED',
- 73: 'REGULATED',
- 24: 'REPEATED_POLICY_VIOLATION',
- 104: 'RESOURCE_COMPROMISED',
- 107: 'REWARD_PROGRAMS_ABUSE',
- 74: 'ROGUE_PHARMA',
- 82: 'ESCORT',
- 75: 'SPAMMY_LOCAL_VERTICAL',
- 39: 'SEND_EMAIL_SPAM',
- 117: 'SEXTORTION',
- 118: 'SEX_TRAFFICKING',
- 44: 'SEXUALLY_EXPLICIT_CONTENT',
- 3: 'SHARDING',
- 95: 'SOCIAL_ENGINEERING',
- 109: 'SUSPICIOUS',
- 19: 'TRADEMARK_CONTENT',
- 50: 'TRAFFIC_PUMPING',
- 76: 'UNSAFE_RACY',
- 103: 'UNUSUAL_ACTIVITY_ALERT',
- 64: 'UNWANTED_CONTENT',
- 26: 'UNWANTED_SOFTWARE',
- 77: 'VIOLENT_EXTREMISM',
- 119: 'UNAUTH_IMAGES_OF_MINORS',
- 85: 'UNAUTHORIZED_SERVICE_RESELLING',
- 98: 'CSAI_EXTERNAL',
- 5: 'SPAM',
- 4: 'UNSAFE',
- 47: 'CHILD_PORNOGRAPHY_INCITATION',
- 18: 'TERRORISM_SUPPORT',
- 56: 'CSAI_WORST_OF_WORST',
-};
-
-// These values will be translated
-export const kItemMetadataStateI18n = {
- 1: 'published',
- 2: 'draft',
- 3: 'automated_abuse_take_down_hide2',
- 4: 'automated_abuse_take_down_delete2',
- 13: 'automated_abuse_reinstate2',
- // TODO: Add the following line and its corresponding translation once we know
- // what the state means: `21: 'automated_abuse_manual_review',`
- 10: 'automated_off_topic_hide2',
- 14: 'automated_flagged_pending_manual_review2',
- 5: 'user_flagged_pending_manual_review',
- 6: 'owner_deleted',
- 7: 'manual_take_down_hide2',
- 17: 'manual_profile_take_down_suspend2',
- 8: 'manual_take_down_delete2',
- 18: 'reinstate_profile_takedown2',
- 9: 'reinstate_abuse_takedown2',
- 11: 'clear_off_topic2',
- 12: 'confirm_off_topic2',
- 15: 'googler_off_topic_hide2',
- 16: 'expert_flagged_pending_manual_review',
- 19: 'awaiting_classification',
- 20: 'generated_answer_adopted',
-};
-export const kItemMetadataState = {
- 0: 'UNDEFINED',
- 1: 'PUBLISHED',
- 2: 'DRAFT',
- 3: 'AUTOMATED_ABUSE_TAKE_DOWN_HIDE',
- 4: 'AUTOMATED_ABUSE_TAKE_DOWN_DELETE',
- 13: 'AUTOMATED_ABUSE_REINSTATE',
- 21: 'AUTOMATED_ABUSE_MANUAL_REVIEW',
- 10: 'AUTOMATED_OFF_TOPIC_HIDE',
- 14: 'AUTOMATED_FLAGGED_PENDING_MANUAL_REVIEW',
- 5: 'USER_FLAGGED_PENDING_MANUAL_REVIEW',
- 6: 'OWNER_DELETED',
- 7: 'MANUAL_TAKE_DOWN_HIDE',
- 17: 'MANUAL_PROFILE_TAKE_DOWN_SUSPEND',
- 8: 'MANUAL_TAKE_DOWN_DELETE',
- 18: 'REINSTATE_PROFILE_TAKEDOWN',
- 9: 'REINSTATE_ABUSE_TAKEDOWN',
- 11: 'CLEAR_OFF_TOPIC',
- 12: 'CONFIRM_OFF_TOPIC',
- 15: 'GOOGLER_OFF_TOPIC_HIDE',
- 16: 'EXPERT_FLAGGED_PENDING_MANUAL_REVIEW',
- 19: 'AWAITING_CLASSIFICATION',
- 20: 'GENERATED_ANSWER_ADOPTED',
-};
diff --git a/src/contentScripts/communityConsole/extraInfo/index.js b/src/contentScripts/communityConsole/extraInfo/index.js
deleted file mode 100644
index c7be5cd..0000000
--- a/src/contentScripts/communityConsole/extraInfo/index.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import OptionsWatcher from '../../../common/optionsWatcher.js';
-
-import ProfileInfoHandler from './infoHandlers/profile.js';
-import ThreadInfoHandler from './infoHandlers/thread.js';
-import ThreadListInfoHandler from './infoHandlers/threadList.js';
-import ExpandedThreadListExtraInfoInjection from './injections/expandedThreadList.js';
-import ProfileAbuseExtraInfoInjection from './injections/profileAbuse.js';
-import ProfilePerForumStatsExtraInfoInjection from './injections/profilePerForumStats.js';
-import ThreadCommentExtraInfoInjection from './injections/threadComment.js';
-import ThreadListExtraInfoInjection from './injections/threadList.js';
-import ThreadQuestionExtraInfoInjection from './injections/threadQuestion.js';
-import ThreadReplyExtraInfoInjection from './injections/threadReply.js';
-
-export default class ExtraInfo {
- constructor() {
- const optionsWatcher = new OptionsWatcher(['extrainfo', 'perforumstats']);
-
- const profileInfoHandler = new ProfileInfoHandler();
- const threadInfoHandler = new ThreadInfoHandler();
- const threadListInfoHandler = new ThreadListInfoHandler();
-
- this.profileAbuse =
- new ProfileAbuseExtraInfoInjection(profileInfoHandler, optionsWatcher);
- this.profilePerForumStats = new ProfilePerForumStatsExtraInfoInjection(
- profileInfoHandler, optionsWatcher);
- this.threadQuestion =
- new ThreadQuestionExtraInfoInjection(threadInfoHandler, optionsWatcher);
- this.threadReply =
- new ThreadReplyExtraInfoInjection(threadInfoHandler, optionsWatcher);
- this.threadComment =
- new ThreadCommentExtraInfoInjection(threadInfoHandler, optionsWatcher);
- this.expandedThreadList = new ExpandedThreadListExtraInfoInjection(
- threadListInfoHandler, optionsWatcher);
- this.threadList =
- new ThreadListExtraInfoInjection(threadListInfoHandler, optionsWatcher);
- }
-
- injectAbuseChipsAtProfileIfEnabled(card) {
- this.profileAbuse.injectIfEnabled({card});
- }
-
- injectAtThreadListIfEnabled(li) {
- const injectionDetails = this.threadList.getInjectionDetails(li);
- this.threadList.injectIfEnabled(injectionDetails);
- }
-
- injectAtExpandedThreadListIfEnabled(toolbelt) {
- const injectionDetails =
- this.expandedThreadList.getInjectionDetails(toolbelt);
- this.expandedThreadList.injectIfEnabled(injectionDetails);
- }
-
- injectPerForumStatsIfEnabled(chart) {
- this.profilePerForumStats.injectIfEnabled({chart});
- }
-
- injectAtQuestionIfEnabled(stateChips) {
- this.threadQuestion.injectIfEnabled({stateChips, isMessageNode: false});
- }
-
- injectAtReplyIfEnabled(messageNode) {
- this.threadReply.injectIfEnabled({messageNode, isMessageNode: true});
- }
-
- injectAtCommentIfEnabled(messageNode) {
- this.threadComment.injectIfEnabled({messageNode, isMessageNode: true});
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/infoHandlers/base.js b/src/contentScripts/communityConsole/extraInfo/infoHandlers/base.js
deleted file mode 100644
index 732ccee..0000000
--- a/src/contentScripts/communityConsole/extraInfo/infoHandlers/base.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import {shouldImplement} from '../../../../common/commonUtils.js';
-
-export default class BaseInfoHandler {
- constructor() {
- if (this.constructor == BaseInfoHandler) {
- throw new Error('The base class cannot be instantiated.');
- }
- }
-
- /**
- * Should return a promise which resolves to the current info in a best-effort
- * manner (if it can't retrieve the current info it is allowed to fail).
- */
- async getCurrentInfo(_injectionDetails) {
- shouldImplement('getCurrentInfo');
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/infoHandlers/basedOnResponseEvent.js b/src/contentScripts/communityConsole/extraInfo/infoHandlers/basedOnResponseEvent.js
deleted file mode 100644
index 30bb36d..0000000
--- a/src/contentScripts/communityConsole/extraInfo/infoHandlers/basedOnResponseEvent.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import {waitFor} from 'poll-until-promise';
-
-import {shouldImplement} from '../../../../common/commonUtils.js';
-
-import BaseInfoHandler from './base.js';
-
-export default class ResponseEventBasedInfoHandler extends BaseInfoHandler {
- constructor() {
- super();
-
- if (this.constructor == ResponseEventBasedInfoHandler) {
- throw new Error('The base class cannot be instantiated.');
- }
-
- this.setUpDefaultInfoValue();
- this.setUpEventHandler();
- }
-
- /**
- * Should return the name of the XHR interceptor event for the API response
- * which has the information being handled.
- */
- getEvent() {
- shouldImplement('getEvent');
- }
-
- /**
- * This function should return a promise which resolves to a boolean
- * specifying whether this.info is the information related to the view that
- * the user is currently on.
- */
- async isInfoCurrent(_injectionDetails) {
- shouldImplement('isInfoCurrent');
- }
-
- /**
- * Should return the options for the waitFor function which is called when
- * checking whether the information is current or not.
- */
- getWaitForCurrentInfoOptions() {
- shouldImplement('getWaitForCurrentInfoOptions');
- }
-
- setUpDefaultInfoValue() {
- this.info = {
- body: {},
- id: -1,
- timestamp: 0,
- };
- }
-
- setUpEventHandler() {
- window.addEventListener(this.getEvent(), e => {
- if (e.detail.id < this.info.id) return;
-
- this.updateInfoWithNewValue(e);
- });
- }
-
- /**
- * Updates the info value with the information obtained from an event.
- * Can be overriden to implement more advanced logic.
- *
- * @param {Event} e
- */
- updateInfoWithNewValue(e) {
- this.info = {
- body: e.detail.body,
- id: e.detail.id,
- timestamp: Date.now(),
- };
- }
-
- async getCurrentInfo(injectionDetails) {
- const options = this.getWaitForCurrentInfoOptions();
- return waitFor(
- () => this.attemptToGetCurrentInfo(injectionDetails), options);
- }
-
- async attemptToGetCurrentInfo(injectionDetails) {
- const isInfoCurrent = await this.isInfoCurrent(injectionDetails);
- if (!isInfoCurrent) throw new Error('Didn\'t receive current information');
-
- return this.info;
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/infoHandlers/profile.js b/src/contentScripts/communityConsole/extraInfo/infoHandlers/profile.js
deleted file mode 100644
index 28e8309..0000000
--- a/src/contentScripts/communityConsole/extraInfo/infoHandlers/profile.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import {kViewUnifiedUserResponseEvent} from '../consts.js';
-
-import ResponseEventBasedInfoHandler from './basedOnResponseEvent.js';
-
-export default class ProfileInfoHandler extends ResponseEventBasedInfoHandler {
- getEvent() {
- return kViewUnifiedUserResponseEvent;
- }
-
- async isInfoCurrent() {
- return Date.now() - this.info.timestamp < 15 * 1000;
- }
-
- getWaitForCurrentInfoOptions() {
- return {
- interval: 500,
- timeout: 15 * 1000,
- };
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/infoHandlers/thread.js b/src/contentScripts/communityConsole/extraInfo/infoHandlers/thread.js
deleted file mode 100644
index 6c8fb73..0000000
--- a/src/contentScripts/communityConsole/extraInfo/infoHandlers/thread.js
+++ /dev/null
@@ -1,107 +0,0 @@
-import {waitFor} from 'poll-until-promise';
-
-import {parseUrl} from '../../../../common/commonUtils.js';
-import ThreadModel from '../../../../models/Thread.js';
-import {kViewThreadResponse} from '../consts.js';
-import MessageExtraInfoService from '../services/message.js';
-
-import ResponseEventBasedInfoHandler from './basedOnResponseEvent.js';
-
-const kIntervalInMs = 500;
-const kTimeoutInMs = 3 * 1000;
-const kCurrentInfoExpiresInMs = kTimeoutInMs * 1.5;
-
-export default class ThreadInfoHandler extends ResponseEventBasedInfoHandler {
- getEvent() {
- return kViewThreadResponse;
- }
-
- getWaitForCurrentInfoOptions() {
- return {
- interval: kIntervalInMs,
- timeout: kTimeoutInMs,
- };
- }
-
- setUpDefaultInfoValue() {
- this.info = {
- thread: new ThreadModel(),
- messages: [],
- id: -1,
- timestamp: 0,
- };
- }
-
- updateInfoWithNewValue(e) {
- const newThread = new ThreadModel(e.detail.body?.[1]);
- if (newThread.getId() != this.info.thread.getId()) {
- this.info.messages = [];
- }
-
- const newMessages = newThread.getAllMessagesList();
- this.updateRecordedMessages(newMessages);
-
- this.info.thread = newThread;
- this.info.id = e.detail.id;
- this.info.timestamp = Date.now();
- }
-
- updateRecordedMessages(newMessages) {
- const nonUpdatedMessages = this.info.messages.filter(message => {
- return !newMessages.some(newMessage => {
- return message.getId() == newMessage.getId();
- });
- });
- this.info.messages = nonUpdatedMessages.concat(newMessages);
- }
-
- async getCurrentInfo(injectionDetails) {
- return this
- .getCurrentThreads(injectionDetails, /* checkRecentTimestamp = */ true)
- .catch(() => {
- console.debug(
- `extraInfo: couldn't get updated thread info. Trying to ` +
- `get the information even if it is old.`);
- return this.getCurrentThreads(
- injectionDetails, /* checkRecentTimestamp = */ false);
- });
- }
-
- async getCurrentThreads(injectionDetails, checkRecentTimestamp) {
- injectionDetails.checkRecentTimestamp = checkRecentTimestamp;
- const options = this.getWaitForCurrentInfoOptions();
- return waitFor(
- () => this.attemptToGetCurrentInfo(injectionDetails), options);
- }
-
- async isInfoCurrent(injectionDetails) {
- const checkRecentTimestamp = injectionDetails.checkRecentTimestamp;
- const isMessageNode = injectionDetails.isMessageNode;
- const messageNode = injectionDetails.messageNode;
-
- return (!checkRecentTimestamp || this.isThreadCurrent()) &&
- (!isMessageNode || this.currentThreadContainsMessage(messageNode));
- }
-
- isThreadCurrent() {
- const currentPage = this.parseThreadUrl();
- return Date.now() - this.info.timestamp < kCurrentInfoExpiresInMs &&
- this.info.thread.getId() == currentPage.thread &&
- this.info.thread.getForumId() == currentPage.forum;
- }
-
- parseThreadUrl() {
- const currentPage = parseUrl(location.href);
- if (currentPage === false)
- throw new Error(`couldn't parse current URL: ${location.href}`);
-
- return currentPage;
- }
-
- currentThreadContainsMessage(messageNode) {
- const messageId = MessageExtraInfoService.getMessageIdFromNode(messageNode);
- const message = MessageExtraInfoService.getMessageFromList(
- messageId, this.info.messages);
- return message !== undefined;
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/infoHandlers/threadList.js b/src/contentScripts/communityConsole/extraInfo/infoHandlers/threadList.js
deleted file mode 100644
index 47d7e65..0000000
--- a/src/contentScripts/communityConsole/extraInfo/infoHandlers/threadList.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import {waitFor} from 'poll-until-promise';
-
-import {kViewForumRequest, kViewForumResponse} from '../consts.js';
-import ThreadExtraInfoService from '../services/thread.js';
-
-import BaseInfoHandler from './base.js';
-
-const kCheckIntervalInMs = 450;
-const kTimeoutInMs = 2 * 1000;
-
-export default class ThreadListInfoHandler extends BaseInfoHandler {
- constructor() {
- super();
-
- this.setUpDefaultValues();
- this.setUpEventHandlers();
- }
-
- setUpDefaultValues() {
- this.threads = [];
- this.isFirstBatch = null;
- this.requestId = -1;
- this.timestamp = 0;
- }
-
- setUpEventHandlers() {
- window.addEventListener(kViewForumRequest, e => this.onThreadRequest(e));
- window.addEventListener(kViewForumResponse, e => this.onThreadResponse(e));
- }
-
- onThreadRequest(e) {
- // Ignore ViewForum requests made by the chat feature and the "Mark as
- // duplicate" dialog.
- //
- // All those requests have |maxNum| set to 10 and 20 respectively, while
- // the requests that we want to handle are the ones to initially load the
- // thread list (which currently requests 100 threads) and the ones to load
- // more threads (which request 50 threads).
- const maxNum = e.detail.body?.['2']?.['1']?.['2'];
- if (maxNum == 10 || maxNum == 20) return;
-
- this.requestId = e.detail.id;
- this.isFirstBatch =
- !e.detail.body?.['2']?.['1']?.['3']?.['2']; // Pagination token
- }
-
- onThreadResponse(e) {
- if (e.detail.id != this.requestId) return;
-
- const threads = e.detail.body?.['1']?.['2'] ?? [];
- if (this.isFirstBatch)
- this.threads = threads;
- else
- this.threads = this.threads.concat(threads);
-
- this.timestamp = Date.now();
- }
-
- async getCurrentInfo(injectionDetails) {
- const currentThreadInfo = injectionDetails.threadInfo;
- const checkRecentTimestamp = !injectionDetails.isExpanded;
-
- return this.getCurrentThreads(currentThreadInfo, checkRecentTimestamp)
- .catch(err => {
- if (checkRecentTimestamp) {
- return this.getCurrentThreads(
- currentThreadInfo, /* checkRecentTimestamp = */ false);
- } else {
- throw err;
- }
- });
- }
-
- async getCurrentThreads(currentThreadInfo, checkRecentTimestamp) {
- const options = {
- interval: kCheckIntervalInMs,
- timeout: kTimeoutInMs,
- };
- return waitFor(
- () => this.attemptToGetCurrentThreads(
- currentThreadInfo, checkRecentTimestamp),
- options);
- }
-
- async attemptToGetCurrentThreads(currentThreadInfo, checkRecentTimestamp) {
- if (!this.isThreadListCurrent(currentThreadInfo, checkRecentTimestamp))
- throw new Error('Didn\'t receive current information');
-
- return this.threads;
- }
-
- isThreadListCurrent(currentThreadInfo, checkRecentTimestamp) {
- if (checkRecentTimestamp && Date.now() - this.timestamp > kTimeoutInMs)
- return false;
-
- const thread = ThreadExtraInfoService.getThreadFromThreadList(
- this.threads, currentThreadInfo);
- return thread !== undefined;
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/injections/base.js b/src/contentScripts/communityConsole/extraInfo/injections/base.js
deleted file mode 100644
index 78ded47..0000000
--- a/src/contentScripts/communityConsole/extraInfo/injections/base.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import {MDCTooltip} from '@material/tooltip';
-
-import {shouldImplement} from '../../../../common/commonUtils.js';
-import {createExtBadge} from '../../utils/common.js';
-
-export default class BaseExtraInfoInjection {
- constructor(infoHandler, optionsWatcher) {
- if (this.constructor == BaseExtraInfoInjection) {
- throw new Error('The base class cannot be instantiated.');
- }
-
- this.infoHandler = infoHandler;
- this.optionsWatcher = optionsWatcher;
- }
-
- /**
- * Method which actually injects the extra information. It should be
- * implemented by the extending class.
- */
- inject() {
- shouldImplement('inject');
- }
-
- /**
- * Overridable method which is called when an error ocurred while retrieving
- * the info needed to inject the extra information. This is useful to show an
- * error component in the screen.
- */
- injectOnInfoRetrievalError() {}
-
- async isEnabled() {
- return await this.optionsWatcher.isEnabled('extrainfo');
- }
-
- /**
- * This is the method which should be called when injecting extra information.
- */
- async injectIfEnabled(injectionDetails) {
- const isEnabled = await this.isEnabled();
- if (!isEnabled) return;
-
- return this.infoHandler.getCurrentInfo(injectionDetails)
- .catch(err => {
- this.injectOnInfoRetrievalError();
- throw err;
- })
- .then(info => this.inject(info, injectionDetails))
- .catch(err => {
- console.error(
- `${this.constructor.name}: error while injecting extra info: `,
- err);
- });
- }
-
- /**
- * Add chips which contain |chipContentList| to |node|. If |withContainer| is
- * set to true, a container will contain all the chips.
- */
- addExtraInfoChips(chipContentList, node, withContainer = false) {
- if (chipContentList.length == 0) return;
-
- let container;
- if (withContainer) {
- container = document.createElement('div');
- container.classList.add('TWPT-extrainfo-container');
- } else {
- container = node;
- }
-
- let tooltips = [];
-
- for (const content of chipContentList) {
- const tooltip = this.addChipToContainer(content, container);
- tooltips.push(tooltip);
- }
-
- if (withContainer) node.append(container);
-
- for (const tooltip of tooltips) new MDCTooltip(tooltip);
- }
-
- /**
- * Adds a chip to the container and returns a tooltip element to be
- * instantiated.
- */
- addChipToContainer(chipContent, container) {
- let chip = document.createElement('div');
- chip.classList.add('TWPT-extrainfo-chip');
-
- const [badge, badgeTooltip] = createExtBadge();
-
- let span = document.createElement('span');
- span.append(chipContent);
-
- chip.append(badge, span);
- container.append(chip);
-
- return badgeTooltip;
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/injections/baseThreadMessage.js b/src/contentScripts/communityConsole/extraInfo/injections/baseThreadMessage.js
deleted file mode 100644
index d0cd162..0000000
--- a/src/contentScripts/communityConsole/extraInfo/injections/baseThreadMessage.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import {MDCTooltip} from '@material/tooltip';
-
-import {shouldImplement} from '../../../../common/commonUtils.js';
-import ThreadModel from '../../../../models/Thread.js';
-import MessageExtraInfoService from '../services/message.js';
-
-import BaseExtraInfoInjection from './base.js';
-
-export default class BaseThreadMessageExtraInfoInjection extends
- BaseExtraInfoInjection {
- /**
- * The class of the interactions root element.
- */
- getInteractionsRootClass() {
- shouldImplement('getInteractionsRootClass');
- }
-
- /**
- * The class of the interactions root element which signifies that it is
- * non-empty.
- */
- getInteractionsRootNonEmptyClass() {
- shouldImplement('getInteractionsRootNonEmptyClass');
- }
-
- inject(threadInfo, injectionDetails) {
- const messageNode = injectionDetails.messageNode;
- const message = this.#getMessage(threadInfo.messages, messageNode);
- const [chips, tooltips] = MessageExtraInfoService.getMessageChips(message);
- this.#injectChips(chips, messageNode);
- for (const tooltip of tooltips) new MDCTooltip(tooltip);
- }
-
- #getMessage(messagesList, messageNode) {
- const messageId = MessageExtraInfoService.getMessageIdFromNode(messageNode);
- return MessageExtraInfoService.getMessageFromList(messageId, messagesList);
- }
-
- #injectChips(chips, messageNode) {
- const interactionsElement =
- messageNode.querySelector('.' + this.getInteractionsRootClass());
- if (interactionsElement === null)
- throw new Error(`Couldn't find interactions element.`);
-
- this.#indicateInteractionsElementIsNonEmpty(interactionsElement);
-
- this.addExtraInfoChips(
- chips, interactionsElement, /* withContainer = */ true);
- }
-
- #indicateInteractionsElementIsNonEmpty(interactionsElement) {
- interactionsElement.classList.add(this.getInteractionsRootNonEmptyClass());
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/injections/expandedThreadList.js b/src/contentScripts/communityConsole/extraInfo/injections/expandedThreadList.js
deleted file mode 100644
index f5a6874..0000000
--- a/src/contentScripts/communityConsole/extraInfo/injections/expandedThreadList.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import {MDCTooltip} from '@material/tooltip';
-
-import {parseUrl} from '../../../../common/commonUtils.js';
-import ThreadExtraInfoService from '../services/thread.js';
-
-import BaseExtraInfoInjection from './base.js';
-
-export default class ExpandedThreadListExtraInfoInjection extends
- BaseExtraInfoInjection {
- getInjectionDetails(toolbelt) {
- const headerContent =
- toolbelt?.parentNode?.parentNode?.parentNode?.querySelector?.(
- '.main-header .header a.header-content');
- if (headerContent === null) {
- throw new Error(
- `extraInfo: Header is not present in the thread item's DOM.`);
- }
-
- const threadInfo = parseUrl(headerContent.href);
- if (threadInfo === false)
- throw new Error(`extraInfo: Thread's link cannot be parsed.`);
-
- return {
- toolbelt,
- threadInfo,
- isExpanded: true,
- };
- }
-
- inject(threads, injectionDetails) {
- const thread = ThreadExtraInfoService.getThreadFromThreadList(
- threads, injectionDetails.threadInfo);
- const [chipContentList, tooltips] =
- ThreadExtraInfoService.getThreadChips(thread);
- this.addExtraInfoChips(
- chipContentList, injectionDetails.toolbelt, /* withContainer = */ true);
- for (const tooltip of tooltips) new MDCTooltip(tooltip);
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/injections/profileAbuse.js b/src/contentScripts/communityConsole/extraInfo/injections/profileAbuse.js
deleted file mode 100644
index 2ba911c..0000000
--- a/src/contentScripts/communityConsole/extraInfo/injections/profileAbuse.js
+++ /dev/null
@@ -1,78 +0,0 @@
-import {kAbuseCategories, kAbuseViolationCategories, kAbuseViolationCategoriesI18n, kAbuseViolationTypes} from '../consts.js';
-
-import BaseExtraInfoInjection from './base.js';
-
-export default class ProfileAbuseExtraInfoInjection extends
- BaseExtraInfoInjection {
- constructor(infoHandler, optionsWatcher) {
- super(infoHandler, optionsWatcher);
- this.unifiedUserView = undefined;
- }
-
- inject(profileInfo, injectionDetails) {
- this.unifiedUserView = profileInfo.body?.['1'];
- const chips = this.getChips();
- this.addExtraInfoChips(
- chips, injectionDetails.card, /* withContainer = */ true);
- }
-
- getChips() {
- const chips = [
- this.getGeneralAbuseViolationCategoryChip(),
- ...this.getProfileAbuseChips(),
- this.getAppealCountChip(),
- ];
-
- return chips.filter(chip => chip !== null);
- }
-
- getGeneralAbuseViolationCategoryChip() {
- const abuseViolationCategory = this.unifiedUserView?.['6'];
- if (!abuseViolationCategory) return null;
- return this.getAbuseViolationCategoryChipContent(abuseViolationCategory);
- }
-
- getProfileAbuseChips() {
- return kAbuseCategories
- .map(category => {
- return this.getProfileAbuseCategoryChip(category);
- })
- .filter(chip => chip !== null);
- }
-
- getAppealCountChip() {
- const profileAbuse = this.unifiedUserView?.['1']?.['8'];
- const appealCount = profileAbuse?.['4'];
- if (appealCount === undefined) return null;
-
- return chrome.i18n.getMessage(
- 'inject_extrainfo_profile_appealsnum', [appealCount]);
- }
-
- getAbuseViolationCategoryChipContent(abuseViolationCategory) {
- const content = document.createElement('span');
-
- const categoryI18nKey = 'inject_extrainfo_profile_abusecategory_' +
- kAbuseViolationCategoriesI18n[abuseViolationCategory];
- const categoryLocalized =
- chrome.i18n.getMessage(categoryI18nKey) ?? abuseViolationCategory;
- content.textContent = chrome.i18n.getMessage(
- 'inject_extrainfo_profile_abusecategory', [categoryLocalized]);
-
- content.title = kAbuseViolationCategories[abuseViolationCategory] ??
- abuseViolationCategory;
-
- return content;
- }
-
- getProfileAbuseCategoryChip(abuseCategory) {
- const [protoIndex, category] = abuseCategory;
- const profileAbuse = this.unifiedUserView?.['1']?.['8'];
- const violation = profileAbuse?.[protoIndex]?.['1']?.['1'];
- if (!violation) return null;
-
- return chrome.i18n.getMessage(
- 'inject_extrainfo_profile_abuse_' + category,
- [kAbuseViolationTypes[violation]]);
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/injections/profilePerForumStats.js b/src/contentScripts/communityConsole/extraInfo/injections/profilePerForumStats.js
deleted file mode 100644
index 57278ee..0000000
--- a/src/contentScripts/communityConsole/extraInfo/injections/profilePerForumStats.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import {getDisplayLanguage} from '../../utils/common.js';
-import PerForumStatsSection from '../../utils/PerForumStatsSection.js';
-
-import BaseExtraInfoInjection from './base.js';
-
-export default class ProfilePerForumStatsExtraInfoInjection extends
- BaseExtraInfoInjection {
- constructor(infoHandler, optionsWatcher) {
- super(infoHandler, optionsWatcher);
- this.displayLanguage = getDisplayLanguage();
- }
-
- async isEnabled() {
- return await this.optionsWatcher.isEnabled('perforumstats');
- }
-
- inject(profileInfo, injectionDetails) {
- new PerForumStatsSection(
- injectionDetails.chart?.parentNode, profileInfo.body,
- this.displayLanguage, /* isCommunityConsole = */ true);
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/injections/threadComment.js b/src/contentScripts/communityConsole/extraInfo/injections/threadComment.js
deleted file mode 100644
index ed14575..0000000
--- a/src/contentScripts/communityConsole/extraInfo/injections/threadComment.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import BaseThreadMessageExtraInfoInjection from './baseThreadMessage.js';
-
-export default class ThreadCommentExtraInfoInjection extends BaseThreadMessageExtraInfoInjection {
- getInteractionsRootClass() {
- return 'scTailwindThreadMessageMessageinteractionsroot';
- }
-
- getInteractionsRootNonEmptyClass() {
- return 'scTailwindThreadMessageMessageinteractionsinteractions';
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/injections/threadList.js b/src/contentScripts/communityConsole/extraInfo/injections/threadList.js
deleted file mode 100644
index 6a78ea3..0000000
--- a/src/contentScripts/communityConsole/extraInfo/injections/threadList.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import {MDCTooltip} from '@material/tooltip';
-
-import {parseUrl} from '../../../../common/commonUtils.js';
-import {createExtBadge} from '../../utils/common.js';
-import {kItemMetadataState, kItemMetadataStateI18n} from '../consts.js';
-import ThreadExtraInfoService from '../services/thread.js';
-
-import BaseExtraInfoInjection from './base.js';
-
-export default class ThreadListExtraInfoInjection extends
- BaseExtraInfoInjection {
- getInjectionDetails(li) {
- const headerContent = li.querySelector(
- 'ec-thread-summary .main-header .header a.header-content');
- if (headerContent === null) {
- throw new Error(
- `extraInfo: Header is not present in the thread item's DOM.`);
- }
-
- const threadInfo = parseUrl(headerContent.href);
- if (threadInfo === false)
- throw new Error(`extraInfo: Thread's link cannot be parsed.`);
-
- return {
- li,
- threadInfo,
- isExpanded: false,
- };
- }
-
- inject(threads, injectionDetails) {
- const thread = ThreadExtraInfoService.getThreadFromThreadList(
- threads, injectionDetails.threadInfo);
-
- const state = thread?.['2']?.['12']?.['1'];
- if (!state || [1, 13, 18, 9].includes(state)) return;
-
- const [label, badgeTooltip] = this.createLabelElement(state);
- const authorLine = this.getAuthorLine(injectionDetails.li);
- authorLine.prepend(label);
-
- new MDCTooltip(badgeTooltip);
- }
-
- createLabelElement(state) {
- const label = document.createElement('div');
- label.classList.add('TWPT-label');
-
- const [badge, badgeTooltip] = createExtBadge();
-
- let span = document.createElement('span');
- let stateLocalized;
- if (kItemMetadataStateI18n[state]) {
- const stateI18nKey =
- 'inject_extrainfo_message_state_' + kItemMetadataStateI18n[state];
- stateLocalized = chrome.i18n.getMessage(stateI18nKey) ?? state;
- } else {
- stateLocalized = kItemMetadataState[state] ?? state;
- }
- span.textContent = stateLocalized;
- span.title = kItemMetadataState[state] ?? state;
-
- label.append(badge, span);
-
- return [label, badgeTooltip];
- }
-
- getAuthorLine(li) {
- const authorLine = li.querySelector(
- 'ec-thread-summary .header-content .top-row .author-line');
- if (!authorLine) {
- throw new Error(
- `extraInfo: Author line is not present in the thread item's DOM.`);
- }
- return authorLine;
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/injections/threadQuestion.js b/src/contentScripts/communityConsole/extraInfo/injections/threadQuestion.js
deleted file mode 100644
index d797ee1..0000000
--- a/src/contentScripts/communityConsole/extraInfo/injections/threadQuestion.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import {MDCTooltip} from '@material/tooltip';
-
-import ThreadModel from '../../../../models/Thread.js';
-import ThreadExtraInfoService from '../services/thread.js';
-
-import BaseExtraInfoInjection from './base.js';
-
-export default class ThreadQuestionExtraInfoInjection extends
- BaseExtraInfoInjection {
- inject(threadInfo, injectionDetails) {
- const [chips, tooltips] =
- ThreadExtraInfoService.getThreadChips(threadInfo.thread.data);
- this.#injectChips(chips, injectionDetails.stateChips);
- for (const tooltip of tooltips) new MDCTooltip(tooltip);
- }
-
- #injectChips(chips, stateChipsElement) {
- const stateChipsContainer = stateChipsElement.querySelector(
- '.scTailwindThreadQuestionStatechipsroot');
- const container = stateChipsContainer ?? stateChipsElement;
- const shouldCreateContainer = stateChipsContainer === null;
- this.addExtraInfoChips(chips, container, shouldCreateContainer);
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/injections/threadReply.js b/src/contentScripts/communityConsole/extraInfo/injections/threadReply.js
deleted file mode 100644
index ed14575..0000000
--- a/src/contentScripts/communityConsole/extraInfo/injections/threadReply.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import BaseThreadMessageExtraInfoInjection from './baseThreadMessage.js';
-
-export default class ThreadCommentExtraInfoInjection extends BaseThreadMessageExtraInfoInjection {
- getInteractionsRootClass() {
- return 'scTailwindThreadMessageMessageinteractionsroot';
- }
-
- getInteractionsRootNonEmptyClass() {
- return 'scTailwindThreadMessageMessageinteractionsinteractions';
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/services/message.js b/src/contentScripts/communityConsole/extraInfo/services/message.js
deleted file mode 100644
index 268fea5..0000000
--- a/src/contentScripts/communityConsole/extraInfo/services/message.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import StatesExtraInfoService from './states.js';
-
-export default class MessageExtraInfoService {
- static getMessageIdFromNode(messageNode) {
- const isMainReply =
- messageNode.tagName == 'SC-TAILWIND-THREAD-MESSAGE-MESSAGE-CARD';
- const cardContentClass = isMainReply ?
- '.scTailwindThreadMessageMessagecardcontent' :
- '.scTailwindThreadMessageCommentcardnested-reply';
- const id = messageNode.querySelector(cardContentClass)
- ?.getAttribute?.('data-stats-id');
- if (id === undefined)
- throw new Error(`Couldn't retrieve message id from node.`);
- return id;
- }
-
- static getMessageFromList(messageId, messagesList) {
- for (const message of messagesList) {
- if (message.getId() == messageId) return message;
- }
- throw new Error(`Couldn't find message ${messageId} in the message list.`);
- }
-
- static getMessageChips(messageModel) {
- const chips = [];
- const tooltips = [];
-
- const endPendingStateTimestampMicros =
- messageModel.getEndPendingStateTimestampMicros();
- const [pendingStateChip, pendingStateTooltip] =
- StatesExtraInfoService.getPendingStateChip(
- endPendingStateTimestampMicros);
- if (pendingStateChip) chips.push(pendingStateChip);
- if (pendingStateTooltip) tooltips.push(pendingStateTooltip);
-
- const itemMetadata = messageModel.data?.[1]?.[5];
- chips.push(...StatesExtraInfoService.getMetadataChips(itemMetadata));
-
- const liveReviewStatus = messageModel.data?.[1]?.[36];
- const [liveReviewChip, liveReviewTooltip] =
- StatesExtraInfoService.getLiveReviewStatusChip(liveReviewStatus);
- if (liveReviewChip) chips.push(liveReviewChip);
- if (liveReviewTooltip) tooltips.push(liveReviewTooltip);
-
- return [chips, tooltips];
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/services/states.js b/src/contentScripts/communityConsole/extraInfo/services/states.js
deleted file mode 100644
index 17dc34e..0000000
--- a/src/contentScripts/communityConsole/extraInfo/services/states.js
+++ /dev/null
@@ -1,111 +0,0 @@
-import {createPlainTooltip} from '../../../../common/tooltip.js';
-import {kItemMetadataState, kItemMetadataStateI18n} from '../consts.js';
-
-export default class StatesExtraInfoService {
- static getPendingStateChip(endPendingStateTimestampMicros) {
- const endPendingStateTimestamp =
- Math.floor(endPendingStateTimestampMicros / 1e3);
- const now = Date.now();
- if (!endPendingStateTimestampMicros || endPendingStateTimestamp < now)
- return [null, null];
-
- const span = document.createElement('span');
- span.textContent =
- chrome.i18n.getMessage('inject_extrainfo_message_pendingstate');
-
- const date = new Date(endPendingStateTimestamp).toLocaleString();
- const pendingTooltip = createPlainTooltip(
- span,
- chrome.i18n.getMessage(
- 'inject_extrainfo_message_pendingstate_tooltip', [date]),
- false);
- return [span, pendingTooltip];
- }
-
- static getLiveReviewStatusChip(liveReviewStatus) {
- const verdict = liveReviewStatus?.['1'];
- if (!verdict) return [null, null];
-
- const [label, labelClass] = this.getLiveReviewStatusLabel(verdict);
- if (!label || !labelClass) return [null, null];
-
- const reviewedBy = liveReviewStatus?.['2'];
- const timestamp = liveReviewStatus?.['3'];
- const date = (new Date(Math.floor(timestamp / 1e3))).toLocaleString();
-
- let a = document.createElement('a');
- a.href = 'https://support.google.com/s/community/user/' + reviewedBy;
- a.classList.add(labelClass);
- a.textContent = chrome.i18n.getMessage(
- 'inject_extrainfo_message_livereviewverdict',
- [chrome.i18n.getMessage(
- 'inject_extrainfo_message_livereviewverdict_' + label)]);
- let liveReviewTooltip = createPlainTooltip(a, date, false);
- return [a, liveReviewTooltip];
- }
-
- static getLiveReviewStatusLabel(verdict) {
- let label, labelClass;
- switch (verdict) {
- case 1: // LIVE_REVIEW_RELEVANT
- label = 'relevant';
- labelClass = 'TWPT-extrainfo-good';
- break;
-
- case 2: // LIVE_REVIEW_OFF_TOPIC
- label = 'offtopic';
- labelClass = 'TWPT-extrainfo-bad';
- break;
-
- case 3: // LIVE_REVIEW_ABUSE
- label = 'abuse';
- labelClass = 'TWPT-extrainfo-bad';
- break;
-
- default:
- return [null, null];
- }
- return [label, labelClass];
- }
-
- static getMetadataChips(itemMetadata) {
- return [
- this.getStateChip(itemMetadata),
- this.getShadowBlockChip(itemMetadata),
- ].filter(chip => chip !== null);
- }
-
- static getStateChip(itemMetadata) {
- const state = itemMetadata?.['1'];
- if (!state || state == 1) return null;
-
- let stateLocalized;
- if (kItemMetadataStateI18n[state]) {
- const stateI18nKey =
- 'inject_extrainfo_message_state_' + kItemMetadataStateI18n[state];
- stateLocalized = chrome.i18n.getMessage(stateI18nKey) ?? state;
- } else {
- stateLocalized = kItemMetadataState[state] ?? state;
- }
-
- const span = document.createElement('span');
- span.textContent = chrome.i18n.getMessage(
- 'inject_extrainfo_message_state', [stateLocalized]);
- span.title = kItemMetadataState[state] ?? state;
- return span;
- }
-
- static getShadowBlockChip(itemMetadata) {
- const shadowBlockInfo = itemMetadata?.['10'];
- const blockedTimestampMicros = shadowBlockInfo?.['2'];
- if (!blockedTimestampMicros) return null;
-
- const isBlocked = shadowBlockInfo?.['1'];
- let span = document.createElement('span');
- span.textContent = chrome.i18n.getMessage(
- 'inject_extrainfo_message_shadowblock' +
- (isBlocked ? 'active' : 'notactive'));
- if (isBlocked) span.classList.add('TWPT-extrainfo-bad');
- return span;
- }
-}
diff --git a/src/contentScripts/communityConsole/extraInfo/services/thread.js b/src/contentScripts/communityConsole/extraInfo/services/thread.js
deleted file mode 100644
index c36dfa5..0000000
--- a/src/contentScripts/communityConsole/extraInfo/services/thread.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import StatesExtraInfoService from './states.js';
-
-export default class ThreadExtraInfoService {
- /**
- * Get a |chipContentList| array with the chips related to the thread, and a
- * |tooltips| array with the corresponding tooltips which should be
- * initialized after the chips are added to the DOM.
- */
- static getThreadChips(thread) {
- let chips = [];
- let tooltips = [];
-
- const endPendingStateTimestampMicros = thread?.['2']?.['39'];
- const [pendingStateInfo, pendingTooltip] =
- StatesExtraInfoService.getPendingStateChip(
- endPendingStateTimestampMicros);
- if (pendingStateInfo) chips.push(pendingStateInfo);
- if (pendingTooltip) tooltips.push(pendingTooltip);
-
- chips.push(...this.getTrendingChips(thread));
-
- const itemMetadata = thread?.['2']?.['12'];
- chips.push(...StatesExtraInfoService.getMetadataChips(itemMetadata));
-
- const liveReviewStatus = thread?.['2']?.['38'];
- const [liveReviewInfo, liveReviewTooltip] =
- StatesExtraInfoService.getLiveReviewStatusChip(liveReviewStatus);
- if (liveReviewInfo) chips.push(liveReviewInfo);
- if (liveReviewTooltip) tooltips.push(liveReviewTooltip);
-
- return [chips, tooltips];
- }
-
- static getTrendingChips(thread) {
- const chips = [];
-
- const isTrending = thread?.['2']?.['25'];
- const isTrendingAutoMarked = thread?.['39'];
- if (isTrendingAutoMarked)
- chips.push(document.createTextNode(
- chrome.i18n.getMessage('inject_extrainfo_thread_autotrending')));
- else if (isTrending)
- chips.push(document.createTextNode(
- chrome.i18n.getMessage('inject_extrainfo_thread_trending')));
-
- return chips;
- }
-
- static getThreadFromThreadList(threadList, currentThreadInfo) {
- return threadList?.find?.(thread => {
- const threadInfo = thread?.['2']?.['1'];
- const threadId = threadInfo?.['1'];
- const forumId = threadInfo?.['3'];
- return threadId == currentThreadInfo.thread &&
- forumId == currentThreadInfo.forum;
- });
- }
-}
diff --git a/src/contentScripts/communityConsole/main.js b/src/contentScripts/communityConsole/main.js
index 6b5c7f7..637e061 100644
--- a/src/contentScripts/communityConsole/main.js
+++ b/src/contentScripts/communityConsole/main.js
@@ -46,12 +46,9 @@
'ec-bulk-actions material-button[debugid="mark-read-button"]',
'ec-bulk-actions material-button[debugid="mark-unread-button"]',
- // Thread list items (used to inject the avatars and extra info)
+ // Thread list items (used to inject the avatars)
'li',
- // Thread list item toolbelt (used for the extra info feature)
- 'ec-thread-summary .main .toolbelt',
-
// Thread list (used for the autorefresh feature)
'ec-thread-list',
@@ -61,19 +58,6 @@
// Canned response tags (for the "import CR" popup for the workflows feature)
'ec-canned-response-row .tags',
- // Question state chips container (for the extra info feature)
- 'sc-tailwind-thread-question-question-card sc-tailwind-thread-question-state-chips',
-
- // Replies (for the extra info feature)
- 'sc-tailwind-thread-message-message-list sc-tailwind-thread-message-message-card',
-
- // Comments (for the extra info feature)
- 'sc-tailwind-thread-message-message-list sc-tailwind-thread-message-comment-card',
-
- // User activity chart (for the per-forum stats feature)
- 'ec-unified-user .scTailwindUser_profileUserprofilesection ' +
- 'sc-tailwind-shared-activity-chart',
-
// Thread page main content
'ec-thread > .page > .material-content > div[role="list"]',
@@ -97,11 +81,6 @@
}
}
- // Show additional details in the profile view.
- if (node.matches('ec-unified-user .scTailwindUser_profileUsercardmain')) {
- window.TWPTExtraInfo.injectAbuseChipsAtProfileIfEnabled(node);
- }
-
// Show the "previous posts" links if the option is currently enabled.
// Here we're selecting the 'ec-user > div' element (unique child)
if (node.matches(
@@ -141,17 +120,9 @@
// Inject avatar links to threads in the thread list. injectIfEnabled is
// responsible of determining whether it should run or not depending on its
// current setting.
- //
- // Also, inject extra info in the thread list.
if (('tagName' in node) && (node.tagName == 'LI') &&
node.querySelector('ec-thread-summary') !== null) {
avatars.injectIfEnabled(node);
- window.TWPTExtraInfo.injectAtThreadListIfEnabled(node);
- }
-
- // Inject extra info in the toolbelt of an expanded thread list item.
- if (node.matches('ec-thread-summary .main .toolbelt')) {
- window.TWPTExtraInfo.injectAtExpandedThreadListIfEnabled(node);
}
if (node.tagName == 'IFRAME') {
@@ -170,28 +141,6 @@
window.TWPTWorkflowsImport.addButtonIfEnabled(node);
}
- // Show additional details in the thread view.
- if (node.matches(
- 'sc-tailwind-thread-question-question-card sc-tailwind-thread-question-state-chips')) {
- window.TWPTExtraInfo.injectAtQuestionIfEnabled(node);
- }
- if (node.matches(
- 'sc-tailwind-thread-message-message-list sc-tailwind-thread-message-message-card')) {
- window.TWPTExtraInfo.injectAtReplyIfEnabled(node);
- }
-
- if (node.matches(
- 'sc-tailwind-thread-message-message-list sc-tailwind-thread-message-comment-card')) {
- window.TWPTExtraInfo.injectAtCommentIfEnabled(node);
- }
-
- // Inject per-forum stats section in the user profile
- if (node.matches(
- 'ec-unified-user .scTailwindUser_profileUserprofilesection ' +
- 'sc-tailwind-shared-activity-chart')) {
- window.TWPTExtraInfo.injectPerForumStatsIfEnabled(node);
- }
-
// Inject old thread page design warning if applicable
if (node.matches(
'ec-thread > .page > .material-content > div[role="list"]')) {
@@ -259,7 +208,7 @@
flattenThreads = new FlattenThreads();
reportDialogColorThemeFix = new ReportDialogColorThemeFix(options);
- // extraInfo, threadPageDesignWarning and workflowsImport are
+ // threadPageDesignWarning and workflowsImport are
// initialized in start.js
// Before starting the mutation Observer, check whether we missed any
@@ -313,9 +262,6 @@
injectStylesheet(chrome.runtime.getURL('css/batchlock_inject.css'));
// Thread list avatars
injectStylesheet(chrome.runtime.getURL('css/thread_list_avatars.css'));
- // Extra info
- injectStylesheet(chrome.runtime.getURL('css/extrainfo.css'));
- injectStylesheet(chrome.runtime.getURL('css/extrainfo_perforumstats.css'));
// Workflows, Thread toolbar
injectScript(chrome.runtime.getURL('litComponentsInject.bundle.js'));
// Thread toolbar
diff --git a/src/contentScripts/communityConsole/start.js b/src/contentScripts/communityConsole/start.js
index 33d2039..a0640ba 100644
--- a/src/contentScripts/communityConsole/start.js
+++ b/src/contentScripts/communityConsole/start.js
@@ -1,7 +1,6 @@
-import {injectScript, injectStylesheet} from '../../common/contentScriptsUtils.js';
+import {injectStylesheet} from '../../common/contentScriptsUtils.js';
import {getOptions} from '../../common/optionsUtils.js';
-import ExtraInfo from './extraInfo/index.js';
import FlattenThreadsReplyActionHandler from './flattenThreads/replyActionHandler.js';
import ThreadPageDesignWarning from './threadPageDesignWarning.js';
import WorkflowsImport from './workflows/import.js';
@@ -9,8 +8,6 @@
const SMEI_NESTED_REPLIES = 15;
const SMEI_RCE_THREAD_INTEROP = 22;
-injectScript(chrome.runtime.getURL('extraInfoInject.bundle.js'));
-
getOptions(null).then(options => {
/* IMPORTANT NOTE: Remember to change this when changing the "ifs" below!! */
if (options.loaddrafts || options.interopthreadpage ||
@@ -47,7 +44,6 @@
// Initialized here instead of in main.js so the first event is received if it
// happens when the page loads.
- window.TWPTExtraInfo = new ExtraInfo();
window.TWPTThreadPageDesignWarning = new ThreadPageDesignWarning();
window.TWPTWorkflowsImport = new WorkflowsImport();
@@ -71,6 +67,7 @@
injectStylesheet(chrome.runtime.getURL('css/ui_spacing/console.css'));
}
- const flattenThreadsReplyActionHandler = new FlattenThreadsReplyActionHandler(options);
+ const flattenThreadsReplyActionHandler =
+ new FlattenThreadsReplyActionHandler(options);
flattenThreadsReplyActionHandler.handleIfApplicable();
});