Repository: eagle Updated Branches: refs/heads/master 7bfe55de7 -> 1c90aa46d
[EAGLE-930] UI Notification support Support alert auto detect and popup notification of current eagle application. Author: zombieJ <[email protected]> Closes #845 from zombieJ/EAGLE-930. Project: http://git-wip-us.apache.org/repos/asf/eagle/repo Commit: http://git-wip-us.apache.org/repos/asf/eagle/commit/1c90aa46 Tree: http://git-wip-us.apache.org/repos/asf/eagle/tree/1c90aa46 Diff: http://git-wip-us.apache.org/repos/asf/eagle/diff/1c90aa46 Branch: refs/heads/master Commit: 1c90aa46d23e8ed2f35bddc37e34900392060dac Parents: 7bfe55d Author: zombieJ <[email protected]> Authored: Tue Feb 28 14:37:56 2017 +0800 Committer: zombieJ <[email protected]> Committed: Tue Feb 28 14:37:56 2017 +0800 ---------------------------------------------------------------------- eagle-server/src/main/webapp/app/dev/index.html | 28 ++++ .../src/main/webapp/app/dev/public/js/app.js | 7 +- .../app/dev/public/js/services/alertSrv.js | 65 +++++++++ .../dev/public/js/services/notificationSrv.js | 144 +++++++++++++++++++ 4 files changed, 243 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/eagle/blob/1c90aa46/eagle-server/src/main/webapp/app/dev/index.html ---------------------------------------------------------------------- diff --git a/eagle-server/src/main/webapp/app/dev/index.html b/eagle-server/src/main/webapp/app/dev/index.html index b4dfea6..3408842 100644 --- a/eagle-server/src/main/webapp/app/dev/index.html +++ b/eagle-server/src/main/webapp/app/dev/index.html @@ -64,6 +64,7 @@ <div class="navbar-custom-menu"> <ul class="nav navbar-nav"> + <!-- Time Picker --> <li class="dropdown time-picker" ng-if="Time.pickerType === Time.TIME_RANGE_PICKER"> <a data-toggle="dropdown" aria-expanded="false"> <span class="fa fa-{{Time.autoRefresh ? 'refresh' : 'calendar'}}"></span> @@ -84,6 +85,8 @@ </li> </ul> </li> + + <!-- Site --> <li class="hover-dropdown"> <a> <span ng-if="!Site.current()"> @@ -105,6 +108,29 @@ </li> </ul> </li> + + <!-- Notification --> + <li class="hover-dropdown"> + <a> + <i class="fa fa-bell"></i> + <span ng-if="$notification.list.length" class="label label-warning"> + {{$notification.list.length}} + </span> + </a> + + <ul class="dropdown-menu"> + <li ng-repeat="notification in $notification.list track by $index"> + <a href="{{notification.url}}" ng-click="$notification.trigger(notification, $event)"> + {{notification.content}} + </a> + </li> + <li ng-if="!$notification.list.length" class="disabled"> + <a class="text-gray">No Notification</a> + </li> + </ul> + </li> + + <!-- FAQ --> <li> <a data-toggle="dropdown" aria-expanded="false"> <i class="glyphicon glyphicon-question-sign"></i> @@ -277,6 +303,8 @@ <script src="public/js/services/applicationSrv.js" type="text/javascript" charset="utf-8"></script> <script src="public/js/services/uiSrv.js" type="text/javascript" charset="utf-8"></script> <script src="public/js/services/policySrv.js" type="text/javascript" charset="utf-8"></script> + <script src="public/js/services/notificationSrv.js" type="text/javascript" charset="utf-8"></script> + <script src="public/js/services/alertSrv.js" type="text/javascript" charset="utf-8"></script> <!-- Components --> <script src="public/js/components/main.js" type="text/javascript" charset="utf-8"></script> http://git-wip-us.apache.org/repos/asf/eagle/blob/1c90aa46/eagle-server/src/main/webapp/app/dev/public/js/app.js ---------------------------------------------------------------------- diff --git a/eagle-server/src/main/webapp/app/dev/public/js/app.js b/eagle-server/src/main/webapp/app/dev/public/js/app.js index a05ac92..e5b87e8 100644 --- a/eagle-server/src/main/webapp/app/dev/public/js/app.js +++ b/eagle-server/src/main/webapp/app/dev/public/js/app.js @@ -273,7 +273,10 @@ var app = {}; // ====================================================================================== // = Main Controller = // ====================================================================================== - eagleApp.controller('MainCtrl', function ($scope, $wrapState, $urlRouter, Server, PageConfig, Portal, Widget, Entity, CompatibleEntity, Site, Application, UI, Time, Policy) { + eagleApp.controller('MainCtrl', function ( + $scope, $wrapState, $urlRouter, $notification, + Server, PageConfig, Portal, Widget, Entity, CompatibleEntity, + Site, Application, UI, Time, Policy, Alert) { window._WrapState = $scope.$wrapState = $wrapState; window._Server = $scope.Server = Server; window._PageConfig = $scope.PageConfig = PageConfig; @@ -286,6 +289,8 @@ var app = {}; window._UI = $scope.UI = UI; window._Time = $scope.Time = Time; window._Policy = $scope.Policy = Policy; + window._Notification = $scope.$notification = $notification; + window._Alert = $scope.Alert = Alert; $scope.common = common; $scope._TRS = window._TRS(); http://git-wip-us.apache.org/repos/asf/eagle/blob/1c90aa46/eagle-server/src/main/webapp/app/dev/public/js/services/alertSrv.js ---------------------------------------------------------------------- diff --git a/eagle-server/src/main/webapp/app/dev/public/js/services/alertSrv.js b/eagle-server/src/main/webapp/app/dev/public/js/services/alertSrv.js new file mode 100644 index 0000000..9905fa0 --- /dev/null +++ b/eagle-server/src/main/webapp/app/dev/public/js/services/alertSrv.js @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function() { + 'use strict'; + + var ALERT_FETCH_LIMIT = 1000 * 60 * 60 * 12; + var ALERT_REFRESH_INTERVAL = 1000 * 10; + var ALERT_TEMPLATE = '#/site/${siteId}/alert/detail/${alertId}?timestamp=${timestamp}'; + + var serviceModule = angular.module('eagle.service'); + + serviceModule.service('Alert', function ($notification, Time, CompatibleEntity) { + var Alert = { + list: null, + }; + + $notification.getPromise().then(function () { + function queryAlerts() { + var endTime = new Time(); + var list = CompatibleEntity.query("LIST", { + query: "AlertService", + startTime: endTime.clone().subtract(ALERT_FETCH_LIMIT, 'ms'), + endTime: endTime + }); + list._then(function () { + if (!Alert.list) { + Alert.list = list; + return; + } + + var subList = common.array.minus(list, Alert.list, ['encodedRowkey'], ['encodedRowkey']); + Alert.list = list; + $.each(subList, function (i, alert) { + $notification(alert.alertSubject, common.template(ALERT_TEMPLATE, { + siteId: alert.tags.siteId, + alertId: alert.tags.alertId, + timestamp: alert.timestamp, + })); + }); + }); + } + + queryAlerts(); + setInterval(queryAlerts, ALERT_REFRESH_INTERVAL); + }); + + return Alert; + }); +})(); http://git-wip-us.apache.org/repos/asf/eagle/blob/1c90aa46/eagle-server/src/main/webapp/app/dev/public/js/services/notificationSrv.js ---------------------------------------------------------------------- diff --git a/eagle-server/src/main/webapp/app/dev/public/js/services/notificationSrv.js b/eagle-server/src/main/webapp/app/dev/public/js/services/notificationSrv.js new file mode 100644 index 0000000..76f9c45 --- /dev/null +++ b/eagle-server/src/main/webapp/app/dev/public/js/services/notificationSrv.js @@ -0,0 +1,144 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function() { + 'use strict'; + + var REFRESH_TIME_LIMIT = 5 * 1000; + var PREEMPTIVE_TIME_LIMIT = 8 * 1000; + var MAX_NOTIFICATION_COUNT = 100; + var serviceModule = angular.module('eagle.service'); + + serviceModule.service('$notification', function ($q, $rootScope) { + var deferred = $q.defer(); + var promised = false; + var id = +new Date(); + var lastHookTime = +new Date(); + var instanceId = 0; + var lastInstance; + + function notification(content, url) { + if (!promised) return; + + instanceId += 1; + + // Add notification in queue + var config = { + content: content, + url: url, + id: instanceId, + }; + notification.list.push(config); + notification.list = notification.list.slice(0, MAX_NOTIFICATION_COUNT); + + // Popup notification + var count = notification.list.length; + var instance = new Notification((count > 1 ? '[' + count + '] ' : '') + 'Apache Eagle:', { + tag: 'eagle', + body: content, + icon: 'public/images/favicon.png', + renotify: true, + }); + instance.onclick = function () { + window.focus(); + notification.trigger(config); + + if (lastInstance === instance) lastInstance = null; + instance.close(); + + $rootScope.$apply(); + }; + + // Close notification + setTimeout(function () { + if (lastInstance === instance) { + lastInstance = null; + instance.close(); + } + }, 5000); + lastInstance = instance; + } + + function uniqueNotification() { + function loopListener() { + var hooker = common.parseJSON(localStorage.getItem('notificationId'), null); + if (promised || !hooker || (+new Date()) - (hooker.lastHookTime || 0) > PREEMPTIVE_TIME_LIMIT) { + promised = true; + deferred.resolve(); + lastHookTime = +new Date(); + localStorage.setItem('notificationId', JSON.stringify({ + id: id, + lastHookTime: lastHookTime, + })); + } + } + + setInterval(loopListener, REFRESH_TIME_LIMIT); + loopListener(); + + $(window).bind("beforeunload", function() { + var hooker = common.parseJSON(localStorage.getItem('notificationId'), {}); + if (hooker.id === id) { + localStorage.removeItem('notificationId'); + } + if (lastInstance) { + lastInstance.close(); + lastInstance = null; + } + }); + } + + if (!'Notification' in window || !'localStorage' in window) { + // Notification not support + console.warn('Browser do not support Notification api. Ignore...'); + } else { + // Check notification state; + if (Notification.permission === 'granted') { + // promised = true; + uniqueNotification(); + } else if (Notification.permission !== 'denied') { + Notification.requestPermission().then(function(permission) { + if (permission === "granted") { + uniqueNotification(); + } else { + console.warn('User deny the notification.'); + } + }); + } else { + console.warn('Web Notification initialization denied. Ignore eagle web notification.'); + } + } + + notification.list = []; + + notification.trigger = function (config, event) { + notification.list = common.array.remove(config.id, notification.list, ['id']); + + if (!event || !event.ctrlKey) { + location.href = config.url; + if (event) event.preventDefault(); + } + }; + + notification.getPromise = function () { + return deferred.promise; + }; + + return notification; + }); +})();
