superdesk/superdesk-client-core

View on GitHub
scripts/core/services/preferencesService.ts

Summary

Maintainability
C
1 day
Test Coverage
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] = {};
                    }
                });
            }
        }]);