From 37b8ce79c99f4e8dd1c5aee0d3dd8cfe1ef4249b Mon Sep 17 00:00:00 2001 From: Alan Guo Xiang Tan Date: Mon, 5 Jul 2021 14:17:31 +0800 Subject: [PATCH] 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`. --- .../app/components/scrolling-post-stream.js | 4 +- .../app/components/topic-list-item.js | 4 +- .../app/components/topic-post-badges.js | 7 ++- .../discourse/app/controllers/topic.js | 2 + .../app/models/topic-tracking-state.js | 21 +++---- .../javascripts/discourse/app/models/topic.js | 35 ++++-------- .../raw-views/list/post-count-or-badges.js | 5 +- .../discourse/app/routes/topic-from-params.js | 2 + .../templates/components/featured-topic.hbs | 2 +- .../components/latest-topic-list-item.hbs | 2 +- .../components/topic-post-badges.hbs | 7 +-- .../templates/list/post-count-or-badges.hbr | 2 +- .../app/templates/list/topic-list-item.hbr | 2 +- .../app/templates/topic-post-badges.hbr | 8 +-- .../discourse/app/templates/topic.hbs | 2 + .../discourse/app/widgets/post-stream.js | 28 +++++++--- .../app/widgets/topic-post-visited-line.js | 18 ++++++ .../discourse/tests/acceptance/tags-test.js | 3 +- .../discourse/tests/acceptance/topic-test.js | 20 +++++++ .../tests/fixtures/discovery-fixtures.js | 9 +-- .../fixtures/private-messages-fixtures.js | 3 +- .../discourse/tests/fixtures/topic.js | 39 +++++-------- .../unit/models/topic-tracking-state-test.js | 15 ++--- .../stylesheets/common/base/_topic-list.scss | 2 +- .../stylesheets/common/base/topic-post.scss | 15 +++++ .../stylesheets/common/components/badges.scss | 3 +- .../stylesheets/desktop/category-list.scss | 4 +- .../stylesheets/desktop/topic-post.scss | 14 +++++ app/assets/stylesheets/mobile/topic-list.scss | 1 - app/assets/stylesheets/mobile/topic-post.scss | 6 ++ app/assets/stylesheets/wcag.scss | 1 - app/models/post_mover.rb | 21 +------ app/models/post_timing.rb | 1 - app/models/topic.rb | 6 +- app/models/topic_user.rb | 32 +++++------ app/serializers/listable_topic_serializer.rb | 14 ++++- app/services/post_alerter.rb | 7 ++- config/locales/client.en.yml | 10 +--- ...ghest_seen_post_number_from_topic_users.rb | 19 +++++++ lib/post_creator.rb | 1 - lib/tasks/import.rake | 4 +- lib/tasks/posts.rake | 1 - lib/topics_bulk_action.rb | 2 +- lib/unread.rb | 26 ++++----- plugins/discourse-narrative-bot/plugin.rb | 2 +- .../poll-in-reply-history-test.js.es6 | 6 +- .../acceptance/poll-quote-test.js.es6 | 6 +- .../acceptance/poll-results-test.js.es6 | 12 ++-- script/import_scripts/base.rb | 4 +- script/import_scripts/telligent.rb | 3 +- spec/components/post_destroyer_spec.rb | 4 -- spec/components/topics_bulk_action_spec.rb | 2 - spec/components/unread_spec.rb | 55 +++++++------------ .../json/sam-s-simple-theme.dcstyle.json | 2 +- spec/models/post_mover_spec.rb | 33 ++--------- spec/models/post_timing_spec.rb | 12 ++-- spec/models/topic_spec.rb | 7 ++- spec/models/topic_tracking_state_spec.rb | 1 - spec/models/topic_user_spec.rb | 3 +- spec/requests/api/private_messages_spec.rb | 6 +- spec/requests/api/tags_spec.rb | 3 +- spec/requests/api/topics_spec.rb | 12 ++-- spec/requests/topics_controller_spec.rb | 5 +- spec/services/post_alerter_spec.rb | 10 +++- 64 files changed, 306 insertions(+), 312 deletions(-) create mode 100644 app/assets/javascripts/discourse/app/widgets/topic-post-visited-line.js create mode 100644 db/post_migrate/20210624023831_remove_highest_seen_post_number_from_topic_users.rb 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 612766c807..da490bb0b3 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 b02993354e..9933e98880 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 78600182a1..aced410bbd 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 b86f9e5570..ae881ebfaf 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 a36bc2d878..3fcbe2d9e1 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 0720289df8..a603aeec26 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 5a9573090b..9b1283377b 100644 --- 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 @@ -1,11 +1,10 @@ -import { and, or } from "@ember/object/computed"; +import { and } from "@ember/object/computed"; import EmberObject from "@ember/object"; import I18n from "I18n"; import discourseComputed from "discourse-common/utils/decorators"; export default EmberObject.extend({ - postCountsPresent: or("topic.unread", "topic.displayNewPosts"), - showBadges: and("postBadgesEnabled", "postCountsPresent"), + showBadges: and("postBadgesEnabled", "topic.unread_posts"), @discourseComputed newDotText() { diff --git a/app/assets/javascripts/discourse/app/routes/topic-from-params.js b/app/assets/javascripts/discourse/app/routes/topic-from-params.js index 35fd8b40dc..f97c7c882e 100644 --- a/app/assets/javascripts/discourse/app/routes/topic-from-params.js +++ b/app/assets/javascripts/discourse/app/routes/topic-from-params.js @@ -69,6 +69,8 @@ export default DiscourseRoute.extend({ "model.currentPost": closest, enteredIndex: topic.postStream.progressIndexOfPost(closestPost), enteredAt: Date.now().toString(), + userLastReadPostNumber: topic.last_read_post_number, + highestPostNumber: topic.highest_post_number, }); this.appEvents.trigger("page:topic-loaded", topic); diff --git a/app/assets/javascripts/discourse/app/templates/components/featured-topic.hbs b/app/assets/javascripts/discourse/app/templates/components/featured-topic.hbs index c6a04a2d69..39b208a305 100644 --- a/app/assets/javascripts/discourse/app/templates/components/featured-topic.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/featured-topic.hbs @@ -1,5 +1,5 @@ {{raw "topic-status" topic=topic}} {{html-safe topic.fancyTitle}} -{{topic-post-badges newPosts=topic.totalUnread unseen=topic.unseen url=topic.lastUnreadUrl}} +{{topic-post-badges unreadPosts=topic.unread_posts unseen=topic.unseen url=topic.lastUnreadUrl}} {{format-age topic.last_posted_at}} diff --git a/app/assets/javascripts/discourse/app/templates/components/latest-topic-list-item.hbs b/app/assets/javascripts/discourse/app/templates/components/latest-topic-list-item.hbs index 6d117176b1..d9b8522daf 100644 --- a/app/assets/javascripts/discourse/app/templates/components/latest-topic-list-item.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/latest-topic-list-item.hbs @@ -11,7 +11,7 @@ {{#if topic.featured_link}} {{topic-featured-link topic}} {{/if}} - {{topic-post-badges newPosts=topic.totalUnread unseen=topic.unseen url=topic.lastUnreadUrl}} + {{topic-post-badges unreadPosts=topic.unread_posts unseen=topic.unseen url=topic.lastUnreadUrl}}
{{category-link topic.category}}{{discourse-tags topic mode="list"}}{{! intentionally inline to avoid whitespace}} diff --git a/app/assets/javascripts/discourse/app/templates/components/topic-post-badges.hbs b/app/assets/javascripts/discourse/app/templates/components/topic-post-badges.hbs index 98e3ea3c3f..9a0f0ff410 100644 --- a/app/assets/javascripts/discourse/app/templates/components/topic-post-badges.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/topic-post-badges.hbs @@ -1,8 +1,5 @@ -{{#if unread }} -  {{unread}} -{{/if}} -{{#if newPosts}} -  {{newPosts}} +{{#if displayUnreadPosts}} +  {{displayUnreadPosts}} {{/if}} {{#if unseen}}  {{newDotText}} diff --git a/app/assets/javascripts/discourse/app/templates/list/post-count-or-badges.hbr b/app/assets/javascripts/discourse/app/templates/list/post-count-or-badges.hbr index f997dcbff0..4c8a662ca2 100644 --- a/app/assets/javascripts/discourse/app/templates/list/post-count-or-badges.hbr +++ b/app/assets/javascripts/discourse/app/templates/list/post-count-or-badges.hbr @@ -1,5 +1,5 @@ {{#if view.showBadges}} - {{raw "topic-post-badges" unread=topic.unread newPosts=topic.displayNewPosts unseen=topic.unseen url=topic.lastUnreadUrl newDotText=newDotText}} + {{raw "topic-post-badges" unreadPosts=topic.unread_posts unseen=topic.unseen url=topic.lastUnreadUrl newDotText=newDotText}} {{else}} {{raw "list/posts-count-column" topic=topic tagName="div"}} {{/if}} diff --git a/app/assets/javascripts/discourse/app/templates/list/topic-list-item.hbr b/app/assets/javascripts/discourse/app/templates/list/topic-list-item.hbr index e7964e1e32..246606ce35 100644 --- a/app/assets/javascripts/discourse/app/templates/list/topic-list-item.hbr +++ b/app/assets/javascripts/discourse/app/templates/list/topic-list-item.hbr @@ -29,7 +29,7 @@ topicId=topic.id unreadClass=unreadClass~}} {{~#if showTopicPostBadges}} - {{~raw "topic-post-badges" unread=topic.unread newPosts=topic.displayNewPosts unseen=topic.unseen url=topic.lastUnreadUrl newDotText=newDotText}} + {{~raw "topic-post-badges" unreadPosts=topic.unread_posts unseen=topic.unseen url=topic.lastUnreadUrl newDotText=newDotText}} {{~/if}}