mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-12-25 17:24:51 +08:00
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:
parent
3538e74b03
commit
5c85595ab4
14 changed files with 312 additions and 4 deletions
1
Gemfile
1
Gemfile
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
39
app/assets/javascripts/system_notifications/index.js
Normal file
39
app/assets/javascripts/system_notifications/index.js
Normal 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();
|
||||
}());
|
103
app/assets/stylesheets/system_notifications.scss
Normal file
103
app/assets/stylesheets/system_notifications.scss
Normal 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;
|
||||
}
|
||||
}
|
53
app/controllers/system_notifications_controller.rb
Normal file
53
app/controllers/system_notifications_controller.rb
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
3
app/views/system_notifications/_list.html.erb
Normal file
3
app/views/system_notifications/_list.html.erb
Normal file
|
@ -0,0 +1,3 @@
|
|||
<% notifications.each do |notification| %>
|
||||
<%= render partial: 'notification.html.erb', locals: { notification: notification} %>
|
||||
<% end %>
|
22
app/views/system_notifications/_notification.html.erb
Normal file
22
app/views/system_notifications/_notification.html.erb
Normal 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>
|
34
app/views/system_notifications/index.html.erb
Normal file
34
app/views/system_notifications/index.html.erb
Normal 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" %>
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -23,4 +23,4 @@ if User.count.zero?
|
|||
[],
|
||||
Extends::INITIAL_USER_OPTIONS
|
||||
)
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue