FEATURE: Add last visit indication to topic view page. (#13471)

FEATURE: Add last visit indication to topic view page. (#13471)

This PR also removes grey old unread bubble from the topic badges by dropping TopicUser#highest_seen_post_number.

diff --git a/app/assets/javascripts/discourse/app/components/scrolling-post-stream.js b/app/assets/javascripts/discourse/app/components/scrolling-post-stream.js
index 612766c..da490bb 100644
--- a/app/assets/javascripts/discourse/app/components/scrolling-post-stream.js
+++ b/app/assets/javascripts/discourse/app/components/scrolling-post-stream.js
@@ -49,7 +49,9 @@ export default MountWidget.extend({
       "selectedPostsCount",
       "searchService",
       "showReadIndicator",
-      "streamFilters"
+      "streamFilters",
+      "lastReadPostNumber",
+      "highestPostNumber"
     );
   },
 
diff --git a/app/assets/javascripts/discourse/app/components/topic-list-item.js b/app/assets/javascripts/discourse/app/components/topic-list-item.js
index b029933..9933e98 100644
--- a/app/assets/javascripts/discourse/app/components/topic-list-item.js
+++ b/app/assets/javascripts/discourse/app/components/topic-list-item.js
@@ -142,8 +142,8 @@ export default Component.extend({
       classes.push("unseen-topic");
     }
 
-    if (topic.get("displayNewPosts")) {
-      classes.push("new-posts");
+    if (topic.unread_posts) {
+      classes.push("unread-posts");
     }
 
     ["liked", "archived", "bookmarked", "pinned", "closed"].forEach((name) => {
diff --git a/app/assets/javascripts/discourse/app/components/topic-post-badges.js b/app/assets/javascripts/discourse/app/components/topic-post-badges.js
index 7860018..aced410 100644
--- a/app/assets/javascripts/discourse/app/components/topic-post-badges.js
+++ b/app/assets/javascripts/discourse/app/components/topic-post-badges.js
@@ -1,13 +1,16 @@
 import Component from "@ember/component";
 import I18n from "I18n";
+import { or } from "@ember/object/computed";
 
 export default Component.extend({
   tagName: "span",
   classNameBindings: [":topic-post-badges"],
-  rerenderTriggers: ["url", "unread", "newPosts", "unseen"],
+  rerenderTriggers: ["url", "unread", "newPosts", "unreadPosts", "unseen"],
   newDotText: null,
+
   init() {
     this._super(...arguments);
+
     this.set(
       "newDotText",
       this.currentUser && this.currentUser.trust_level > 0
@@ -15,4 +18,6 @@ export default Component.extend({
         : I18n.t("filters.new.lower_title")
     );
   },
+
+  displayUnreadPosts: or("newPosts", "unreadPosts"),
 });
diff --git a/app/assets/javascripts/discourse/app/controllers/topic.js b/app/assets/javascripts/discourse/app/controllers/topic.js
index b86f9e5..ae881eb 100644
--- a/app/assets/javascripts/discourse/app/controllers/topic.js
+++ b/app/assets/javascripts/discourse/app/controllers/topic.js
@@ -68,6 +68,8 @@ export default Controller.extend(bufferedProperty("model"), {
   filter: null,
   quoteState: null,
   currentPostId: null,
+  userLastReadPostNumber: null,
+  highestPostNumber: null,
 
   init() {
     this._super(...arguments);
diff --git a/app/assets/javascripts/discourse/app/models/topic-tracking-state.js b/app/assets/javascripts/discourse/app/models/topic-tracking-state.js
index a36bc2d..3fcbe2d 100644
--- a/app/assets/javascripts/discourse/app/models/topic-tracking-state.js
+++ b/app/assets/javascripts/discourse/app/models/topic-tracking-state.js
@@ -352,15 +352,14 @@ const TopicTrackingState = EmberObject.extend({
         isSeen !== state.is_seen
       ) {
         const postsCount = topic.get("posts_count");
-        let newPosts = postsCount - state.highest_post_number,
-          unread = postsCount - state.last_read_post_number;
+        let unread;
 
-        if (newPosts < 0) {
-          newPosts = 0;
-        }
-        if (!state.last_read_post_number) {
+        if (state.last_read_post_number) {
+          unread = postsCount - state.last_read_post_number;
+        } else {
           unread = 0;
         }
+
         if (unread < 0) {
           unread = 0;
         }
@@ -368,8 +367,7 @@ const TopicTrackingState = EmberObject.extend({
         topic.setProperties({
           highest_post_number: state.highest_post_number,
           last_read_post_number: state.last_read_post_number,
-          new_posts: newPosts,
-          unread: unread,
+          unread_posts: unread,
           is_seen: state.is_seen,
           unseen: !state.last_read_post_number && isUnseen(state),
         });
@@ -654,14 +652,13 @@ const TopicTrackingState = EmberObject.extend({
     newState.topic_id = topic.id;
     newState.notification_level = topic.notification_level;
 
-    // see ListableTopicSerializer for unread/unseen/new_posts and other
+    // see ListableTopicSerializer for unread_posts/unseen and other
     // topic property logic
     if (topic.unseen) {
       newState.last_read_post_number = null;
-    } else if (topic.unread || topic.new_posts) {
+    } else if (topic.unread_posts) {
       newState.last_read_post_number =
-        topic.highest_post_number -
-        ((topic.unread || 0) + (topic.new_posts || 0));
+        topic.highest_post_number - (topic.unread_posts || 0);
     } else {
       // remove the topic if it is no longer unread/new (it has been seen)
       // and if there are too many topics in memory
diff --git a/app/assets/javascripts/discourse/app/models/topic.js b/app/assets/javascripts/discourse/app/models/topic.js
index 0720289..a603aee 100644
--- a/app/assets/javascripts/discourse/app/models/topic.js
+++ b/app/assets/javascripts/discourse/app/models/topic.js
@@ -7,7 +7,6 @@ import I18n from "I18n";
 import PreloadStore from "discourse/lib/preload-store";
 import { Promise } from "rsvp";
 import RestModel from "discourse/models/rest";
-import Session from "discourse/models/session";
 import Site from "discourse/models/site";
 import User from "discourse/models/user";
 import { ajax } from "discourse/lib/ajax";
@@ -21,6 +20,7 @@ import { longDate } from "discourse/lib/formatter";
 import { popupAjaxError } from "discourse/lib/ajax-error";
 import { resolveShareUrl } from "discourse/helpers/share-url";
 import DiscourseURL, { userPath } from "discourse/lib/url";
+import deprecated from "discourse-common/lib/deprecated";
 
 export function loadTopicView(topic, args) {
   const data = deepMerge({}, args);
@@ -239,10 +239,16 @@ const Topic = RestModel.extend({
     return url;
   },
 
-  @discourseComputed("new_posts", "unread")
-  totalUnread(newPosts, unread) {
-    const count = (unread || 0) + (newPosts || 0);
-    return count > 0 ? count : null;
+  @discourseComputed("unread_posts", "new_posts")
+  totalUnread(unreadPosts, newPosts) {
+    deprecated("The totalUnread property of the topic model is deprecated");
+    return unreadPosts || newPosts;
+  },
+
+  @discourseComputed("unread_posts", "new_posts")
+  displayNewPosts(unreadPosts, newPosts) {
+    deprecated("The displayNewPosts property of the topic model is deprecated");
+    return unreadPosts || newPosts;
   },
 
   @discourseComputed("last_read_post_number", "url")
@@ -284,25 +290,6 @@ const Topic = RestModel.extend({
     return userPath(username);
   },
 
-  // The amount of new posts to display. It might be different than what the server
-  // tells us if we are still asynchronously flushing our "recently read" data.
-  // So take what the browser has seen into consideration.
-  @discourseComputed("new_posts", "id")
-  displayNewPosts(newPosts, id) {
-    const highestSeen = Session.currentProp("highestSeenByTopic")[id];
-    if (highestSeen) {
-      const delta = highestSeen - this.last_read_post_number;
-      if (delta > 0) {
-        let result = newPosts - delta;
-        if (result < 0) {
-          result = 0;
-        }
-        return result;
-      }
-    }
-    return newPosts;
-  },
-
   @discourseComputed("views")
   viewsHeat(v) {
     if (v >= this.siteSettings.topic_views_heat_high) {
diff --git a/app/assets/javascripts/discourse/app/raw-views/list/post-count-or-badges.js b/app/assets/javascripts/discourse/app/raw-views/list/post-count-or-badges.js
index 5a95730..9b12833 100644
--- a/app/assets/javascripts/discourse/app/raw-views/list/post-count-or-badges.js

[... diff too long, it was truncated ...]

GitHub sha: 37b8ce79c99f4e8dd1c5aee0d3dd8cfe1ef4249b

This commit appears in #13471 which was approved by SamSaffron and martin. It was merged by tgxworld.

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