System Notifications - View All Page [SCI-2956 and SCI-3001] (#1482)

* System notification view and notification partial (SCI 2956 and SCI 3001)
This commit is contained in:
aignatov-bio 2019-02-13 13:06:14 +01:00 committed by GitHub
parent 3538e74b03
commit 5c85595ab4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 312 additions and 4 deletions

View file

@ -10,6 +10,7 @@ gem 'devise', '~> 4.3.0'
gem 'devise_invitable'
gem 'figaro'
gem 'pg', '~> 0.18'
gem 'pg_search' # PostgreSQL full text search
gem 'rails', '5.1.6'
gem 'recaptcha', require: 'recaptcha/rails'
gem 'sanitize', '~> 4.4'

View file

@ -346,6 +346,9 @@ GEM
parser (2.5.1.2)
ast (~> 2.4.0)
pg (0.21.0)
pg_search (2.1.4)
activerecord (>= 4.2)
activesupport (>= 4.2)
phantomjs (2.1.1.0)
poltergeist (1.17.0)
capybara (~> 2.1)
@ -599,6 +602,7 @@ DEPENDENCIES
overcommit
paperclip (~> 5.3)
pg (~> 0.18)
pg_search
phantomjs
poltergeist
pry
@ -641,7 +645,7 @@ DEPENDENCIES
yomu
RUBY VERSION
ruby 2.4.4p296
ruby 2.4.5p335
BUNDLED WITH
1.17.1
1.17.2

View file

@ -0,0 +1,39 @@
'use strict';
// update selected notiifcations
function SystemNotificationsMarkAsSeen() {
var WindowSize = $(window).height();
var NotificaitonSize = 75;
var NotificationsToUpdate = [];
_.each($('.system-notification[data-new="1"]'), function(el) {
var NotificationTopPosition = el.getBoundingClientRect().top;
if (NotificationTopPosition > 0 && NotificationTopPosition < (WindowSize - NotificaitonSize)) {
NotificationsToUpdate.push(el.dataset.systemNotificationId);
el.dataset.new = 0;
}
});
if (NotificationsToUpdate.length > 0) {
$.post('system_notifications/mark_as_seen', { notifications: JSON.stringify(NotificationsToUpdate) });
}
}
function initSystemNotificationsButton() {
$('.btn-more-notifications')
.on('ajax:success', function(e, data) {
$(data.html).insertAfter($('.notifications-container .system-notification').last());
if (data.more_url) {
$(this).attr('href', data.more_url);
} else {
$(this).remove();
}
});
}
(function() {
$(window).scroll(function() {
SystemNotificationsMarkAsSeen();
});
initSystemNotificationsButton();
SystemNotificationsMarkAsSeen();
}());

View file

@ -0,0 +1,103 @@
@import "constants";
@import "mixins";
// System notification partial
.system-notification {
border-bottom: 1px solid $color-gainsboro;
display: inline-block;
margin-bottom: 5px;
min-height: 70px;
padding-bottom: 5px;
position: relative;
width: 100%;
.status-block {
float: left;
height: 0;
width: 60px;
.status-icon {
background: $brand-success;
border-radius: 15px;
height: 30px;
line-height: 32px;
margin: 15px 20px;
text-align: center;
width: 30px;
&.seen {
background: $color-gainsboro;
}
i {
color: $color-white;
font-size: 18px;
margin-top: 5px;
}
}
}
.body-block {
float: right;
width: calc(100% - 70px);
.datetime {
font-size: 12px;
line-height: 20px;
opacity: .7;
}
.title {
line-height: 20px;
margin: 0;
}
.message {
line-height: 20px;
}
}
}
// Global system notification
#system-notifications-index {
#search-bar-notifications {
border-bottom: 1px solid $color-gainsboro;
margin: 10px 0;
padding: 0 0 10px;
width: 100%;
.form-group {
width: 100%;
.input-group {
max-width: 600px;
width: 100%;
}
}
}
.no-notification-meessage {
padding: 20px 0;
}
.title-container {
border-bottom: 1px solid $color-gainsboro;
left: -20px;
margin: 0;
padding-bottom: 10px;
padding-left: 20px;
position: relative;
width: calc(100% + 40px);
a {
font-size: 14px;
margin-left: 30px;
}
}
.btn-more-notifications {
margin-top: 0;
}
}

View file

@ -0,0 +1,53 @@
# frozen_string_literal: true
class SystemNotificationsController < ApplicationController
before_action :prepare_notifications, only: :index
def index
respond_to do |format|
format.json do
render json: {
more_url: @system_notifications.fetch(:more_notifications_url),
html: render_to_string(
partial: 'list.html.erb', locals: @system_notifications
)
}
end
format.html
end
end
# Update seen_at parameter for system notifications
def mark_as_seen
notifications = JSON.parse(params[:notifications])
current_user.user_system_notifications.mark_as_seen(notifications)
render json: { result: 'ok' }
rescue StandardError
render json: { result: 'failed' }
end
private
def prepare_notifications
page = (params[:page] || 1).to_i
query = params[:search_queue]
per_page = Constants::ACTIVITY_AND_NOTIF_SEARCH_LIMIT
notifications = SystemNotification.last_notifications(current_user, query)
.page(page)
.per(per_page)
unless notifications.blank? || notifications.last_page?
more_url = url_for(
system_notifications_url(
format: :json,
page: page + 1,
search_queue: query
)
)
end
@system_notifications = {
notifications: notifications,
more_notifications_url: more_url
}
end
end

View file

@ -1,10 +1,40 @@
# frozen_string_literal: true
class SystemNotification < ApplicationRecord
include PgSearch
# Full text postgreSQL search configuration
pg_search_scope :search_notifications, against: %i(title description),
using: {
tsearch: {
dictionary: 'english'
}
}
# ignoring: :accents
has_many :user_system_notifications
has_many :users, through: :user_system_notifications
validates :title, :modal_title, :modal_body, :description,
:source_created_at, :source_id, :last_time_changed_at,
presence: true
validates :title, :description,
length: { maximum: Constants::NAME_MAX_LENGTH }
def self.last_notifications(
user,
query = nil
)
notifications = order(last_time_changed_at: :DESC)
notifications = notifications.search_notifications(query) if query.present?
notifications.joins(:user_system_notifications)
.where('user_system_notifications.user_id = ?', user.id)
.select(
'system_notifications.id',
:title,
:description,
:last_time_changed_at,
'user_system_notifications.seen_at'
)
end
end

View file

@ -3,4 +3,9 @@
class UserSystemNotification < ApplicationRecord
belongs_to :user
belongs_to :system_notification
def self.mark_as_seen(notifications)
where(system_notification_id: notifications)
.update_all(seen_at: Time.now)
end
end

View file

@ -0,0 +1,3 @@
<% notifications.each do |notification| %>
<%= render partial: 'notification.html.erb', locals: { notification: notification} %>
<% end %>

View file

@ -0,0 +1,22 @@
<div
class="system-notification"
data-new="<%= notification.seen_at ? 0 : 1 %>"
data-system-notification-id="<%= notification.id %>"
>
<div class="status-block">
<div class="status-icon <%= notification.seen_at ? "seen" : "" %>">
<i class="fas fa-gift"></i>
</div>
</div>
<div class="body-block">
<div class="datetime">
<span><%= l(notification.last_time_changed_at, format: :full) %></span>
</div>
<h5 class="title">
<strong><%= notification.title %></strong>
</h5>
<div class="message">
<span><%= notification.description %></span>
</div>
</div>
</div>

View file

@ -0,0 +1,34 @@
<% provide(:head_title, t("system_notifications.index.whats_new")) %>
<div class="content-pane" id="system-notifications-index">
<h3 class="title-container">
<strong><%= t("system_notifications.index.whats_new") %></strong>
<%= link_to t("system_notifications.index.settings"), nil %>
</h3>
<%= form_tag system_notifications_path, method: :get, id: "search-bar-notifications", class: "navbar-form navbar-left", role: "search" do %>
<div class="form-group">
<div class="input-group">
<input class="form-control" type="text" name="search_queue" placeholder="<%= t("nav.label.search") %>" value="<%= params[:search_queue] %>" />
<span class="input-group-btn">
<button id="search-button" class="btn btn-default" type="submit">
<span class="fas fa-search"></span>
</button>
</span>
</div>
</div>
<% end %>
<div class="notifications-container">
<%= render partial: "list", locals: { notifications: @system_notifications[:notifications] } %>
</div>
<div class="text-center">
<% if @system_notifications[:more_notifications_url] && @system_notifications[:notifications].present? %>
<a class="btn btn-default btn-more-notifications"
href="<%= @system_notifications[:more_notifications_url] %>"
data-remote="true">
<%= t("system_notifications.index.more_notifications") %></a>
<% else %>
<span class="no_notification_meessage"><%= t("system_notifications.index.no_notifications") %></span>
<% end %>
</div>
</div>
<%= javascript_include_tag "system_notifications/index.js" %>

View file

@ -77,6 +77,7 @@ Rails.application.config.assets.precompile += %w(assets.js)
Rails.application.config.assets.precompile += %w(comments.js)
Rails.application.config.assets.precompile += %w(projects/show.js)
Rails.application.config.assets.precompile += %w(notifications.js)
Rails.application.config.assets.precompile += %w(system_notifications/index.js)
Rails.application.config.assets.precompile += %w(users/invite_users_modal.js)
# Rails.application.config.assets.precompile += %w(samples/sample_types_groups.js)
Rails.application.config.assets.precompile += %w(highlightjs-github-theme.css)

View file

@ -1291,7 +1291,12 @@ en:
color_label: "Sample group color"
create:
success_flash: "Successfully added sample group <strong>%{sample_group}</strong> to team <strong>%{team}</strong>."
system_notifications:
index:
whats_new: "What's new"
more_notifications: "More system notifications"
no_notifications: "No system notifications"
settings: "Settings"
activities:
index:
today: "Today"

View file

@ -1,4 +1,5 @@
Rails.application.routes.draw do
use_doorkeeper do
skip_controllers :applications, :authorized_applications, :token_info
end
@ -424,6 +425,13 @@ Rails.application.routes.draw do
end
end
# System notifications routes
resources :system_notifications, only: [:index] do
collection do
post 'mark_as_seen'
end
end
# tinyMCE image uploader endpoint
post '/tinymce_assets', to: 'tiny_mce_assets#create', as: :tiny_mce_assets

View file

@ -23,4 +23,4 @@ if User.count.zero?
[],
Extends::INITIAL_USER_OPTIONS
)
end
end