Source: widget/whatsnew/Data.js

(function() {
    /**
     * Data handler for whatsnew widget.
     * @augments Help4.widget.help.Data
     * @property {Function} _onDataRetrieved
     * @property {boolean} _initialized
     */
    Help4.widget.whatsnew.Data = class extends Help4.widget.help.Data {
        static SCREEN_ID_EXTENSION = Help4.widget.help.CatalogueBackend.WHATSNEW_SCREEN_ID;

        /**
         * @override
         * @param {Help4.widget.help.Data.Params} params
         */
        constructor(params) {
            /** @returns {Promise<void>} */
            const onDataRetrieved = async () => this.isDestroyed() || await this._onHelpRetrieveReady();

            super(params, {
                statics: {
                    _onDataRetrieved: {init: onDataRetrieved, destroy: false},
                    _initialized:     {init: false, destroy: false}
                },
                retrieve: {
                    /**
                     * override function from Help4.widget.help.Data as we do not want to load data twice
                     * instead we will listen and react to changes from help data; see below
                     * @returns {null}
                     */
                    catalogues: () => {
                        // reset loaded information
                        this._cataloguesLoaded = null;
                        this._helpLoaded = null;

                        return null;
                    }
                }
            });

            const {_onDataRetrieved} = this;
            const helpWidget = Help4.widget.getInstance('help');
            const {widget: {help: {data}}} = helpWidget.getContext();
            data.addListener('dataRetrieved', _onDataRetrieved);

            // get all data that exists already before we added our listener
            // call only after constructor is done
            setTimeout(async () => {
                await _onDataRetrieved();
                this._initialized = true;
            }, 1);
        }

        /** @override */
        destroy() {
            const {_onDataRetrieved} = this;
            const helpWidget = Help4.widget.getInstance('help');
            const {widget: {help: {data} = {}} = {}} = helpWidget?.getContext() || {};
            data && _onDataRetrieved && data.removeListener('dataRetrieved', _onDataRetrieved);

            super.destroy();
        }

        /**
         * @override
         * @returns {Promise<void>}
         */
        initialize() {
            return new Help4.Promise(resolve => {
                const check = () => {
                    this._initialized
                        ? resolve()
                        : setTimeout(check, 100);
                }
                check();
            });
        }

        /**
         * @override
         * @returns {Promise<void>}
         */
        async update() {
            this._cataloguesLoaded = null;
            this._helpLoaded = null;

            const {configuration: {core: {screenId}}} = this.__widget.getContext();
            await this.waitCataloguesLoaded(screenId);
            await this.waitHelpLoaded();

            // fire this.help event again; to allow proper view update (timing issues)
            this._fireEvent({type: 'dataChange', name: 'help', value: this.help});
        }

        /**
         * @private
         * @returns {Promise<void>}
         */
        async _onHelpRetrieveReady() {
            const helpWidget = Help4.widget.getInstance('help');
            const {
                widget: {help: {data}},
                configuration: {core: {screenId}}
            } = helpWidget.getContext();

            await data.waitCataloguesLoaded(screenId);
            if (this.isDestroyed()) return;

            // copy data from help widget
            this.catalogues = Help4.cloneValue(data.catalogues);

            // mark catalogues for this screen as loaded
            this._cataloguesLoaded = screenId;

            // update help information
            await this._retrieveHelp();
            if (this.isDestroyed()) return;

            // mark help for this screen as loaded
            this._helpLoaded = screenId;

            await this.updateCatalogue();  // handle WN tours
        }

        /**
         * updates tour list based on catalogue from help widget
         * see {@link Help4.widget.tourlist.Data#updateCatalogue}
         * @returns {Promise<void>}
         */
        async updateCatalogue() {
            const {
                tourlist: {Data},
                help: {project: {WNTours}},
                companionCore: {data: {Tour}}
            } = Help4.widget;
            const {__widget, catalogues} = this;

            /**
             * will get all tours from help catalogue in case they<br>
             * - are not extended (EXT for tour completely overrides; therefore ignore extended tours)<br>
             * - not filtered out by extension<br>
             * - only whatsnew
             * @param {Help4.widget.help.CatalogueProject} project
             * @returns {boolean}
             */
            const getTours = ({contextType: ct, _ext, _ignore, _whatsnew})=>
                ct === 'TOUR' && _ext === undefined && !_ignore && !!_whatsnew;

            const {
                pub: {projects: pubProjects},
                head: {projects: headProjects}
            } = catalogues;

            const {
                pub: pubTours,
                head: headTours
            } = Data.getTours(__widget, getTours);

            const pub = /** @type {Help4.widget.help.CatalogueProject[]} */ pubTours.map(projectId => pubProjects.find(({id}) => id === projectId));
            const head = /** @type {Help4.widget.help.CatalogueProject[]} */ headTours.map(projectId => headProjects.find(({id}) => id === projectId));

            // evaluate tour conditions
            const promisePub = Tour.getFilteredTourProjects({whatsnew: true, catalogueKey: 'pub', projects: pub});
            const promiseHead = Tour.getFilteredTourProjects({whatsnew: true, catalogueKey: 'head', projects: head});
            Help4.Promise.all([promisePub, promiseHead]);
            if (this.isDestroyed()) return;

            const filteredPub = await promisePub;
            const filteredHead = await promiseHead;
            await WNTours.setData({pub: filteredPub, head: filteredHead});
        }

        /**
         * disables unsupported features in whatsnew mode
         * @override
         * @returns {Promise<Help4.widget.help.ProjectTile[]>}
         */
        async getHelpTiles() {
            const tiles = /** @type {Help4.widget.help.ProjectTile[]} */ await super.getHelpTiles();

            tiles.forEach(tile => {
                if (tile.type === 'help') {
                    tile.instantHelp = false;
                    tile.callout = false;
                }
            });

            return tiles;
        }
    }
})();