Prompt users to reload the CC when the extension updates

When the extension updates, it stops working in the Community Console.
Thus, this change adds logic so when the extension detects it has been
recently installed or updated it injects a banner to the top of the CC
with a message which prompts the user to reload the page.

Fixed: twpowertools:82
Change-Id: I0c901c72574c7c64d9ba94f56be96a12f7770049
diff --git a/src/contentScripts/communityConsole/handleInstall.js b/src/contentScripts/communityConsole/handleInstall.js
new file mode 100644
index 0000000..14362c0
--- /dev/null
+++ b/src/contentScripts/communityConsole/handleInstall.js
@@ -0,0 +1,4 @@
+import UpdateHandler from './updateHandler/index.js';
+
+const updateHandler = new UpdateHandler();
+updateHandler.handle('install');
diff --git a/src/contentScripts/communityConsole/handleUpdate.js b/src/contentScripts/communityConsole/handleUpdate.js
new file mode 100644
index 0000000..88c187b
--- /dev/null
+++ b/src/contentScripts/communityConsole/handleUpdate.js
@@ -0,0 +1,4 @@
+import UpdateHandler from './updateHandler/index.js';
+
+const updateHandler = new UpdateHandler();
+updateHandler.handle('update');
diff --git a/src/contentScripts/communityConsole/updateHandler/banner/components/consts.js b/src/contentScripts/communityConsole/updateHandler/banner/components/consts.js
new file mode 100644
index 0000000..d279671
--- /dev/null
+++ b/src/contentScripts/communityConsole/updateHandler/banner/components/consts.js
@@ -0,0 +1 @@
+export const TWPT_UPDATE_BANNER_TAG = 'twpt-update-banner-v1';
diff --git a/src/contentScripts/communityConsole/updateHandler/banner/components/index.js b/src/contentScripts/communityConsole/updateHandler/banner/components/index.js
new file mode 100644
index 0000000..81bd20c
--- /dev/null
+++ b/src/contentScripts/communityConsole/updateHandler/banner/components/index.js
@@ -0,0 +1,122 @@
+import consoleCommonStyles from '!!raw-loader!../../../../../static/css/common/console.css';
+import {msg} from '@lit/localize';
+import {MDCBanner} from '@material/banner';
+import {css, html, unsafeCSS} from 'lit';
+import {createRef, ref} from 'lit/directives/ref.js';
+
+import {I18nLitElement} from '../../../../../common/litI18nUtils.js';
+import {SHARED_MD3_STYLES} from '../../../../../common/styles/md3.js';
+
+import {TWPT_UPDATE_BANNER_TAG} from './consts.js';
+import mdcStyles from './mdcStyles.scss?string';
+
+export default class TwptUpdateBanner extends I18nLitElement {
+  static properties = {
+    isinstall: {type: Boolean},
+  };
+
+  static styles = [
+    css`
+    :host {
+      position: sticky;
+      top: 0;
+      z-index: 97;
+    }
+
+    .mdc-banner {
+      background-color: var(--TWPT-drawer-background, #fff)!important;
+    }
+
+    .mdc-banner__graphic {
+      color: var(--mdc-theme-on-primary)!important;
+      background-color: var(--mdc-theme-primary)!important;
+    }
+
+    .mdc-banner__text {
+      color: var(--TWPT-primary-text, #000)!important;
+    }
+    `,
+    css`${unsafeCSS(consoleCommonStyles)}`,
+    SHARED_MD3_STYLES,
+    css`${unsafeCSS(mdcStyles)}`,
+  ];
+
+  bannerRef = createRef();
+
+  constructor() {
+    super();
+    this.isinstall = false;
+    this.mdcBanner = null;
+  }
+
+  firstUpdated() {
+    this.mdcBanner = new MDCBanner(this.bannerRef.value);
+    this.mdcBanner.open();
+  }
+
+  render() {
+    let descriptionMsg;
+    if (this.isinstall) {
+      descriptionMsg = msg(
+          'The TW Power Tools extension has been installed. Please reload this page so that it is activated.',
+          {
+            desc:
+                'Message shown as a banner when the extension has been installed, to let the user know that they should reload the page.'
+          });
+    } else {
+      descriptionMsg = msg(
+          'The TW Power Tools extension has been updated. Please reload this page so that it continues to work properly.',
+          {
+            desc:
+                'Message shown as a banner when the extension has been updated, to let the user know that they should reload the page.'
+          });
+    }
+    const reloadMsg =
+        msg('Reload', {desc: 'Button which reloads the current page.'});
+    return html`
+      <div ${ref(this.bannerRef)}
+          class="mdc-banner mdc-banner--centered mdc-banner--mobile-stacked"
+          role="banner">
+        <div class="mdc-banner__content"
+             role="alertdialog"
+             aria-live="assertive">
+          <div class="mdc-banner__graphic-text-wrapper">
+            <div
+                class="mdc-banner__graphic"
+                role="img"
+                alt="Update">
+              <md-icon class="mdc-banner__icon">update</md-icon>
+            </div>
+            <div class="mdc-banner__text">
+              ${descriptionMsg}
+            </div>
+          </div>
+          <div class="mdc-banner__actions">
+            <md-text-button
+                class="mdc-banner__primary-action"
+                label="${reloadMsg}"
+                @click=${this._reloadPage}>
+            </md-text-button>
+          </div>
+        </div>
+      </div>
+    `;
+  }
+
+  _reloadPage() {
+    location.reload();
+  }
+}
+// This element is injected each time an extension is installed/updated, so it
+// might already be defined. If it isn't, register it. If there are any breaking
+// changes, change TWPT_UPDATE_BANNER_TAG to a higher version.
+if (window.customElements.get(TWPT_UPDATE_BANNER_TAG) === undefined) {
+import(
+      /* webpackMode: "eager" */
+      '@material/web/icon/icon.js');
+import(
+      /* webpackMode: "eager" */
+      '@material/web/button/text-button.js');
+
+  window.customElements.define(TWPT_UPDATE_BANNER_TAG, TwptUpdateBanner);
+}
diff --git a/src/contentScripts/communityConsole/updateHandler/banner/components/mdcStyles.scss b/src/contentScripts/communityConsole/updateHandler/banner/components/mdcStyles.scss
new file mode 100644
index 0000000..1f87a5c
--- /dev/null
+++ b/src/contentScripts/communityConsole/updateHandler/banner/components/mdcStyles.scss
@@ -0,0 +1 @@
+@use "@material/banner/styles";
diff --git a/src/contentScripts/communityConsole/updateHandler/banner/index.js b/src/contentScripts/communityConsole/updateHandler/banner/index.js
new file mode 100644
index 0000000..c2da6b4
--- /dev/null
+++ b/src/contentScripts/communityConsole/updateHandler/banner/index.js
@@ -0,0 +1,14 @@
+import {TWPT_UPDATE_BANNER_TAG} from './components/consts.js';
+
+export default class UpdateBanner {
+  addBanner(reason) {
+    const main = document.querySelector('.scrollable-content > main');
+    if (main === null) {
+      console.error(`[updateHandlerBanner] Couldn't find main element.`);
+      return;
+    }
+    const banner = document.createElement(TWPT_UPDATE_BANNER_TAG);
+    if (reason === 'install') banner.setAttribute('isinstall', '');
+    main.prepend(banner);
+  }
+}
diff --git a/src/contentScripts/communityConsole/updateHandler/index.js b/src/contentScripts/communityConsole/updateHandler/index.js
new file mode 100644
index 0000000..8eaa815
--- /dev/null
+++ b/src/contentScripts/communityConsole/updateHandler/index.js
@@ -0,0 +1,17 @@
+import {injectScript} from '../../../common/contentScriptsUtils.js';
+import MWI18nServer from '../../../common/mainWorldI18n/Server.js';
+
+import UpdateBanner from './banner/index.js';
+
+export default class UpdateHandler {
+  constructor() {
+    new MWI18nServer();
+    injectScript(chrome.runtime.getURL('updateHandlerLitComponents.bundle.js'));
+    this.updateBanner = new UpdateBanner();
+  }
+
+  handle(reason) {
+    console.debug(`Handling extension update (reason: ${reason}).`);
+    this.updateBanner.addBanner(reason);
+  }
+}