@uportal/app-framework
Version:
Application Framework for uPortal
440 lines (396 loc) • 16.3 kB
JavaScript
/*
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo 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 the following location:
*
* 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.
*/
'use strict';
define(['angular'], function(angular) {
return angular.module('portal.messages.controllers', [])
.controller('MessagesController', [
'$q', '$log', '$scope', '$rootScope', '$location', '$localStorage',
'$sessionStorage', '$filter', '$mdDialog', 'APP_FLAGS', 'MISC_URLS',
'SERVICE_LOC', 'mainService', 'miscService', 'messagesService',
function($q, $log, $scope, $rootScope, $location, $localStorage,
$sessionStorage, $filter, $mdDialog, APP_FLAGS, MISC_URLS,
SERVICE_LOC, mainService, miscService, messagesService) {
// //////////////////
// Local variables //
// //////////////////
var allMessages = [];
$scope.APP_FLAGS = APP_FLAGS;
$scope.MISC_URLS = MISC_URLS;
$scope.showMessagesFeatures = false;
// ////////////////
// Local methods //
// ////////////////
/**
* Get all messages, then pass result on for filtering
*/
var getMessages = function() {
messagesService.getAllMessages()
.then(function(result) {
// Ensure messages exist and check for group filtering
if (angular.isArray(result) && result.length > 0) {
allMessages = result;
} else if (angular.isString(result)) {
$scope.messagesError = result;
}
if ( $localStorage.showAllMessages ) {
// simulate the side effect of filterMessages
$scope.messages = allMessages;
$scope.hasMessages = true;
$scope.seenMessageIds = messagesService.getSeenMessageIds();
} else {
filterMessages(allMessages);
// side effects:
// sets $scope.messages and $scope.hasMessages
// and $scope.seenMessageIds
}
return allMessages;
})
.catch(function(error) {
$log.warn('Problem getting all messages for messages controller');
$log.warn(error);
return error;
});
};
// Promise to get seen message IDs
var promiseSeenMessageIds = function() {
return messagesService.getSeenMessageIds();
};
// Promise to resolve urls in messages
var promiseMessagesByData = function(messages) {
messagesService.getMessagesByData(messages)
.then(dataMessageSuccess)
.catch(filterMessagesFailure);
};
// Promise to resolve group memberships
var promiseMessagesByGroup = function(messages) {
return messagesService.getMessagesByGroup(messages);
};
/**
* Determine whether or not messages need to be filtered
* by group and data, then execute the relevant promises
* @param {Object} allMessages
*/
var filterMessages = function(allMessages) {
var dateFilterMessages =
$filter('filterByDate')(allMessages);
var groupPromise = promiseMessagesByGroup(dateFilterMessages);
$q.all([promiseSeenMessageIds(),
groupPromise])
.then(filterMessagesSuccess)
.catch(filterMessagesFailure);
};
/**
* Separate the message types in scope for child controllers
* @param {Object} result
*/
var filterMessagesSuccess = function(result) {
$scope.seenMessageIds = result[0];
var grpMsg = result[1];
var seenAndUnseen =
$filter('filterSeenAndUnseen')(grpMsg, $scope.seenMessageIds);
$q.all(promiseMessagesByData(seenAndUnseen.unseen));
};
var dataMessageSuccess = function(result) {
$scope.messages = result;
$scope.hasMessages = true;
};
/**
* Handle errors that occur while resolving promises to
* get notifications
* @param {Object} error
*/
var filterMessagesFailure = function(error) {
$log.warn('Problem getting messages from messagesService');
};
/**
* Get messages if messaging features are enabled
*/
var init = function() {
$scope.hasMessages = false;
$scope.messages = {};
mainService.isGuest().then(function(isGuest) {
$scope.guestMode = isGuest;
if (!isGuest && SERVICE_LOC.messagesURL &&
SERVICE_LOC.messagesURL.length > 0) {
$scope.showMessagesFeatures = true;
getMessages();
}
return isGuest;
}).catch(function() {
$log.warn('Problem checking guestMode');
});
};
init();
},
])
.controller('NotificationsController', ['$q', '$log', '$scope', '$window',
'$rootScope', '$location', '$localStorage', '$filter', 'MESSAGES',
'SERVICE_LOC', 'miscService', 'messagesService', 'orderByFilter',
function($q, $log, $scope, $window, $rootScope, $location, $localStorage,
$filter, MESSAGES, SERVICE_LOC, miscService, messagesService,
orderByFilter) {
// //////////////////
// Local variables //
// //////////////////
var vm = this;
var allNotifications = [];
var separatedNotifications = {};
var dismissedNotificationIds = [];
var allSeenMessageIds = [];
// Promise to get seen message IDs
var promiseSeenMessageIds = {
seenMessageIds: messagesService.getSeenMessageIds(),
};
// ///////////////////
// Bindable members //
// ///////////////////
vm.notifications = [];
vm.dismissedNotifications = [];
vm.priorityNotifications = [];
vm.notificationsUrl = MESSAGES.notificationsPageURL;
vm.status = 'View notifications';
vm.isLoading = true;
vm.renderLimit = 3;
vm.titleLengthLimit = 140;
// //////////////////
// Event listeners //
// //////////////////
/**
* Process event where notifications have changed,
* i.e. a dismissal, and ensure that all instances of the
* controller are updated.
*/
var notificationChange = $rootScope.$on('notificationChange',
function() {
configureNotificationsScope();
configurePriorityNotificationsScope();
});
/**
* When the parent controller has messages, initialize
* things dependent on messages
*/
$scope.$watch('$parent.hasMessages', function(hasMessages) {
// If the parent scope has messages and notifications are enabled,
// complete initialization
if (hasMessages) {
// Check if messages service failed
if ($scope.$parent.messagesError) {
vm.messagesError = $scope.$parent.messagesError;
}
// If messages config is set up, configure scope
// Else hide messages features
if (angular.equals($scope.$parent.showMessagesFeatures, true)) {
configureNotificationsScope();
} else {
vm.showMessagesFeatures = false;
vm.isLoading = false;
}
}
});
// ////////////////
// Local methods //
// ////////////////
/**
* Get notifications from parent scope, then pass them on
* for filtering by seen/unseen
*/
var configureNotificationsScope = function() {
if ($scope.$parent.messages) {
allNotifications = $scope.$parent.messages;
vm.showMessagesFeatures = true;
// Get seen message IDs, then configure scope
$q.all(promiseSeenMessageIds)
.then(getSeenMessageIdsSuccess)
.catch(getSeenMessageIdsFailure);
}
};
/**
* Separate seen and unseen notifications, then set scope variables
* @param {Object} result
*/
var getSeenMessageIdsSuccess = function(result) {
if (result.seenMessageIds && angular.isArray(result.seenMessageIds)
&& result.seenMessageIds.length > 0) {
// Save all seenMessageIds for later
allSeenMessageIds = result.seenMessageIds;
// Separate seen and unseen
separatedNotifications = $filter('filterSeenAndUnseen')(
allNotifications,
result.seenMessageIds
);
// Set scope notifications and dismissed notifications
vm.notifications = separatedNotifications.unseen;
vm.dismissedNotifications = separatedNotifications.seen;
// Set local dismissedNotificationIds (used for tracking
// dismissed messages in the K/V store
angular.forEach(vm.dismissedNotifications, function(value) {
dismissedNotificationIds.push(value.id);
});
} else {
// If there aren't any seen notification IDs, just set all
// notifications
vm.notifications = allNotifications;
}
// Configure priority notifications scope
configurePriorityNotificationsScope();
// Set aria-label in notifications bell
vm.status = 'You have '
+ (vm.notifications.length === 0
? 'no' : vm.notifications.length)
+ ' notifications';
// Stop loading spinner
vm.isLoading = false;
};
/**
* Handle errors getting seen message IDs
* @param {Object} error
*/
var getSeenMessageIdsFailure = function(error) {
$log.warn('Couldn\'t get seen message IDs for notifications ' +
' controller.');
// Stop loading spinner
vm.isLoading = false;
};
/**
* Alert the UI to show priority notifications if they exist
*/
var configurePriorityNotificationsScope = function() {
// Use angular's built-in filter to grab priority notifications
vm.priorityNotifications = $filter('filter')(
vm.notifications,
{priority: 'high'}
);
// If priority notifications exist, notify listeners
messagesService.broadcastPriorityFlag(
vm.priorityNotifications.length > 0
);
// If there is only one priority notification, track
// rendering in analytics
if (vm.priorityNotifications.length === 1) {
vm.pushGAEvent(
'Priority notification',
'Render',
vm.priorityNotifications[0].id
);
}
};
/**
* Alerts the UI that there are no priority notifications to show
*/
var clearPriorityNotificationsFlags = function() {
vm.priorityNotifications = [];
// Notify listeners that priority notifications are gone
messagesService.broadcastPriorityFlag(false);
};
// ////////////////
// Scope methods //
// ////////////////
/**
* Check if user is viewing notifications page
* @return {boolean}
*/
vm.isNotificationsPage = function() {
return $window.location.pathname === MESSAGES.notificationsPageURL;
};
/**
* On-click event to mark a notification as "seen"
* @param {Object} notification
* @param {boolean} isHighPriority
*/
vm.dismissNotification = function(notification, isHighPriority) {
vm.notifications = $filter('filterOutMessageWithId')(
vm.notifications,
notification.id
);
// Add notification to dismissed array
vm.dismissedNotifications.push(notification);
// Add notification's ID to local array of dismissed notification IDs
dismissedNotificationIds.push(notification.id);
// Call service to save changes if k/v store enabled
if (SERVICE_LOC.kvURL) {
messagesService.setMessagesSeen(allSeenMessageIds,
dismissedNotificationIds, 'dismiss');
}
// Clear priority notification flags if it was a priority
// notification
if (isHighPriority) {
clearPriorityNotificationsFlags();
}
};
/**
* On-click event to mark a notification as "unseen"
* @param {Object} notification
* @param {boolean} isHighPriority
*/
vm.restoreNotification = function(notification, isHighPriority) {
// Remove notification from dismissed array
vm.dismissedNotifications = $filter('filterOutMessageWithId')(
vm.dismissedNotifications,
notification.id
);
// Add notification to non-dismissed array
vm.notifications.push(notification);
// Remove the corresponding entry from
// local array of dismissed notification IDs
var index = dismissedNotificationIds.indexOf(notification.id);
if (index !== -1) {
dismissedNotificationIds.splice(index, 1);
}
// Call service to save changes if k/v store enabled
if (SERVICE_LOC.kvURL) {
messagesService.setMessagesSeen(allSeenMessageIds,
dismissedNotificationIds, 'restore');
}
notificationChange();
};
/**
* Log a Google Analytics event for each notification rendered
* when a user opens the notifications bell menu
*/
vm.trackRenderedNotifications = function() {
// Order notifications by priority flag
var orderedNotifications = orderByFilter(
vm.notifications,
'priority'
);
// Slice array to first 3 entries (the ones that would be rendered)
orderedNotifications = $filter('limitTo')(
orderedNotifications,
vm.renderLimit
);
// Log a render event for each rendered notification
angular.forEach(orderedNotifications, function(notification) {
vm.pushGAEvent(
'Notification menu',
'Rendered notification',
notification.id
);
});
};
/**
* Track clicks on "Notifications" links in mobile menu and top bar
* @param {string} category - Context of the event
* @param {string} action - Action taken
* @param {string} label - Label/data pertinent to event
*/
vm.pushGAEvent = function(category, action, label) {
miscService.pushGAEvent(category, action, label);
};
}]);
});