FEATURE: civilized mute

Allow user to mute all notifications generated by specific users
This commit is contained in:
Sam 2015-03-24 11:55:22 +11:00
parent ff3e1e1dd7
commit 92e371f0b3
12 changed files with 102 additions and 5 deletions

View file

@ -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')});

View file

@ -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'}}

View file

@ -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;

4
app/models/muted_user.rb Normal file
View file

@ -0,0 +1,4 @@
class MutedUser < ActiveRecord::Base
belongs_to :user
belongs_to :muted_user, class_name: 'User'
end

View file

@ -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

View file

@ -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

View file

@ -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]

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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]')