|
45 | 45 | // this one is treated separately from the rootScopeWatches as
|
46 | 46 | // it may need to be updated outside of the lifecycle of init/destroy
|
47 | 47 | var notificationListener;
|
48 |
| - // our internal notifications |
49 |
| - // var clientGeneratedNotifications = []; |
50 |
| - |
51 | 48 | var eventsWatcher;
|
52 |
| - var eventsByNameData = {}; |
53 |
| - var eventsMap = {}; |
54 |
| - |
55 |
| - // TODO: |
56 |
| - // include both Notifications & Events, |
57 |
| - // rather than destroying the map each time maintain it & add new items |
| 49 | + // data |
| 50 | + var eventsMap = { |
| 51 | + // projName: { events } |
| 52 | + }; |
| 53 | + var notificationsMap = { |
| 54 | + // projName: { notifications } |
| 55 | + }; |
58 | 56 |
|
59 |
| - // final Processed set of notification groups for UI |
60 |
| - // IF POSSIBLE, avoid having to convert back to an array. |
61 |
| - // var notificationGroupsMap = {}; |
62 |
| - var notificationGroups = []; |
| 57 | + // internal notifications have different types than API events |
| 58 | + var notificationEventTypeMap = { |
| 59 | + success: 'Normal', |
| 60 | + error: 'Warning' |
| 61 | + }; |
63 | 62 |
|
| 63 | + var iconClassByEventSeverity = { |
| 64 | + Normal: 'pficon pficon-info', |
| 65 | + Warning: 'pficon pficon-warning-triangle-o' |
| 66 | + }; |
64 | 67 |
|
65 | 68 | var projects = {};
|
66 | 69 |
|
| 70 | + var hideIfNoProject = function(projectName) { |
| 71 | + if(!projectName) { |
| 72 | + drawer.drawerHidden = true; |
| 73 | + } |
| 74 | + }; |
| 75 | + |
| 76 | + var projectChanged = function(next, current) { |
| 77 | + return _.get(next, 'params.project') !== _.get(current, 'params.project'); |
| 78 | + }; |
| 79 | + |
67 | 80 | var getProject = function(projectName) {
|
68 | 81 | return DataService
|
69 | 82 | .get('projects', projectName, {}, {errorNotification: false})
|
|
73 | 86 | });
|
74 | 87 | };
|
75 | 88 |
|
76 |
| - var ensureProjectGroupExists = function(groups, projectName) { |
77 |
| - if(projectName && !groups[projectName]) { |
78 |
| - groups[projectName] = { |
79 |
| - heading: $filter('displayName')(projects[projectName]) || projectName, |
80 |
| - project: projects[projectName], |
81 |
| - notifications: [] |
82 |
| - }; |
83 |
| - } |
84 |
| - }; |
85 |
| - |
86 |
| - var deregisterEventsWatch = function() { |
87 |
| - if(eventsWatcher) { |
88 |
| - DataService.unwatch(eventsWatcher); |
89 |
| - } |
90 |
| - }; |
91 |
| - |
92 |
| - var watchEvents = function(projectName, cb) { |
93 |
| - deregisterEventsWatch(); |
94 |
| - if(projectName) { |
95 |
| - eventsWatcher = DataService.watch('events', {namespace: projectName}, _.debounce(cb, 400), { skipDigest: true }); |
96 |
| - } |
97 |
| - }; |
98 |
| - |
99 |
| - // NotificationService notifications are minimal, they do no necessarily contain projectName info. |
100 |
| - // ATM tacking this on via watching the current project. |
101 |
| - // var watchNotifications = function(projectName, cb) { |
102 |
| - // deregisterNotificationListener(); |
103 |
| - // if(!projectName) { |
104 |
| - // return; |
105 |
| - // } |
106 |
| - // notificationListener = $rootScope.$on('NotificationsService.onNotificationAdded', cb); |
107 |
| - // }; |
108 |
| - |
109 |
| - var deregisterNotificationListener = function() { |
110 |
| - notificationListener && notificationListener(); |
111 |
| - notificationListener = null; |
| 89 | + var makeProjectGroup = function(projectName, notifications) { |
| 90 | + return { |
| 91 | + heading: $filter('displayName')(projects[projectName]) || projectName, |
| 92 | + project: projects[projectName], |
| 93 | + notifications: notifications |
| 94 | + }; |
112 | 95 | };
|
113 | 96 |
|
114 | 97 | var unread = function(notifications) {
|
115 | 98 | return _.filter(notifications, 'unread');
|
116 | 99 | };
|
117 | 100 |
|
118 |
| - // returns a count for each type of notification, example: |
119 |
| - // {Normal: 1, Warning: 5} |
120 |
| - // TODO: eliminate this $rootScope.$applyAsync, |
121 |
| - // there is a quirk here where the values are not picked up the |
122 |
| - // first time the function runs, despite the same $applyAsync |
123 |
| - // in the render() function |
124 |
| - var countUnreadNotificationsForGroup = function(group) { |
125 |
| - $rootScope.$applyAsync(function() { |
| 101 | + |
| 102 | + // currently we only show 1 at a time anyway |
| 103 | + var countUnreadNotifications = function() { |
| 104 | + _.each(drawer.notificationGroups, function(group) { |
126 | 105 | group.totalUnread = unread(group.notifications).length;
|
127 | 106 | group.hasUnread = !!group.totalUnread;
|
128 | 107 | $rootScope.$emit('NotificationDrawerWrapper.count', group.totalUnread);
|
129 | 108 | });
|
130 | 109 | };
|
131 | 110 |
|
132 |
| - // currently we only show 1 at a time anyway |
133 |
| - var countUnreadNotificationsForAllGroups = function() { |
134 |
| - _.each(notificationGroups, countUnreadNotificationsForGroup); |
135 |
| - }; |
136 |
| - |
137 |
| - var sortNotifications = function(notifications) { |
138 |
| - return _.orderBy(notifications, ['event.lastTimestamp', 'event.firstTimestamp'], ['desc', 'desc']); |
| 111 | + // returns a map of filtered events ONLY. |
| 112 | + // will worry about unread, actions, etc in render. |
| 113 | + var formatAndFilterEvents = function(events) { |
| 114 | + return _.reduce( |
| 115 | + events, |
| 116 | + function(result, event) { |
| 117 | + if(EventsService.isImportantEvent(event) && !EventsService.isCleared(event)) { |
| 118 | + result[event.metadata.uid] = { |
| 119 | + actions: null, |
| 120 | + uid: event.metadata.uid, |
| 121 | + unread: !EventsService.isRead(event), |
| 122 | + event: event |
| 123 | + }; |
| 124 | + } |
| 125 | + return result; |
| 126 | + }, {}); |
139 | 127 | };
|
140 | 128 |
|
141 |
| - var sortNotificationGroups = function(groupsMap) { |
142 |
| - // convert the map into a sorted array |
143 |
| - var sortedGroups = _.sortBy(groupsMap, function(group) { |
144 |
| - return group.heading; |
145 |
| - }); |
146 |
| - // and sort the notifications under each one |
147 |
| - _.each(sortedGroups, function(group) { |
148 |
| - group.notifications = sortNotifications(group.notifications); |
149 |
| - group.counts = countUnreadNotificationsForGroup(group); |
150 |
| - }); |
151 |
| - return sortedGroups; |
| 129 | + // Since the events map & internal notifications map are separate |
| 130 | + // data and updated asyncronously, they need to be copied/merged |
| 131 | + // at some point before being sent to the UI to render. |
| 132 | + var mergeAndSortMaps = function(firstMap, secondMap) { |
| 133 | + var proj = $routeParams.project; |
| 134 | + return _.orderBy( |
| 135 | + _.assign({}, firstMap[proj], secondMap[proj]), |
| 136 | + ['event.lastTimestamp', 'event.firstTimestamp'], |
| 137 | + ['desc', 'desc']); |
152 | 138 | };
|
153 | 139 |
|
154 |
| - var formatAndFilterEvents = function(eventMap) { |
155 |
| - var filtered = {}; |
156 |
| - ensureProjectGroupExists(filtered, $routeParams.project); |
157 |
| - _.each(eventMap, function(event) { |
158 |
| - if(EventsService.isImportantEvent(event) && !EventsService.isCleared(event)) { |
159 |
| - ensureProjectGroupExists(filtered, event.metadata.namespace); |
160 |
| - filtered[event.metadata.namespace].notifications.push({ |
161 |
| - unread: !EventsService.isRead(event), |
162 |
| - event: event, |
163 |
| - actions: null |
164 |
| - }); |
165 |
| - } |
| 140 | + var render = function() { |
| 141 | + $rootScope.$evalAsync(function() { |
| 142 | + drawer.notificationGroups = [ |
| 143 | + makeProjectGroup($routeParams.project, mergeAndSortMaps(eventsMap, notificationsMap)) |
| 144 | + ]; |
| 145 | + countUnreadNotifications(); |
166 | 146 | });
|
167 |
| - return filtered; |
168 | 147 | };
|
169 | 148 |
|
170 | 149 | var deregisterRootScopeWatches = function() {
|
|
174 | 153 | rootScopeWatches = [];
|
175 | 154 | };
|
176 | 155 |
|
177 |
| - var hideIfNoProject = function(projectName) { |
178 |
| - if(!projectName) { |
179 |
| - drawer.drawerHidden = true; |
| 156 | + var deregisterEventsWatch = function() { |
| 157 | + if(eventsWatcher) { |
| 158 | + DataService.unwatch(eventsWatcher); |
180 | 159 | }
|
181 | 160 | };
|
182 | 161 |
|
183 |
| - var render = function() { |
184 |
| - $rootScope.$evalAsync(function () { |
185 |
| - countUnreadNotificationsForAllGroups(); |
186 |
| - // NOTE: we are currently only showing one project in the drawer at a |
187 |
| - // time. If we go back to multiple projects, we can eliminate the filter here |
188 |
| - // and just pass the whole array as notificationGroups. |
189 |
| - // if we do, we will have to handle group.open to keep track of what the |
190 |
| - // user is viewing at the time & indicate to the user that the non-active |
191 |
| - // project is "asleep"/not being watched. |
192 |
| - drawer.notificationGroups = _.filter(notificationGroups, function(group) { |
193 |
| - return group.project.metadata.name === $routeParams.project; |
194 |
| - }); |
195 |
| - }); |
| 162 | + var deregisterNotificationListener = function() { |
| 163 | + notificationListener && notificationListener(); |
| 164 | + notificationListener = null; |
196 | 165 | };
|
197 | 166 |
|
198 |
| - // TODO: follow-on PR to decide which of these events to toast, |
199 |
| - // via config in constants.js |
200 | 167 | var eventWatchCallback = function(eventData) {
|
201 |
| - eventsByNameData = eventData.by('metadata.name'); |
202 |
| - eventsMap = formatAndFilterEvents(eventsByNameData); |
203 |
| - // TODO: Update to an intermediate map, so that we can then combine both |
204 |
| - // events + notifications into the final notificationGroups output |
205 |
| - notificationGroups = sortNotificationGroups(eventsMap); |
| 168 | + eventsMap[$routeParams.project] = formatAndFilterEvents(eventData.by('metadata.name')); |
206 | 169 | render();
|
207 | 170 | };
|
208 | 171 |
|
209 |
| - // TODO: Follow-on PR to update & add the internal notifications to the |
210 |
| - // var notificationWatchCallback = function(event, notification) { |
211 |
| - // // will need to add .event = {} and immitate structure |
212 |
| - // if(!notification.lastTimestamp) { |
213 |
| - // // creates a timestamp that matches event format: 2017-08-09T19:55:35Z |
214 |
| - // notification.lastTimestamp = moment.parseZone(new Date()).utc().format(); |
215 |
| - // } |
216 |
| - // clientGeneratedNotifications.push(notification); |
217 |
| - // }; |
| 172 | + var notificationWatchCallback = function(event, notification) { |
| 173 | + var uid = notification.id || _.uniqueId('notification-'); |
| 174 | + var project = notification.namespace || $routeParams.project; |
| 175 | + notificationsMap[project] = notificationsMap[project] || {}; |
| 176 | + notificationsMap[project][uid] = { |
| 177 | + actions: null, |
| 178 | + unread: true, |
| 179 | + uid: uid, |
| 180 | + // emulating an API event to simplify the template |
| 181 | + event: { |
| 182 | + lastTimestamp: notification.lastTimestamp || moment.parseZone(new Date()).utc().format(), |
| 183 | + message: notification.message, |
| 184 | + namespace: project, |
| 185 | + type: notificationEventTypeMap[notification.type] || 'Normal', |
| 186 | + links: notification.links |
| 187 | + } |
| 188 | + }; |
| 189 | + render(); |
| 190 | + }; |
218 | 191 |
|
219 |
| - var iconClassByEventSeverity = { |
220 |
| - Normal: 'pficon pficon-info', |
221 |
| - Warning: 'pficon pficon-warning-triangle-o' |
| 192 | + var watchEvents = function(projectName, cb) { |
| 193 | + deregisterEventsWatch(); |
| 194 | + if(projectName) { |
| 195 | + eventsWatcher = DataService.watch('events', {namespace: projectName}, _.debounce(cb, 400), { skipDigest: true }); |
| 196 | + } |
| 197 | + }; |
| 198 | + |
| 199 | + // NotificationsService notifications are minimal, they do no necessarily contain projectName info. |
| 200 | + // ATM tacking this on via watching the current project. |
| 201 | + var watchNotifications = function(projectName, cb) { |
| 202 | + deregisterNotificationListener(); |
| 203 | + if(!projectName) { |
| 204 | + return; |
| 205 | + } |
| 206 | + notificationListener = $rootScope.$on('NotificationsService.onNotificationAdded', cb); |
| 207 | + }; |
| 208 | + |
| 209 | + var reset = function() { |
| 210 | + getProject($routeParams.project).then(function() { |
| 211 | + watchEvents($routeParams.project, eventWatchCallback); |
| 212 | + watchNotifications($routeParams.project, notificationWatchCallback); |
| 213 | + hideIfNoProject($routeParams.project); |
| 214 | + render(); |
| 215 | + }); |
222 | 216 | };
|
223 | 217 |
|
224 | 218 | angular.extend(drawer, {
|
|
249 | 243 | render();
|
250 | 244 | $rootScope.$emit('NotificationDrawerWrapper.onMarkAllRead');
|
251 | 245 | },
|
252 |
| - notificationGroups: notificationGroups, |
| 246 | + notificationGroups: [], |
253 | 247 | headingInclude: 'views/directives/notifications/header.html',
|
254 | 248 | notificationBodyInclude: 'views/directives/notifications/notification-body.html',
|
255 | 249 | customScope: {
|
256 | 250 | clear: function(notification, index, group) {
|
257 | 251 | EventsService.markCleared(notification.event);
|
258 | 252 | group.notifications.splice(index, 1);
|
259 |
| - countUnreadNotificationsForAllGroups(); |
| 253 | + countUnreadNotifications(); |
260 | 254 | },
|
261 | 255 | markRead: function(notification) {
|
262 | 256 | notification.unread = false;
|
263 | 257 | EventsService.markRead(notification.event);
|
264 |
| - countUnreadNotificationsForAllGroups(); |
| 258 | + countUnreadNotifications(); |
265 | 259 | },
|
266 | 260 | getNotficationStatusIconClass: function(event) {
|
267 | 261 | return iconClassByEventSeverity[event.type] || iconClassByEventSeverity.info;
|
|
275 | 269 | }
|
276 | 270 | });
|
277 | 271 |
|
278 |
| - var projectChanged = function(next, current) { |
279 |
| - return _.get(next, 'params.project') !== _.get(current, 'params.project'); |
280 |
| - }; |
281 |
| - |
282 |
| - var reset = function() { |
283 |
| - getProject($routeParams.project).then(function() { |
284 |
| - watchEvents($routeParams.project, eventWatchCallback); |
285 |
| - //watchNotifications($routeParams.project, notificationWatchCallback); |
286 |
| - hideIfNoProject($routeParams.project); |
287 |
| - render(); |
288 |
| - }); |
289 |
| - }; |
290 | 272 |
|
291 | 273 | var initWatches = function() {
|
292 | 274 | if($routeParams.project) {
|
|
322 | 304 | deregisterEventsWatch();
|
323 | 305 | deregisterRootScopeWatches();
|
324 | 306 | };
|
325 |
| - |
326 | 307 | }
|
327 | 308 |
|
328 | 309 | })();
|
0 commit comments