FEATURE: civilized mute
Allow user to mute all notifications generated by specific users
This commit is contained in:
parent
ff3e1e1dd7
commit
92e371f0b3
|
@ -189,7 +189,8 @@ const User = Discourse.Model.extend({
|
|||
'enable_quoting',
|
||||
'disable_jump_reply',
|
||||
'custom_fields',
|
||||
'user_fields');
|
||||
'user_fields',
|
||||
'muted_usernames');
|
||||
|
||||
['muted','watched','tracked'].forEach(function(s){
|
||||
var cats = self.get(s + 'Categories').map(function(c){ return c.get('id')});
|
||||
|
|
|
@ -237,6 +237,14 @@
|
|||
<div class="instructions">{{i18n 'user.muted_categories_instructions'}}</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group muting">
|
||||
<label class="control-label">{{i18n 'user.users'}}</label>
|
||||
<div class="controls category-controls">
|
||||
<label>{{i18n 'user.muted_users'}}</label>
|
||||
{{user-selector excludeCurrentUser=true usernames=muted_usernames class="user-selector"}}
|
||||
</div>
|
||||
<div class="instructions">{{i18n 'user.muted_users_instructions'}}</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
{{partial 'user/preferences/saveButton'}}
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
}
|
||||
|
||||
.user-preferences {
|
||||
input.category-group {
|
||||
width: 500px;
|
||||
input.category-group, input.user-selector {
|
||||
width: 530px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
|
@ -31,6 +31,8 @@
|
|||
height: 100px;
|
||||
}
|
||||
|
||||
input
|
||||
|
||||
input[type=text] {
|
||||
@include small-width {
|
||||
width: 450px;
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
class MutedUser < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
belongs_to :muted_user, class_name: 'User'
|
||||
end
|
|
@ -55,6 +55,9 @@ class User < ActiveRecord::Base
|
|||
has_many :group_managers, dependent: :destroy
|
||||
has_many :managed_groups, through: :group_managers, source: :group
|
||||
|
||||
has_many :muted_user_records, class_name: 'MutedUser'
|
||||
has_many :muted_users, through: :muted_user_records
|
||||
|
||||
has_one :user_search_data, dependent: :destroy
|
||||
has_one :api_key, dependent: :destroy
|
||||
|
||||
|
|
|
@ -95,7 +95,8 @@ class UserSerializer < BasicUserSerializer
|
|||
:custom_avatar_upload_id,
|
||||
:has_title_badges,
|
||||
:card_image_badge,
|
||||
:card_image_badge_id
|
||||
:card_image_badge_id,
|
||||
:muted_usernames
|
||||
|
||||
untrusted_attributes :bio_raw,
|
||||
:bio_cooked,
|
||||
|
@ -252,6 +253,10 @@ class UserSerializer < BasicUserSerializer
|
|||
CategoryUser.lookup(object, :watching).pluck(:category_id)
|
||||
end
|
||||
|
||||
def muted_usernames
|
||||
MutedUser.where(user_id: object.id).joins(:muted_user).pluck(:username)
|
||||
end
|
||||
|
||||
def include_private_message_stats?
|
||||
can_edit && !(omit_stats == true)
|
||||
end
|
||||
|
|
|
@ -86,6 +86,9 @@ class PostAlerter
|
|||
# Make sure the user can see the post
|
||||
return unless Guardian.new(user).can_see?(post)
|
||||
|
||||
# apply muting here
|
||||
return if post.user_id && MutedUser.where(user_id: user.id, muted_user_id: post.user_id).exists?
|
||||
|
||||
# skip if muted on the topic
|
||||
return if TopicUser.get(post.topic, user).try(:notification_level) == TopicUser.notification_levels[:muted]
|
||||
|
||||
|
|
|
@ -66,6 +66,11 @@ class UserUpdater
|
|||
end
|
||||
|
||||
User.transaction do
|
||||
|
||||
if attributes.key?(:muted_usernames)
|
||||
update_muted_users(attributes[:muted_usernames])
|
||||
end
|
||||
|
||||
user_profile.save && user.save
|
||||
end
|
||||
end
|
||||
|
@ -74,6 +79,29 @@ class UserUpdater
|
|||
|
||||
attr_reader :user, :guardian
|
||||
|
||||
def update_muted_users(usernames)
|
||||
usernames ||= ""
|
||||
desired_ids = User.where(username: usernames.split(",")).pluck(:id)
|
||||
if desired_ids.empty?
|
||||
MutedUser.where(user_id: user.id).destroy_all
|
||||
else
|
||||
MutedUser.where('id not in (?)', desired_ids).destroy_all
|
||||
|
||||
# SQL is easier here than figuring out how to do the same in AR
|
||||
MutedUser.exec_sql("INSERT into muted_users(user_id, muted_user_id, created_at, updated_at)
|
||||
SELECT :user_id, id, :now, :now
|
||||
FROM users
|
||||
WHERE
|
||||
id in (:desired_ids) AND
|
||||
id NOT IN (
|
||||
SELECT muted_user_id
|
||||
FROM muted_users
|
||||
WHERE user_id = :user_id
|
||||
)",
|
||||
now: Time.now, user_id: user.id, desired_ids: desired_ids)
|
||||
end
|
||||
end
|
||||
|
||||
def format_url(website)
|
||||
if website =~ /^http/
|
||||
website
|
||||
|
|
|
@ -360,6 +360,9 @@ en:
|
|||
delete_yourself_not_allowed: "You cannot delete your account right now. Contact an admin to do delete your account for you."
|
||||
unread_message_count: "Messages"
|
||||
admin_delete: "Delete"
|
||||
users: "Users"
|
||||
muted_users: "Muted"
|
||||
muted_users_instructions: "Suppress all notifications from these users."
|
||||
|
||||
staff_counters:
|
||||
flags_given: "helpful flags"
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
class AddMutedUsers < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :muted_users do |t|
|
||||
t.integer :user_id, null: false
|
||||
t.integer :muted_user_id, null: false
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_index :muted_users, [:user_id, :muted_user_id], unique: true
|
||||
add_index :muted_users, [:muted_user_id, :user_id], unique: true
|
||||
end
|
||||
end
|
|
@ -983,13 +983,32 @@ describe UsersController do
|
|||
let!(:user) { log_in(:user) }
|
||||
|
||||
it 'allows the update' do
|
||||
put :update, username: user.username, name: 'Jim Tom', custom_fields: {test: :it}
|
||||
|
||||
user2 = Fabricate(:user)
|
||||
user3 = Fabricate(:user)
|
||||
|
||||
put :update,
|
||||
username: user.username,
|
||||
name: 'Jim Tom',
|
||||
custom_fields: {test: :it},
|
||||
muted_usernames: "#{user2.username},#{user3.username}"
|
||||
|
||||
expect(response).to be_success
|
||||
|
||||
user.reload
|
||||
|
||||
expect(user.name).to eq 'Jim Tom'
|
||||
expect(user.custom_fields['test']).to eq 'it'
|
||||
expect(user.muted_users.pluck(:username).sort).to eq [user2.username,user3.username].sort
|
||||
|
||||
put :update,
|
||||
username: user.username,
|
||||
muted_usernames: ""
|
||||
|
||||
user.reload
|
||||
|
||||
expect(user.muted_users.pluck(:username).sort).to be_empty
|
||||
|
||||
end
|
||||
|
||||
context "with user fields" do
|
||||
|
|
|
@ -11,6 +11,15 @@ describe PostAlerter do
|
|||
|
||||
context 'quotes' do
|
||||
|
||||
it 'does not notify for muted users' do
|
||||
post = Fabricate(:post, raw: '[quote="EvilTrout, post:1"]whatup[/quote]')
|
||||
MutedUser.create!(user_id: evil_trout.id, muted_user_id: post.user_id)
|
||||
|
||||
lambda {
|
||||
PostAlerter.post_created(post)
|
||||
}.should change(evil_trout.notifications, :count).by(0)
|
||||
end
|
||||
|
||||
it 'notifies a user by username' do
|
||||
lambda {
|
||||
create_post_with_alerts(raw: '[quote="EvilTrout, post:1"]whatup[/quote]')
|
||||
|
|
Loading…
Reference in New Issue