diff --git a/app/assets/javascripts/discourse/app/components/sidebar/topics-section.js b/app/assets/javascripts/discourse/app/components/sidebar/topics-section.js index d96b7229763..6a3de9ddc94 100644 --- a/app/assets/javascripts/discourse/app/components/sidebar/topics-section.js +++ b/app/assets/javascripts/discourse/app/components/sidebar/topics-section.js @@ -6,6 +6,7 @@ import { customSectionLinks } from "discourse/lib/sidebar/custom-topics-section- import EverythingSectionLink from "discourse/lib/sidebar/topics-section/everything-section-link"; import TrackedSectionLink from "discourse/lib/sidebar/topics-section/tracked-section-link"; import BookmarkedSectionLink from "discourse/lib/sidebar/topics-section/bookmarked-section-link"; +import MyPostsSectionLink from "discourse/lib/sidebar/topics-section/my-posts-section-link"; import { action } from "@ember/object"; import { next } from "@ember/runloop"; @@ -14,6 +15,7 @@ const DEFAULT_SECTION_LINKS = [ EverythingSectionLink, TrackedSectionLink, BookmarkedSectionLink, + MyPostsSectionLink, ]; export default class SidebarTopicsSection extends GlimmerComponent { @@ -23,6 +25,7 @@ export default class SidebarTopicsSection extends GlimmerComponent { return new sectionLinkClass({ topicTrackingState: this.topicTrackingState, currentUser: this.currentUser, + appEvents: this.appEvents, }); }); diff --git a/app/assets/javascripts/discourse/app/initializers/subscribe-user-changes.js b/app/assets/javascripts/discourse/app/initializers/subscribe-user-changes.js deleted file mode 100644 index bc25dc22035..00000000000 --- a/app/assets/javascripts/discourse/app/initializers/subscribe-user-changes.js +++ /dev/null @@ -1,15 +0,0 @@ -export default { - name: "subscribe-user-changes", - after: "message-bus", - - initialize(container) { - const user = container.lookup("current-user:main"); - - if (user) { - const bus = container.lookup("message-bus:main"); - bus.subscribe("/user", (data) => { - user.setProperties(data); - }); - } - }, -}; diff --git a/app/assets/javascripts/discourse/app/initializers/subscribe-user-notifications.js b/app/assets/javascripts/discourse/app/initializers/subscribe-user-notifications.js index 9170ab69a9c..bf4235ba858 100644 --- a/app/assets/javascripts/discourse/app/initializers/subscribe-user-notifications.js +++ b/app/assets/javascripts/discourse/app/initializers/subscribe-user-notifications.js @@ -108,6 +108,10 @@ export default { user.notification_channel_position ); + bus.subscribe(`/user-drafts/${user.id}`, (data) => { + user.updateDraftProperties(data); + }); + bus.subscribe(`/do-not-disturb/${user.get("id")}`, (data) => { user.updateDoNotDisturbStatus(data.ends_at); }); diff --git a/app/assets/javascripts/discourse/app/lib/sidebar/topics-section/base-section-link.js b/app/assets/javascripts/discourse/app/lib/sidebar/topics-section/base-section-link.js index d62ab343518..fa217833ef9 100644 --- a/app/assets/javascripts/discourse/app/lib/sidebar/topics-section/base-section-link.js +++ b/app/assets/javascripts/discourse/app/lib/sidebar/topics-section/base-section-link.js @@ -2,9 +2,10 @@ * Base class representing a sidebar topics section link interface. */ export default class BaseSectionLink { - constructor({ topicTrackingState, currentUser } = {}) { + constructor({ topicTrackingState, currentUser, appEvents } = {}) { this.topicTrackingState = topicTrackingState; this.currentUser = currentUser; + this.appEvents = appEvents; } /** diff --git a/app/assets/javascripts/discourse/app/lib/sidebar/topics-section/my-posts-section-link.js b/app/assets/javascripts/discourse/app/lib/sidebar/topics-section/my-posts-section-link.js new file mode 100644 index 00000000000..703bce8cf5c --- /dev/null +++ b/app/assets/javascripts/discourse/app/lib/sidebar/topics-section/my-posts-section-link.js @@ -0,0 +1,65 @@ +import I18n from "I18n"; +import { tracked } from "@glimmer/tracking"; + +import BaseSectionLink from "discourse/lib/sidebar/topics-section/base-section-link"; + +const USER_DRAFTS_CHANGED_EVENT = "user-drafts:changed"; + +export default class MyPostsSectionLink extends BaseSectionLink { + @tracked draftCount = this.currentUser.draft_count; + + constructor() { + super(...arguments); + this.appEvents.on(USER_DRAFTS_CHANGED_EVENT, this, this._updateDraftCount); + } + + teardown() { + this.appEvents.off(USER_DRAFTS_CHANGED_EVENT, this, this._updateDraftCount); + } + + _updateDraftCount() { + this.draftCount = this.currentUser.draft_count; + } + + get name() { + return "my-posts"; + } + + get route() { + if (this._hasDraft) { + return "userActivity.drafts"; + } else { + return "userActivity.index"; + } + } + + get currentWhen() { + if (this._hasDraft) { + return "userActivity.index userActivity.drafts"; + } + } + + get model() { + return this.currentUser; + } + + get title() { + return I18n.t("sidebar.sections.topics.links.my_posts.title"); + } + + get text() { + return I18n.t("sidebar.sections.topics.links.my_posts.content"); + } + + get badgeText() { + if (this._hasDraft) { + return I18n.t("sidebar.sections.topics.links.my_posts.draft_count", { + count: this.draftCount, + }); + } + } + + get _hasDraft() { + return this.draftCount > 0; + } +} diff --git a/app/assets/javascripts/discourse/app/models/user.js b/app/assets/javascripts/discourse/app/models/user.js index 6cc4116b2ea..fe3cb65b65b 100644 --- a/app/assets/javascripts/discourse/app/models/user.js +++ b/app/assets/javascripts/discourse/app/models/user.js @@ -1002,6 +1002,11 @@ const User = RestModel.extend({ this.appEvents.trigger("do-not-disturb:changed", this.do_not_disturb_until); }, + updateDraftProperties(properties) { + this.setProperties(properties); + this.appEvents.trigger("user-drafts:changed"); + }, + isInDoNotDisturb() { return ( this.do_not_disturb_until && diff --git a/app/assets/javascripts/discourse/tests/acceptance/sidebar-topics-section-test.js b/app/assets/javascripts/discourse/tests/acceptance/sidebar-topics-section-test.js index 49770538f96..898faa49bcb 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/sidebar-topics-section-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/sidebar-topics-section-test.js @@ -203,6 +203,79 @@ acceptance("Sidebar - Topics Section", function (needs) { } ); + conditionalTest( + "clicking on my posts link", + !isLegacyEmber(), + async function (assert) { + await visit("/t/280"); + await click(".sidebar-section-topics .sidebar-section-link-my-posts"); + + assert.strictEqual( + currentURL(), + `/u/${loggedInUser().username}/activity`, + "it should transistion to the user's activity url" + ); + + assert.strictEqual( + queryAll(".sidebar-section-topics .sidebar-section-link.active").length, + 1, + "only one link is marked as active" + ); + + assert.ok( + exists(".sidebar-section-topics .sidebar-section-link-my-posts.active"), + "the my posts link is marked as active" + ); + + await visit(`/u/${loggedInUser().username}/activity/drafts`); + + assert.notOk( + exists(".sidebar-section-topics .sidebar-section-link-my-posts.active"), + "the my posts link is not marked as active when user has no drafts and visiting the user activity drafts URL" + ); + } + ); + + conditionalTest( + "clicking on my posts link when user has a draft", + !isLegacyEmber(), + async function (assert) { + await visit("/t/280"); + + publishToMessageBus(`/user-drafts/${loggedInUser().id}`, { + draft_count: 1, + }); + + await settled(); + + await click(".sidebar-section-topics .sidebar-section-link-my-posts"); + + assert.strictEqual( + currentURL(), + `/u/${loggedInUser().username}/activity/drafts`, + "it transistions to the user's activity drafts url" + ); + + assert.strictEqual( + queryAll(".sidebar-section-topics .sidebar-section-link.active").length, + 1, + "only one link is marked as active" + ); + + assert.ok( + exists(".sidebar-section-topics .sidebar-section-link-my-posts.active"), + "the my posts link is marked as active" + ); + + await visit(`/u/${loggedInUser().username}/activity`); + + assert.ok( + exists(".sidebar-section-topics .sidebar-section-link-my-posts.active"), + "the my posts link is marked as active" + ); + } + ); + conditionalTest( "visiting top route", !isLegacyEmber(), diff --git a/app/models/user_stat.rb b/app/models/user_stat.rb index 7d0093eeadc..e7d41994d1a 100644 --- a/app/models/user_stat.rb +++ b/app/models/user_stat.rb @@ -215,7 +215,7 @@ class UserStat < ActiveRecord::Base SQL MessageBus.publish( - '/user', + "/user-drafts/#{user_id}", { draft_count: draft_count, has_topic_draft: !!has_topic_draft diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 86a01bc6279..5f1cd46e365 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -4058,6 +4058,12 @@ en: bookmarked: content: "Bookmarked" title: "All bookmarked topics" + my_posts: + content: "My Posts" + title: "My posts" + draft_count: + one: "%{count} draft" + other: "%{count} drafts" # This section is exported to the javascript for i18n in the admin section admin_js: diff --git a/spec/lib/post_creator_spec.rb b/spec/lib/post_creator_spec.rb index de5533e5317..1c929c26095 100644 --- a/spec/lib/post_creator_spec.rb +++ b/spec/lib/post_creator_spec.rb @@ -174,9 +174,9 @@ describe PostCreator do "/latest", "/topic/#{created_post.topic_id}", "/topic/#{created_post.topic_id}", - "/user", - "/user", - "/user" + "/user-drafts/#{admin.id}", + "/user-drafts/#{admin.id}", + "/user-drafts/#{admin.id}", ].sort ) @@ -205,7 +205,7 @@ describe PostCreator do user_action = messages.find { |m| m.channel == "/u/#{p.user.username}" } expect(user_action).not_to eq(nil) - draft_count = messages.find { |m| m.channel == "/user" } + draft_count = messages.find { |m| m.channel == "/user-drafts/#{p.user_id}" } expect(draft_count).not_to eq(nil) expect(messages.filter { |m| m.channel != "/distributed_hash" }.length).to eq(7) diff --git a/spec/models/draft_spec.rb b/spec/models/draft_spec.rb index 1c26e1765c8..9c63d306ae3 100644 --- a/spec/models/draft_spec.rb +++ b/spec/models/draft_spec.rb @@ -179,14 +179,14 @@ describe Draft do it 'updates draft count when a draft is created or destroyed' do Draft.set(Fabricate(:user), Draft::NEW_TOPIC, 0, "data") - messages = MessageBus.track_publish("/user") do + messages = MessageBus.track_publish("/user-drafts/#{user.id}") do Draft.set(user, Draft::NEW_TOPIC, 0, "data") end expect(messages.first.data[:draft_count]).to eq(1) expect(messages.first.data[:has_topic_draft]).to eq(true) - messages = MessageBus.track_publish("/user") do + messages = MessageBus.track_publish("/user-drafts/#{user.id}") do Draft.where(user: user).destroy_all end