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;
+       });
+})();

Reply via email to