• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geosolutions-it / MapStore2 / 15163566361

21 May 2025 01:31PM UTC coverage: 76.93% (-0.06%) from 76.985%
15163566361

Pull #10950

github

web-flow
Merge 652f28b2c into 1fe44f98a
Pull Request #10950: Fix #10947 Updated dockerfile to static files for standard templates

30980 of 48267 branches covered (64.18%)

38608 of 50186 relevant lines covered (76.93%)

36.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.87
/web/client/epics/tutorial.js
1
/*
2
 * Copyright 2017, GeoSolutions Sas.
3
 * All rights reserved.
4
 *
5
 * This source code is licensed under the BSD-style license found in the
6
 * LICENSE file in the root directory of this source tree.
7
 */
8

9
import Rx from 'rxjs';
10

11
import {
12
    START_TUTORIAL,
13
    UPDATE_TUTORIAL,
14
    CLOSE_TUTORIAL,
15
    INIT_TUTORIAL,
16
    CHANGE_PRESET,
17
    closeTutorial,
18
    setupTutorial
19
} from '../actions/tutorial';
20
import { openDetailsPanel } from '../actions/details';
21
import { CHANGE_MAP_VIEW } from '../actions/map';
22
import { modeSelector } from '../selectors/geostory';
23
import { CHANGE_MODE } from '../actions/geostory';
24
import { creationStepSelector } from '../selectors/contextcreator';
25
import { CONTEXT_TUTORIALS } from '../actions/contextcreator';
26
import { LOCATION_CHANGE } from 'connected-react-router';
27
import { isEmpty, isArray, isObject } from 'lodash';
28
import { getApi } from '../api/userPersistedStorage';
29
import {REDUCERS_LOADED} from "../actions/storemanager";
30
import { VISUALIZATION_MODE_CHANGED } from '../actions/maptype';
31
import { detailsSettingsSelector } from '../selectors/details';
32

33
const findTutorialId = path => path.match(/\/(viewer)\/(\w+)\/(\d+)/) && path.replace(/\/(viewer)\/(\w+)\/(\d+)/, "$2")
9✔
34
    || path.match(/\/(\w+)\/(\d+)/) && path.replace(/\/(\w+)\/(\d+)/, "$1")
35
    || path.match(/\/(\w+)\//) && path.replace(/\/(\w+)\//, "$1");
36

37
/**
38
 * Closes the tutorial if 3D button has been toggled
39
 * @memberof epics.tutorial
40
 * @param {external:Observable} action$ manages `START_TUTORIAL`
41
 * @return {external:Observable}
42
 */
43

44
export const closeTutorialEpic = (action$) =>
1✔
45
    action$.ofType(START_TUTORIAL)
×
46
        .audit(() => action$.ofType(VISUALIZATION_MODE_CHANGED))
×
47
        .switchMap( () => Rx.Observable.of(closeTutorial()));
×
48

49
/**
50
 * Setup new steps based on the current path
51
 * @memberof epics.tutorial
52
 * @param {external:Observable} action$ manages `LOCATION_CHANGE`, `REDUCERS_LOADED`
53
 * @param {external:Observable} store
54
 * @return {external:Observable}
55
 */
56

57
export const switchTutorialEpic = (action$, store) =>
1✔
58
    action$.ofType(LOCATION_CHANGE, REDUCERS_LOADED)
9✔
59
        .filter(action => {
60
            const state = store.getState();
9✔
61
            return (action.type === LOCATION_CHANGE && state.router?.location?.pathname)
9!
62
                    || (action.type === REDUCERS_LOADED && action.reducers.includes('tutorial'));
63
        })
64
        .switchMap( () =>
65
            action$.ofType(CHANGE_MAP_VIEW, INIT_TUTORIAL)
9✔
66
                .take(1)
67
                .switchMap( () => {
68
                    const state = store.getState();
9✔
69
                    const location = state.router.location;
9✔
70
                    let id = findTutorialId(location.pathname);
9✔
71
                    const presetList = state.tutorial && state.tutorial.presetList || {};
9!
72
                    const browser = state.browser;
9✔
73
                    const mobile = browser && browser.mobile ? '_mobile' : '';
9✔
74
                    const defaultName = id ? 'default' : location.pathname || 'default';
9!
75
                    const prevTutorialId = state.tutorial && state.tutorial.id;
9✔
76
                    let presetName = id + mobile + '_tutorial';
9✔
77
                    if (defaultName.indexOf("context") !== -1) {
9✔
78
                        const currentStep = creationStepSelector(state) || "general-settings";
1✔
79
                        const currentPreset = CONTEXT_TUTORIALS[currentStep];
1✔
80
                        return Rx.Observable.of(setupTutorial(currentPreset, presetList[currentPreset], null, null, null, prevTutorialId === (currentPreset)));
1✔
81
                    }
82
                    if (id && id?.indexOf("geostory") !== -1 && !isEmpty(presetList)) {
8✔
83
                        // this is needed to setup correct geostory tutorial based on the current mode and page
84
                        if (modeSelector(state) === "edit" || id && id?.indexOf("newgeostory") !== -1) {
2✔
85
                            id  = "geostory";
1✔
86
                            presetName = `geostory_edit_tutorial`;
1✔
87
                            return Rx.Observable.from([
1✔
88
                                setupTutorial(id, presetList[presetName], null, null, null, false)
89
                            ]);
90
                        }
91
                        presetName = `geostory_view_tutorial`;
1✔
92
                        return Rx.Observable.of(setupTutorial(id, presetList[presetName], null, null, null, true));
1✔
93
                    }
94
                    return !isEmpty(presetList) ? Rx.Observable.of(presetList[presetName] ?
6✔
95
                        setupTutorial(id + mobile, presetList[presetName], null, null, null, prevTutorialId === (id + mobile)) :
96
                        setupTutorial(defaultName + mobile, presetList['default' + mobile + '_tutorial'], null, null, null, prevTutorialId === (defaultName + mobile))
97
                    ) : Rx.Observable.empty();
98
                })
99
        );
100

101
/**
102
 * It changes the Geostory tutorial when changing mode only
103
 * when changing to edit the tutorial is shown if not disabled
104
*/
105
export const switchGeostoryTutorialEpic = (action$, store) =>
1✔
106
    action$.ofType(CHANGE_MODE)
5✔
107
        .switchMap( ({mode}) => {
108
            const id = "geostory";
5✔
109
            const state = store.getState();
5✔
110
            const presetList = state.tutorial && state.tutorial.presetList || {};
5!
111
            const geostoryMode = `_${mode}`;
5✔
112
            const steps = !isEmpty(presetList) ? presetList[id + geostoryMode + '_tutorial'] : null;
5!
113
            let isGeostoryTutorialDisabled = false;
5✔
114
            try {
5✔
115
                isGeostoryTutorialDisabled = getApi().getItem("mapstore.plugin.tutorial.geostory.disabled") === "true";
5✔
116
            } catch (e) {
117
                console.error(e);
1✔
118
            }
119
            // if no steps are found then do nothing
120
            return steps ? Rx.Observable.from(
5✔
121
                [
122
                    setupTutorial(id, steps, null, null, null, mode === "view" || isGeostoryTutorialDisabled)
5✔
123
                ]
124
            ) : Rx.Observable.empty();
125
        });
126

127

128
/**
129
 * Handle changePreset action
130
 * @param {external:Observable} action$ manages `CHANGE_PRESET`
131
 * @param {external:Observable} store
132
 */
133
export const changePresetEpic = (action$, store) =>
1✔
134
    action$.ofType(CHANGE_PRESET)
×
135
        .switchMap(({preset, presetGroup, ignoreDisabled}) => {
136
            const state = store.getState();
×
137
            const presetList = state.tutorial && state.tutorial.presetList || {};
×
138
            const checkbox = state.tutorial && state.tutorial.checkbox;
×
139
            const tutorial = presetList[preset];
×
140

141
            return tutorial ?
×
142
                Rx.Observable.of(setupTutorial(preset, tutorial, null, checkbox, null, false, presetGroup, ignoreDisabled)) :
143
                Rx.Observable.empty();
144
        });
145

146
/**
147
 * Get actions from tutorial steps
148
 * @memberof epics.tutorial
149
 * @param {external:Observable} action$ manages `UPDATE_TUTORIAL`
150
 * @return {external:Observable}
151
 */
152

153
export const getActionsFromStepEpic = (action$) =>
1✔
154
    action$.ofType(UPDATE_TUTORIAL)
2✔
155
        .filter(action => action.tour && action.tour.step && action.tour.step.action && action.tour.step.action[action.tour.action])
2✔
156
        .switchMap( (action) => {
157
            return isArray(action.tour.step.action[action.tour.action]) && Rx.Observable.of(...action.tour.step.action[action.tour.action])
2!
158
            || isObject(action.tour.step.action[action.tour.action]) && Rx.Observable.of(action.tour.step.action[action.tour.action])
159
            || Rx.Observable.empty();
160
        });
161

162
/**
163
 * Epics for Tutorial
164
 * @name epics.tutorial
165
 * @type {Object}
166
 */
167

168
export const openDetailsPanelEpic = (action$, store) =>
1✔
169
    action$.ofType(CLOSE_TUTORIAL)
4✔
170
        .filter(() => {
171
            const state = store.getState();
4✔
172
            let detailsSettings = detailsSettingsSelector(state);
4✔
173
            if (detailsSettings && typeof detailsSettings === 'string') {
4!
174
                detailsSettings = JSON.parse(detailsSettings);
×
175
            }
176
            return detailsSettings?.showAtStartup;
4✔
177
        })
178
        .switchMap( () => {
179
            return Rx.Observable.of(openDetailsPanel());
2✔
180
        });
181

182

183
export default {
184
    closeTutorialEpic,
185
    switchTutorialEpic,
186
    getActionsFromStepEpic,
187
    changePresetEpic,
188
    switchGeostoryTutorialEpic,
189
    openDetailsPanelEpic
190
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc