Add warning message when using old thread design

In threads which are using nested replies, a warning will be shown if
the user has enabled the old thread design to notify them that they
won't work properly.

Fixed: twpowertools:139
Change-Id: I393ac187fef22a4c5f062bf99d84c373011d514b
diff --git a/docs/features.md b/docs/features.md
index ccb3cdd..815a62c 100644
--- a/docs/features.md
+++ b/docs/features.md
@@ -225,6 +225,20 @@
 Console. The old thread page design was exclusive to the Console, while the new
 one has the same design of the public TW thread pages.
 
+#### Known issues
+The TW team is currently testing the new nested replies feature in some
+threads/forums. However, the way how threads with nested replies are represented
+internally is different than before, and this causes the old design to not load
+these type of threads correctly.
+
+This is why when the user chooses the old UI, the extension now shows a warning
+banner in these threads to make them aware of why the thread doesn't look right.
+
+A possible way to work around this is to load these affected threads via TW
+Basic instead of the Community Console. Otherwise, you might have to disable the
+old UI temporarily from the options page and reload the Community Console to
+view them via the new UI.
+
 ### Reduce whitespace
 > **Option name:** _Reduce the whitespace in the Community Console and TW._
 
diff --git a/src/common/extUtils.js b/src/common/extUtils.js
index cabf8cb..39cf196 100644
--- a/src/common/extUtils.js
+++ b/src/common/extUtils.js
@@ -13,3 +13,19 @@
   var manifest = chrome.runtime.getManifest();
   return manifest?.version;
 }
+
+// Get a URL to a document which is part of the extension documentation (using
+// |ref| as the Git ref).
+export function getDocURLWithRef(doc, ref) {
+  return 'https://gerrit.avm99963.com/plugins/gitiles/infinitegforums/+/' +
+      ref + '/docs/' + doc;
+}
+
+// Get a URL to a document which is part of the extension documentation
+// (autodetect the appropriate Git ref)
+export function getDocURL(doc) {
+  if (!isProdVersion()) return getDocURLWithRef(doc, 'HEAD');
+
+  var version = getExtVersion();
+  return getDocURLWithRef(doc, 'refs/tags/v' + version);
+}
diff --git a/src/contentScripts/communityConsole/main.js b/src/contentScripts/communityConsole/main.js
index 3f2402d..bc7988d 100644
--- a/src/contentScripts/communityConsole/main.js
+++ b/src/contentScripts/communityConsole/main.js
@@ -9,9 +9,10 @@
 import {applyDragAndDropFixIfEnabled} from './dragAndDropFix.js';
 // #!endif
 import InfiniteScroll from './infiniteScroll.js';
+import ThreadPageDesignWarning from './threadPageDesignWarning.js';
 import {unifiedProfilesFix} from './unifiedProfiles.js';
 
-var mutationObserver, options, avatars, infiniteScroll;
+var mutationObserver, options, avatars, infiniteScroll, threadPageDesignWarning;
 
 const watchedNodesSelectors = [
   // App container (used to set up the intersection observer and inject the dark
@@ -65,6 +66,9 @@
   // 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"]',
 ];
 
 function handleCandidateNode(node) {
@@ -184,6 +188,10 @@
             'sc-tailwind-shared-activity-chart')) {
       window.TWPTExtraInfo.injectPerForumStatsIfEnabled(node);
     }
+
+    if (node.matches('ec-thread > .page > .material-content > div[role="list"]')) {
+      threadPageDesignWarning.injectWarningIfApplicable(node);
+    }
   }
 }
 
@@ -219,6 +227,7 @@
   // Initialize classes needed by the mutation observer
   avatars = new AvatarsHandler();
   infiniteScroll = new InfiniteScroll();
+  threadPageDesignWarning = new ThreadPageDesignWarning();
 
   // autoRefresh and extraInfo are initialized in start.js
 
diff --git a/src/contentScripts/communityConsole/threadPageDesignWarning.js b/src/contentScripts/communityConsole/threadPageDesignWarning.js
new file mode 100644
index 0000000..ae26938
--- /dev/null
+++ b/src/contentScripts/communityConsole/threadPageDesignWarning.js
@@ -0,0 +1,101 @@
+import {MDCTooltip} from '@material/tooltip';
+import {waitFor} from 'poll-until-promise';
+
+import {parseUrl} from '../../common/commonUtils.js';
+import {injectStylesheet} from '../../common/contentScriptsUtils.js';
+import {getDocURL} from '../../common/extUtils.js';
+import {getOptions} from '../../common/optionsUtils.js';
+
+import {createExtBadge} from './utils/common.js';
+
+// Forums where Nested Replies have been enabled
+const NRS_ENABLED_FORUM_IDS = [51488989];
+
+export default class ThreadPageDesignWarning {
+  constructor() {
+    // 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'));
+      }
+    });
+  }
+
+  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(this.shouldShowWarning);
+               },
+               {
+                 interval: 500,
+                 timeout: 10 * 1000,
+               })
+        .then(shouldShowWarning => {
+          if (!shouldShowWarning) return;
+
+          let thread = parseUrl(location.href);
+          if (thread === false)
+            throw new Error('current thread cannot be parsed.');
+
+          if (NRS_ENABLED_FORUM_IDS.includes(parseInt(thread.forum)))
+            this.injectWarning(content);
+        })
+        .catch(err => {
+          console.error(
+              '[threadPageDesignWarning] An error ocurred while trying to decide whether to show warning: ',
+              err);
+        });
+  }
+}
diff --git a/src/options/optionsCommon.js b/src/options/optionsCommon.js
index f0b20bf..557f6aa 100644
--- a/src/options/optionsCommon.js
+++ b/src/options/optionsCommon.js
@@ -1,4 +1,4 @@
-import {getExtVersion, isProdVersion} from '../common/extUtils.js';
+import {getDocURL, getDocURLWithRef, getExtVersion, isProdVersion} from '../common/extUtils.js';
 import {ensureOptPermissions, grantedOptPermissions, isPermissionsObjectEmpty, missingPermissions} from '../common/optionsPermissions.js';
 import {cleanUpOptions, optionsPrototype, specialOptions} from '../common/optionsUtils.js';
 
@@ -9,22 +9,6 @@
 const exclusiveOptions = [['thread', 'threadall']];
 const kClickShouldEnableFeat = 'data-click-should-enable-feature';
 
-// Get a URL to a document which is part of the extension documentation (using
-// |ref| as the Git ref).
-function getDocURLWithRef(doc, ref) {
-  return 'https://gerrit.avm99963.com/plugins/gitiles/infinitegforums/+/' +
-      ref + '/docs/' + doc;
-}
-
-// Get a URL to a document which is part of the extension documentation
-// (autodetect the appropriate Git ref)
-function getDocURL(doc) {
-  if (!isProdVersion()) return getDocURLWithRef(doc, 'HEAD');
-
-  var version = getExtVersion();
-  return getDocURLWithRef(doc, 'refs/tags/v' + version);
-}
-
 // Get the value of the option set in the options/experiments page
 function getOptionValue(opt) {
   if (specialOptions.includes(opt)) {
diff --git a/src/static/_locales/en/messages.json b/src/static/_locales/en/messages.json
index 678066f..94fd093 100644
--- a/src/static/_locales/en/messages.json
+++ b/src/static/_locales/en/messages.json
@@ -585,6 +585,14 @@
     "message": "Trending",
     "description": "Label used in a thread to show it has been manually marked as trending."
   },
+  "inject_threadpagedesign_warning": {
+    "message": "This thread might not work properly. This is because it uses the nested replies feature, and you've enabled the old thread design in the TW Power Tools options, which doesn't support nested replies.",
+    "description": "Message showed above threads which might not work properly because of the fact that the old thread design is not compatible with nested replies."
+  },
+  "btn_learnmore": {
+    "message": "Learn more",
+    "description": "Text shown besides a warning message which the user can click to learn more about the message itself."
+  },
   "actionbadge_permissions_requested": {
     "message": "Some features need additional permissions to work. Click to fix it.",
     "description": "Tooltip for the extension icon when a feature is enabled but it needs several permissions to be granted."
diff --git a/src/static/css/ccdarktheme.css b/src/static/css/ccdarktheme.css
index be6b4d9..ddc4b3f 100644
--- a/src/static/css/ccdarktheme.css
+++ b/src/static/css/ccdarktheme.css
@@ -2289,3 +2289,7 @@
 .TWPT-log-entry.TWPT-log-entry--error {
   color: #ff8A80!important;
 }
+
+.TWPT-warning {
+  background-color: #670505!important;
+}
diff --git a/src/static/css/thread_page_design_warning.css b/src/static/css/thread_page_design_warning.css
new file mode 100644
index 0000000..71e4989
--- /dev/null
+++ b/src/static/css/thread_page_design_warning.css
@@ -0,0 +1,32 @@
+.TWPT-warning {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+
+  padding: 8px 48px;
+  margin-bottom: 16px;
+  border-radius: 8px;
+
+  background-color: #feecec;
+}
+
+.TWPT-warning--icon {
+  margin-right: 8px;
+}
+
+.TWPT-warning--text {
+  flex-grow: 1;
+}
+
+.TWPT-warning--btn {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  min-width: 100px;
+  margin-left: 8px;
+}
+
+.TWPT-warning--btn .TWPT-badge {
+  --icon-size: 14px;
+  margin-right: 4px;
+}
diff --git a/templates/manifest.gjson b/templates/manifest.gjson
index d649b71..e3eb47f 100644
--- a/templates/manifest.gjson
+++ b/templates/manifest.gjson
@@ -98,6 +98,7 @@
         "css/ui_spacing/shared.css",
         "css/ui_spacing/console.css",
         "css/ui_spacing/twbasic.css",
+        "css/thread_page_design_warning.css",
 
         "communityConsoleMain.bundle.js.map",
         "communityConsoleStart.bundle.js.map",