Commit 25856a47 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets
Browse files

save each notification setting with ajax on change

parent 06f362320ca2
......@@ -20,3 +20,16 @@
border: 1px solid #ddd;
}
}
.save-status-fixed {
position: fixed;
left: 20px;
bottom: 50px;
}
.update-notifications {
margin-bottom: 0;
label {
margin-bottom: 0;
}
}
......@@ -3,11 +3,19 @@ class NotificationsController < ApplicationController
def show
@notification = current_user.notification
@projects = current_user.authorized_projects
@users_projects = current_user.users_projects
end
def update
current_user.notification_level = params[:notification_level]
@saved = current_user.save
type = params[:notification_type]
@saved = if type == 'global'
current_user.notification_level = params[:notification_level]
current_user.save
else
users_project = current_user.users_projects.find(params[:notification_id])
users_project.notification_level = params[:notification_level]
users_project.save
end
end
end
......@@ -5,26 +5,35 @@ class Notification
N_DISABLED = 0
N_PARTICIPATING = 1
N_WATCH = 2
N_GLOBAL = 3
attr_accessor :user
attr_accessor :target
def self.notification_levels
[N_DISABLED, N_PARTICIPATING, N_WATCH]
end
def initialize(user)
@user = user
def self.project_notification_levels
[N_DISABLED, N_PARTICIPATING, N_WATCH, N_GLOBAL]
end
def initialize(target)
@target = target
end
def disabled?
user.notification_level == N_DISABLED
target.notification_level == N_DISABLED
end
def participating?
user.notification_level == N_PARTICIPATING
target.notification_level == N_PARTICIPATING
end
def watch?
user.notification_level == N_WATCH
target.notification_level == N_WATCH
end
def global?
target.notification_level == N_GLOBAL
end
end
......@@ -19,6 +19,7 @@
# issues_tracker :string(255) default("gitlab"), not null
# issues_tracker_id :string(255)
# snippets_enabled :boolean default(TRUE), not null
# last_activity_at :datetime
#
require "grit"
......
......@@ -2,12 +2,13 @@
#
# Table name: users_projects
#
# id :integer not null, primary key
# user_id :integer not null
# project_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# project_access :integer default(0), not null
# id :integer not null, primary key
# user_id :integer not null
# project_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# project_access :integer default(0), not null
# notification_level :integer default(3), not null
#
class UsersProject < ActiveRecord::Base
......@@ -29,6 +30,7 @@ class UsersProject < ActiveRecord::Base
validates :user_id, uniqueness: { scope: [:project_id], message: "already exists in project" }
validates :project_access, inclusion: { in: [GUEST, REPORTER, DEVELOPER, MASTER] }, presence: true
validates :project, presence: true
validates :notification_level, inclusion: { in: Notification.project_notification_levels }, presence: true
delegate :name, :username, :email, to: :user, prefix: true
......@@ -134,4 +136,8 @@ def repo_access_human
def skip_git?
!!@skip_git
end
def notification
@notification ||= Notification.new(self)
end
end
......@@ -80,7 +80,7 @@ def close_mr(merge_request, current_user)
# * project team members with notification level higher then Participating
#
def merge_mr(merge_request)
recipients = reject_muted_users([merge_request.author, merge_request.assignee])
recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.project)
recipients = recipients.concat(project_watchers(merge_request.project)).uniq
recipients.each do |recipient|
......@@ -122,7 +122,7 @@ def new_note(note)
recipients = recipients.concat(project_watchers(note.project)).compact.uniq
# Reject mutes users
recipients = reject_muted_users(recipients)
recipients = reject_muted_users(recipients, note.project)
# Reject author
recipients.delete(note.author)
......@@ -147,19 +147,41 @@ def update_team_member(users_project)
# Get project users with WATCH notification level
def project_watchers(project)
project.users.where(notification_level: Notification::N_WATCH)
# Get project notification settings since it has higher priority
user_ids = project.users_projects.where(notification_level: Notification::N_WATCH).pluck(:user_id)
project_watchers = User.where(id: user_ids)
# next collect users who use global settings with watch state
user_ids = project.users_projects.where(notification_level: Notification::N_GLOBAL).pluck(:user_id)
project_watchers += User.where(id: user_ids, notification_level: Notification::N_WATCH)
project_watchers.uniq
end
# Remove users with disabled notifications from array
# Also remove duplications and nil recipients
def reject_muted_users(users)
users.compact.uniq.reject do |user|
user.notification.disabled?
def reject_muted_users(users, project = nil)
users = users.compact.uniq
users.reject do |user|
next user.notification.disabled? unless project
tm = project.users_projects.find_by_user_id(user.id)
# reject users who globally disabled notification and has no membership
next user.notification.disabled? unless tm
# reject users who disabled notification in project
next true if tm.notification.disabled?
# reject users who have N_GLOBAL in project and disabled in global settings
tm.notification.global? && user.notification.disabled?
end
end
def new_resource_email(target, method)
recipients = reject_muted_users([target.assignee])
recipients = reject_muted_users([target.assignee], target.project)
recipients = recipients.concat(project_watchers(target.project)).uniq
recipients.delete(target.author)
......@@ -169,7 +191,7 @@ def new_resource_email(target, method)
end
def close_resource_email(target, current_user, method)
recipients = reject_muted_users([target.author, target.assignee])
recipients = reject_muted_users([target.author, target.assignee], target.project)
recipients = recipients.concat(project_watchers(target.project)).uniq
recipients.delete(current_user)
......@@ -185,7 +207,7 @@ def reassign_resource_email(target, current_user, method)
recipients = recipients.concat(project_watchers(target.project))
# reject users with disabled notifications
recipients = reject_muted_users(recipients)
recipients = reject_muted_users(recipients, target.project)
# Reject me from recipients if I reassign an item
recipients.delete(current_user)
......
......@@ -13,56 +13,65 @@
&ndash; You will receive all notifications from projects in which you participate
%hr
= form_tag profile_notifications_path, method: :put, remote: true, class: 'update-notifications' do
%ul.well-list
.row
.span4
%h5 Global
.span7
= form_tag profile_notifications_path, method: :put, remote: true, class: 'update-notifications' do
= hidden_field_tag :notification_type, 'global'
= label_tag do
= radio_button_tag :notification_level, Notification::N_DISABLED, @notification.disabled?, class: 'trigger-submit'
%span Disabled
= label_tag do
= radio_button_tag :notification_level, Notification::N_PARTICIPATING, @notification.participating?, class: 'trigger-submit'
%span Participating
= label_tag do
= radio_button_tag :notification_level, Notification::N_WATCH, @notification.watch?, class: 'trigger-submit'
%span Watch
%hr
= link_to '#', class: 'js-toggle-visibility-link' do
%h6.btn.btn-tiny
%i.icon-chevron-down
%span Per project notifications settings
%ul.well-list.js-toggle-visibility-container.hide
- @users_projects.each do |users_project|
- notification = Notification.new(users_project)
%li
.row
.span4
%h5 Global
%span
= link_to_project(users_project.project)
.span7
= label_tag do
= radio_button_tag :notification_level, Notification::N_DISABLED, @notification.disabled?
%span Disabled
= label_tag do
= radio_button_tag :notification_level, Notification::N_PARTICIPATING, @notification.participating?
%span Participating
= label_tag do
= radio_button_tag :notification_level, Notification::N_WATCH, @notification.watch?
%span Watch
= form_tag profile_notifications_path, method: :put, remote: true, class: 'update-notifications' do
= hidden_field_tag :notification_type, 'project', id: dom_id(users_project, 'notification_type')
= hidden_field_tag :notification_id, users_project.id, id: dom_id(users_project, 'notification_id')
= label_tag do
= radio_button_tag :notification_level, Notification::N_GLOBAL, notification.global?, id: dom_id(users_project, 'notification_level'), class: 'trigger-submit'
%span Use global settings
= link_to '#', class: 'js-toggle-visibility-link' do
%h6.btn.btn-tiny
%i.icon-chevron-down
%span Per project notifications settings
%ul.well-list.js-toggle-visibility-container.hide
- @projects.each do |project|
%li
.row
.span4
%span
= project.name_with_namespace
.span7
= label_tag do
= radio_button_tag :"notification_level[#{project.id}]", Notification::N_DISABLED, @notification.disabled?, disabled: true
= radio_button_tag :notification_level, Notification::N_DISABLED, notification.disabled?, id: dom_id(users_project, 'notification_level'), class: 'trigger-submit'
%span Disabled
= label_tag do
= radio_button_tag :"notification_level[#{project.id}]", Notification::N_PARTICIPATING, @notification.participating?, disabled: true
= radio_button_tag :notification_level, Notification::N_PARTICIPATING, notification.participating?, id: dom_id(users_project, 'notification_level'), class: 'trigger-submit'
%span Participating
= label_tag do
= radio_button_tag :"notification_level[#{project.id}]", Notification::N_WATCH, @notification.watch?, disabled: true
= radio_button_tag :notification_level, Notification::N_WATCH, notification.watch?, id: dom_id(users_project, 'notification_level'), class: 'trigger-submit'
%span Watch
.form-actions
= submit_tag 'Save', class: 'btn btn-save'
%span.update-success.cgreen.hide
%i.icon-ok
Saved
%span.update-failed.cred.hide
%i.icon-remove
Failed
.save-status-fixed
%span.update-success.cgreen.hide
%i.icon-ok
Saved
%span.update-failed.cred.hide
%i.icon-remove
Failed
- if @saved
:plain
$('.update-notifications .update-success').showAndHide();
$('.save-status-fixed .update-success').showAndHide();
- else
:plain
$('.update-notifications .update-failed').showAndHide();
$('.save-status-fixed .update-failed').showAndHide();
......@@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20130325173941) do
ActiveRecord::Schema.define(:version => 20130404164628) do
create_table "events", :force => true do |t|
t.string "target_type"
......@@ -156,9 +156,11 @@
t.string "issues_tracker", :default => "gitlab", :null => false
t.string "issues_tracker_id"
t.boolean "snippets_enabled", :default => true, :null => false
t.datetime "last_activity_at"
end
add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id"
add_index "projects", ["last_activity_at"], :name => "index_projects_on_last_activity_at"
add_index "projects", ["namespace_id"], :name => "index_projects_on_namespace_id"
create_table "protected_branches", :force => true do |t|
......@@ -281,11 +283,12 @@
add_index "users", ["username"], :name => "index_users_on_username"
create_table "users_projects", :force => true do |t|
t.integer "user_id", :null => false
t.integer "project_id", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "project_access", :default => 0, :null => false
t.integer "user_id", :null => false
t.integer "project_id", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "project_access", :default => 0, :null => false
t.integer "notification_level", :default => 3, :null => false
end
add_index "users_projects", ["project_access"], :name => "index_users_projects_on_project_access"
......
......@@ -19,6 +19,7 @@
# issues_tracker :string(255) default("gitlab"), not null
# issues_tracker_id :string(255)
# snippets_enabled :boolean default(TRUE), not null
# last_activity_at :datetime
#
require 'spec_helper'
......
......@@ -2,12 +2,13 @@
#
# Table name: users_projects
#
# id :integer not null, primary key
# user_id :integer not null
# project_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# project_access :integer default(0), not null
# id :integer not null, primary key
# user_id :integer not null
# project_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# project_access :integer default(0), not null
# notification_level :integer default(3), not null
#
require 'spec_helper'
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment