(function() {
/**
* @namespace whatsnew
* @memberof Help4.widget
*/
Help4.widget.whatsnew = {};
/**
* @typedef {Help4.widget.help.Widget.Context} Help4.widget.whatsnew.Widget.Context
* @property {Object} widget.whatsnew
* @property {Help4.widget.whatsnew.Data} widget.whatsnew.data
*/
/**
* SEN What's New functionality widget
* @augments Help4.widget.help.Widget
* @property {Function} _domRefreshExecutor
*/
Help4.widget.whatsnew.Widget = class extends Help4.widget.help.Widget {
static NAME = 'whatsnew';
static DATA = 'Help4.widget.whatsnew.Data';
static HELP_NAME = 'help';
/** @override */
constructor() {
const domRefreshExecutor = async () => {
// recalculate tours based on conditions
this.isStarted() && await this._data.updateCatalogue();
}
super({
statics: {
_domRefreshExecutor: {init: domRefreshExecutor, destroy: false}
}
});
}
/**
* @override
* @returns {Help4.widget.whatsnew.Widget.Context}
*/
getContext() {
const {_data: data} = this;
const context = /** @type {Help4.widget.help.Widget.Context} */ super.getContext();
context.widget.whatsnew = {data};
return context;
}
/**
* @override
* @returns {Promise<Help4.widget.Widget.Descriptor>}
*/
async _onGetDescriptor() {
const {
Localization,
control2: {ICONS},
widget: {COLOR}
} = Help4;
const {WM} = this.getContext().configuration;
const {NAME, HELP_NAME} = this.constructor;
const text = Localization.getText('button.widget.whatsnew');
return {
id: NAME,
enabled: WM < 2,
showPanel: true,
requires: {
namespaces: [
'Help4.widget.help',
'Help4.widget.tourlist'
],
instances: [HELP_NAME]
},
tile: {
text,
title: text,
icon: ICONS.whatsnew,
color: COLOR.color3,
position: 4
}
};
}
/** @override */
async _onBeforeDestroy() {
await super._onBeforeDestroy();
Help4.widget.tourlist.Widget.observeDomRefresh(this, true);
}
/** @override */
async _onAfterInit() {
await super._onAfterInit();
setTimeout(() => _handleAutoStart.call(this, true), 500);
Help4.widget.tourlist.Widget.observeDomRefresh(this);
}
async _onControllerOpen() {
await super._onControllerOpen();
Help4.widget.tourlist.Widget.observeDomRefresh(this);
}
async _onControllerClose() {
await super._onControllerClose();
Help4.widget.tourlist.Widget.observeDomRefresh(this);
}
/**
* @override
* @param {Object} [data = null]
* @returns {Promise<void>}
*/
async _onAfterActivate(data = null) {
// activate specific whatsnew theming
const {NAME} = this.constructor;
const {Theme, [`_${NAME}`]: rules} = Help4.theme;
const {controller} = this.getContext();
const shadowRoot = controller.getDom2('shadow');
Theme.setCustomVariables(shadowRoot, NAME, rules);
return super._onAfterActivate(data);
}
/**
* @override
* @returns {Promise<boolean|void>}
*/
async _onBeforeDeactivate() {
// deactivate specific whatsnew theming
const {Theme} = Help4.theme;
const {NAME} = this.constructor;
const {controller} = this.getContext();
const shadowRoot = controller.getDom2('shadow');
Theme.removeCustomVariables(shadowRoot, NAME);
await super._onBeforeDeactivate();
}
/** @override */
async _onSystemNavigate() {
await super._onSystemNavigate();
setTimeout(() => _handleAutoStart.call(this), 500);
}
/**
* @override
* @protected
* @param {Help4.widget.help.TileDescriptor} data
* @returns {Promise<void>}
*/
async _trackProject(data) {
const {tileId} = data;
const tiles = await this._data.getHelpTiles();
const tile = tiles.find(({id}) => id === tileId);
if (tile && tile.type === 'tour') return; // will be tracked separately
await super._trackProject(data, tiles);
}
/**
* @override
* @protected
* @param {?Help4.widget.help.view2.SerializedStatus} status
* @param {Object} [data = null]
* @param {Object} [params = {}]
* @param {boolean} [params.stealth]
* @returns {Promise<void>}
*/
async _createView2(status, data = null, {stealth} = {}) {
await super._createView2(status, data, {stealth});
this._view2.addListener('select', async ({data}) => {
const {type, tileId: projectId} = data || {};
if (type !== 'tour') return;
const {
help: {project: {WNTours}},
companionCore: {Core}
} = Help4.widget;
const {configuration} = this.getContext();
const catalogueKey = Core.getCatalogueKey({configuration});
const {catalogueProject} = WNTours.getData(catalogueKey).find(({id}) => id === projectId);
const {_catalogueType: catalogueType, _dataType: dataType} = catalogueProject;
const tour = /** @type {Help4.widget.tour.Widget.StartStatus} */ {projectId, catalogueKey, catalogueType, dataType, whatsnew: true};
const {Widget: TourlistWidget} = Help4.widget.tourlist;
await this.deactivate({tour: true});
await TourlistWidget.startTour(configuration, tour);
});
}
/**
* @override
* @param {*} [data = null]
* @returns {{visible: boolean, isEditMode: boolean, isActiveCMP4: boolean}}
*/
_isView2Visible(data = null) {
const {configuration: {core: {isEditMode, isActiveCMP4}}} = this.getContext();
const isActive = this.isActive();
// show view if
// - not in edit mode (CMP3)
// - my widget is active
return {
visible: !isEditMode && isActiveCMP4 && isActive,
isEditMode,
isActiveCMP4
}
}
/**
* @override
* @protected
* @param {boolean} stealth
* @returns {Promise<void>}
*/
async _view2Stealth(stealth) {
// stealth not supported in whatsnew
stealth && this._destroyView2();
}
}
/**
* @memberof Help4.widget.whatsnew.Widget
* @private
* @param {boolean} [isBoot = false]
* @returns {Promise<void>}
*/
async function _handleAutoStart(isBoot = false) {
const {
COOKIE_KEYS: {WHATS_NEW},
control2: {InfoBar: {TYPES: {whatsnew}}},
Localization,
widget
} = Help4;
const {help: {Cookie}} = widget;
// in case whatsnew mode has already been played here; do not start automatically
const hasWhatsNewCookie = await Cookie.getPageCookie(this, {type: WHATS_NEW});
if (hasWhatsNewCookie || this.isDestroyed()) return;
// XRAY-6241: do not do anything in case no whatsnew data is available
await this._data.waitHelpLoaded();
if (this.isDestroyed()) return;
await this._setVisible();
if (this.isDestroyed() || !this.isVisible()) return;
const {
controller,
service: {infobarService},
configuration: {
help: {whatsNewDirect, whatsNewExpiration},
core: {recentlyClosed},
isEditMode
}
} = this.getContext();
const infobarOpen = infobarService.count() > 0;
const activeWidgetName = widget.getActiveInstance()?.getName();
const isTourMode = activeWidgetName === 'tour';
// XRAY-1128; in case a tour is running: do not disturb it and do not even mark this screen as visited
// XRAY-2372: Don't show WN and do not mark the page if there is already an infobar
// XRAY-5985: do not show whatsnew message in edit mode
if (isTourMode || isEditMode || infobarOpen) return;
// currently playing whatsnew content; mark screen
if (this.isActive()) return Cookie.setPageCookie(this, {type: WHATS_NEW});
// whatsnew mode not yet active
// XRAY-1276: do not show whatsnew message after a certain date
if (typeof whatsNewExpiration === 'number' && whatsNewExpiration - (new Date()).getTime() < 0) return;
const showWhatsnew = () => {
if (!controller.isOpen()) controller.open();
this.activate();
}
if (whatsNewDirect && isBoot) {
// XRAY-1277: start whatsnew directly
if (!recentlyClosed) {
await Cookie.setPageCookie(this, {type: WHATS_NEW}); // mark screen
showWhatsnew();
}
} else {
// show info message that whatsnew is available
await Cookie.setPageCookie(this, {type: WHATS_NEW}); // mark screen
const content = Localization.getText('label.whatsnew');
infobarService.add({
content,
clickable: true,
type: whatsnew
})
.addListener('click', showWhatsnew);
}
}
})();