scripts/apps/desks/services/DesksFactory.ts
import _ from 'lodash';
import {gettext} from 'core/utils';
import {IDesk} from 'superdesk-api';
import {logger} from 'core/services/logger';
import {dispatchCustomEvent} from 'core/get-superdesk-api-implementation';
import {
DESK_OUTPUT,
SENT_OUTPUT,
SCHEDULED_OUTPUT,
HIGHLIGHTS,
} from '../constants';
import {initEntity} from 'core/data';
const OUTPUT_TYPES = [
DESK_OUTPUT,
SENT_OUTPUT,
SCHEDULED_OUTPUT,
];
const PUBLISH_TYPES = [
DESK_OUTPUT,
SENT_OUTPUT,
SCHEDULED_OUTPUT,
HIGHLIGHTS,
];
/**
* @ngdoc service
* @module superdesk.apps.desks
* @name desks
*
* @requires $q
* @requires api
* @requires preferencesService
* @requires userList
* @requires notify
* @requires session
* @requires $filter
* @requires privileges
*
* @description Desks Service is responsible for managing desks and stages
*/
DesksFactory.$inject = ['$q', 'api', 'preferencesService', 'userList', 'notify',
'session', '$filter', 'privileges', '$rootScope'];
export function DesksFactory($q, api, preferencesService, userList, notify,
session, $filter, privileges, $rootScope) {
let _cache = {};
var _fetchAll = function(endpoint, parent?, page = 1, items = [], refresh = false) {
let key;
if (page === 1) {
key = angular.toJson({resource: endpoint, parent: parent});
if (!refresh && _cache[key]) {
return _cache[key];
}
}
let promise = api.query(endpoint, {max_results: 200, page: page}, parent)
.then((result) => {
let pg = page;
let extended = items.concat(result._items);
if (result._links && result._links.next) {
pg++;
return _fetchAll(endpoint, parent, pg, extended, refresh);
}
return extended;
});
if (page === 1) {
_cache[key] = promise;
}
return promise;
};
/**
* Set desks.active which contains both desk and stage
* refs and is updated only when one of those is changed.
*/
function setActive(desks) {
if (desks.active && desks.active.desk === desks.activeDeskId && desks.active.stage === desks.activeStageId) {
// pass
return;
}
desks.active = {
desk: desks.activeDeskId,
stage: desks.activeStageId,
};
dispatchCustomEvent('activeDeskChanged', {
desk: desks.activeDeskId,
stage: desks.activeStageId,
});
}
var desksService = {
desks: null,
users: null,
stages: null,
deskLookup: {},
stageLookup: {},
userLookup: {},
deskMembers: {},
deskStages: {},
loading: null,
activeDeskId: null,
activeStageId: null,
active: {desk: null, stage: null},
/**
* @description Fetches all desks in the system.
* @returns {Promise}
*/
fetchDesks: function() {
var self = this;
return _fetchAll('desks')
.then((items) => {
let byName = $filter('sortByName')(items);
self.desks = {_items: byName};
_.each(byName, (item) => {
self.deskLookup[item._id] = item;
});
return self.desks;
});
},
fetchUsers: function() {
var self = this;
return userList.getAll()
.then((result) => {
self.users = {};
self.users._items = result;
_.each(result, (user) => {
self.userLookup[user._id] = user;
});
initEntity('users', self.users._items);
});
},
fetchStages: function(refresh = false) {
var self = this;
return _fetchAll('stages', undefined, undefined, undefined, refresh)
.then((items) => {
self.stages = {_items: items};
_.each(items, (item) => {
self.stageLookup[item._id] = item;
});
return self.stages;
});
},
fetchDeskStages: function(desk, refresh) {
var self = this;
if (self.deskStages[desk] && !refresh) {
return $q.when().then(returnDeskStages);
}
return self.fetchStages(refresh)
.then(angular.bind(self, self.generateDeskStages))
.then(returnDeskStages);
function returnDeskStages() {
return self.deskStages[desk];
}
},
generateDeskMembers: function() {
var self = this;
_.each(this.desks._items, (desk) => {
self.deskMembers[desk._id] = [];
_.each(desk.members, (member, index) => {
var user = _.find(self.users._items, {_id: member.user});
if (user) {
self.deskMembers[desk._id].push(user);
}
});
});
return $q.when();
},
generateDeskStages: function() {
var self = this;
this.deskStages = _.groupBy(self.stages._items, 'desk');
return $q.when();
},
fetchUserDesks: function(user) {
return _fetchAll('user_desks', {_id: user._id}).then((response) => {
if (!response) {
return;
}
return $q.when($filter('sortByName')(response));
});
},
/**
* Fetch current user desks and make sure active desk is present in there
*/
fetchCurrentUserDesks: function() {
var self = this;
if (self.userDesks) {
return $q.when(self.userDesks);
}
return this.fetchCurrentDeskId() // make sure there will be current desk
.then(angular.bind(session, session.getIdentity))
.then(angular.bind(this, this.fetchUserDesks))
.then(angular.bind(this, function(desks) {
self.userDesks = desks;
setActive(this);
return desks;
}));
},
fetchCurrentDeskId: function() {
var self = this;
if (self.activeDeskId) {
return $q.when(self.activeDeskId);
}
return preferencesService.get('desk:last_worked').then((result) => {
self.activeDeskId = null;
if (angular.isDefined(result) && result !== '') {
self.activeDeskId = result;
} else {
self.activeDeskId = self.getCurrentDeskId();
}
return self.activeDeskId;
});
},
fetchCurrentStageId: function() {
var self = this;
if (self.activeStageId) {
return $q.when(self.activeStageId);
}
return preferencesService.get('stage:items').then((result) => {
if (angular.isDefined(result)) {
self.activeStageId = angular.isArray(result) ? result[0] : result;
}
});
},
getCurrentDeskId: function(): IDesk['_id'] | null {
if (!this.userDesks || this.userDesks.length === 0) {
return null;
}
if (!this.activeDeskId || !_.find(this.userDesks, {_id: this.activeDeskId})) {
if (session.identity.desk) {
var defaultDesk = _.find(this.userDesks, {_id: session.identity.desk});
return defaultDesk && defaultDesk._id || this.userDesks[0]._id;
}
return this.userDesks[0]._id;
}
return this.activeDeskId;
},
setCurrentDeskId: function(deskId) {
if (this.activeDeskId !== deskId) {
this.activeDeskId = deskId;
this.activeStageId = null;
setActive(this);
preferencesService.update({
'desk:last_worked': this.activeDeskId,
'stage:items': [],
}, 'desk:last_worked');
}
},
getCurrentStageId: function() {
return this.activeStageId;
},
setCurrentStageId: function(stageId) {
if (this.activeStageId !== stageId) {
this.activeStageId = stageId;
setActive(this);
preferencesService.update({
'desk:last_worked': this.activeDeskId,
'stage:items': [this.activeStageId],
}, 'desk:last_worked');
}
},
fetchDeskById: function(Id) {
return api.desks.getById(Id).then((_desk) => {
return _desk;
}, () => {
logger.error(new Error('Something went wrong: desk not found'));
return Promise.reject();
});
},
getCurrentDesk: function() {
return this.deskLookup[this.getCurrentDeskId()] || null;
},
setWorkspace: function(deskId = null, stageId = null) {
if (this.activeDeskId !== deskId || this.activeStageId !== stageId) {
this.activeDeskId = deskId;
this.activeStageId = stageId;
setActive(this);
preferencesService.update({
'desk:last_worked': this.activeDeskId,
'stage:items': [this.activeStageId],
}, 'desk:last_worked');
}
},
initialize: function() {
if (!this.loading) {
this.fetchCurrentDeskId();
this.fetchCurrentStageId();
this.loading = this.fetchUsers()
.then(angular.bind(this, this.fetchDesks))
.then(angular.bind(this, this.generateDeskMembers))
.then(angular.bind(this, this.fetchStages))
.then(angular.bind(this, this.generateDeskStages))
.then(angular.bind(this, this.initActive));
}
return this.loading;
},
initActive: function() {
setActive(this);
},
save: function(desk, diff) {
return api.save('desks', desk, diff)
.then((res) => {
if (diff.members) {
// if desk members were changed update them inside deskMembers as well
const _deskMembers = [];
_.values(res.members).forEach((value) => {
_deskMembers.push(this.users._items.find((user) => user._id === value.user));
});
this.deskMembers[res._id] = _deskMembers;
}
return reset(res);
}, handleSaveError);
},
remove: function(desk) {
return api.remove(desk)
.then(reset);
},
refreshStages: function() {
return this.fetchStages().then(angular.bind(this, this.generateDeskStages));
},
refreshUsers: function() {
return this.fetchUsers().then(angular.bind(this, this.generateDeskMembers));
},
/**
* Get current desk for given item
*
* @param {Object} item
*/
getItemDesk: function(item) {
if (item.task && item.task.desk) {
return this.deskLookup[item.task.desk] || null;
}
},
isOutputType: (type) => OUTPUT_TYPES.includes(type),
isPublishType: (type) => PUBLISH_TYPES.includes(type),
isReadOnlyStage: function(stageId) {
return this.stageLookup[stageId] ? this.stageLookup[stageId].local_readonly : false;
},
/**
* @ngdoc method
* @name desks#markItem
* @public
* @description Toggles the marking for the given story
* @param {string} deskId
* @param {Object} markedItem
* @returns {Object}
*/
markItem: function(deskId, markedItem) {
return api.save('marked_for_desks', {marked_desk: deskId, marked_item: markedItem._id});
},
/**
* @ngdoc method
* @name desks#hasMarkItemPrivilege
* @public
* @description Checks if the current user has the privilege
* for marking stories for desks
* @returns {boolean}
*/
hasMarkItemPrivilege: function() {
return !!privileges.privileges.mark_for_desks;
},
};
$rootScope.$on('desk', reset);
$rootScope.$on('stage', reset);
// re-fetch stages when order changes for any stage
$rootScope.$on('resource:updated', (event, data) => {
if (data.resource === 'stages' && data.fields?.['desk_order'] === 1) {
desksService.fetchStages(true).then(() => {
desksService.generateDeskStages();
});
}
});
return desksService;
function reset(res) {
desksService.loading = null;
_cache = {};
return res;
}
function handleSaveError(response) {
if (response.status === 412) {
notify.error(gettext('Desk has been modified elsewhere. Please reload the desks.'));
}
return $q.reject(response);
}
}