feat(extra-info): inject extra info in nested replies

Bug: twpowertools:93
Change-Id: Id60030804146981ca3f59a6b2c1d419bbf1f731c
diff --git a/src/contentScripts/communityConsole/extraInfo/injections/baseThreadMessage.js b/src/contentScripts/communityConsole/extraInfo/injections/baseThreadMessage.js
new file mode 100644
index 0000000..d086f9d
--- /dev/null
+++ b/src/contentScripts/communityConsole/extraInfo/injections/baseThreadMessage.js
@@ -0,0 +1,55 @@
+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, messageNode);
+    const [chips, tooltips] = MessageExtraInfoService.getMessageChips(message);
+    this.#injectChips(chips, messageNode);
+    for (const tooltip of tooltips) new MDCTooltip(tooltip);
+  }
+
+  #getMessage(threadInfo, messageNode) {
+    const thread = new ThreadModel(threadInfo.body?.[1]);
+    const messageId = MessageExtraInfoService.getMessageIdFromNode(messageNode);
+    return MessageExtraInfoService.getMessageFromThreadModel(messageId, thread);
+  }
+
+  #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());
+  }
+}