FEATURE: Incorporate PWA install prompt into Discourse UI (#8013)

FEATURE: Incorporate PWA install prompt into Discourse UI (#8013)

  • FEATURE: Incorporate PWA install prompt into Discourse UI

This is mainly done so Discourse forums stop nagging people to install on the very first visits to a website.

We will prevent the native install “mini-info” bar from ever appearing, capture the event that pops with it, and delay it until the user meets our criteria, which currently is trust_level 1.

If the event happens and the user meets our criteria we show a Discourse alert banner proposing the install to the user. Dismissal of the banner is recorded so the user ins’t bothered anymore on the same device.

Co-Authored-By: Gerhard Schlager mail@gerhard-schlager.at Co-Authored-By: Joffrey JAFFEUX j.jaffeux@gmail.com

diff --git a/app/assets/javascripts/discourse/components/pwa-install-banner.js.es6 b/app/assets/javascripts/discourse/components/pwa-install-banner.js.es6
new file mode 100644
index 0000000..069660a
--- /dev/null
+++ b/app/assets/javascripts/discourse/components/pwa-install-banner.js.es6
@@ -0,0 +1,62 @@
+import {
+  default as computed,
+  on
+} from "ember-addons/ember-computed-decorators";
+
+const USER_DISMISSED_PROMPT_KEY = "dismissed-pwa-install-banner";
+
+export default Ember.Component.extend({
+  deferredInstallPromptEvent: null,
+
+  _handleInstallPromptEvent(event) {
+    // Prevent Chrome 76+ from automatically showing the prompt
+    event.preventDefault();
+    // Stash the event so it can be triggered later
+    this.set("deferredInstallPromptEvent", event);
+  },
+
+  @on("didInsertElement")
+  _registerListener() {
+    this._promptEventHandler = Ember.run.bind(
+      this,
+      this._handleInstallPromptEvent
+    );
+    window.addEventListener("beforeinstallprompt", this._promptEventHandler);
+  },
+
+  @on("willDestroyElement")
+  _unregisterListener() {
+    window.removeEventListener("beforeinstallprompt", this._promptEventHandler);
+  },
+
+  @computed
+  bannerDismissed: {
+    set(value) {
+      this.keyValueStore.set({ key: USER_DISMISSED_PROMPT_KEY, value });
+      return this.keyValueStore.get(USER_DISMISSED_PROMPT_KEY);
+    },
+    get() {
+      return this.keyValueStore.get(USER_DISMISSED_PROMPT_KEY);
+    }
+  },
+
+  @computed("deferredInstallPromptEvent", "bannerDismissed")
+  showPWAInstallBanner() {
+    return (
+      this.get("currentUser.trust_level") > 0 &&
+      this.deferredInstallPromptEvent && // Pass the browser engagement checks
+      !window.matchMedia("(display-mode: standalone)").matches && // Not be in the installed PWA already
+      !this.bannerDismissed // Have not a previously dismissed install banner
+    );
+  },
+
+  actions: {
+    turnOn() {
+      this.set("bannerDismissed", true);
+      this.deferredInstallPromptEvent.prompt();
+    },
+    dismiss() {
+      this.set("bannerDismissed", true);
+    }
+  }
+});
diff --git a/app/assets/javascripts/discourse/templates/application.hbs b/app/assets/javascripts/discourse/templates/application.hbs
index f0dbf0d..437754c 100644
--- a/app/assets/javascripts/discourse/templates/application.hbs
+++ b/app/assets/javascripts/discourse/templates/application.hbs
@@ -16,6 +16,7 @@
       {{custom-html name="top"}}
     {{/if}}
     {{notification-consent-banner}}
+    {{pwa-install-banner}}
     {{global-notice}}
     {{create-topics-notice}}
     {{plugin-outlet name="top-notices" args=(hash currentPath=router._router.currentPath)}}
diff --git a/app/assets/javascripts/discourse/templates/components/pwa-install-banner.hbs b/app/assets/javascripts/discourse/templates/components/pwa-install-banner.hbs
new file mode 100644
index 0000000..d039174
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/components/pwa-install-banner.hbs
@@ -0,0 +1,12 @@
+{{#if showPWAInstallBanner}}
+  <div class="row">
+    <div class="pwa-install-banner alert alert-info">
+      <div class="close" {{action "dismiss"}}>
+        {{d-icon 'times'}}
+      </div>
+      <span>
+        {{discourse-linked-text action=(action "turnOn") translatedText=(i18n "pwa.install_banner" title=siteSettings.title)}}
+      </span>
+    </div>
+  </div>
+{{/if}}
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 9c97b8d..d444d49 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -352,6 +352,9 @@ en:
       close: "Dismiss this banner."
       edit: "Edit this banner >>"
 
+    pwa:
+      install_banner: "Do you want to <a href>install %{title} on this device?</a>"
+
     choose_topic:
       none_found: "No topics found."
       title:

GitHub sha: 0a5b332b