diff --git a/app/assets/javascripts/discourse/app/models/topic-details.js b/app/assets/javascripts/discourse/app/models/topic-details.js index 8162a0537d4..c96e73c3830 100644 --- a/app/assets/javascripts/discourse/app/models/topic-details.js +++ b/app/assets/javascripts/discourse/app/models/topic-details.js @@ -1,6 +1,4 @@ import EmberObject from "@ember/object"; -import I18n from "I18n"; -import { NotificationLevels } from "discourse/lib/notification-levels"; import RestModel from "discourse/models/rest"; import User from "discourse/models/user"; import { ajax } from "discourse/lib/ajax"; @@ -9,8 +7,6 @@ import { ajax } from "discourse/lib/ajax"; A model representing a Topic's details that aren't always present, such as a list of participants. When showing topics in lists and such this information should not be required. **/ -import discourseComputed from "discourse-common/utils/decorators"; -import getURL from "discourse-common/lib/get-url"; const TopicDetails = RestModel.extend({ loaded: false, @@ -35,34 +31,6 @@ const TopicDetails = RestModel.extend({ this.set("loaded", true); }, - @discourseComputed("notification_level", "notifications_reason_id") - notificationReasonText(level, reason) { - if (typeof level !== "number") { - level = 1; - } - - let localeString = `topic.notifications.reasons.${level}`; - if (typeof reason === "number") { - const tmp = localeString + "_" + reason; - // some sane protection for missing translations of edge cases - if (I18n.lookup(tmp, { locale: "en" })) { - localeString = tmp; - } - } - - if ( - User.currentProp("mailing_list_mode") && - level > NotificationLevels.MUTED - ) { - return I18n.t("topic.notifications.reasons.mailing_list_mode"); - } else { - return I18n.t(localeString, { - username: User.currentProp("username_lower"), - basePath: getURL(""), - }); - } - }, - updateNotifications(level) { return ajax(`/t/${this.get("topic.id")}/notifications`, { type: "POST", diff --git a/app/assets/javascripts/discourse/tests/integration/components/select-kit/topic-notifications-button-test.js b/app/assets/javascripts/discourse/tests/integration/components/select-kit/topic-notifications-button-test.js index a939555bf76..c6e1e9f4630 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/select-kit/topic-notifications-button-test.js +++ b/app/assets/javascripts/discourse/tests/integration/components/select-kit/topic-notifications-button-test.js @@ -3,19 +3,25 @@ import componentTest, { } from "discourse/tests/helpers/component-test"; import I18n from "I18n"; import Topic from "discourse/models/topic"; -import { discourseModule } from "discourse/tests/helpers/qunit-helpers"; +import { + discourseModule, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; import hbs from "htmlbars-inline-precompile"; import selectKit from "discourse/tests/helpers/select-kit-helper"; -const buildTopic = function (level, archetype = "regular") { +const buildTopic = function (opts) { return Topic.create({ id: 4563, }).updateFromJson({ title: "Qunit Test Topic", details: { - notification_level: level, + notification_level: opts.level, + notifications_reason_id: opts.reason || null, }, - archetype, + archetype: opts.archetype || "regular", + category_id: opts.category_id || null, + tags: opts.tags || [], }); }; @@ -40,7 +46,7 @@ discourseModule( `, beforeEach() { - this.set("topic", buildTopic(1)); + this.set("topic", buildTopic({ level: 1 })); }, async test(assert) { @@ -50,7 +56,7 @@ discourseModule( "it has the correct label" ); - await this.set("topic", buildTopic(2)); + await this.set("topic", buildTopic({ level: 2 })); assert.equal( selectKit().header().label(), @@ -70,7 +76,10 @@ discourseModule( beforeEach() { I18n.translations.en.js.topic.notifications.tracking_pm.title = `${originalTranslation} PM`; - this.set("topic", buildTopic(2, "private_message")); + this.set( + "topic", + buildTopic({ level: 2, archetype: "private_message" }) + ); }, test(assert) { @@ -81,5 +90,194 @@ discourseModule( ); }, }); + + componentTest("notification reason text - user mailing list mode", { + template: hbs` + {{topic-notifications-button + notificationLevel=topic.details.notification_level + topic=topic + }} + `, + + beforeEach() { + this.currentUser.set("mailing_list_mode", true); + this.set("topic", buildTopic({ level: 2 })); + }, + + test(assert) { + assert.equal( + queryAll(".topic-notifications-button .text").text(), + I18n.t("topic.notifications.reasons.mailing_list_mode"), + "mailing_list_mode enabled for the user shows unique text" + ); + }, + }); + + componentTest("notification reason text - bad notification reason", { + template: hbs` + {{topic-notifications-button + notificationLevel=topic.details.notification_level + topic=topic + }} + `, + + beforeEach() { + this.set("topic", buildTopic({ level: 2 })); + }, + + test(assert) { + this.set("topic", buildTopic({ level: 3, reason: 999 })); + + assert.equal( + queryAll(".topic-notifications-button .text").text(), + I18n.t("topic.notifications.reasons.3"), + "fallback to regular level translation if reason does not exist" + ); + }, + }); + + componentTest("notification reason text - user tracking category", { + template: hbs` + {{topic-notifications-button + notificationLevel=topic.details.notification_level + topic=topic + }} + `, + + beforeEach() { + this.currentUser.set("tracked_category_ids", [88]); + this.set("topic", buildTopic({ level: 2, reason: 8, category_id: 88 })); + }, + + test(assert) { + assert.equal( + queryAll(".topic-notifications-button .text").text(), + I18n.t("topic.notifications.reasons.2_8"), + "use 2_8 notification if user is still tracking category" + ); + }, + }); + + componentTest( + "notification reason text - user no longer tracking category", + { + template: hbs` + {{topic-notifications-button + notificationLevel=topic.details.notification_level + topic=topic + }} + `, + + beforeEach() { + this.currentUser.set("tracked_category_ids", []); + this.set( + "topic", + buildTopic({ level: 2, reason: 8, category_id: 88 }) + ); + }, + + test(assert) { + assert.equal( + queryAll(".topic-notifications-button .text").text(), + I18n.t("topic.notifications.reasons.2_8_stale"), + "use _stale notification if user is no longer tracking category" + ); + }, + } + ); + + componentTest("notification reason text - user watching category", { + template: hbs` + {{topic-notifications-button + notificationLevel=topic.details.notification_level + topic=topic + }} + `, + + beforeEach() { + this.currentUser.set("watched_category_ids", [88]); + this.set("topic", buildTopic({ level: 3, reason: 6, category_id: 88 })); + }, + + test(assert) { + assert.equal( + queryAll(".topic-notifications-button .text").text(), + I18n.t("topic.notifications.reasons.3_6"), + "use 3_6 notification if user is still watching category" + ); + }, + }); + + componentTest( + "notification reason text - user no longer watching category", + { + template: hbs` + {{topic-notifications-button + notificationLevel=topic.details.notification_level + topic=topic + }} + `, + + beforeEach() { + this.currentUser.set("watched_category_ids", []); + this.set( + "topic", + buildTopic({ level: 3, reason: 6, category_id: 88 }) + ); + }, + + test(assert) { + assert.equal( + queryAll(".topic-notifications-button .text").text(), + I18n.t("topic.notifications.reasons.3_6_stale"), + "use _stale notification if user is no longer watching category" + ); + }, + } + ); + + componentTest("notification reason text - user watching tag", { + template: hbs` + {{topic-notifications-button + notificationLevel=topic.details.notification_level + topic=topic + }} + `, + + beforeEach() { + this.currentUser.set("watched_tags", ["test"]); + this.set("topic", buildTopic({ level: 3, reason: 10, tags: ["test"] })); + }, + + test(assert) { + assert.equal( + queryAll(".topic-notifications-button .text").text(), + I18n.t("topic.notifications.reasons.3_10"), + "use 3_10 notification if user is still watching tag" + ); + }, + }); + + componentTest("notification reason text - user no longer watching tag", { + template: hbs` + {{topic-notifications-button + notificationLevel=topic.details.notification_level + topic=topic + }} + `, + + beforeEach() { + this.currentUser.set("watched_tags", []); + this.set("topic", buildTopic({ level: 3, reason: 10, tags: ["test"] })); + }, + + test(assert) { + assert.equal( + queryAll(".topic-notifications-button .text").text(), + I18n.t("topic.notifications.reasons.3_10_stale"), + "use _stale notification if user is no longer watching tag" + ); + }, + }); } ); diff --git a/app/assets/javascripts/discourse/tests/unit/models/topic-details-test.js b/app/assets/javascripts/discourse/tests/unit/models/topic-details-test.js index 4e11fe03243..5dd146e5ebf 100644 --- a/app/assets/javascripts/discourse/tests/unit/models/topic-details-test.js +++ b/app/assets/javascripts/discourse/tests/unit/models/topic-details-test.js @@ -2,8 +2,8 @@ import { module, test } from "qunit"; import Topic from "discourse/models/topic"; import User from "discourse/models/user"; -function buildDetails(id) { - const topic = Topic.create({ id: id }); +function buildDetails(id, topicParams = {}) { + const topic = Topic.create(Object.assign({ id }, topicParams)); return topic.get("details"); } diff --git a/app/assets/javascripts/select-kit/addon/components/topic-notifications-button.js b/app/assets/javascripts/select-kit/addon/components/topic-notifications-button.js index 13faac30b10..6eb41942251 100644 --- a/app/assets/javascripts/select-kit/addon/components/topic-notifications-button.js +++ b/app/assets/javascripts/select-kit/addon/components/topic-notifications-button.js @@ -1,4 +1,9 @@ import { action, computed } from "@ember/object"; +import I18n from "I18n"; +import { isEmpty } from "@ember/utils"; +import { NotificationLevels } from "discourse/lib/notification-levels"; +import discourseComputed from "discourse-common/utils/decorators"; +import getURL from "discourse-common/lib/get-url"; import Component from "@ember/component"; import layout from "select-kit/templates/components/topic-notifications-button"; @@ -25,4 +30,85 @@ export default Component.extend({ .finally(() => this.set("isLoading", false)); } }, + + @discourseComputed("topic", "topic.details") + notificationReasonText(topic, topicDetails) { + let level = topicDetails.notification_level; + let reason = topicDetails.notifications_reason_id; + + if (typeof level !== "number") { + level = 1; + } + + let localeString = `topic.notifications.reasons.${level}`; + if (typeof reason === "number") { + let localeStringWithReason = localeString + "_" + reason; + + if ( + this._notificationReasonStale(level, reason, topic, this.currentUser) + ) { + localeStringWithReason += "_stale"; + } + + // some sane protection for missing translations of edge cases + if (I18n.lookup(localeStringWithReason, { locale: "en" })) { + localeString = localeStringWithReason; + } + } + + if ( + this.currentUser && + this.currentUser.mailing_list_mode && + level > NotificationLevels.MUTED + ) { + return I18n.t("topic.notifications.reasons.mailing_list_mode"); + } else { + return I18n.t(localeString, { + username: this.currentUser && this.currentUser.username_lower, + basePath: getURL(""), + }); + } + }, + + // The user may have changed their category or tag tracking settings + // since this topic was tracked/watched based on those settings in the + // past. In that case we need to alter the reason message we show them + // otherwise it is very confusing for the end user to be told they are + // tracking a topic because of a category, when they are no longer tracking + // that category. + _notificationReasonStale(level, reason, topic, currentUser) { + if (!currentUser) { + return; + } + + let categoryId = topic.category_id; + let tags = topic.tags; + let watchedCategoryIds = currentUser.watched_category_ids || []; + let trackedCategoryIds = currentUser.tracked_category_ids || []; + let watchedTags = currentUser.watched_tags || []; + + // 2_8 tracking category + if (categoryId) { + if (level === 2 && reason === 8) { + if (!trackedCategoryIds.includes(categoryId)) { + return true; + } + + // 3_6 watching category + } else if (level === 3 && reason === 6) { + if (!watchedCategoryIds.includes(categoryId)) { + return true; + } + } + } else if (!isEmpty(tags)) { + // 3_10 watching tag + if (level === 3 && reason === 10) { + if (!tags.some((tag) => watchedTags.includes(tag))) { + return true; + } + } + } + + return false; + }, }); diff --git a/app/assets/javascripts/select-kit/addon/templates/components/topic-notifications-button.hbs b/app/assets/javascripts/select-kit/addon/templates/components/topic-notifications-button.hbs index c64cf9dbf26..30b71a5fd1b 100644 --- a/app/assets/javascripts/select-kit/addon/templates/components/topic-notifications-button.hbs +++ b/app/assets/javascripts/select-kit/addon/templates/components/topic-notifications-button.hbs @@ -11,7 +11,7 @@ showCaret=showCaret ) }} - {{html-safe topic.details.notificationReasonText}} + {{html-safe notificationReasonText}}

{{else}} {{topic-notifications-options diff --git a/app/models/category_user.rb b/app/models/category_user.rb index 04fe4691b02..8a664c7fd05 100644 --- a/app/models/category_user.rb +++ b/app/models/category_user.rb @@ -202,15 +202,21 @@ class CategoryUser < ActiveRecord::Base end def self.notification_levels_for(guardian) + # Anonymous users have all default categories set to regular tracking, + # except for default muted categories which stay muted. if guardian.anonymous? notification_levels = [ SiteSetting.default_categories_watching.split("|"), SiteSetting.default_categories_tracking.split("|"), SiteSetting.default_categories_watching_first_post.split("|"), SiteSetting.default_categories_regular.split("|") - ].flatten.map { |id| [id.to_i, self.notification_levels[:regular]] } + ].flatten.map do |id| + [id.to_i, self.notification_levels[:regular]] + end - notification_levels += SiteSetting.default_categories_muted.split("|").map { |id| [id.to_i, self.notification_levels[:muted]] } + notification_levels += SiteSetting.default_categories_muted.split("|").map do |id| + [id.to_i, self.notification_levels[:muted]] + end else notification_levels = CategoryUser.where(user: guardian.user).pluck(:category_id, :notification_level) end diff --git a/app/models/tag_user.rb b/app/models/tag_user.rb index 22328e21d22..e44c458448d 100644 --- a/app/models/tag_user.rb +++ b/app/models/tag_user.rb @@ -192,6 +192,28 @@ class TagUser < ActiveRecord::Base auto_track_tag: TopicUser.notification_reasons[:auto_track_tag]) end + def self.notification_levels_for(guardian) + # Anonymous users have all default tags set to regular tracking, + # except for default muted tags which stay muted. + if guardian.anonymous? + notification_levels = [ + SiteSetting.default_tags_watching_first_post.split("|"), + SiteSetting.default_tags_watching.split("|"), + SiteSetting.default_tags_tracking.split("|") + ].flatten.map do |name| + [name, self.notification_levels[:regular]] + end + + notification_levels += SiteSetting.default_tags_muted.split("|").map do |name| + [name, self.notification_levels[:muted]] + end + else + notification_levels = TagUser.where(user: guardian.user).joins(:tag).pluck("tags.name", :notification_level) + end + + Hash[*notification_levels.flatten] + end + end # == Schema Information diff --git a/app/models/user.rb b/app/models/user.rb index eb2489b16c5..e90984675cf 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1546,17 +1546,27 @@ class User < ActiveRecord::Base values = [] - %w{watching watching_first_post tracking regular muted}.each do |s| - category_ids = SiteSetting.get("default_categories_#{s}").split("|").map(&:to_i) + # The following site settings are used to pre-populate default category + # tracking settings for a user: + # + # * default_categories_watching + # * default_categories_tracking + # * default_categories_watching_first_post + # * default_categories_regular + # * default_categories_muted + %w{watching watching_first_post tracking regular muted}.each do |setting| + category_ids = SiteSetting.get("default_categories_#{setting}").split("|").map(&:to_i) category_ids.each do |category_id| next if category_id == 0 - values << "(#{self.id}, #{category_id}, #{CategoryUser.notification_levels[s.to_sym]})" + values << { + user_id: self.id, + category_id: category_id, + notification_level: CategoryUser.notification_levels[setting.to_sym] + } end end - if values.present? - DB.exec("INSERT INTO category_users (user_id, category_id, notification_level) VALUES #{values.join(",")}") - end + CategoryUser.insert_all!(values) if values.present? end def set_default_tags_preferences @@ -1564,12 +1574,25 @@ class User < ActiveRecord::Base values = [] - %w{watching watching_first_post tracking muted}.each do |s| - tag_names = SiteSetting.get("default_tags_#{s}").split("|") + # The following site settings are used to pre-populate default tag + # tracking settings for a user: + # + # * default_tags_watching + # * default_tags_tracking + # * default_tags_watching_first_post + # * default_tags_muted + %w{watching watching_first_post tracking muted}.each do |setting| + tag_names = SiteSetting.get("default_tags_#{setting}").split("|") now = Time.zone.now Tag.where(name: tag_names).pluck(:id).each do |tag_id| - values << { user_id: self.id, tag_id: tag_id, notification_level: TagUser.notification_levels[s.to_sym], created_at: now, updated_at: now } + values << { + user_id: self.id, + tag_id: tag_id, + notification_level: TagUser.notification_levels[setting.to_sym], + created_at: now, + updated_at: now + } end end diff --git a/app/serializers/basic_user_serializer.rb b/app/serializers/basic_user_serializer.rb index 964cb5b3e71..b6daa48a06f 100644 --- a/app/serializers/basic_user_serializer.rb +++ b/app/serializers/basic_user_serializer.rb @@ -22,4 +22,24 @@ class BasicUserSerializer < ApplicationSerializer def user object[:user] || object.try(:user) || object end + + def categories_with_notification_level(lookup_level) + category_user_notification_levels.select do |id, level| + level == CategoryUser.notification_levels[lookup_level] + end.keys + end + + def category_user_notification_levels + @category_user_notification_levels ||= CategoryUser.notification_levels_for(scope) + end + + def tags_with_notification_level(lookup_level) + tag_user_notification_levels.select do |id, level| + level == TagUser.notification_levels[lookup_level] + end.keys + end + + def tag_user_notification_levels + @tag_user_notification_levels ||= TagUser.notification_levels_for(scope) + end end diff --git a/app/serializers/current_user_serializer.rb b/app/serializers/current_user_serializer.rb index 8f1a9a8bfe0..d1ab9eafcb1 100644 --- a/app/serializers/current_user_serializer.rb +++ b/app/serializers/current_user_serializer.rb @@ -28,7 +28,16 @@ class CurrentUserSerializer < BasicUserSerializer :redirected_to_top, :custom_fields, :muted_category_ids, + :regular_category_ids, + :tracked_category_ids, + :watched_first_post_category_ids, + :watched_category_ids, :muted_tag_ids, + :watched_tags, + :watching_first_post_tags, + :tracked_tags, + :muted_tags, + :regular_tags, :dismissed_banner_key, :is_anonymous, :reviewable_count, @@ -53,7 +62,7 @@ class CurrentUserSerializer < BasicUserSerializer :skip_new_user_tips, :do_not_disturb_until, :has_topic_draft, - :can_review + :can_review, def groups object.visible_groups.pluck(:id, :name).map { |id, name| { id: id, name: name } } @@ -181,13 +190,51 @@ class CurrentUserSerializer < BasicUserSerializer end def muted_category_ids - CategoryUser.lookup(object, :muted).pluck(:category_id) + categories_with_notification_level(:muted) end + def regular_category_ids + categories_with_notification_level(:regular) + end + + def tracked_category_ids + categories_with_notification_level(:tracking) + end + + def watched_category_ids + categories_with_notification_level(:watching) + end + + def watched_first_post_category_ids + categories_with_notification_level(:watching_first_post) + end + + # this is a weird outlier that is used for topic tracking state which + # needs the actual ids, which is why it is duplicated with muted_tags def muted_tag_ids TagUser.lookup(object, :muted).pluck(:tag_id) end + def muted_tags + tags_with_notification_level(:muted) + end + + def tracked_tags + tags_with_notification_level(:tracked) + end + + def watching_first_post_tags + tags_with_notification_level(:watching_first_post) + end + + def watched_tags + tags_with_notification_level(:watching) + end + + def regular_tags + tags_with_notification_level(:regular) + end + def ignored_users IgnoredUser.where(user: object.id).joins(:ignored_user).pluck(:username) end diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index 56d47786b83..d1bb28dc4e2 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -207,39 +207,39 @@ class UserSerializer < UserCardSerializer ### PRIVATE ATTRIBUTES ### def muted_tags - TagUser.lookup(object, :muted).joins(:tag).pluck('tags.name') + tags_with_notification_level(:muted) end def tracked_tags - TagUser.lookup(object, :tracking).joins(:tag).pluck('tags.name') + tags_with_notification_level(:tracked) end def watching_first_post_tags - TagUser.lookup(object, :watching_first_post).joins(:tag).pluck('tags.name') + tags_with_notification_level(:watching_first_post) end def watched_tags - TagUser.lookup(object, :watching).joins(:tag).pluck('tags.name') + tags_with_notification_level(:watching) end def muted_category_ids - CategoryUser.lookup(object, :muted).pluck(:category_id) + categories_with_notification_level(:muted) end def regular_category_ids - CategoryUser.lookup(object, :regular).pluck(:category_id) + categories_with_notification_level(:regular) end def tracked_category_ids - CategoryUser.lookup(object, :tracking).pluck(:category_id) + categories_with_notification_level(:tracking) end def watched_category_ids - CategoryUser.lookup(object, :watching).pluck(:category_id) + categories_with_notification_level(:watching) end def watched_first_post_category_ids - CategoryUser.lookup(object, :watching_first_post).pluck(:category_id) + categories_with_notification_level(:watching_first_post) end def muted_usernames diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 280f26797ad..eaa0cd65302 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2595,12 +2595,15 @@ en: reasons: mailing_list_mode: "You have mailing list mode enabled, so you will be notified of replies to this topic via email." "3_10": "You will receive notifications because you are watching a tag on this topic." + "3_10_stale": "You will receive notifications because you were watching a tag on this topic in the past." "3_6": "You will receive notifications because you are watching this category." + "3_6_stale": "You will receive notifications because you were watching this category in the past." "3_5": "You will receive notifications because you started watching this topic automatically." "3_2": "You will receive notifications because you are watching this topic." "3_1": "You will receive notifications because you created this topic." "3": "You will receive notifications because you are watching this topic." "2_8": "You will see a count of new replies because you are tracking this category." + "2_8_stale": "You will see a count of new replies because you were tracking this category in the past." "2_4": "You will see a count of new replies because you posted a reply to this topic." "2_2": "You will see a count of new replies because you are tracking this topic." "2": 'You will see a count of new replies because you read this topic.' diff --git a/spec/models/category_user_spec.rb b/spec/models/category_user_spec.rb index f777b9e8206..dc05ab12e85 100644 --- a/spec/models/category_user_spec.rb +++ b/spec/models/category_user_spec.rb @@ -219,6 +219,54 @@ describe CategoryUser do expect(CategoryUser.where(user_id: user.id).count).to eq(0) end + end + describe "#notification_levels_for" do + let(:guardian) { Guardian.new(user) } + let!(:category1) { Fabricate(:category) } + let!(:category2) { Fabricate(:category) } + let!(:category3) { Fabricate(:category) } + let!(:category4) { Fabricate(:category) } + let!(:category5) { Fabricate(:category) } + + context "for anon" do + let(:user) { nil } + before do + SiteSetting.default_categories_watching = category1.id.to_s + SiteSetting.default_categories_tracking = category2.id.to_s + SiteSetting.default_categories_watching_first_post = category3.id.to_s + SiteSetting.default_categories_regular = category4.id.to_s + SiteSetting.default_categories_muted = category5.id.to_s + end + it "every category from the default_categories_* site settings get overridden to regular, except for muted" do + levels = CategoryUser.notification_levels_for(guardian) + expect(levels[category1.id]).to eq(CategoryUser.notification_levels[:regular]) + expect(levels[category2.id]).to eq(CategoryUser.notification_levels[:regular]) + expect(levels[category3.id]).to eq(CategoryUser.notification_levels[:regular]) + expect(levels[category4.id]).to eq(CategoryUser.notification_levels[:regular]) + expect(levels[category5.id]).to eq(CategoryUser.notification_levels[:muted]) + end + end + + context "for a user" do + before do + CategoryUser.create(user: user, category: category1, notification_level: CategoryUser.notification_levels[:watching]) + CategoryUser.create(user: user, category: category2, notification_level: CategoryUser.notification_levels[:tracking]) + CategoryUser.create(user: user, category: category3, notification_level: CategoryUser.notification_levels[:watching_first_post]) + CategoryUser.create(user: user, category: category4, notification_level: CategoryUser.notification_levels[:regular]) + CategoryUser.create(user: user, category: category5, notification_level: CategoryUser.notification_levels[:muted]) + end + it "gets the category_user notification levels for all categories the user is tracking and does not + include categories the user is not tracking at all" do + category6 = Fabricate(:category) + levels = CategoryUser.notification_levels_for(guardian) + expect(levels[category1.id]).to eq(CategoryUser.notification_levels[:watching]) + expect(levels[category2.id]).to eq(CategoryUser.notification_levels[:tracking]) + expect(levels[category3.id]).to eq(CategoryUser.notification_levels[:watching_first_post]) + expect(levels[category4.id]).to eq(CategoryUser.notification_levels[:regular]) + expect(levels[category5.id]).to eq(CategoryUser.notification_levels[:muted]) + expect(levels.key?(category6.id)).to eq(false) + end + end end end diff --git a/spec/models/tag_user_spec.rb b/spec/models/tag_user_spec.rb index 33f769f6396..8b6a2933d26 100644 --- a/spec/models/tag_user_spec.rb +++ b/spec/models/tag_user_spec.rb @@ -232,4 +232,49 @@ describe TagUser do end end end + + describe "#notification_levels_for" do + let(:guardian) { Guardian.new(user) } + let!(:tag1) { Fabricate(:tag) } + let!(:tag2) { Fabricate(:tag) } + let!(:tag3) { Fabricate(:tag) } + let!(:tag4) { Fabricate(:tag) } + + context "for anon" do + let(:user) { nil } + before do + SiteSetting.default_tags_watching = tag1.name + SiteSetting.default_tags_tracking = tag2.name + SiteSetting.default_tags_watching_first_post = tag3.name + SiteSetting.default_tags_muted = tag4.name + end + it "every tag from the default_tags_* site settings get overridden to watching_first_post, except for muted" do + levels = TagUser.notification_levels_for(guardian) + expect(levels[tag1.name]).to eq(TagUser.notification_levels[:regular]) + expect(levels[tag2.name]).to eq(TagUser.notification_levels[:regular]) + expect(levels[tag3.name]).to eq(TagUser.notification_levels[:regular]) + expect(levels[tag4.name]).to eq(TagUser.notification_levels[:muted]) + end + end + + context "for a user" do + let(:user) { Fabricate(:user) } + before do + TagUser.create(user: user, tag: tag1, notification_level: TagUser.notification_levels[:watching]) + TagUser.create(user: user, tag: tag2, notification_level: TagUser.notification_levels[:tracking]) + TagUser.create(user: user, tag: tag3, notification_level: TagUser.notification_levels[:watching_first_post]) + TagUser.create(user: user, tag: tag4, notification_level: TagUser.notification_levels[:muted]) + end + it "gets the tag_user notification levels for all tags the user is tracking and does not + include tags the user is not tracking at all" do + tag5 = Fabricate(:tag) + levels = TagUser.notification_levels_for(guardian) + expect(levels[tag1.name]).to eq(TagUser.notification_levels[:watching]) + expect(levels[tag2.name]).to eq(TagUser.notification_levels[:tracking]) + expect(levels[tag3.name]).to eq(TagUser.notification_levels[:watching_first_post]) + expect(levels[tag4.name]).to eq(TagUser.notification_levels[:muted]) + expect(levels.key?(tag5.name)).to eq(false) + end + end + end end