Merge pull request #4477 from cpradio/watching-state-on-reply

FEATURE: Add notification level user preference when replying to a topic
This commit is contained in:
Sam 2016-10-11 10:05:37 +11:00 committed by GitHub
commit ea1f0683c8
17 changed files with 127 additions and 27 deletions

View File

@ -3,6 +3,7 @@ import CanCheckEmails from 'discourse/mixins/can-check-emails';
import { popupAjaxError } from 'discourse/lib/ajax-error'; import { popupAjaxError } from 'discourse/lib/ajax-error';
import computed from "ember-addons/ember-computed-decorators"; import computed from "ember-addons/ember-computed-decorators";
import { cook } from 'discourse/lib/text'; import { cook } from 'discourse/lib/text';
import { NotificationLevels } from 'discourse/lib/notification-levels';
export default Ember.Controller.extend(CanCheckEmails, { export default Ember.Controller.extend(CanCheckEmails, {
@ -110,6 +111,10 @@ export default Ember.Controller.extend(CanCheckEmails, {
{ name: I18n.t('user.auto_track_options.after_5_minutes'), value: 300000 }, { name: I18n.t('user.auto_track_options.after_5_minutes'), value: 300000 },
{ name: I18n.t('user.auto_track_options.after_10_minutes'), value: 600000 }], { name: I18n.t('user.auto_track_options.after_10_minutes'), value: 600000 }],
notificationLevelsForReplying: [{ name: I18n.t('topic.notifications.watching.title'), value: NotificationLevels.WATCHING },
{ name: I18n.t('topic.notifications.tracking.title'), value: NotificationLevels.TRACKING }],
considerNewTopicOptions: [{ name: I18n.t('user.new_topic_duration.not_viewed'), value: -1 }, considerNewTopicOptions: [{ name: I18n.t('user.new_topic_duration.not_viewed'), value: -1 },
{ name: I18n.t('user.new_topic_duration.after_1_day'), value: 60 * 24 }, { name: I18n.t('user.new_topic_duration.after_1_day'), value: 60 * 24 },
{ name: I18n.t('user.new_topic_duration.after_2_days'), value: 60 * 48 }, { name: I18n.t('user.new_topic_duration.after_2_days'), value: 60 * 48 },

View File

@ -222,6 +222,7 @@ const User = RestModel.extend({
'digest_after_minutes', 'digest_after_minutes',
'new_topic_duration_minutes', 'new_topic_duration_minutes',
'auto_track_topics_after_msecs', 'auto_track_topics_after_msecs',
'notification_level_when_replying',
'like_notification_frequency', 'like_notification_frequency',
'include_tl0_in_digests' 'include_tl0_in_digests'
].forEach(s => { ].forEach(s => {

View File

@ -234,6 +234,11 @@
{{combo-box valueAttribute="value" content=autoTrackDurations value=model.user_option.auto_track_topics_after_msecs}} {{combo-box valueAttribute="value" content=autoTrackDurations value=model.user_option.auto_track_topics_after_msecs}}
</div> </div>
<div class="controls controls-dropdown">
<label>{{i18n 'user.notification_level_when_replying'}}</label>
{{combo-box valueAttribute="value" content=notificationLevelsForReplying value=model.user_option.notification_level_when_replying}}
</div>
<div class="controls controls-dropdown"> <div class="controls controls-dropdown">
<label>{{i18n 'user.like_notification_frequency.title'}}</label> <label>{{i18n 'user.like_notification_frequency.title'}}</label>
{{combo-box valueAttribute="value" content=likeNotificationFrequencies value=model.user_option.like_notification_frequency}} {{combo-box valueAttribute="value" content=likeNotificationFrequencies value=model.user_option.like_notification_frequency}}

View File

@ -0,0 +1,26 @@
require_dependency 'enum_site_setting'
require_dependency 'notification_levels'
class NotificationLevelWhenReplyingSiteSetting < EnumSiteSetting
def self.valid_value?(val)
val.to_i.to_s == val.to_s &&
values.any? { |v| v[:value] == val.to_i }
end
def self.notification_levels
NotificationLevels.topic_levels
end
def self.values
@values ||= [
{ name: 'topic.notifications.watching.title', value: notification_levels[:watching] },
{ name: 'topic.notifications.tracking.title', value: notification_levels[:tracking] }
]
end
def self.translate_names?
true
end
end

View File

@ -37,24 +37,24 @@ class TopicUser < ActiveRecord::Base
auto_track_tag: 12) auto_track_tag: 12)
end end
def auto_track(user_id, topic_id, reason) def auto_notification(user_id, topic_id, reason, notification_level)
if TopicUser.where(user_id: user_id, topic_id: topic_id, notifications_reason_id: nil).exists? if TopicUser.where(user_id: user_id, topic_id: topic_id, notifications_reason_id: nil).exists?
change(user_id, topic_id, change(user_id, topic_id,
notification_level: notification_levels[:tracking], notification_level: notification_level,
notifications_reason_id: reason notifications_reason_id: reason
) )
MessageBus.publish("/topic/#{topic_id}", { MessageBus.publish("/topic/#{topic_id}", {
notification_level_change: notification_levels[:tracking], notification_level_change: notification_level,
notifications_reason_id: reason notifications_reason_id: reason
}, user_ids: [user_id]) }, user_ids: [user_id])
end end
end end
def auto_watch(user_id, topic_id) def auto_notification_for_staging(user_id, topic_id, reason)
topic_user = TopicUser.find_or_initialize_by(user_id: user_id, topic_id: topic_id) topic_user = TopicUser.find_or_initialize_by(user_id: user_id, topic_id: topic_id)
topic_user.notification_level = notification_levels[:watching] topic_user.notification_level = notification_levels[:watching]
topic_user.notifications_reason_id = notification_reasons[:auto_watch] topic_user.notifications_reason_id = reason
topic_user.save topic_user.save
end end

View File

@ -38,6 +38,7 @@ class UserOption < ActiveRecord::Base
self.new_topic_duration_minutes = SiteSetting.default_other_new_topic_duration_minutes self.new_topic_duration_minutes = SiteSetting.default_other_new_topic_duration_minutes
self.auto_track_topics_after_msecs = SiteSetting.default_other_auto_track_topics_after_msecs self.auto_track_topics_after_msecs = SiteSetting.default_other_auto_track_topics_after_msecs
self.notification_level_when_replying = SiteSetting.default_other_notification_level_when_replying
self.like_notification_frequency = SiteSetting.default_other_like_notification_frequency self.like_notification_frequency = SiteSetting.default_other_like_notification_frequency
@ -129,26 +130,27 @@ end
# #
# Table name: user_options # Table name: user_options
# #
# user_id :integer not null, primary key # user_id :integer not null, primary key
# email_always :boolean default(FALSE), not null # email_always :boolean default(FALSE), not null
# mailing_list_mode :boolean default(FALSE), not null # mailing_list_mode :boolean default(FALSE), not null
# email_digests :boolean # email_digests :boolean
# email_direct :boolean default(TRUE), not null # email_direct :boolean default(TRUE), not null
# email_private_messages :boolean default(TRUE), not null # email_private_messages :boolean default(TRUE), not null
# external_links_in_new_tab :boolean default(FALSE), not null # external_links_in_new_tab :boolean default(FALSE), not null
# enable_quoting :boolean default(TRUE), not null # enable_quoting :boolean default(TRUE), not null
# dynamic_favicon :boolean default(FALSE), not null # dynamic_favicon :boolean default(FALSE), not null
# disable_jump_reply :boolean default(FALSE), not null # disable_jump_reply :boolean default(FALSE), not null
# automatically_unpin_topics :boolean default(TRUE), not null # automatically_unpin_topics :boolean default(TRUE), not null
# digest_after_minutes :integer # digest_after_minutes :integer
# auto_track_topics_after_msecs :integer # auto_track_topics_after_msecs :integer
# new_topic_duration_minutes :integer # notification_level_when_replying :integer
# last_redirected_to_top_at :datetime # new_topic_duration_minutes :integer
# email_previous_replies :integer default(2), not null # last_redirected_to_top_at :datetime
# email_in_reply_to :boolean default(TRUE), not null # email_previous_replies :integer default(2), not null
# like_notification_frequency :integer default(1), not null # email_in_reply_to :boolean default(TRUE), not null
# include_tl0_in_digests :boolean default(FALSE) # like_notification_frequency :integer default(1), not null
# mailing_list_mode_frequency :integer default(0), not null # include_tl0_in_digests :boolean default(FALSE)
# mailing_list_mode_frequency :integer default(0), not null
# #
# Indexes # Indexes
# #

View File

@ -13,6 +13,7 @@ class UserOptionSerializer < ApplicationSerializer
:digest_after_minutes, :digest_after_minutes,
:automatically_unpin_topics, :automatically_unpin_topics,
:auto_track_topics_after_msecs, :auto_track_topics_after_msecs,
:notification_level_when_replying,
:new_topic_duration_minutes, :new_topic_duration_minutes,
:email_previous_replies, :email_previous_replies,
:email_in_reply_to, :email_in_reply_to,
@ -24,6 +25,10 @@ class UserOptionSerializer < ApplicationSerializer
object.auto_track_topics_after_msecs || SiteSetting.default_other_auto_track_topics_after_msecs object.auto_track_topics_after_msecs || SiteSetting.default_other_auto_track_topics_after_msecs
end end
def notification_level_when_replying
object.notification_level_when_replying || SiteSetting.default_other_notification_level_when_replying
end
def new_topic_duration_minutes def new_topic_duration_minutes
object.new_topic_duration_minutes || SiteSetting.default_other_new_topic_duration_minutes object.new_topic_duration_minutes || SiteSetting.default_other_new_topic_duration_minutes
end end

View File

@ -29,6 +29,7 @@ class UserUpdater
:digest_after_minutes, :digest_after_minutes,
:new_topic_duration_minutes, :new_topic_duration_minutes,
:auto_track_topics_after_msecs, :auto_track_topics_after_msecs,
:notification_level_when_replying,
:email_previous_replies, :email_previous_replies,
:email_in_reply_to, :email_in_reply_to,
:like_notification_frequency, :like_notification_frequency,

View File

@ -740,6 +740,8 @@ en:
after_5_minutes: "after 5 minutes" after_5_minutes: "after 5 minutes"
after_10_minutes: "after 10 minutes" after_10_minutes: "after 10 minutes"
notification_level_when_replying: "When I post in a topic, set that topic to"
invited: invited:
search: "type to search invites..." search: "type to search invites..."
title: "Invites" title: "Invites"

View File

@ -1368,6 +1368,7 @@ en:
default_other_new_topic_duration_minutes: "Global default condition for which a topic is considered new." default_other_new_topic_duration_minutes: "Global default condition for which a topic is considered new."
default_other_auto_track_topics_after_msecs: "Global default time before a topic is automatically tracked." default_other_auto_track_topics_after_msecs: "Global default time before a topic is automatically tracked."
default_other_notification_level_when_replying: "Global default notification level when the user replies to a topic."
default_other_external_links_in_new_tab: "Open external links in a new tab by default." default_other_external_links_in_new_tab: "Open external links in a new tab by default."
default_other_enable_quoting: "Enable quote reply for highlighted text by default." default_other_enable_quoting: "Enable quote reply for highlighted text by default."
default_other_dynamic_favicon: "Show new/updated topic count on browser icon by default." default_other_dynamic_favicon: "Show new/updated topic count on browser icon by default."

View File

@ -1250,6 +1250,9 @@ user_preferences:
default_other_auto_track_topics_after_msecs: default_other_auto_track_topics_after_msecs:
enum: 'AutoTrackDurationSiteSetting' enum: 'AutoTrackDurationSiteSetting'
default: 240000 default: 240000
default_other_notification_level_when_replying:
enum: 'NotificationLevelWhenReplyingSiteSetting'
default: 2
default_other_external_links_in_new_tab: false default_other_external_links_in_new_tab: false
default_other_enable_quoting: true default_other_enable_quoting: true
default_other_dynamic_favicon: false default_other_dynamic_favicon: false

View File

@ -0,0 +1,5 @@
class AddNotificationLevelWhenReplying < ActiveRecord::Migration
def change
add_column :user_options, :notification_level_when_replying, :integer
end
end

View File

@ -452,9 +452,11 @@ class PostCreator
end end
if @user.staged if @user.staged
TopicUser.auto_watch(@user.id, @topic.id) TopicUser.auto_notification_for_staging(@user.id, @topic.id, TopicUser.notification_reasons[:auto_watch])
elsif @user.user_option.notification_level_when_replying === NotificationLevels.topic_levels[:watching]
TopicUser.auto_notification(@user.id, @topic.id, TopicUser.notification_reasons[:created_post], NotificationLevels.topic_levels[:watching])
else else
TopicUser.auto_track(@user.id, @topic.id, TopicUser.notification_reasons[:created_post]) TopicUser.auto_notification(@user.id, @topic.id, TopicUser.notification_reasons[:created_post], NotificationLevels.topic_levels[:tracking])
end end
end end

View File

@ -798,6 +798,42 @@ describe PostCreator do
end end
end end
context "topic tracking" do
it "automatically watches topic based on preference" do
user.user_option.notification_level_when_replying = 3
admin = Fabricate(:admin)
topic = PostCreator.create(admin,
title: "this is the title of a topic created by an admin for watching notification",
raw: "this is the content of a topic created by an admin for keeping a watching notification state on a topic ;)"
)
post = PostCreator.create(user,
topic_id: topic.topic_id,
raw: "this is a reply to set the tracking state to watching ;)"
)
topic_user = TopicUser.find_by(user_id: user.id, topic_id: post.topic_id)
expect(topic_user.notification_level).to eq(TopicUser.notification_levels[:watching])
end
it "topic notification level remains tracking based on preference" do
user.user_option.notification_level_when_replying = 2
admin = Fabricate(:admin)
topic = PostCreator.create(admin,
title: "this is the title of a topic created by an admin for tracking notification",
raw: "this is the content of a topic created by an admin for keeping a tracking notification state on a topic ;)"
)
post = PostCreator.create(user,
topic_id: topic.topic_id,
raw: "this is a reply to set the tracking state to tracking ;)"
)
topic_user = TopicUser.find_by(user_id: user.id, topic_id: post.topic_id)
expect(topic_user.notification_level).to eq(TopicUser.notification_levels[:tracking])
end
end
describe '#create!' do describe '#create!' do
it "should return the post if it was successfully created" do it "should return the post if it was successfully created" do
title = "This is a valid title" title = "This is a valid title"

View File

@ -1246,6 +1246,7 @@ describe User do
SiteSetting.default_other_new_topic_duration_minutes = -1 # not viewed SiteSetting.default_other_new_topic_duration_minutes = -1 # not viewed
SiteSetting.default_other_auto_track_topics_after_msecs = 0 # immediately SiteSetting.default_other_auto_track_topics_after_msecs = 0 # immediately
SiteSetting.default_other_notification_level_when_replying = 3 # immediately
SiteSetting.default_other_external_links_in_new_tab = true SiteSetting.default_other_external_links_in_new_tab = true
SiteSetting.default_other_enable_quoting = false SiteSetting.default_other_enable_quoting = false
SiteSetting.default_other_dynamic_favicon = true SiteSetting.default_other_dynamic_favicon = true
@ -1273,6 +1274,7 @@ describe User do
expect(options.email_direct).to eq(false) expect(options.email_direct).to eq(false)
expect(options.new_topic_duration_minutes).to eq(-1) expect(options.new_topic_duration_minutes).to eq(-1)
expect(options.auto_track_topics_after_msecs).to eq(0) expect(options.auto_track_topics_after_msecs).to eq(0)
expect(options.notification_level_when_replying).to eq(3)
expect(CategoryUser.lookup(user, :watching).pluck(:category_id)).to eq([1]) expect(CategoryUser.lookup(user, :watching).pluck(:category_id)).to eq([1])
expect(CategoryUser.lookup(user, :tracking).pluck(:category_id)).to eq([2]) expect(CategoryUser.lookup(user, :tracking).pluck(:category_id)).to eq([2])

View File

@ -19,6 +19,7 @@ describe UserSerializer do
it "serializes options correctly" do it "serializes options correctly" do
# so we serialize more stuff # so we serialize more stuff
SiteSetting.default_other_auto_track_topics_after_msecs = 0 SiteSetting.default_other_auto_track_topics_after_msecs = 0
SiteSetting.default_other_notification_level_when_replying = 3
SiteSetting.default_other_new_topic_duration_minutes = 60*24 SiteSetting.default_other_new_topic_duration_minutes = 60*24
user = Fabricate.build(:user, user = Fabricate.build(:user,
@ -32,6 +33,7 @@ describe UserSerializer do
expect(json[:user_option][:dynamic_favicon]).to eq(true) expect(json[:user_option][:dynamic_favicon]).to eq(true)
expect(json[:user_option][:new_topic_duration_minutes]).to eq(60*24) expect(json[:user_option][:new_topic_duration_minutes]).to eq(60*24)
expect(json[:user_option][:auto_track_topics_after_msecs]).to eq(0) expect(json[:user_option][:auto_track_topics_after_msecs]).to eq(0)
expect(json[:user_option][:notification_level_when_replying]).to eq(3)
end end
end end

View File

@ -71,6 +71,7 @@ describe UserUpdater do
digest_after_minutes: "45", digest_after_minutes: "45",
new_topic_duration_minutes: 100, new_topic_duration_minutes: 100,
auto_track_topics_after_msecs: 101, auto_track_topics_after_msecs: 101,
notification_level_when_replying: 3,
email_in_reply_to: false email_in_reply_to: false
) )
expect(val).to be_truthy expect(val).to be_truthy
@ -83,6 +84,7 @@ describe UserUpdater do
expect(user.user_option.digest_after_minutes).to eq 45 expect(user.user_option.digest_after_minutes).to eq 45
expect(user.user_option.new_topic_duration_minutes).to eq 100 expect(user.user_option.new_topic_duration_minutes).to eq 100
expect(user.user_option.auto_track_topics_after_msecs).to eq 101 expect(user.user_option.auto_track_topics_after_msecs).to eq 101
expect(user.user_option.notification_level_when_replying).to eq 3
expect(user.user_option.email_in_reply_to).to eq false expect(user.user_option.email_in_reply_to).to eq false
end end