Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Snooze notifications #1858

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .rubocop_todo.yml
Expand Up @@ -40,7 +40,7 @@ Metrics/ClassLength:

# Offense count: 9
Metrics/CyclomaticComplexity:
Max: 28
Max: 30

# Offense count: 2
# Configuration parameters: CountComments, ExcludedMethods.
Expand Down
1 change: 1 addition & 0 deletions app/assets/javascripts/application.js
Expand Up @@ -45,6 +45,7 @@ $(document).on('click', 'button.delete', Octobox.deleteThread);
$(document).on('click', 'button.delete_selected', Octobox.deleteSelected);
$(document).on('click', 'button.mark_read_selected', Octobox.markReadSelected);
$(document).on('click', 'button.closethread', Octobox.closeThread);
$(document).on('click', '.submit_snooze_selected', Octobox.snoozeSelected);

$(document).on('click', 'tr.notification', Octobox.moveCursorToClickedRow);
$(document).on('click', '[data-toggle="offcanvas"]', Octobox.toggleOffCanvas);
Expand Down
22 changes: 19 additions & 3 deletions app/assets/javascripts/octobox.js
Expand Up @@ -320,7 +320,7 @@ var Octobox = (function() {

var changeArchive = function() {
if ( hasMarkedRows() ) {
$("button.archive_selected, button.unarchive_selected, button.mute_selected, button.delete_selected").show().css("display", "inline-block");
$("button.archive_selected, button.unarchive_selected, button.mute_selected, button.delete_selected, .snooze_selected").show().css("display", "inline-block");
if ( !hasMarkedRows(true) ) {
$(".js-select_all").prop("checked", true).prop("indeterminate", false);
$("button.select_all").show().css("display", "inline-block");
Expand All @@ -330,7 +330,7 @@ var Octobox = (function() {
}
} else {
$(".js-select_all").prop("checked", false).prop("indeterminate", false);
$("button.archive_selected, button.unarchive_selected, button.mute_selected, button.select_all, button.delete_selected").hide();
$("button.archive_selected, button.unarchive_selected, button.mute_selected, button.select_all, button.delete_selected, .snooze_selected").hide();
}
var marked_unread_length = getMarkedRows().filter(".active").length;
if ( marked_unread_length > 0 ) {
Expand Down Expand Up @@ -463,6 +463,21 @@ var Octobox = (function() {
return false;
}

var snoozeSelected = function(value){
console.log('snooze')
if (getDisplayedRows().length === 0) return;

var ids = getIdsFromRows(getMarkedOrCurrentRows());
console.log(getMarkedOrCurrentRows())
var duration = $(this).data('duration')

$.post( "/notifications/snooze_selected" + location.search, { "id[]": ids, "duration": duration } ).done(function() {
resetCursorAfterRowsRemoved(ids);
updateFavicon();
});
return false;
}

// private methods

var getDisplayedRows = function() {
Expand Down Expand Up @@ -667,6 +682,7 @@ var Octobox = (function() {
deleteSelected: deleteSelected,
deleteThread: deleteThread,
viewThread: viewThread,
expandComments: expandComments
expandComments: expandComments,
snoozeSelected: snoozeSelected
}
})();
24 changes: 24 additions & 0 deletions app/controllers/notifications_controller.rb
Expand Up @@ -236,6 +236,24 @@ def delete_selected
end
end

# Snooze a notification
#
# :category: Notifications Actions
#
# ==== Example
#
# <code>POST notifications/:id/snooze_selected.json</code>
# HEAD 204
#
def snooze_selected
Notification.snooze(selected_notifications, snooze_params)
if request.xhr?
head :ok
else
redirect_back fallback_location: root_path
end
end

# Star a notification
#
# :category: Notifications Actions
Expand Down Expand Up @@ -370,6 +388,8 @@ def notifications_for_presentation
scope.starred
elsif params[:archive].present?
scope.archived
elsif params[:snoozed].present?
scope.snoozed
else
scope.inbox
end
Expand Down Expand Up @@ -422,4 +442,8 @@ def per_page_param
def per_page_cookie
Integer(cookies[:per_page]) rescue nil
end

def snooze_params
params.permit(:id, :duration, :id => [])
end
end
21 changes: 17 additions & 4 deletions app/helpers/notifications_helper.rb
Expand Up @@ -58,11 +58,12 @@ def filters
assigned: params[:assigned],
is_private: params[:is_private],
status: params[:status],
snoozed: params[:snoozed]
}
end

def inbox_selected?
!archive_selected? && !starred_selected? && !showing_search_results?
!archive_selected? && !starred_selected? && !showing_search_results? && !snoozed_selected?
end

def archive_selected?
Expand All @@ -73,6 +74,10 @@ def starred_selected?
filters[:starred].present?
end

def snoozed_selected?
filters[:snoozed].present?
end

def showing_search_results?
filters[:q].present?
end
Expand All @@ -86,7 +91,7 @@ def notification_param_keys
end

def bucket_param_keys
[:archive, :starred]
[:archive, :starred, :snoozed]
end

def filter_param_keys
Expand Down Expand Up @@ -117,6 +122,10 @@ def unarchive_button
function_button("Unarchive", 'inbox', "archive_toggle unarchive", 'Unarchive notification', false)
end

def snooze_button
function_button("Snooze", 'clock', "dropdown-toggle", 'Select snooze time', false, data_toggle: 'dropdown')
end

def mute_selected_button
function_button('Mute selected', 'mute', 'mute_selected', 'Mute selected items')
end
Expand All @@ -137,6 +146,10 @@ def delete_selected_button
function_button("Delete selected", 'trashcan', "delete_selected", 'Delete selected items')
end

def snooze_selected_button
function_button("Snooze selected", 'clock', "dropdown-toggle", 'Select snooze time', false, data_toggle: 'dropdown')
end

def select_all_button(cur_selected, total)
button_tag(type: 'button', class: "select_all btn btn-sm btn-outline-dark hidden-button", 'data-toggle': "tooltip", 'data-placement': "bottom", 'title': "Number of items selected") do
octicon('check', height: 16) +
Expand All @@ -146,8 +159,8 @@ def select_all_button(cur_selected, total)
end if cur_selected < total
end

def function_button(title, octicon, css_class, tooltip, hidden=true)
button_tag(type: 'button', class: "#{css_class} btn btn-sm btn-outline-dark #{'hidden-button' if hidden}", 'data-toggle': "tooltip", 'data-placement': "bottom", 'title': tooltip ) do
def function_button(title, octicon, css_class, tooltip, hidden=true, data_toggle: 'tooltip', data_content: nil)
button_tag(type: 'button', class: "#{css_class} btn btn-sm btn-outline-dark #{'hidden-button' if hidden}", 'data-toggle': data_toggle, 'data-placement': "bottom", 'title': tooltip, 'data-content': data_content ) do
octicon(octicon, height: 16) + content_tag(:span, "#{title}", class: 'd-none d-xl-inline-block ml-1')
end
end
Expand Down
12 changes: 12 additions & 0 deletions app/models/notification.rb
Expand Up @@ -130,6 +130,18 @@ def self.mute_on_github(user, notification_ids)
end
end

def self.snooze(notifications, snooze_params)
return if snooze_params[:duration].to_i.zero?
notifications.update_all(
snooze_until: Time.now.advance(:seconds => snooze_params[:duration].to_i)
)
mark_read(notifications)
end

def snoozed?
snooze_until.present? && snooze_until > Time.now
end

def expanded_subject_url
return subject_url unless display_subject?
subject.try(:html_url) || subject_url # Use the sync'd HTML URL if possible, else the API one
Expand Down
14 changes: 10 additions & 4 deletions app/models/search.rb
Expand Up @@ -31,7 +31,9 @@ def results
res = res.exclude_status(exclude_status) if exclude_status.present?
res = res.starred(starred) unless starred.nil?
res = res.archived(archived) unless archived.nil?
res = res.snoozed(snoozed) unless snoozed.nil?
res = res.archived(!inbox) unless inbox.nil?
res = res.snoozed(!inbox) unless inbox.nil?
res = res.unread(unread) unless unread.nil?
res = res.bot_author(bot_author) unless bot_author.nil?
res = res.unlabelled unless unlabelled.nil?
Expand All @@ -52,22 +54,22 @@ def to_query
end

def inbox_selected?
inbox == true && archived != true
inbox == true && archived != true && snoozed != true
end

def archive_selected?
inbox != true && archived == true
inbox != true && archived == true && snoozed != true
end

private

def convert(params)
[:starred, :unlabelled, :bot].each do |param|
[:starred, :unlabelled, :bot, :snoozed].each do |param|
@parsed_query[param] = ['true'] if params[param].present?
end

@parsed_query[:archived] = ['true'] if params[:archive].present?
@parsed_query[:inbox] = ['true'] if params[:archive].blank? && params[:starred].blank? && params[:q].blank?
@parsed_query[:inbox] = ['true'] if params[:archive].blank? && params[:starred].blank? && params[:snoozed].blank? && params[:q].blank?

[:reason, :type, :unread, :state, :is_private, :draft].each do |filter|
next if params[filter].blank?
Expand Down Expand Up @@ -159,6 +161,10 @@ def exclude_author
parsed_query[:'-author']
end

def snoozed
boolean_prefix(:snoozed)
end

def unread
boolean_prefix(:unread)
end
Expand Down
6 changes: 6 additions & 0 deletions app/views/notifications/_list.html.erb
Expand Up @@ -31,6 +31,12 @@
<% end %>
<%= archive_selected_button unless archive_selected? %>
<%= unarchive_selected_button unless inbox_selected? %>

<div class="btn-group snooze_selected hidden-button">
<%= snooze_selected_button %>
<%= render 'snooze' %>
</div>

<%= mute_selected_button %>
<%= mark_read_selected_button %>
<%= delete_selected_button unless inbox_selected? %>
Expand Down
10 changes: 10 additions & 0 deletions app/views/notifications/_notification.html.erb
Expand Up @@ -20,6 +20,8 @@
<td class='notification-archived align-middle'>
<% if notification.archived? %>
<%= octicon 'archive', height: 16, data: { toggle: 'tooltip' }, title: 'Archived' %>
<% elsif notification.snoozed? %>
<%= octicon 'clock', height: 16, data: { toggle: 'tooltip' }, title: "Snoozed for #{distance_of_time_in_words(Time.now, notification.snooze_until) }" %>
<% end %>
</td>

Expand Down Expand Up @@ -126,6 +128,14 @@
<%= octicon('archive', height: 15) %>
<% end %>
<% end %>

<div class="btn-group align-top">
<a class='mr-1 mt-0' data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<%= octicon('clock', height: 15) %>
</a>
<%= render 'notifications/snooze' %>
</div>

<%= link_to(mute_selected_notifications_path(id: notification.id), method: :post, 'data-toggle': "tooltip", 'data-placement': "bottom", 'title': 'Mute', 'data-confirm': "Are you sure you want to mute?") do %>
<%= octicon('mute', height: 15) %>
<% end %>
Expand Down
9 changes: 9 additions & 0 deletions app/views/notifications/_sidebar.html.erb
Expand Up @@ -26,6 +26,15 @@
<% end %>
<% end %>
</li>
<li role="presentation" class="nav-item">
<%= link_to root_path(snoozed: true, per_page: params[:per_page]), { :class=>"nav-link #{'active' if snoozed_selected?}" } do %>
<%= octicon 'clock', height: 16, class: 'sidebar-icon' %>
Snoozed
<% if snoozed_selected? %>
<span class="badge badge-light"><%= @unread_notifications.sum(&:last) %></span>
<% end %>
<% end %>
</li>
</ul>

<% if current_user.pinned_searches.present? || any_active_filters? %>
Expand Down
6 changes: 6 additions & 0 deletions app/views/notifications/_snooze.html.erb
@@ -0,0 +1,6 @@
<ul class="dropdown-menu">
<li class="dropdown-item"><a href="#" class='submit_snooze_selected' data-duration="<%= 1.hour %>">One Hour</a></li>
<li class="dropdown-item"><a href="#" class='submit_snooze_selected' data-duration="<%= 1.day %>">One Day</a></li>
<li class="dropdown-item"><a href="#" class='submit_snooze_selected' data-duration="<%= 1.week %>">One Week</a></li>
<li class="dropdown-item"><a href="#" class='submit_snooze_selected' data-duration="<%= 1.month %>">One Month</a></li>
</ul>
6 changes: 5 additions & 1 deletion app/views/notifications/_thread.html.erb
Expand Up @@ -5,6 +5,10 @@
</button>
<%= archive_button unless archive_selected? %>
<%= unarchive_button unless inbox_selected? %>
<div class="btn-group snooze_selected">
<%= snooze_button %>
<%= render 'notifications/snooze' %>
</div>
<%= mute_button %>
<%= delete_button %>
</div>
Expand Down Expand Up @@ -69,7 +73,7 @@
<%= render partial: 'comment', locals:{comment: comment, index: index} %>
<% end %>
<% end %>

</div>

<% if current_user.can_comment?(@notification.subject)%>
Expand Down
6 changes: 6 additions & 0 deletions app/views/shared/_search_prefixes.html.erb
Expand Up @@ -148,5 +148,11 @@
</td>
<td>Search for only draft pull requests. Also accepts false for the inverse.</td>
</tr>
<tr>
<td>
<code>snoozed:true</code>
</td>
<td>Search notifications that you've snoozed. Also accepts false for the inverse.</td>
</tr>
</tbody>
</table>
3 changes: 2 additions & 1 deletion config/routes.rb
Expand Up @@ -45,13 +45,14 @@
post :mark_read_selected
get :unread_count
post :delete_selected
post :snooze_selected
end

member do
get :show
post :star
get :expand_comments
post :comment
post :comment
end
end

Expand Down
@@ -0,0 +1,5 @@
class AddSnoozeUntilToNotifications < ActiveRecord::Migration[5.2]
def change
add_column :notifications, :snooze_until, :datetime
end
end
7 changes: 4 additions & 3 deletions db/schema.rb
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2019_04_06_173231) do
ActiveRecord::Schema.define(version: 2019_05_27_194557) do

# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
Expand Down Expand Up @@ -70,15 +70,16 @@
t.string "subject_type"
t.string "reason"
t.boolean "unread"
t.datetime "updated_at", null: false
t.string "last_read_at"
t.string "url"
t.boolean "archived", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "starred", default: false
t.string "repository_owner_name", default: ""
t.string "latest_comment_url"
t.datetime "muted_at"
t.datetime "snooze_until"
t.index ["muted_at"], name: "index_notifications_on_muted_at"
t.index ["repository_full_name"], name: "index_notifications_on_repository_full_name"
t.index ["subject_url"], name: "index_notifications_on_subject_url"
Expand Down Expand Up @@ -171,7 +172,7 @@
t.string "encrypted_app_token"
t.string "encrypted_app_token_iv"
t.string "theme", default: "light"
t.boolean "display_comments", default: false
t.boolean "display_comments", default: true
t.index ["api_token"], name: "index_users_on_api_token", unique: true
t.index ["github_id"], name: "index_users_on_github_id", unique: true
end
Expand Down