FEATURE: Scroll-based logo on mobile (#6632)

FEATURE: Scroll-based logo on mobile (#6632)

From ee6c0170ce1ce63235b2c21f036b6fdaaed135e9 Mon Sep 17 00:00:00 2001
From: Joe <33972521+hnb-ku@users.noreply.github.com>
Date: Thu, 22 Nov 2018 10:21:49 +0800
Subject: [PATCH] FEATURE: Scroll-based logo on mobile  (#6632)


diff --git a/app/assets/javascripts/discourse/components/discourse-topic.js.es6 b/app/assets/javascripts/discourse/components/discourse-topic.js.es6
index 96c11ab..15716de 100644
--- a/app/assets/javascripts/discourse/components/discourse-topic.js.es6
+++ b/app/assets/javascripts/discourse/components/discourse-topic.js.es6
@@ -12,6 +12,9 @@ function highlight(postNumber) {
   $contents.on("animationend", () => $contents.removeClass("highlighted"));
 }
 
+// used to determine scroll direction on mobile
+let lastScroll, scrollDirection, delta;
+
 export default Ember.Component.extend(AddArchetypeClass, Scrolling, {
   userFilters: Ember.computed.alias("topic.userFilters"),
   classNameBindings: [
@@ -94,6 +97,23 @@ export default Ember.Component.extend(AddArchetypeClass, Scrolling, {
         this.appEvents.trigger("header:hide-topic");
       }
     });
+    // setup mobile scroll logo
+    if (this.site.mobileView) {
+      this.appEvents.on("topic:scrolled", offset =>
+        this.mobileScrollGaurd(offset)
+      );
+      // used to animate header contents on scroll
+      this.appEvents.on("header:show-topic", () => {
+        $("header.d-header")
+          .removeClass("scroll-up")
+          .addClass("scroll-down");
+      });
+      this.appEvents.on("header:hide-topic", () => {
+        $("header.d-header")
+          .removeClass("scroll-down")
+          .addClass("scroll-up");
+      });
+    }
   },
 
   willDestroyElement() {
@@ -109,6 +129,11 @@ export default Ember.Component.extend(AddArchetypeClass, Scrolling, {
     // this happens after route exit, stuff could have trickled in
     this.appEvents.trigger("header:hide-topic");
     this.appEvents.off("post:highlight");
+    // mobile scroll logo clean up.
+    if (this.site.mobileView) {
+      this.appEvents.off("topic:scrolled");
+      $("header.d-header").removeClass("scroll-down scroll-up");
+    }
   },
 
   @observes("Discourse.hasFocus")
@@ -123,9 +148,18 @@ export default Ember.Component.extend(AddArchetypeClass, Scrolling, {
   },
 
   showTopicInHeader(topic, offset) {
-    return offset > this.get("dockAt");
+    // conditions for showing topic title in the header for mobile
+    if (
+      this.site.mobileView &&
+      scrollDirection !== "up" &&
+      offset > this.dockAt
+    ) {
+      return true;
+      // condition for desktops
+    } else {
+      return offset > this.dockAt;
+    }
   },
-
   // The user has scrolled the window, or it is finished rendering and ready for processing.
   scrolled() {
     if (this.isDestroyed || this.isDestroying || this._state !== "inDOM") {
@@ -161,5 +195,23 @@ export default Ember.Component.extend(AddArchetypeClass, Scrolling, {
 
     // Trigger a scrolled event
     this.appEvents.trigger("topic:scrolled", offset);
+  },
+
+  // determines scroll direction, triggers header topic info on mobile
+  // and ensures that the switch happens only once per scroll direction change
+  mobileScrollGaurd(offset) {
+    // user hasn't scrolled past topic title.
+    if (offset < this.dockAt) return;
+
+    delta = offset - lastScroll;
+    // 3px buffer so that the switch doesn't happen with tiny scrolls
+    if (delta > 3 && scrollDirection !== "down") {
+      scrollDirection = "down";
+      this.appEvents.trigger("header:show-topic", this.topic);
+    } else if (delta < -3 && scrollDirection !== "up") {
+      scrollDirection = "up";
+      this.appEvents.trigger("header:hide-topic");
+    }
+    lastScroll = offset;
   }
 });
diff --git a/app/assets/javascripts/discourse/components/topic-progress.js.es6 b/app/assets/javascripts/discourse/components/topic-progress.js.es6
index eca9ebb..1a107c9 100644
--- a/app/assets/javascripts/discourse/components/topic-progress.js.es6
+++ b/app/assets/javascripts/discourse/components/topic-progress.js.es6
@@ -1,3 +1,4 @@
+import { getOwner } from "discourse-common/lib/get-owner";
 import {
   default as computed,
   observes
@@ -154,15 +155,14 @@ export default Ember.Component.extend({
     const $wrapper = this.$();
     if (!$wrapper || $wrapper.length === 0) return;
 
-    const offset = window.pageYOffset || $("html").scrollTop();
-    const progressHeight = this.site.mobileView
-      ? 0
-      : $("#topic-progress").height();
-    const maximumOffset = $("#topic-bottom").offset().top + progressHeight;
-    const windowHeight = $(window).height();
-    const composerHeight = $("#reply-control").height() || 0;
-    const isDocked = offset >= maximumOffset - windowHeight + composerHeight;
-    const bottom = $("body").height() - maximumOffset;
+    const offset = window.pageYOffset || $("html").scrollTop(),
+      progressHeight = this.site.mobileView ? 0 : $("#topic-progress").height(),
+      maximumOffset = $("#topic-bottom").offset().top + progressHeight,
+      windowHeight = $(window).height(),
+      bodyHeight = $("body").height(),
+      composerHeight = $("#reply-control").height() || 0,
+      isDocked = offset >= maximumOffset - windowHeight + composerHeight,
+      bottom = bodyHeight - maximumOffset;
 
     if (composerHeight > 0) {
       $wrapper.css("bottom", isDocked ? bottom : composerHeight);
@@ -178,6 +178,23 @@ export default Ember.Component.extend({
     } else {
       $wrapper.css("right", "1em");
     }
+
+    // switch mobile scroll logo at the very bottom of topics
+    const isIOS = this.capabilities.isIOS,
+      switchHeight = bodyHeight - offset - windowHeight,
+      appEvents = getOwner(this).lookup("app-events:main");
+
+    if (isIOS && switchHeight < -10) {
+      // match elastic-scroll behaviour in iOS
+      setTimeout(function() {
+        appEvents.trigger("header:hide-topic");
+      }, 300);
+    } else if (!isIOS && switchHeight < 5) {
+      // normal switch for everyone else
+      setTimeout(function() {
+        appEvents.trigger("header:hide-topic");
+      }, 300);
+    }
   },
 
   click(e) {
diff --git a/app/assets/javascripts/discourse/widgets/header-contents.js.es6 b/app/assets/javascripts/discourse/widgets/header-contents.js.es6
index b339b08..82bae85 100644
--- a/app/assets/javascripts/discourse/widgets/header-contents.js.es6
+++ b/app/assets/javascripts/discourse/widgets/header-contents.js.es6
@@ -5,9 +5,9 @@ createWidget("header-contents", {
   tagName: "div.contents.clearfix",
   template: hbs`
     {{attach widget="home-logo" attrs=attrs}}
-    <div class="panel clearfix">{{yield}}</div>
     {{#if attrs.topic}}
       {{attach widget="header-topic-info" attrs=attrs}}
     {{/if}}
+    <div class="panel clearfix">{{yield}}</div>
   `
 });
diff --git a/app/assets/javascripts/discourse/widgets/header-topic-info.js.es6 b/app/assets/javascripts/discourse/widgets/header-topic-info.js.es6
index e02f388..6f79883 100644
--- a/app/assets/javascripts/discourse/widgets/header-topic-info.js.es6
+++ b/app/assets/javascripts/discourse/widgets/header-topic-info.js.es6
@@ -20,7 +20,7 @@ export default createWidget("header-topic-info", {
       if (href) {
         heading.push(
           h(
-            "a",
+            "a.private-message-glyph-wrapper",
             { attributes: { href } },
             h("span.private-message-glyph", iconNode("envelope"))
           )
@@ -47,6 +47,7 @@ export default createWidget("header-topic-info", {
 
     const title = [h("h1", heading)];
     const category = topic.get("category");
+
     if (loaded || category) {
       if (
         category &&
@@ -54,12 +55,15 @@ export default createWidget("header-topic-info", {
           !this.siteSettings.suppress_uncategorized_badge)
       ) {
         const parentCategory = category.get("parentCategory");
+        const categories = [];
         if (parentCategory) {
-          title.push(
+          categories.push(
             this.at

GitHub

2 Likes

Super happy this is merged in :confetti_ball: :confetti_ball: :confetti_ball:

2 Likes

This commit has been mentioned on Discourse Meta. There might be relevant details there:

2 Likes