Add pagination to the notifications API and make the UI only show a maximum of 5 notifications (beyond that, it shows "5+").
This commit is contained in:
parent
c1b0b2383a
commit
d76d4704a0
8 changed files with 67 additions and 26 deletions
|
@ -1717,19 +1717,20 @@ def create_notification(kind_name, target, metadata={}):
|
||||||
|
|
||||||
def create_unique_notification(kind_name, target, metadata={}):
|
def create_unique_notification(kind_name, target, metadata={}):
|
||||||
with config.app_config['DB_TRANSACTION_FACTORY'](db):
|
with config.app_config['DB_TRANSACTION_FACTORY'](db):
|
||||||
if list_notifications(target, kind_name).count() == 0:
|
if list_notifications(target, kind_name, limit=1).count() == 0:
|
||||||
create_notification(kind_name, target, metadata)
|
create_notification(kind_name, target, metadata)
|
||||||
|
|
||||||
|
|
||||||
def lookup_notification(user, uuid):
|
def lookup_notification(user, uuid):
|
||||||
results = list(list_notifications(user, id_filter=uuid, include_dismissed=True))
|
results = list(list_notifications(user, id_filter=uuid, include_dismissed=True, limit=1))
|
||||||
if not results:
|
if not results:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return results[0]
|
return results[0]
|
||||||
|
|
||||||
|
|
||||||
def list_notifications(user, kind_name=None, id_filter=None, include_dismissed=False):
|
def list_notifications(user, kind_name=None, id_filter=None, include_dismissed=False,
|
||||||
|
page=None, limit=None):
|
||||||
Org = User.alias()
|
Org = User.alias()
|
||||||
AdminTeam = Team.alias()
|
AdminTeam = Team.alias()
|
||||||
AdminTeamMember = TeamMember.alias()
|
AdminTeamMember = TeamMember.alias()
|
||||||
|
@ -1767,6 +1768,11 @@ def list_notifications(user, kind_name=None, id_filter=None, include_dismissed=F
|
||||||
.switch(Notification)
|
.switch(Notification)
|
||||||
.where(Notification.uuid == id_filter))
|
.where(Notification.uuid == id_filter))
|
||||||
|
|
||||||
|
if page:
|
||||||
|
query = query.paginate(page, limit)
|
||||||
|
elif limit:
|
||||||
|
query = query.limit(limit)
|
||||||
|
|
||||||
return query
|
return query
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,9 @@ from flask.ext.principal import identity_changed, AnonymousIdentity
|
||||||
|
|
||||||
from app import app, billing as stripe, authentication
|
from app import app, billing as stripe, authentication
|
||||||
from endpoints.api import (ApiResource, nickname, resource, validate_json_request, request_error,
|
from endpoints.api import (ApiResource, nickname, resource, validate_json_request, request_error,
|
||||||
log_action, internal_only, NotFound, require_user_admin,
|
log_action, internal_only, NotFound, require_user_admin, parse_args,
|
||||||
InvalidToken, require_scope, format_date, hide_if, show_if, license_error)
|
query_param, InvalidToken, require_scope, format_date, hide_if, show_if,
|
||||||
|
license_error)
|
||||||
from endpoints.api.subscribe import subscribe
|
from endpoints.api.subscribe import subscribe
|
||||||
from endpoints.common import common_login
|
from endpoints.common import common_login
|
||||||
from data import model
|
from data import model
|
||||||
|
@ -403,11 +404,24 @@ class Recovery(ApiResource):
|
||||||
@internal_only
|
@internal_only
|
||||||
class UserNotificationList(ApiResource):
|
class UserNotificationList(ApiResource):
|
||||||
@require_user_admin
|
@require_user_admin
|
||||||
|
@parse_args
|
||||||
|
@query_param('page', 'Offset page number. (int)', type=int, default=0)
|
||||||
|
@query_param('limit', 'Limit on the number of results (int)', type=int, default=5)
|
||||||
@nickname('listUserNotifications')
|
@nickname('listUserNotifications')
|
||||||
def get(self):
|
def get(self, args):
|
||||||
notifications = model.list_notifications(get_authenticated_user())
|
page = args['page']
|
||||||
|
limit = args['limit']
|
||||||
|
|
||||||
|
notifications = list(model.list_notifications(get_authenticated_user(), page=page, limit=limit + 1))
|
||||||
|
has_more = False
|
||||||
|
|
||||||
|
if len(notifications) > limit:
|
||||||
|
has_more = True
|
||||||
|
notifications = notifications[0:limit]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'notifications': [notification_view(notification) for notification in notifications]
|
'notifications': [notification_view(notification) for notification in notifications],
|
||||||
|
'additional': has_more
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -184,6 +184,8 @@ class BuildFailureEvent(NotificationEvent):
|
||||||
return 'build_failure'
|
return 'build_failure'
|
||||||
|
|
||||||
def get_sample_data(self, repository):
|
def get_sample_data(self, repository):
|
||||||
|
build_uuid = 'fake-build-id'
|
||||||
|
|
||||||
return build_event_data(repository, {
|
return build_event_data(repository, {
|
||||||
'build_id': build_uuid,
|
'build_id': build_uuid,
|
||||||
'build_name': 'some-fake-build',
|
'build_name': 'some-fake-build',
|
||||||
|
|
|
@ -745,7 +745,7 @@ i.toggle-icon:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-notification.notification-animated {
|
.user-notification.notification-animated {
|
||||||
width: 21px;
|
min-width: 21px;
|
||||||
|
|
||||||
transform: scale(0);
|
transform: scale(0);
|
||||||
-moz-transform: scale(0);
|
-moz-transform: scale(0);
|
||||||
|
|
|
@ -37,15 +37,7 @@
|
||||||
<a href="javascript:void(0)" class="dropdown-toggle user-dropdown user-view" data-toggle="dropdown">
|
<a href="javascript:void(0)" class="dropdown-toggle user-dropdown user-view" data-toggle="dropdown">
|
||||||
<img src="//www.gravatar.com/avatar/{{ user.gravatar }}?s=32&d=identicon" />
|
<img src="//www.gravatar.com/avatar/{{ user.gravatar }}?s=32&d=identicon" />
|
||||||
{{ user.username }}
|
{{ user.username }}
|
||||||
<span class="badge user-notification notification-animated"
|
<span class="notifications-bubble"></span>
|
||||||
ng-show="notificationService.notifications.length"
|
|
||||||
ng-class="notificationService.notificationClasses"
|
|
||||||
bs-tooltip=""
|
|
||||||
data-title="User Notifications"
|
|
||||||
data-placement="left"
|
|
||||||
data-container="body">
|
|
||||||
{{ notificationService.notifications.length }}
|
|
||||||
</span>
|
|
||||||
<b class="caret"></b>
|
<b class="caret"></b>
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
|
@ -58,11 +50,7 @@
|
||||||
<a href="javascript:void(0)" data-template="/static/directives/notification-bar.html"
|
<a href="javascript:void(0)" data-template="/static/directives/notification-bar.html"
|
||||||
data-animation="am-slide-right" bs-aside="aside" data-container="body">
|
data-animation="am-slide-right" bs-aside="aside" data-container="body">
|
||||||
Notifications
|
Notifications
|
||||||
<span class="badge user-notification"
|
<span class="notifications-bubble"></span>
|
||||||
ng-class="notificationService.notificationClasses"
|
|
||||||
ng-show="notificationService.notifications.length">
|
|
||||||
{{ notificationService.notifications.length }}
|
|
||||||
</span>
|
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a ng-href="/organizations/" target="{{ appLinkTarget() }}">Organizations</a></li>
|
<li><a ng-href="/organizations/" target="{{ appLinkTarget() }}">Organizations</a></li>
|
||||||
|
|
|
@ -3,7 +3,10 @@
|
||||||
<div class="aside-content">
|
<div class="aside-content">
|
||||||
<div class="aside-header">
|
<div class="aside-header">
|
||||||
<button type="button" class="close" ng-click="$hide()">×</button>
|
<button type="button" class="close" ng-click="$hide()">×</button>
|
||||||
<h4 class="aside-title">Notifications</h4>
|
<h4 class="aside-title">
|
||||||
|
Notifications
|
||||||
|
<span class="notifications-bubble"></span>
|
||||||
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="aside-body">
|
<div class="aside-body">
|
||||||
<div ng-repeat="notification in notificationService.notifications">
|
<div ng-repeat="notification in notificationService.notifications">
|
||||||
|
|
7
static/directives/notifications-bubble.html
Normal file
7
static/directives/notifications-bubble.html
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<span class="notifications-bubble-element">
|
||||||
|
<span class="badge user-notification notification-animated"
|
||||||
|
ng-show="notificationService.notifications.length"
|
||||||
|
ng-class="notificationService.notificationClasses">
|
||||||
|
{{ notificationService.notifications.length }}<span ng-if="notificationService.additionalNotifications">+</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
|
@ -1155,7 +1155,8 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
||||||
'user': null,
|
'user': null,
|
||||||
'notifications': [],
|
'notifications': [],
|
||||||
'notificationClasses': [],
|
'notificationClasses': [],
|
||||||
'notificationSummaries': []
|
'notificationSummaries': [],
|
||||||
|
'additionalNotifications': false
|
||||||
};
|
};
|
||||||
|
|
||||||
var pollTimerHandle = null;
|
var pollTimerHandle = null;
|
||||||
|
@ -1251,7 +1252,9 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
||||||
'uuid': notification.id
|
'uuid': notification.id
|
||||||
};
|
};
|
||||||
|
|
||||||
ApiService.updateUserNotification(notification, params);
|
ApiService.updateUserNotification(notification, params, function() {
|
||||||
|
notificationService.update();
|
||||||
|
}, ApiService.errorDisplay('Could not update notification'));
|
||||||
|
|
||||||
var index = $.inArray(notification, notificationService.notifications);
|
var index = $.inArray(notification, notificationService.notifications);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
|
@ -1308,6 +1311,7 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
||||||
|
|
||||||
ApiService.listUserNotifications().then(function(resp) {
|
ApiService.listUserNotifications().then(function(resp) {
|
||||||
notificationService.notifications = resp['notifications'];
|
notificationService.notifications = resp['notifications'];
|
||||||
|
notificationService.additionalNotifications = resp['additional'];
|
||||||
notificationService.notificationClasses = notificationService.getClasses(notificationService.notifications);
|
notificationService.notificationClasses = notificationService.getClasses(notificationService.notifications);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -5021,6 +5025,23 @@ quayApp.directive('twitterView', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
quayApp.directive('notificationsBubble', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/notifications-bubble.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: false,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
},
|
||||||
|
controller: function($scope, UserService, NotificationService) {
|
||||||
|
$scope.notificationService = NotificationService;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
quayApp.directive('notificationView', function () {
|
quayApp.directive('notificationView', function () {
|
||||||
var directiveDefinitionObject = {
|
var directiveDefinitionObject = {
|
||||||
priority: 0,
|
priority: 0,
|
||||||
|
|
Reference in a new issue