123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635 |
- 'use strict';
- function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
- function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
- function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
- /**
- * Licensed Materials - Property of IBM
- * IBM Cognos Products: BI
- * (C) Copyright IBM Corp. 2018, 2020
- * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
- */
- define(['../../../lib/@waca/core-client/js/core-client/ui/core/Events', '../../../app/nls/StringResources', 'underscore', '../../../lib/@waca/core-client/js/core-client/i18n/Formatter', 'moment-timezone'], function (Events, StringResources, _, Formatter, moment) {
- var DashboardPinningService = function (_Events) {
- _inherits(DashboardPinningService, _Events);
- function DashboardPinningService() {
- _classCallCheck(this, DashboardPinningService);
- var _this = _possibleConstructorReturn(this, _Events.call(this, arguments));
- _this.PIN_VERSION = '3';
- _this.PIN_URL = 'v1/users/~/pins';
- _this._pinIdsToUpgradeInCM = [];
- _this._timeframePinBuckets = null;
- _this._timeframePinBucketNames = ['all', 'today', 'yesterday', 'pastWeek', 'pastMonth', 'earlier'];
- _this._pinIdMap = {};
- return _this;
- }
- DashboardPinningService.prototype.initialize = function initialize(_ref) {
- var _this2 = this;
- var appController = _ref.appController;
- this._glassContext = appController.glassContext;
- return this._glassContext.getSvc('.UpgradeService').then(function (upgrades) {
- _this2._latestDashboardSpecVersion = upgrades.getLatestDashboardSpecVersion();
- // Do some work ahead of time to figure out our time buckets
- var userProfileService = _this2._glassContext.getCoreSvc('.UserProfile');
- _this2._timezone = userProfileService.preferences.timeZoneID || 'America/New_York';
- _this2._today = moment().tz(_this2._timezone).startOf('day');
- _this2._yesterday = _this2._today.clone().subtract(1, 'days');
- _this2._pastWeek = _this2._today.clone().subtract(7, 'days');
- _this2._pastMonth = _this2._today.clone().subtract(31, 'days');
- });
- };
- /**
- * Used by search UI to get the cached pins. This UI only renders after we've queried for the pins,
- * so we return without a promise here which is what the search logic needs.
- * @param {String} timeframe
- */
- DashboardPinningService.prototype.getCachedPins = function getCachedPins() {
- var timeframe = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'all';
- return this._timeframePinBuckets[timeframe] || [];
- };
- /**
- * Get the list of pins from the server
- * @param {String} timeframe - Must be one of the following 'all', 'today', 'yesterday', 'pastWeek', 'pastMonth', 'earlier'
- */
- DashboardPinningService.prototype.getPins = function getPins() {
- var _this3 = this;
- var timeframe = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'all';
- if (this._timeframePinBuckets) {
- return Promise.resolve(this._timeframePinBuckets[timeframe]);
- }
- return this._glassContext.getCoreSvc('.Ajax').ajax({
- url: this.PIN_URL,
- type: 'GET',
- contentType: 'application/json; charset=utf-8',
- dataType: 'json'
- }).then(function () {
- var response = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
- _this3._timeframePinBuckets = { all: [], today: [], yesterday: [], pastWeek: [], pastMonth: [], earlier: [] };
- // Upgrade the pins locally (on the client) before returning them. We'll update the pins on the server lazilly
- return _this3._upgradePinsLocally(response.data);
- }).then(function (upgradedPins) {
- var pins = _this3._sortPins(upgradedPins);
- _this3._prepPinsForUI(pins);
- _this3._cachePinsInAppropriateTimeFrameBuckets(pins);
- // No need to wait for this to finish before the UI can render
- _this3._slowlySaveUpgradedPinsInCM();
- return _this3._timeframePinBuckets[timeframe];
- }).catch(function (result) {
- _this3._glassContext.getCoreSvc('.Logger').error('Failed to get pins', result);
- var message = _this3._retrieveErrorMessage({
- result: result,
- defaultMessage: StringResources.get('pinRetrieveError')
- });
- _this3._glassContext.appController.showErrorMessage(message, StringResources.get('pinErrorTitle'));
- return [];
- });
- };
- /**
- * Get the pin for the given ID
- * @param {String} pinId
- */
- DashboardPinningService.prototype.getPin = function getPin(pinId) {
- if (this._pinIdMap[pinId]) {
- return Promise.resolve(this._pinIdMap[pinId]);
- }
- // We should never ask for a specific pin before querying all of them.. safe guard in case we do for some odd reason.
- return this.getPins().then(function (pins) {
- return _.find(pins, function (pin) {
- return pin.id === pinId;
- });
- });
- };
- /**
- * Saves a pin in CM
- * @param {Object} pin Pin object to save
- */
- DashboardPinningService.prototype.addPin = function addPin(pin) {
- var _this4 = this;
- pin.version = this.PIN_VERSION;
- var pinJSON = JSON.stringify(pin);
- return this._glassContext.getCoreSvc('.Ajax').ajax({
- type: 'POST',
- url: this.PIN_URL,
- contentType: 'application/json; charset=utf-8',
- dataType: 'json',
- data: pinJSON
- }).then(function () {
- var result = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
- var newPin = result.data;
- _this4._prepPinsForUI([newPin]);
- _this4._addPinToCache(newPin, ['all', 'today', 'pastWeek', 'pastMonth'], true);
- _this4.trigger('pin:created', newPin);
- return newPin;
- }).catch(function (result) {
- var message = _this4._retrieveErrorMessage({
- result: result,
- defaultMessage: StringResources.get('contentPinFailOld')
- });
- var error = new Error(message);
- _this4._glassContext.getCoreSvc('.Logger').error('addPin error', error);
- throw error;
- });
- };
- /**
- * Removes the pin locally and shows a toast. Once the toast goes away (can no longer undo)
- * we complete the delete by deleting it from CM
- * @param {String} pinId
- */
- DashboardPinningService.prototype.deletePin = function deletePin(pinId) {
- var _this5 = this;
- return new Promise(function (resolve, reject) {
- try {
- if (_this5._glassContext.getCoreSvc('.UserProfile').preferences.accessibilityFeatures) {
- _this5._glassContext.appController.showMessage(StringResources.get('deletePinConfirmationMessage'), StringResources.get('deletePinConfirm'), 'info', ['ok', 'cancel'], undefined, function (evt) {
- if (evt.btn === 'ok') {
- _this5._removePinFromCache(pinId);
- _this5.trigger('pin:fakeDeleted', pinId);
- _this5._completePinDelete(pinId);
- }
- resolve();
- });
- } else {
- var removedInfo = _this5._removePinFromCache(pinId);
- if (!removedInfo.removedPin) {
- _this5._glassContext.getCoreSvc('.Logger').error('Trying to remove a pin that is not in our array', pinId, _this5);
- }
- _this5.trigger('pin:fakeDeleted', pinId);
- var undidDelete = false;
- _this5._glassContext.appController.showToast(StringResources.get('pinDeletedToast'), {
- type: 'info',
- btnLabel: StringResources.get('undo'),
- callback: function callback() {
- undidDelete = true;
- _this5._addPinToCache(removedInfo.removedPin, removedInfo.timeframeBucketNames, false, true);
- _this5.trigger('pin:undoDeletion', pinId);
- },
- newestOnTop: true,
- preventDuplicates: false,
- timeOut: 6000,
- extendedTimeOut: 1000,
- onHidden: function onHidden() {
- if (!undidDelete) {
- _this5._completePinDelete(pinId);
- }
- resolve();
- }
- });
- }
- } catch (error) {
- reject(error);
- }
- });
- };
- /**
- * The toast message is gone so it's safe to delete the pin from CM
- */
- DashboardPinningService.prototype._completePinDelete = function _completePinDelete(pinId) {
- var _this6 = this;
- // If the pin we've just deleted is still in the queue to update in CM then remove it from the queue
- this._pinIdsToUpgradeInCM = this._pinIdsToUpgradeInCM.filter(function (id) {
- return id !== pinId;
- });
- return this._glassContext.getCoreSvc('.Ajax').ajax({
- type: 'DELETE',
- url: this.PIN_URL + '/' + pinId,
- contentType: 'application/json; charset=utf-8',
- dataType: 'json'
- }).then(function () {
- var result = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
- _this6.trigger('pin:deleted', pinId);
- return result;
- }).catch(function (result) {
- var message = _this6._retrieveErrorMessage({
- result: result,
- defaultMessage: StringResources.get('pinDeleteErrorOld')
- });
- var error = new Error(message);
- _this6._glassContext.appController.showErrorMessage(message, StringResources.get('pinErrorTitle'));
- _this6._glassContext.getCoreSvc('.Logger').error('deletePin error', error);
- throw error;
- });
- };
- /**
- * Go through the upgraded pins one by one and push the upgraded spec to CM. Only
- * send one PUT request at a time to not hurt performance for the user.
- */
- DashboardPinningService.prototype._slowlySaveUpgradedPinsInCM = function _slowlySaveUpgradedPinsInCM() {
- var _this7 = this;
- var pinId = this._pinIdsToUpgradeInCM.shift();
- if (pinId) {
- return this.getPin(pinId).then(function (pin) {
- _this7._glassContext.getCoreSvc('.Logger').info('Updating upgraded pin spec in CM', pin);
- return _this7._glassContext.getCoreSvc('.Ajax').ajax({
- type: 'PUT',
- url: _this7.PIN_URL + '/' + pinId,
- contentType: 'application/json; charset=utf-8',
- dataType: 'json',
- data: JSON.stringify(pin)
- }).then(function () {
- return _this7._slowlySaveUpgradedPinsInCM();
- }).catch(function (error) {
- // Stop trying to save the upgrades on the first error. Possible timeout or logged off.
- _this7._glassContext.getCoreSvc('.Logger').error('Pin upgrade failed to save in CM.', error);
- });
- });
- }
- return Promise.resolve();
- };
- /**
- * Removes a pin from our caches
- * @param {String} pinId
- */
- DashboardPinningService.prototype._removePinFromCache = function _removePinFromCache(pinId) {
- var _this8 = this;
- if (this._pinIdMap) {
- delete this._pinIdMap[pinId];
- }
- // Need to keep track of the timeframe buckets this pin was in case the user undoes the delete
- var timeframeBucketNames = [];
- var removedPin = null;
- if (this._timeframePinBuckets) {
- var filterOutPin = function filterOutPin(pin, timeframeBucketName) {
- if (pin.id === pinId) {
- removedPin = pin;
- timeframeBucketNames.push(timeframeBucketName);
- return false;
- }
- return true;
- };
- this._timeframePinBucketNames.forEach(function (bucketName) {
- _this8._timeframePinBuckets[bucketName] = _this8._timeframePinBuckets[bucketName].filter(function (pin) {
- return filterOutPin(pin, bucketName);
- });
- });
- }
- return {
- removedPin: removedPin,
- timeframeBucketNames: timeframeBucketNames
- };
- };
- /**
- * Adds a pin to our caches
- * @param {Object} pin - the pin to add
- * @param {[String]} buckets - the list of buckets to add the pin to
- * @param {Boolean} addToBeginning - Should the pin get added to the beginning of the list
- * @param {Boolean} sortPins - should the timeframe bucket be sorted after the pin gets added.
- */
- DashboardPinningService.prototype._addPinToCache = function _addPinToCache(pin) {
- var buckets = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this._timeframePinBucketNames;
- var _this9 = this;
- var addToBeginning = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
- var sortPins = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
- // If we haven't queried for all the pins yet then no need to add the pin. We'll grab them
- // all when the pin panel is open.
- if (!this._timeframePinBuckets) {
- return;
- }
- this._pinIdMap[pin.id] = pin;
- buckets.forEach(function (bucket) {
- if (addToBeginning) {
- _this9._timeframePinBuckets[bucket].unshift(pin);
- } else {
- _this9._timeframePinBuckets[bucket].push(pin);
- }
- // Currently only true when we undo a delete. We need to resort the buckets once the pin is placed back
- if (sortPins) {
- _this9._sortPins(_this9._timeframePinBuckets[bucket]);
- }
- });
- };
- DashboardPinningService.prototype._retrieveErrorMessage = function _retrieveErrorMessage(input) {
- input = input || {};
- var message = input.defaultMessage;
- try {
- var text = JSON.parse(input.result.responseText);
- message = text.error;
- } catch (e) {
- // ignore JSON parsing errors
- }
- return message;
- };
- /**
- * Convert properties from the pin object to the required values for the UI
- */
- DashboardPinningService.prototype._prepPinsForUI = function _prepPinsForUI(pins) {
- var _this10 = this;
- var dashboardApi = this._getCurrentContentViewDashboardApi();
- var smartNamingService = dashboardApi.getDashboardCoreSvc('.SmartNamingSvc');
- pins.forEach(function (pin) {
- // process pin date
- if (pin.timestamp) {
- pin.pinAge = Formatter.formatDateTime(pin.timestamp, {
- type: 'datetime',
- formatLength: 'short',
- timezone: _this10._timezone
- });
- }
- // smart name
- pin.displayName = smartNamingService.getPinName(pin);
- // check for pin image
- if (!pin.thumbUri) {
- pin.defaultImage = 'wfg_pin';
- }
- });
- };
- /**
- * Caches the pins into their timeframe buckets
- */
- DashboardPinningService.prototype._cachePinsInAppropriateTimeFrameBuckets = function _cachePinsInAppropriateTimeFrameBuckets(pins) {
- var _this11 = this;
- pins.forEach(function (pin) {
- var timestamp = moment.tz(pin.timestamp, _this11._timezone);
- _this11._timeframePinBuckets.all.push(pin);
- var timeframeBucketNames = null;
- if (timestamp.isSame(_this11._today, 'day')) {
- timeframeBucketNames = ['today', 'pastWeek', 'pastMonth'];
- } else if (timestamp.isSame(_this11._yesterday, 'day')) {
- timeframeBucketNames = ['yesterday', 'pastWeek', 'pastMonth'];
- } else if (timestamp.isAfter(_this11._pastWeek)) {
- timeframeBucketNames = ['pastWeek', 'pastMonth'];
- } else if (timestamp.isAfter(_this11._pastMonth)) {
- timeframeBucketNames = ['pastMonth'];
- } else {
- timeframeBucketNames = ['earlier'];
- }
- _this11._addPinToCache(pin, timeframeBucketNames);
- });
- };
- /**
- * Sorts the given array of pins by modified date, with the newest first in the list.
- */
- DashboardPinningService.prototype._sortPins = function _sortPins(pins) {
- return pins.sort(function (a, b) {
- var aTime = a.timestamp;
- var bTime = b.timestamp;
- return aTime > bTime ? -1 : aTime < bTime ? 1 : 0;
- });
- };
- /**
- * Upgrade the pin specs but do not push the upgraded specs to CM yet. We'll do that slowly once we render the Pins panel
- * @param {*} pins
- */
- DashboardPinningService.prototype._upgradePinsLocally = function _upgradePinsLocally() {
- var _this12 = this;
- var pins = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
- //const upgradePromises = [];
- var upgradeOptions = {
- ajaxSvc: this._glassContext.getCoreSvc('.Ajax'),
- services: this._getCurrentContentViewServices(),
- logger: this._glassContext.getCoreSvc('.Logger'),
- pinUpgrade: true,
- glassContext: this._glassContext,
- dashboardApi: this._getCurrentContentViewDashboardApi(),
- showErrorToast: false
- };
- var pinsToUpgrade = [];
- return this._glassContext.getSvc('.UpgradeService').then(function (upgradeService) {
- pins.forEach(function (pin) {
- if (pin.content && pin.content.specVersion !== _this12._latestDashboardSpecVersion) {
- pinsToUpgrade.push(pin);
- }
- });
- if (pinsToUpgrade.length > 0) {
- console.log(pinsToUpgrade.length + ' pins to upgrade.');
- }
- /**
- * Our upgrade service uses static upgrade classes and some of those keep state during the upgrade process.
- * For this reason we have to run the upgrades in sequence instead of in parallel. Use the reduce method
- * to programatically chain promises
- */
- return new Promise(function (resolve) {
- var upgradePromise = pinsToUpgrade.reduce(function (currentPromise, pin) {
- return currentPromise.then(function () {
- try {
- return _this12._upgradePinSpec(pin, upgradeService, upgradeOptions).then(function () {
- _this12._pinIdsToUpgradeInCM.push(pin.id);
- }).catch(function (error) {
- // if one pin fails, omit it from the list and move on
- _this12._glassContext.getCoreSvc('.Logger').error('Unable to upgrade pin:', pin, 'Failed with error: ', error);
- });
- } catch (error) {
- // if one pin fails, omit it from the list and move on
- _this12._glassContext.getCoreSvc('.Logger').error('Unable to upgrade pin:', pin, 'Failed with error: ', error);
- }
- });
- }, Promise.resolve([]));
- upgradePromise.then(function () {
- resolve(pins);
- });
- });
- });
- };
- DashboardPinningService.prototype._upgradePinSpec = function _upgradePinSpec(pinSpec, upgradeService, upgradeOptions) {
- var _this13 = this;
- // assuming the board spec version is 6 if there is no version attribute
- var boardSpec = {
- version: pinSpec.version ? pinSpec.content.specVersion : 6,
- layout: pinSpec.content.layout,
- widgets: this._getObjectFromArrayById(pinSpec.content.widgets)
- };
- // Old pin specs used datasetShapings while every other part of the code uses datasetShaping
- if (pinSpec.content.datasetShapings) {
- boardSpec.datasetShaping = this._getObjectFromArrayById(pinSpec.content.datasetShapings);
- } else if (pinSpec.content.datasetShaping) {
- boardSpec.datasetShaping = this._getObjectFromArrayById(pinSpec.content.datasetShaping);
- }
- if (pinSpec.content.dataSources) {
- boardSpec.dataSources = pinSpec.content.dataSources;
- }
- if (pinSpec.content.episodes) {
- boardSpec.timeline = {
- episodes: this._getObjectFromArrayById(pinSpec.content.episodes)
- };
- }
- // save the original version
- var upgrades = pinSpec.content.upgrades || [];
- var original = boardSpec.version;
- if (upgrades.indexOf(original) === -1) {
- upgrades.push(original);
- pinSpec.content.upgrades = upgrades;
- } else {
- this._glassContext.getCoreSvc('.Logger').warn('Pin spec with id "' + pinSpec.id + '" and board spec version "' + pinSpec.content.specVersion + '" being upgraded to an previously upgraded spec version: ', original);
- }
- return upgradeService.upgrade(boardSpec, null /*means latest*/, upgradeOptions).then(function (upgradedBoardSpec) {
- // TODO the logic to create a pin spec from a board spec should be put in one place
- // currently it is here and in PinAction. See Work Item 187395
- var upgradedPinContent = {
- layout: upgradedBoardSpec.layout,
- widgets: _.toArray(upgradedBoardSpec.widgets),
- specVersion: upgradedBoardSpec.version,
- upgrades: upgrades
- };
- if (upgradedBoardSpec.dataSources) {
- upgradedPinContent.dataSources = upgradedBoardSpec.dataSources;
- }
- if (pinSpec.content.episodes) {
- upgradedPinContent.episodes = _.toArray(upgradedBoardSpec.timeline.episodes);
- }
- // if for some reason the spec did not upgrade fully it may still
- // contain datasetShaping
- if (upgradedBoardSpec.datasetShaping) {
- upgradedPinContent.datasetShaping = _.toArray(upgradedBoardSpec.datasetShaping);
- }
- pinSpec.content = upgradedPinContent;
- // we should be the newest pin version now
- pinSpec.version = _this13.PIN_VERSION;
- return pinSpec;
- }).catch(function (error) {
- // IF we have an error.obj then the upgrade had some succefull steps, return that spec
- if (error.obj) {
- _this13._glassContext.getCoreSvc('.Logger').warn('Pin spec did not fully upgrade', error, pinSpec);
- return error.obj;
- }
- throw error;
- });
- };
- DashboardPinningService.prototype._getObjectFromArrayById = function _getObjectFromArrayById(array) {
- return _.object(_.map(array, function (item) {
- return [item.id, item];
- }));
- };
- /**
- * Yuck yuck yuck.. look away. This is really only needed since the pin service (glass service) needs access to
- * some dashboard specific services when querying/adding pins. In the long run a lot of these services should
- * be moved to glass services since they can be shared accross dashboards.
- */
- DashboardPinningService.prototype._getCurrentContentViewDashboardApi = function _getCurrentContentViewDashboardApi() {
- try {
- return this._glassContext.appController.currentAppView.currentContentView.getDashboardApi();
- } catch (e) {
- this._glassContext.getCoreSvc('.Logger').error('Trying to use the pinning service outside of a dashboard.', e);
- }
- };
- /**
- * Yuck yuck yuck.. look away. This is really only needed since the pin service (glass service) needs access to
- * some dashboard specific services when querying/adding pins. In the long run a lot of these services should
- * be moved to glass services since they can be shared accross dashboards.
- */
- DashboardPinningService.prototype._getCurrentContentViewServices = function _getCurrentContentViewServices() {
- try {
- return this._glassContext.appController.currentAppView.currentContentView.services;
- } catch (e) {
- this._glassContext.getCoreSvc('.Logger').error('Trying to use the pinning service outside of a dashboard.', e);
- }
- };
- return DashboardPinningService;
- }(Events);
- return DashboardPinningService;
- });
- //# sourceMappingURL=DashboardPinningService.js.map
|