Merge pull request #805 from ZmagoD/zd_SCI_1566

added notification alert [fixes SCI-1566]
This commit is contained in:
Zmago Devetak 2017-10-09 09:35:33 +02:00 committed by GitHub
commit 5ab5c7e898
9 changed files with 181 additions and 29 deletions

View file

@ -1,23 +1,28 @@
module ClientApi
class NotificationsController < ApplicationController
before_action :last_notifications, only: :recent_notifications
def recent_notifications
respond_to do |format|
format.json do
render template: '/client_api/notifications/index',
status: :ok,
locals: { notifications: @recent_notifications }
locals: {
notifications:
UserNotification.recent_notifications(current_user)
}
end
end
# clean the unseen notifications
UserNotification.seen_by_user(current_user)
end
def unread_notifications_count
respond_to do |format|
format.json do
render json: {
count: UserNotification.unseen_notification_count(current_user)
}, status: :ok
end
end
end
private
def last_notifications
@recent_notifications =
UserNotification.recent_notifications(current_user)
UserNotification.seen_by_user(current_user)
end
end
end

View file

@ -1,15 +1,21 @@
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { NavDropdown } from "react-bootstrap";
import { FormattedMessage } from "react-intl";
import axios from "axios";
import styled from "styled-components";
import { RECENT_NOTIFICATIONS_PATH } from "../../../config/routes";
import {
getRecentNotifications,
getUnreadNotificationsCount
} from "../../../services/api/notifications_api";
import {
MAIN_COLOR_BLUE,
WILD_SAND_COLOR,
MYSTIC_COLOR
} from "../../../config/constants/colors";
import {
SETTINGS_ACCOUNT_PREFERENCES
} from "../../../config/routes"
import NotificationItem from "./NotificationItem";
import Spinner from "../../Spinner";
@ -21,7 +27,9 @@ const StyledListHeader = styled(CustomNavItem)`
font-weight: bold;
padding: 8px;
& a, a:hover, a:active {
& a,
a:hover,
a:active {
color: ${WILD_SAND_COLOR};
}
`;
@ -44,40 +52,81 @@ const StyledNavDropdown = styled(NavDropdown)`
}
`;
const StyledSpan = styled.span`
background-color: ${MAIN_COLOR_BLUE};
border-radius: 5px;
color: ${WILD_SAND_COLOR};
font-size: 11px;
font-weight: bold;
margin-left: 12px;
padding: 1px 6px;
right: 19px;
top: 3px;
position: relative;
`;
class NotificationsDropdown extends Component {
constructor(props) {
super(props);
this.state = { notifications: [] };
this.state = {
notifications: [],
notificationsCount: 0
};
this.getRecentNotifications = this.getRecentNotifications.bind(this);
this.renderNotifications = this.renderNotifications.bind(this);
this.renderNotificationStatus = this.renderNotificationStatus.bind(this);
this.loadStatus = this.loadStatus.bind(this);
}
componentWillMount() {
this.loadStatus();
}
componentDidMount() {
const minutes = 60 * 1000;
setInterval(this.loadStatus, minutes);
}
getRecentNotifications(e) {
e.preventDefault();
axios
.get(RECENT_NOTIFICATIONS_PATH, { withCredentials: true })
.then(({ data }) => {
this.setState({ notifications: data });
})
getRecentNotifications()
.then(response =>
this.setState({ notifications: response, notificationsCount: 0 })
)
.catch(error => {
console.log("get Notifications Error: ", error); // TODO change this
});
}
loadStatus() {
getUnreadNotificationsCount().then(response => {
this.setState({ notificationsCount: parseInt(response.count, 10) });
});
}
renderNotifications() {
const list = this.state.notifications.map(notification =>
const list = this.state.notifications.map(notification => (
<NotificationItem key={notification.id} notification={notification} />
);
));
const items =
this.state.notifications.length > 0
? list
: <CustomNavItem>
<Spinner />
</CustomNavItem>;
this.state.notifications.length > 0 ? (
list
) : (
<CustomNavItem>
<Spinner />
</CustomNavItem>
);
return items;
}
renderNotificationStatus() {
if (this.state.notificationsCount > 0) {
return <StyledSpan>{this.state.notificationsCount}</StyledSpan>;
}
return <span />;
}
render() {
return (
<StyledNavDropdown
@ -89,6 +138,7 @@ class NotificationsDropdown extends Component {
<span className="visible-xs-inline visible-sm-inline">
<FormattedMessage id="navbar.notifications_label" />
</span>
{this.renderNotificationStatus()}
</span>
}
onClick={this.getRecentNotifications}
@ -98,9 +148,9 @@ class NotificationsDropdown extends Component {
<FormattedMessage id="notifications.dropdown_title" />
</span>
<span className="pull-right">
<a href="/users/settings/account/preferences">
<Link to={SETTINGS_ACCOUNT_PREFERENCES}>
<FormattedMessage id="notifications.dropdown_settings_link" />
</a>
</Link>
</span>
</StyledListHeader>
{this.renderNotifications()}

View file

@ -1,3 +1,8 @@
// notifications
export const RECENT_NOTIFICATIONS_PATH = "/client_api/recent_notifications";
export const UNREADED_NOTIFICATIONS_PATH =
"/client_api/unread_notifications_count";
// activities
export const ACTIVITIES_PATH = "/client_api/activities";

View file

@ -0,0 +1,15 @@
import { axiosInstance } from "./config";
import {
RECENT_NOTIFICATIONS_PATH,
UNREADED_NOTIFICATIONS_PATH
} from "./endpoints";
export const getRecentNotifications = () => {
return axiosInstance.get(RECENT_NOTIFICATIONS_PATH).then(({ data }) => data);
};
export const getUnreadNotificationsCount = () => {
return axiosInstance
.get(UNREADED_NOTIFICATIONS_PATH)
.then(({ data }) => data);
};

View file

@ -27,6 +27,8 @@ Rails.application.routes.draw do
end
# notifications
get '/recent_notifications', to: 'notifications#recent_notifications'
get '/unread_notifications_count',
to: 'notifications#unread_notifications_count'
# users
get '/current_user_info', to: 'users/users#current_user_info'

View file

@ -0,0 +1,27 @@
require 'rails_helper'
describe ClientApi::NotificationsController, type: :controller do
login_user
let(:notification) { create :notification }
let(:user_notification) do
create :user_notification,
user: User.first,
notification: notification
end
describe '#recent_notifications' do
it 'returns a list of notifications' do
get :recent_notifications, format: :json
expect(response).to be_success
expect(response).to render_template('client_api/notifications/index')
end
end
describe '#unreaded_notifications_number' do
it 'returns a number of unreaded notifications' do
get :unread_notifications_count, format: :json
expect(response).to be_success
expect(response.body).to include('count')
end
end
end

View file

@ -0,0 +1,8 @@
FactoryGirl.define do
factory :notification do
title '<i>Admin</i> was added as Owner to project ' \
'<strong>Demo project - qPCR</strong> by <i>User</i>.'
message 'Project: <a href=\"/projects/3\"> Demo project - qPCR</a>'
type_of 'assignment'
end
end

View file

@ -0,0 +1,5 @@
FactoryGirl.define do
factory :user_notification do
checked false
end
end

View file

@ -1,6 +1,8 @@
require 'rails_helper'
describe UserNotification, type: :model do
let(:user) { create :user }
it 'should be of class UserNotification' do
expect(subject.class).to eq UserNotification
end
@ -17,4 +19,37 @@ describe UserNotification, type: :model do
it { should belong_to :user }
it { should belong_to :notification }
end
describe '#unseen_notification_count ' do
let(:notifcation) { create :notification }
it 'returns a number of unseen notifications' do
create :user_notification, user: user, notification: notifcation
expect(UserNotification.unseen_notification_count(user)).to eq 1
end
end
describe '#recent_notifications' do
let(:notifcation_one) { create :notification }
let(:notifcation_two) { create :notification }
it 'returns a list of notifications ordered by created_at DESC' do
create :user_notification, user: user, notification: notifcation_one
create :user_notification, user: user, notification: notifcation_two
notifications = UserNotification.recent_notifications(user)
expect(notifications).to eq [notifcation_two, notifcation_one]
end
end
describe '#seen_by_user' do
let!(:notification) { create :notification }
let!(:user_notification_one) do
create :user_notification, user: user, notification: notification
end
it 'set the check status to false' do
expect {
UserNotification.seen_by_user(user)
}.to change { user_notification_one.reload.checked }.from(false).to(true)
end
end
end