|
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 | + var projects = {}; |
63 | 58 |
|
| 59 | + var hideIfNoProject = function(projectName) { |
| 60 | + if(!projectName) { |
| 61 | + drawer.drawerHidden = true; |
| 62 | + } |
| 63 | + }; |
64 | 64 |
|
65 |
| - var projects = {}; |
| 65 | + var projectChanged = function(next, current) { |
| 66 | + return _.get(next, 'params.project') !== _.get(current, 'params.project'); |
| 67 | + }; |
66 | 68 |
|
67 | 69 | var getProject = function(projectName) {
|
68 | 70 | return DataService
|
|
73 | 75 | });
|
74 | 76 | };
|
75 | 77 |
|
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; |
| 78 | + var makeProjectGroup = function(projectName, notifications) { |
| 79 | + return { |
| 80 | + heading: $filter('displayName')(projects[projectName]), |
| 81 | + project: projects[projectName], |
| 82 | + notifications: notifications |
| 83 | + }; |
112 | 84 | };
|
113 | 85 |
|
114 | 86 | var unread = function(notifications) {
|
115 | 87 | return _.filter(notifications, 'unread');
|
116 | 88 | };
|
117 | 89 |
|
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() { |
| 90 | + |
| 91 | + // currently we only show 1 at a time anyway |
| 92 | + var countUnreadNotifications = function() { |
| 93 | + _.each(drawer.notificationGroups, function(group) { |
126 | 94 | group.totalUnread = unread(group.notifications).length;
|
127 | 95 | group.hasUnread = !!group.totalUnread;
|
128 | 96 | $rootScope.$emit('NotificationDrawerWrapper.count', group.totalUnread);
|
129 | 97 | });
|
130 | 98 | };
|
131 | 99 |
|
132 |
| - // currently we only show 1 at a time anyway |
133 |
| - var countUnreadNotificationsForAllGroups = function() { |
134 |
| - _.each(notificationGroups, countUnreadNotificationsForGroup); |
| 100 | + // returns a map of filtered events ONLY. |
| 101 | + // will worry about unread, actions, etc in render. |
| 102 | + var formatAndFilterAPIEvents = function(events) { |
| 103 | + return _.reduce(events, function(result, event) { |
| 104 | + if(EventsService.isImportantAPIEvent(event) && !EventsService.isCleared(event.id)) { |
| 105 | + result[event.metadata.uid] = { |
| 106 | + actions: null, |
| 107 | + uid: event.metadata.uid, |
| 108 | + unread: !EventsService.isRead(event.uid), |
| 109 | + type: event.type, |
| 110 | + timestamp: event.lastTimestamp, |
| 111 | + event: event |
| 112 | + }; |
| 113 | + } |
| 114 | + return result; |
| 115 | + }, {}); |
135 | 116 | };
|
136 | 117 |
|
137 |
| - var sortNotifications = function(notifications) { |
138 |
| - return _.orderBy(notifications, ['event.lastTimestamp', 'event.firstTimestamp'], ['desc', 'desc']); |
| 118 | + // we have to keep notifications & events separate as |
| 119 | + // notifications are ephemerial, but events have a time to live |
| 120 | + // set by the server. we can merge them right before we update |
| 121 | + // the UI. |
| 122 | + var mergeMaps = function(firstMap, secondMap) { |
| 123 | + var proj = $routeParams.project; |
| 124 | + return _.assign({}, firstMap[proj], secondMap[proj]); |
139 | 125 | };
|
140 | 126 |
|
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; |
| 127 | + // |
| 128 | + var sortMap = function(map) { |
| 129 | + return _.orderBy(map, ['event.lastTimestamp', 'event.firstTimestamp'], ['desc', 'desc']); |
152 | 130 | };
|
153 | 131 |
|
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 |
| - } |
| 132 | + var render = function() { |
| 133 | + $rootScope.$evalAsync(function() { |
| 134 | + drawer.notificationGroups = [ |
| 135 | + makeProjectGroup($routeParams.project, sortMap( mergeMaps(eventsMap, notificationsMap ))) |
| 136 | + ]; |
| 137 | + countUnreadNotifications(); |
166 | 138 | });
|
167 |
| - return filtered; |
168 | 139 | };
|
169 | 140 |
|
170 | 141 | var deregisterRootScopeWatches = function() {
|
|
174 | 145 | rootScopeWatches = [];
|
175 | 146 | };
|
176 | 147 |
|
177 |
| - var hideIfNoProject = function(projectName) { |
178 |
| - if(!projectName) { |
179 |
| - drawer.drawerHidden = true; |
| 148 | + var deregisterEventsWatch = function() { |
| 149 | + if(eventsWatcher) { |
| 150 | + DataService.unwatch(eventsWatcher); |
| 151 | + eventsWatcher = null; |
180 | 152 | }
|
181 | 153 | };
|
182 | 154 |
|
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 |
| - }); |
| 155 | + var deregisterNotificationListener = function() { |
| 156 | + notificationListener && notificationListener(); |
| 157 | + notificationListener = null; |
196 | 158 | };
|
197 | 159 |
|
198 |
| - // TODO: follow-on PR to decide which of these events to toast, |
199 |
| - // via config in constants.js |
200 | 160 | 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); |
| 161 | + eventsMap[$routeParams.project] = formatAndFilterAPIEvents(eventData.by('metadata.name')); |
| 162 | + render(); |
| 163 | + }; |
| 164 | + |
| 165 | + var notificationWatchCallback = function(event, notification) { |
| 166 | + var project = notification.namespace || $routeParams.project; |
| 167 | + notificationsMap[project] = notificationsMap[project] || {}; |
| 168 | + |
| 169 | + notificationsMap[project][notification.id] = { |
| 170 | + actions: null, |
| 171 | + unread: !EventsService.isRead(notification.id), |
| 172 | + // using uid to match API events and have one filed to pass |
| 173 | + // to EventsService for read/cleared, etc |
| 174 | + uid: notification.id, |
| 175 | + type: notification.type, |
| 176 | + timestamp: notification.timestamp, |
| 177 | + message: notification.message, |
| 178 | + namespace: project, |
| 179 | + links: notification.links |
| 180 | + }; |
206 | 181 | render();
|
207 | 182 | };
|
208 | 183 |
|
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 |
| - // }; |
218 |
| - |
219 |
| - var iconClassByEventSeverity = { |
220 |
| - Normal: 'pficon pficon-info', |
221 |
| - Warning: 'pficon pficon-warning-triangle-o' |
| 184 | + var watchEvents = function(projectName, cb) { |
| 185 | + deregisterEventsWatch(); |
| 186 | + if(projectName) { |
| 187 | + eventsWatcher = DataService.watch('events', {namespace: projectName}, _.debounce(cb, 400), { skipDigest: true }); |
| 188 | + } |
| 189 | + }; |
| 190 | + |
| 191 | + var watchNotifications = _.once(function(projectName, cb) { |
| 192 | + deregisterNotificationListener(); |
| 193 | + notificationListener = $rootScope.$on('NotificationsService.onNotificationAdded', cb); |
| 194 | + }); |
| 195 | + |
| 196 | + var reset = function() { |
| 197 | + getProject($routeParams.project).then(function() { |
| 198 | + watchEvents($routeParams.project, eventWatchCallback); |
| 199 | + watchNotifications($routeParams.project, notificationWatchCallback); |
| 200 | + hideIfNoProject($routeParams.project); |
| 201 | + render(); |
| 202 | + }); |
222 | 203 | };
|
223 | 204 |
|
224 | 205 | angular.extend(drawer, {
|
|
235 | 216 | onMarkAllRead: function(group) {
|
236 | 217 | _.each(group.notifications, function(notification) {
|
237 | 218 | notification.unread = false;
|
238 |
| - EventsService.markRead(notification.event); |
| 219 | + EventsService.markRead(notification.uid); |
239 | 220 | });
|
240 | 221 | render();
|
241 | 222 | $rootScope.$emit('NotificationDrawerWrapper.onMarkAllRead');
|
242 | 223 | },
|
243 | 224 | onClearAll: function(group) {
|
244 | 225 | _.each(group.notifications, function(notification) {
|
245 |
| - EventsService.markRead(notification.event); |
246 |
| - EventsService.markCleared(notification.event); |
| 226 | + notification.unread = false; |
| 227 | + EventsService.markRead(notification.uid); |
| 228 | + EventsService.markCleared(notification.uid); |
247 | 229 | });
|
248 |
| - group.notifications = []; |
| 230 | + eventsMap[$routeParams.project] = {}; |
| 231 | + notificationsMap[$routeParams.project] = {}; |
249 | 232 | render();
|
250 | 233 | $rootScope.$emit('NotificationDrawerWrapper.onMarkAllRead');
|
251 | 234 | },
|
252 |
| - notificationGroups: notificationGroups, |
| 235 | + notificationGroups: [], |
253 | 236 | headingInclude: 'views/directives/notifications/header.html',
|
254 | 237 | notificationBodyInclude: 'views/directives/notifications/notification-body.html',
|
255 | 238 | customScope: {
|
256 | 239 | clear: function(notification, index, group) {
|
257 |
| - EventsService.markCleared(notification.event); |
| 240 | + EventsService.markCleared(notification.uid); |
258 | 241 | group.notifications.splice(index, 1);
|
259 |
| - countUnreadNotificationsForAllGroups(); |
| 242 | + countUnreadNotifications(); |
260 | 243 | },
|
261 | 244 | markRead: function(notification) {
|
262 | 245 | notification.unread = false;
|
263 |
| - EventsService.markRead(notification.event); |
264 |
| - countUnreadNotificationsForAllGroups(); |
265 |
| - }, |
266 |
| - getNotficationStatusIconClass: function(event) { |
267 |
| - return iconClassByEventSeverity[event.type] || iconClassByEventSeverity.info; |
268 |
| - }, |
269 |
| - getStatusForCount: function(countKey) { |
270 |
| - return iconClassByEventSeverity[countKey] || iconClassByEventSeverity.info; |
| 246 | + EventsService.markRead(notification.uid); |
| 247 | + countUnreadNotifications(); |
271 | 248 | },
|
272 | 249 | close: function() {
|
273 | 250 | drawer.drawerHidden = true;
|
274 | 251 | }
|
275 | 252 | }
|
276 | 253 | });
|
277 | 254 |
|
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 | 255 |
|
291 | 256 | var initWatches = function() {
|
292 | 257 | if($routeParams.project) {
|
|
322 | 287 | deregisterEventsWatch();
|
323 | 288 | deregisterRootScopeWatches();
|
324 | 289 | };
|
325 |
| - |
326 | 290 | }
|
327 | 291 |
|
328 | 292 | })();
|
0 commit comments