From ebb389ef8ae23c8cfb3f0a6ea57fb0afd8b90454 Mon Sep 17 00:00:00 2001 From: Roman Rizzi Date: Thu, 29 Aug 2019 12:03:43 -0300 Subject: [PATCH] UX: Read indicator improvements. (#8049) * The read indicator now shows up when no member has read the last post of the topic (written by a non-member) * The read indicator works on mobile and receives live updates from message bus * The icon we display in the topic list was changed * Added a title to the indicator to indicate its purpose when hovering over it --- .../components/basic-topic-list.js.es6 | 45 +++++++++++++++++++ .../components/topic-list-item.js.es6 | 28 ++++++------ .../templates/list/topic-list-item.raw.hbs | 6 +-- .../templates/list/unread-indicator.raw.hbs | 5 +++ .../mobile/components/basic-topic-list.hbs | 3 ++ .../stylesheets/common/base/_topic-list.scss | 12 +++-- app/models/topic_tracking_state.rb | 14 +++--- app/serializers/listable_topic_serializer.rb | 8 ++-- config/locales/client.en.yml | 1 + lib/svg_sprite/svg_sprite.rb | 1 + spec/models/topic_tracking_state_spec.rb | 17 +++++-- vendor/assets/svg-icons/fontawesome/solid.svg | 4 ++ 12 files changed, 107 insertions(+), 37 deletions(-) create mode 100644 app/assets/javascripts/discourse/templates/list/unread-indicator.raw.hbs diff --git a/app/assets/javascripts/discourse/components/basic-topic-list.js.es6 b/app/assets/javascripts/discourse/components/basic-topic-list.js.es6 index e08bf9d5892..e8af6acc94f 100644 --- a/app/assets/javascripts/discourse/components/basic-topic-list.js.es6 +++ b/app/assets/javascripts/discourse/components/basic-topic-list.js.es6 @@ -33,6 +33,51 @@ export default Ember.Component.extend({ } }, + didInsertElement() { + this._super(...arguments); + + this.topics.forEach(topic => { + const includeUnreadIndicator = + typeof topic.unread_by_group_member !== "undefined"; + + if (includeUnreadIndicator) { + const unreadIndicatorChannel = `/private-messages/unread-indicator/${topic.id}`; + this.messageBus.subscribe(unreadIndicatorChannel, data => { + const nodeClassList = document.querySelector( + `.indicator-topic-${data.topic_id}` + ).classList; + + if (data.show_indicator) { + nodeClassList.remove("read"); + } else { + nodeClassList.add("read"); + } + }); + } + }); + }, + + willDestroyElement() { + this._super(...arguments); + + this.topics.forEach(topic => { + const includeUnreadIndicator = + typeof topic.unread_by_group_member !== "undefined"; + + if (includeUnreadIndicator) { + const unreadIndicatorChannel = `/private-messages/unread-indicator/${topic.id}`; + this.messageBus.unsubscribe(unreadIndicatorChannel); + } + }); + }, + + @computed("topics") + showUnreadIndicator(topics) { + return topics.some( + topic => typeof topic.unread_by_group_member !== "undefined" + ); + }, + click(e) { // Mobile basic-topic-list doesn't use the `topic-list-item` view so // the event for the topic entrance is never wired up. diff --git a/app/assets/javascripts/discourse/components/topic-list-item.js.es6 b/app/assets/javascripts/discourse/components/topic-list-item.js.es6 index e7ed6186b34..1116d97e610 100644 --- a/app/assets/javascripts/discourse/components/topic-list-item.js.es6 +++ b/app/assets/javascripts/discourse/components/topic-list-item.js.es6 @@ -38,16 +38,16 @@ export const ListItemDefaults = { didInsertElement() { this._super(...arguments); - if (this.includeReadIndicator) { - this.messageBus.subscribe(this.readIndicatorChannel, data => { + if (this.includeUnreadIndicator) { + this.messageBus.subscribe(this.unreadIndicatorChannel, data => { const nodeClassList = document.querySelector( `.indicator-topic-${data.topic_id}` ).classList; if (data.show_indicator) { - nodeClassList.remove("unread"); + nodeClassList.remove("read"); } else { - nodeClassList.add("unread"); + nodeClassList.add("read"); } }); } @@ -56,24 +56,24 @@ export const ListItemDefaults = { willDestroyElement() { this._super(...arguments); - if (this.includeReadIndicator) { - this.messageBus.unsubscribe(this.readIndicatorChannel); + if (this.includeUnreadIndicator) { + this.messageBus.unsubscribe(this.unreadIndicatorChannel); } }, @computed("topic.id") - readIndicatorChannel(topicId) { - return `/private-messages/read-indicator/${topicId}`; + unreadIndicatorChannel(topicId) { + return `/private-messages/unread-indicator/${topicId}`; }, - @computed("topic.read_by_group_member") - unreadClass(readByGroupMember) { - return readByGroupMember ? "" : "unread"; + @computed("topic.unread_by_group_member") + unreadClass(unreadByGroupMember) { + return unreadByGroupMember ? "" : "read"; }, - @computed("topic.read_by_group_member") - includeReadIndicator(readByGroupMember) { - return typeof readByGroupMember !== "undefined"; + @computed("topic.unread_by_group_member") + includeUnreadIndicator(unreadByGroupMember) { + return typeof unreadByGroupMember !== "undefined"; }, @computed diff --git a/app/assets/javascripts/discourse/templates/list/topic-list-item.raw.hbs b/app/assets/javascripts/discourse/templates/list/topic-list-item.raw.hbs index f6d2ddfc382..db3ca305695 100644 --- a/app/assets/javascripts/discourse/templates/list/topic-list-item.raw.hbs +++ b/app/assets/javascripts/discourse/templates/list/topic-list-item.raw.hbs @@ -20,14 +20,10 @@ {{~topic-featured-link topic}} {{~/if}} {{~raw-plugin-outlet name="topic-list-after-title"}} + {{~raw "list/unread-indicator" includeUnreadIndicator=topic.unread_by_group_member topicId=topic.id ~}} {{~#if showTopicPostBadges}} {{~raw "topic-post-badges" unread=topic.unread newPosts=topic.displayNewPosts unseen=topic.unseen url=topic.lastUnreadUrl newDotText=newDotText}} {{~/if}} - {{~#if includeReadIndicator}} - - {{~d-icon "book-reader"}} - - {{~/if}}