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

geosolutions-it / MapStore2 / 14511615375

17 Apr 2025 08:38AM UTC coverage: 76.981% (-0.01%) from 76.993%
14511615375

Pull #11030

github

web-flow
Merge 547acb875 into 48d6a1a15
Pull Request #11030: Improve GeoServer user integration doc

30795 of 47937 branches covered (64.24%)

38282 of 49729 relevant lines covered (76.98%)

35.97 hits per line

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

80.0
/web/client/components/app/StandardApp.jsx
1
/*
2
 * Copyright 2016, 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 React from 'react';
10
import { Provider } from 'react-redux';
11
import PropTypes from 'prop-types';
12
import { DragDropContext as dragDropContext } from 'react-dnd';
13
import html5Backend from 'react-dnd-html5-backend';
14

15
import { changeBrowserProperties } from '../../actions/browser';
16
import { loadLocale } from '../../actions/locale';
17
import { localConfigLoaded } from '../../actions/localConfig';
18
import { loadPrintCapabilities } from '../../actions/print';
19

20
import ConfigUtils from '../../utils/ConfigUtils';
21
import PluginsUtils from '../../utils/PluginsUtils';
22

23
import url from 'url';
24
const urlQuery = url.parse(window.location.href, true).query;
1✔
25

26
import isObject from 'lodash/isObject';
27
import isArray from 'lodash/isArray';
28

29
import './appPolyfill';
30

31
const DefaultAppLoaderComponent = () => (
1✔
32
    <span>
29✔
33
        <div className="_ms2_init_spinner _ms2_init_center"><div></div></div>
34
        <div className="_ms2_init_text _ms2_init_center">Loading MapStore</div>
35
    </span>
36
);
37

38
/**
39
 * Standard MapStore2 application component
40
 *
41
 * @name  StandardApp
42
 * @memberof components.app
43
 * @prop {function} appStore store creator function
44
 * @prop {object} pluginsDef plugins definition object (e.g. as loaded from plugins.js)
45
 * @prop {object} storeOpts options for the store
46
 * @prop {array} initialActions list of actions to be dispatched on startup
47
 * @prop {function|object} appComponent root component for the application
48
 * @prop {bool} printingEnabled initializes printing environment based on mapfish-print
49
 * @prop {function} onStoreInit optional callback called just after store creation
50
 * @prop {function} onInit optional callback called before first rendering, can delay first rendering
51
 * to do custom initialization (e.g. force SSO login)
52
 * @prop {string} mode current application mode (e.g. desktop/mobile) drives plugins loaded from localConfig
53
 */
54
class StandardApp extends React.Component {
55
    static propTypes = {
1✔
56
        appStore: PropTypes.func,
57
        pluginsDef: PropTypes.object,
58
        storeOpts: PropTypes.object,
59
        initialActions: PropTypes.array,
60
        appComponent: PropTypes.func,
61
        onStoreInit: PropTypes.func,
62
        onInit: PropTypes.func,
63
        onAfterInit: PropTypes.func,
64
        mode: PropTypes.string,
65
        loaderComponent: PropTypes.func,
66
        errorFallbackComponent: PropTypes.func
67
    };
68

69
    static defaultProps = {
1✔
70
        pluginsDef: {plugins: {}, requires: {}},
71
        initialActions: [],
72
        appStore: () => ({dispatch: () => {}, getState: () => ({}), subscribe: () => {}}),
2✔
73
        appComponent: () => <span/>,
13✔
74
        onStoreInit: () => {},
75
        loaderComponent: DefaultAppLoaderComponent
76
    };
77

78
    state = {
26✔
79
        initialized: false
80
    };
81

82
    addProjDefinitions(config) {
83
        if (config.projectionDefs && config.projectionDefs.length) {
26!
84
            import('proj4').then(mod => {
×
85
                const proj4 = mod.default;
×
86
                config.projectionDefs.forEach((proj) => {
×
87
                    proj4.defs(proj.code, proj.def);
×
88
                });
89
            });
90
        }
91
    }
92

93
    shouldComponentUpdate(nextProps, nextState) {
94
        if (this.state.initialized !== nextState.initialized) {
27✔
95
            return true;
22✔
96
        }
97
        if (this.props.pluginsDef !== nextProps.pluginsDef) {
5!
98
            return true;
5✔
99
        }
100
        return false;
×
101
    }
102

103
    UNSAFE_componentWillMount() {
104
        const onInit = (config) => {
26✔
105
            if (!global.Intl ) {
26!
106
                require.ensure(['intl', 'intl/locale-data/jsonp/en.js', 'intl/locale-data/jsonp/it.js'], (require) => {
×
107
                    global.Intl = require('intl');
×
108
                    require('intl/locale-data/jsonp/en.js');
×
109
                    require('intl/locale-data/jsonp/it.js');
×
110
                    this.init(config);
×
111
                });
112
            } else {
113
                this.init(config);
26✔
114
            }
115
        };
116

117
        if (urlQuery.localConfig) {
26!
118
            ConfigUtils.setLocalConfigurationFile(urlQuery.localConfig + '.json');
×
119
        }
120
        ConfigUtils.loadConfiguration().then((config) => {
26✔
121
            const opts = {
26✔
122
                ...this.props.storeOpts,
123
                onPersist: onInit.bind(null, config),
124
                initialState: this.parseInitialState(config.initialState, {
26!
125
                    mode: this.props.mode || (ConfigUtils.getBrowserProperties().mobile ? 'mobile' : 'desktop')
76!
126
                }) || { defaultState: {}, mobile: {} }
127
            };
128
            this.store = this.props.appStore(this.props.pluginsDef.plugins, opts);
26✔
129
            this.props.onStoreInit(this.store);
26✔
130

131
            if (!opts.persist) {
26!
132
                onInit(config);
26✔
133
            }
134
        });
135

136
    }
137

138
    render() {
139
        const {plugins, requires} = this.props.pluginsDef;
53✔
140
        const {appStore, initialActions, appComponent, mode, ...other} = this.props;
53✔
141
        const App = dragDropContext(html5Backend)(this.props.appComponent);
53✔
142
        const Loader = this.props.loaderComponent;
53✔
143
        return this.state.initialized
53✔
144
            ? <Provider store={this.store}>
145
                <App
146
                    {...other}
147
                    plugins={{ ...PluginsUtils.getPlugins(plugins), requires }}
148
                />
149
            </Provider>
150
            : <Loader />;
151
    }
152
    afterInit = () => {
26✔
153
        if (this.props.printingEnabled) {
28✔
154
            this.store.dispatch(loadPrintCapabilities(ConfigUtils.getConfigProp('printUrl')));
1✔
155
        }
156
        if (this.props.onAfterInit) {
28!
157
            this.props.onAfterInit(this.store);
×
158
        }
159
        this.props.initialActions.forEach((action) => {
28✔
160
            this.store.dispatch(action());
15✔
161
        });
162
        this.setState({
28✔
163
            initialized: true
164
        });
165
    };
166

167
    init = (config) => {
26✔
168
        this.store.dispatch(changeBrowserProperties(ConfigUtils.getBrowserProperties()));
26✔
169
        this.store.dispatch(localConfigLoaded(config));
26✔
170
        this.addProjDefinitions(config);
26✔
171
        if (this.props.onInit) {
26✔
172
            this.props.onInit(this.store, this.afterInit.bind(this, [config]), config);
14✔
173
        } else {
174
            const locale = ConfigUtils.getConfigProp('locale');
12✔
175
            this.store.dispatch(loadLocale(null, locale));
12✔
176
            this.afterInit(config);
12✔
177
        }
178
    };
179
    /**
180
     * It returns an object of the same structure of the initialState but replacing strings like "{someExpression}" with the result of the expression between brackets.
181
     * @param {object} state the object to parse
182
     * @param {object} context context for expression
183
     * @return {object} the modified object
184
    */
185
    parseInitialState = (state, context) => {
26✔
186
        return Object.keys(state || {}).reduce((previous, key) => {
78✔
187
            return { ...previous, ...{ [key]: isObject(state[key]) ?
103✔
188
                (isArray(state[key]) ? state[key].map(s => {
53✔
189
                    return isObject(s) ? this.parseInitialState(s, context) : s;
3✔
190
                }) : this.parseInitialState(state[key], context)) :
191
                PluginsUtils.handleExpression({}, context, state[key])}};
192
        }, {});
193
    };
194
}
195

196
export default StandardApp;
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