Revert "feat!: remove features related to the old thread page view"

This reverts commit 879d44aef99524c84719d91c628e772fba9335cb.

Reason for revert: It turns out the old reply editor can still be used
in the new thread page view by pressing the "R" key, and this can/will
make the CC communicate with the draft endpoints to save/retrieve
information.

Apart from this, some PEs were using the "old thread page" option as
well, even if it doesn't load all replies.

Finally, this revert also contains some changes to the modifications to
the data startup object so they work correctly (since now we have to use
StartupDataStorage, otherwise changes might not be saved).

Bug: twpowertools:124
Change-Id: Ie8153b9c8ef150b2417cb19b73bde0a5593876e2
diff --git a/src/contentScripts/communityConsole/main.js b/src/contentScripts/communityConsole/main.js
index 890f3b0..f5d5a47 100644
--- a/src/contentScripts/communityConsole/main.js
+++ b/src/contentScripts/communityConsole/main.js
@@ -43,6 +43,9 @@
   // Thread list (used for the autorefresh feature)
   'ec-thread-list',
 
+  // Thread page main content
+  'ec-thread > .page > .material-content > div[role="list"]',
+
   // Thread page reply section (for the thread page toolbar)
   kRepliesSectionSelector,
 
@@ -90,6 +93,12 @@
       avatars.injectIfEnabled(node);
     }
 
+    // Inject old thread page design warning if applicable
+    if (node.matches(
+            'ec-thread > .page > .material-content > div[role="list"]')) {
+      window.TWPTThreadPageDesignWarning.injectWarningIfApplicable(node);
+    }
+
     // Inject thread toolbar
     if (threadToolbar.shouldInject(node)) {
       threadToolbar.injectIfApplicable(node);
diff --git a/src/contentScripts/communityConsole/start.js b/src/contentScripts/communityConsole/start.js
index f890bd0..64bc2a0 100644
--- a/src/contentScripts/communityConsole/start.js
+++ b/src/contentScripts/communityConsole/start.js
@@ -2,8 +2,13 @@
 import {getOptions} from '../../common/options/optionsUtils.js';
 
 import FlattenThreadsReplyActionHandler from './flattenThreads/replyActionHandler.js';
+import ThreadPageDesignWarning from './threadPageDesignWarning.js';
 
 getOptions(null).then(options => {
+  // Initialized here instead of in main.js so the first event is received if it
+  // happens when the page loads.
+  window.TWPTThreadPageDesignWarning = new ThreadPageDesignWarning();
+
   if (options.uispacing) {
     injectStylesheet(chrome.runtime.getURL('css/ui_spacing/shared.css'));
     injectStylesheet(chrome.runtime.getURL('css/ui_spacing/console.css'));
diff --git a/src/contentScripts/communityConsole/threadPageDesignWarning.js b/src/contentScripts/communityConsole/threadPageDesignWarning.js
new file mode 100644
index 0000000..955108a
--- /dev/null
+++ b/src/contentScripts/communityConsole/threadPageDesignWarning.js
@@ -0,0 +1,162 @@
+import {MDCTooltip} from '@material/tooltip';
+import {waitFor} from 'poll-until-promise';
+
+import {parseUrl} from '../../common/commonUtils.js';
+import {injectStylesheet} from '../../common/contentScriptsUtils';
+import {getDocURL} from '../../common/extUtils.js';
+import {getOptions} from '../../common/options/optionsUtils.js';
+
+import {createExtBadge} from './utils/common.js';
+
+const kSMEINestedReplies = 15;
+const kViewThreadResponse = 'TWPT_ViewThreadResponse';
+
+export default class ThreadPageDesignWarning {
+  constructor() {
+    this.lastThread = {
+      body: {},
+      id: -1,
+      timestamp: 0,
+    };
+    this.setUpHandler();
+
+    // We have to save whether the old UI was enabled at startup, since that's
+    // the moment when it takes effect. If the option changes while the tab is
+    // open, it won't take effect.
+    getOptions([
+      'interopthreadpage', 'interopthreadpage_mode'
+    ]).then(options => {
+      this.shouldShowWarning = options.interopthreadpage &&
+          options.interopthreadpage_mode == 'previous';
+
+      if (this.shouldShowWarning) {
+        injectStylesheet(
+            chrome.runtime.getURL('css/thread_page_design_warning.css'));
+      } else {
+        this.removeHandler();
+      }
+    });
+
+    this.isExperimentEnabled = this.isNestedRepliesExperimentEnabled();
+  }
+
+  isNestedRepliesExperimentEnabled() {
+    if (!document.documentElement.hasAttribute('data-startup')) return false;
+
+    let startup =
+        JSON.parse(document.documentElement.getAttribute('data-startup'));
+    return startup?.[1]?.[6]?.includes?.(kSMEINestedReplies);
+  }
+
+  eventHandler(e) {
+    if (e.detail.id < this.lastThread.id) return;
+
+    this.lastThread = {
+      body: e.detail.body,
+      id: e.detail.id,
+      timestamp: Date.now(),
+    };
+  }
+
+  setUpHandler() {
+    window.addEventListener(kViewThreadResponse, this.eventHandler.bind(this));
+  }
+
+  removeHandler() {
+    window.removeEventListener(
+        kViewThreadResponse, this.eventHandler.bind(this));
+  }
+
+  injectWarning(content) {
+    let div = document.createElement('div');
+    div.classList.add('TWPT-warning');
+
+    let icon = document.createElement('material-icon');
+    icon.classList.add('TWPT-warning--icon');
+
+    let iconContent = document.createElement('i');
+    iconContent.classList.add('material-icon-i', 'material-icons-extended');
+    iconContent.setAttribute('role', 'img');
+    iconContent.setAttribute('aria-hidden', 'true');
+    iconContent.textContent = 'warning';
+
+    icon.append(iconContent);
+
+    let text = document.createElement('div');
+    text.classList.add('TWPT-warning--text');
+    text.textContent =
+        chrome.i18n.getMessage('inject_threadpagedesign_warning');
+
+    let btn = document.createElement('a');
+    btn.classList.add('TWPT-warning--btn');
+    btn.href =
+        getDocURL('features.md#Thread-page-design-in-the-Community-Console');
+    btn.setAttribute('target', '_blank');
+    btn.setAttribute('rel', 'noopener noreferrer');
+
+    const [badge, badgeTooltip] = createExtBadge();
+
+    let btnText = document.createElement('div');
+    btnText.textContent = chrome.i18n.getMessage('btn_learnmore');
+
+    btn.append(badge, btnText);
+
+    div.append(icon, text, btn);
+    content.prepend(div);
+
+    new MDCTooltip(badgeTooltip);
+  }
+
+  injectWarningIfApplicable(content) {
+    return waitFor(
+               () => {
+                 if (this.shouldShowWarning === undefined)
+                   return Promise.reject(
+                       new Error('shouldShowWarning is not defined.'));
+
+                 return Promise.resolve({result: this.shouldShowWarning});
+               },
+               {
+                 interval: 500,
+                 timeout: 10 * 1000,
+               })
+        .then(preShouldShowWarning => {
+          if (!preShouldShowWarning.result) return;
+
+          // If the global SMEI experiment is enabled, all threads use nested
+          // replies, so we'll skip the per-thread check and always show the
+          // warning banner.
+          if (this.isExperimentEnabled) return Promise.resolve({result: true});
+
+          let currentThread = parseUrl(location.href);
+          if (currentThread === false)
+            throw new Error('current thread id cannot be parsed.');
+
+          return waitFor(() => {
+            let now = Date.now();
+            let lastThreadInfo = this.lastThread.body['1']?.['2']?.['1'];
+            if (now - this.lastThread.timestamp > 30 * 1000 ||
+                lastThreadInfo?.['1'] != currentThread.thread ||
+                lastThreadInfo?.['3'] != currentThread.forum)
+              throw new Error(
+                  'cannot obtain information about current thread.');
+
+            // If the messageOrGap field contains any items, the thread is using
+            // nested replies. Otherwise, it probably isn't using them.
+            return Promise.resolve(
+                {result: this.lastThread.body['1']?.['40']?.length > 0});
+          }, {
+            interval: 500,
+            timeout: 10 * 1000,
+          });
+        })
+        .then(shouldShowWarning => {
+          if (shouldShowWarning.result) this.injectWarning(content);
+        })
+        .catch(err => {
+          console.error(
+              '[threadPageDesignWarning] An error ocurred while trying to decide whether to show warning: ',
+              err);
+        });
+  }
+}