scripts/core/services/preferencesService.ts
import {AUTHORING_FIELD_PREFERENCES} from 'core/constants';
export default angular.module('superdesk.core.preferences', ['superdesk.core.notify', 'superdesk.core.auth.session'])
/**
* @ngdoc service
* @module superdesk.core.services
* @name preferencesService
*
* @requires https://docs.angularjs.org/api/ng/service/$injector $injector
* @requires https://docs.angularjs.org/api/ng/service/$rootScope $rootScope
* @requires https://docs.angularjs.org/api/ng/service/$q $q
* @requires session
* @requires notify
*
* @description Preferences Service (TODO)
*/
.service('preferencesService', ['$injector', '$rootScope', '$q', 'session', 'notify', 'lodash',
function PreferencesService($injector, $rootScope, $q, session, notify, _) {
var USER_PREFERENCES = 'user_preferences',
SESSION_PREFERENCES = 'session_preferences',
ACTIVE_PRIVILEGES = 'active_privileges',
ACTIONS = 'allowed_actions',
userPreferences = {
'feature:preview': 1,
'archive:view': 1,
'email:notification': 1,
'desktop:notification': 1,
'slack:notification': 1,
'workqueue:items': 1,
'dashboard:ingest': 1,
'agg:view': 1,
'workspace:active': 1,
'categories:preferred': 1,
'desks:preferred': 1,
'destination:active': 1, // key to store last desk/stage for send to/fetch to.
'spellchecker:status': 1,
'singleline:view': 1,
'contacts:view': 1,
'monitoring:view': 1,
'cvs:preferred_items': 1,
'masterdesk:desks': 1,
'editor:pinned_widget': 1,
[AUTHORING_FIELD_PREFERENCES]: 1,
},
preferences,
preferencesPromise;
$rootScope.$watch(() => session.token, resetPreferences);
/**
* @ngdoc method
* @name preferencesService#getPrivileges
* @public
* @returns {Promise}
*
* @description Get privileges for current user.
*/
this.getPrivileges = function getPrivileges() {
return this.get().then(() => preferences[ACTIVE_PRIVILEGES] || {});
};
/**
* @ngdoc method
* @name preferencesService#getActions
* @public
*
* @returns {Promise}
*
* @description Get available content actions for current user.
*/
this.getActions = function getActions() {
return this.get().then(() => preferences[ACTIONS] || []);
};
/**
* @ngdoc object
* @name preferencesService#desktopNotification
* @public
*
* @returns {object}
*
* @description All the methods related to desktop notifications
*/
const desktopNotification = {
// ask for permission
requestPermission: () => {
if ('Notification' in window) {
Notification.requestPermission();
}
},
// ask for permission and send a desktop notification
send: (msg) => {
if (_.get(preferences, 'user_preferences.desktop:notification.enabled')) {
if ('Notification' in window && Notification.permission !== 'denied') {
Notification.requestPermission((permission) => {
if (permission === 'granted') {
// tslint:disable-next-line:no-unused-expression
new Notification(msg);
}
});
}
}
},
};
this.desktopNotification = desktopNotification;
/**
* @ngdoc method
* @name preferencesService#getPreferences
* @private
*
* @description Fetch preferences from server and store local copy.
* On next call it will remove local copy and fetch again.
*/
function getPreferences(cached) {
var api = $injector.get('api');
preferences = null;
preferencesPromise = session.getIdentity()
.then(fetchPreferences)
.then(null, (response) => {
if (response && response.status === 404) {
return fetchPreferences();
}
return $q.reject(response);
})
.then(setPreferences);
/**
* Fetch preferences for current session
*
* @return {Promise}
*/
function fetchPreferences() {
return api.find('preferences', session.sessionId, null, cached);
}
/**
* Set preferences to memory for further usage
*/
function setPreferences(_preferences) {
preferences = _preferences;
initPreferences(preferences);
return preferences;
}
}
/**
* Get preference value from user or session preferences based on key.
*
* @param {string} key
* @returns {Object}
*/
function getValue(key) {
if (!key) {
return preferences[USER_PREFERENCES];
} else if (userPreferences[key]) {
return preferences[USER_PREFERENCES][key];
}
return preferences[SESSION_PREFERENCES][key];
}
/**
* @ngdoc method
* @name preferencesService#get
* @private
* @returns {Promise}
*
* @param {string} key
* @param {boolean} force
*
* @description Get preference value, in case preferences are not
* loaded yet it will fetch it. Parameter force is used to bypass
* the cache.
*/
this.get = function(key, force) {
if (!preferencesPromise || force) {
getPreferences(!force);
}
return preferencesPromise.then(returnValue);
function returnValue() {
return getValue(key);
}
};
this.getSync = getValue;
this.registerUserPreference = function(key) {
userPreferences[key] = 1;
};
/**
* @ngdoc method
* @name preferencesService#update
* @private
*
* @param {object} updatesObject
* @param {string} key
*
* @description
* Update preferences
*
* It's done in 2 steps - schedule and commit. Schedule caches the changes
* and calls commit async. Following calls to update in same $digest will
* only cache changes. In next $digest those changes are pushed to api.
* This way we can update multiple preferences without getting etag conflicts.
*/
this.update = function(updatesObject, key) {
if (!key || userPreferences[key]) {
return scheduleUpdate(USER_PREFERENCES, updatesObject);
}
return scheduleUpdate(SESSION_PREFERENCES, updatesObject);
};
var updates,
deferUpdate;
/**
* Schedule an update.
*
* Cache the changes and schedule a commit if it's first update in currect $digest.
*
* @param {string} type
* @param {object} _updates
*/
function scheduleUpdate(type, _updates) {
angular.extend(preferences[type], _updates);
// schedule commit
if (!updates) {
updates = {};
deferUpdate = $q.defer();
$rootScope.$applyAsync(commitUpdates);
}
// adding updates to current schedule
updates[type] = updates[type] || {};
angular.extend(updates[type], _updates);
return deferUpdate.promise;
}
/**
* Commit updates.
*/
function commitUpdates() {
var api = $injector.get('api'),
serverUpdates = updates;
updates = null;
return api.save('preferences', preferences, serverUpdates)
.then((result) => {
preferences._etag = result._etag;
deferUpdate.resolve(result);
return result;
}, (response) => {
console.error(response);
deferUpdate.reject(response);
})
.finally(() => {
deferUpdate = null;
});
}
/**
* Make preferences reload after session expiry - token is set from something to null.
*/
function resetPreferences(newId, oldId) {
if (oldId && !newId) {
preferencesPromise = null;
}
}
/**
* Make sure all segments are presented in preferences.
*/
function initPreferences(prefs) {
if (_.get(prefs, 'user_preferences.desktop:notification.enabled')) {
desktopNotification.requestPermission();
}
angular.forEach([
USER_PREFERENCES,
SESSION_PREFERENCES,
ACTIVE_PRIVILEGES,
ACTIONS,
], (key) => {
if (_.isNil(prefs[key])) {
prefs[key] = {};
}
});
}
}]);