@eeacms/volto-arcgis-block 0.1.377 → 0.1.379

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,8 +4,26 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
+ ### [0.1.379](https://github.com/eea/volto-arcgis-block/compare/0.1.378...0.1.379) - 11 August 2025
8
+
9
+ #### :hammer_and_wrench: Others
10
+
11
+ - Merge pull request #998 from eea/CLMS-LOCAL-STORAGE-ISSUES-REFACTOR-CLEAN [Unai Bolivar - [`3b84fbd`](https://github.com/eea/volto-arcgis-block/commit/3b84fbd1cc3706976cc746a95c6885da7a746e22)]
12
+ - (lint): cleared console statements [Unai Bolivar - [`f6d649d`](https://github.com/eea/volto-arcgis-block/commit/f6d649d51d9254a250e49ca31b83bc4cc8c6b30a)]
13
+ - (bug): implemented safe store and recall for all user data in mapviewer fix [Unai Bolivar - [`a0491a7`](https://github.com/eea/volto-arcgis-block/commit/a0491a7bbeffdcd527eabc91b716757d680ea5d3)]
14
+ - (bug): rendering session data correctly [Unai Bolivar - [`0fd8434`](https://github.com/eea/volto-arcgis-block/commit/0fd843442b72b7d0980f98e182f4ec68dc00850c)]
15
+ - (bug): wrapping mapviewer in mapviewerstatemonitor to control and eliminate multiple mapviewer mounts and race conditions [Unai Bolivar - [`c3e5232`](https://github.com/eea/volto-arcgis-block/commit/c3e5232b64f318256f504575f7e6ee685d80e121)]
16
+ - (bug): fixed user anonymous session storage maintenance [Unai Bolivar - [`7bb1054`](https://github.com/eea/volto-arcgis-block/commit/7bb105406e272d73c115b1515313723890830d8e)]
17
+ - (bug): saving updates to retrieve current jsx file [Unai Bolivar - [`10e8b69`](https://github.com/eea/volto-arcgis-block/commit/10e8b69a2f9c6ebda49864bb431c6f8e4a49fa66)]
18
+ - (bug): prettified the file [Unai Bolivar - [`e561931`](https://github.com/eea/volto-arcgis-block/commit/e5619310b30c1c8935735d8de53939cc551fb1ba)]
19
+ - (bug): moved status monitoring to mapviewer HOC component for better synchronization and widget updates that actually catch the user login and userID information and setup session storage accordingly before proceeding with a full state update after mount is ready. [Unai Bolivar - [`16dd623`](https://github.com/eea/volto-arcgis-block/commit/16dd62302b4fa520185b4d9957e69e41c31e1fa6)]
20
+ ### [0.1.378](https://github.com/eea/volto-arcgis-block/compare/0.1.377...0.1.378) - 11 August 2025
21
+
7
22
  ### [0.1.377](https://github.com/eea/volto-arcgis-block/compare/0.1.376...0.1.377) - 30 July 2025
8
23
 
24
+ #### :hammer_and_wrench: Others
25
+
26
+ - Merge pull request #995 from eea/develop [Unai Bolivar - [`194ef38`](https://github.com/eea/volto-arcgis-block/commit/194ef385764f9d153fb46ac121bdc0f55d75ce20)]
9
27
  ### [0.1.376](https://github.com/eea/volto-arcgis-block/compare/0.1.375...0.1.376) - 17 July 2025
10
28
 
11
29
  ### [0.1.375](https://github.com/eea/volto-arcgis-block/compare/0.1.374...0.1.375) - 14 July 2025
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-arcgis-block",
3
- "version": "0.1.377",
3
+ "version": "0.1.379",
4
4
  "description": "volto-arcgis-block: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: CodeSyntax",
@@ -34,6 +34,12 @@ class AreaWidget extends React.Component {
34
34
  showMapMenu: false,
35
35
  showInfoPopup: this.props.download ? true : false,
36
36
  infoPopupType: 'area',
37
+ coordinates: {
38
+ north: '',
39
+ south: '',
40
+ east: '',
41
+ west: '',
42
+ },
37
43
  };
38
44
  this.menuClass =
39
45
  'esri-icon-cursor-marquee esri-widget--button esri-widget esri-interactive';
@@ -245,18 +251,14 @@ class AreaWidget extends React.Component {
245
251
  document
246
252
  .querySelector('.closeCoordinates')
247
253
  .classList.replace('esri-icon-up-arrow', 'esri-icon-close');
248
- if (document.getElementById('menu-north')) {
249
- document.getElementById('menu-north').value = null;
250
- }
251
- if (document.getElementById('menu-south')) {
252
- document.getElementById('menu-south').value = null;
253
- }
254
- if (document.getElementById('menu-east')) {
255
- document.getElementById('menu-east').value = null;
256
- }
257
- if (document.getElementById('menu-west')) {
258
- document.getElementById('menu-west').value = null;
259
- }
254
+ this.setState({
255
+ coordinates: {
256
+ north: '',
257
+ south: '',
258
+ east: '',
259
+ west: '',
260
+ },
261
+ });
260
262
  }
261
263
  }
262
264
  nuts0handler(e) {
@@ -1020,41 +1022,45 @@ class AreaWidget extends React.Component {
1020
1022
  addCoordinates() {
1021
1023
  this.clearWidget();
1022
1024
  let valid = true;
1023
- let pointNorth = document.getElementById('menu-north');
1025
+ let pointNorth = this.state.coordinates.north;
1024
1026
  if (
1025
- pointNorth.value > 90 ||
1026
- pointNorth.value < -90 ||
1027
- pointNorth.value == null
1027
+ pointNorth > 90 ||
1028
+ pointNorth < -90 ||
1029
+ pointNorth === null ||
1030
+ pointNorth === ''
1028
1031
  ) {
1029
1032
  valid = false;
1030
1033
  }
1031
- let pointSouth = document.getElementById('menu-south');
1034
+ let pointSouth = this.state.coordinates.south;
1032
1035
  if (
1033
- pointSouth.value > 90 ||
1034
- pointSouth.value < -90 ||
1035
- pointSouth.value == null
1036
+ pointSouth > 90 ||
1037
+ pointSouth < -90 ||
1038
+ pointSouth === null ||
1039
+ pointSouth === ''
1036
1040
  ) {
1037
1041
  valid = false;
1038
1042
  }
1039
- let pointEast = document.getElementById('menu-east');
1043
+ let pointEast = this.state.coordinates.east;
1040
1044
  if (
1041
- pointEast.value > 180 ||
1042
- pointEast.value < -180 ||
1043
- pointEast.value == null
1045
+ pointEast > 180 ||
1046
+ pointEast < -180 ||
1047
+ pointEast === null ||
1048
+ pointEast === ''
1044
1049
  ) {
1045
1050
  valid = false;
1046
1051
  }
1047
- let pointWest = document.getElementById('menu-west');
1052
+ let pointWest = this.state.coordinates.west;
1048
1053
  if (
1049
- pointWest.value > 180 ||
1050
- pointWest.value < -180 ||
1051
- pointWest.value == null
1054
+ pointWest > 180 ||
1055
+ pointWest < -180 ||
1056
+ pointWest === null ||
1057
+ pointWest === ''
1052
1058
  ) {
1053
1059
  valid = false;
1054
1060
  }
1055
1061
  if (valid) {
1056
- let pointNW = [pointNorth.value, pointWest.value];
1057
- let pointSE = [pointSouth.value, pointEast.value];
1062
+ let pointNW = [pointNorth, pointWest];
1063
+ let pointSE = [pointSouth, pointEast];
1058
1064
  var fillSymbol = {
1059
1065
  type: 'simple-fill',
1060
1066
  color: [255, 255, 255, 0.5],
@@ -1107,6 +1113,17 @@ class AreaWidget extends React.Component {
1107
1113
  });
1108
1114
  }
1109
1115
  }
1116
+ handleCoordinateChange = (e) => {
1117
+ const { id, value } = e.target;
1118
+ // id will be "menu-north", "menu-south", "menu-east", or "menu-west"
1119
+ const key = id.replace('menu-', ''); // gives you 'north', 'south', etc.
1120
+ this.setState((prevState) => ({
1121
+ coordinates: {
1122
+ ...prevState.coordinates,
1123
+ [key]: value,
1124
+ },
1125
+ }));
1126
+ };
1110
1127
  openCoordinates(event) {
1111
1128
  this.clearWidget();
1112
1129
  this.nutsRadioButton(event.target.value);
@@ -1823,6 +1840,8 @@ class AreaWidget extends React.Component {
1823
1840
  max="90"
1824
1841
  placeholder="00.000000"
1825
1842
  className="coordinateInput"
1843
+ value={this.state.coordinates.north}
1844
+ onChange={this.handleCoordinateChange}
1826
1845
  />
1827
1846
  </div>
1828
1847
  <div>
@@ -1834,6 +1853,8 @@ class AreaWidget extends React.Component {
1834
1853
  max="180"
1835
1854
  placeholder="00.000000"
1836
1855
  className="coordinateInput"
1856
+ value={this.state.coordinates.west}
1857
+ onChange={this.handleCoordinateChange}
1837
1858
  />
1838
1859
  </div>
1839
1860
  </div>
@@ -1848,6 +1869,8 @@ class AreaWidget extends React.Component {
1848
1869
  max="90"
1849
1870
  placeholder="00.000000"
1850
1871
  className="coordinateInput"
1872
+ value={this.state.coordinates.south}
1873
+ onChange={this.handleCoordinateChange}
1851
1874
  />
1852
1875
  </div>
1853
1876
  <div>
@@ -1859,6 +1882,8 @@ class AreaWidget extends React.Component {
1859
1882
  max="180"
1860
1883
  placeholder="00.000000"
1861
1884
  className="coordinateInput"
1885
+ value={this.state.coordinates.east}
1886
+ onChange={this.handleCoordinateChange}
1862
1887
  />
1863
1888
  </div>
1864
1889
  </div>
@@ -1,4 +1,12 @@
1
- import React, { createRef } from 'react';
1
+ import React, {
2
+ createRef,
3
+ createContext,
4
+ useContext,
5
+ useState,
6
+ useEffect,
7
+ useCallback,
8
+ useRef,
9
+ } from 'react';
2
10
  import './css/ArcgisMap.css';
3
11
  import classNames from 'classnames';
4
12
  import { loadModules, loadCss } from 'esri-loader';
@@ -28,13 +36,417 @@ import { getTaxonomy } from '@eeacms/volto-taxonomy/actions';
28
36
  //import "isomorphic-fetch"; <-- Necessary to use fetch?
29
37
  var Map, MapView, Zoom, intl, Basemap, WebTileLayer, Extent;
30
38
  let mapStatus = {};
39
+
31
40
  const CheckLanguage = () => {
32
41
  const { locale } = useIntl();
33
42
  intl.setLocale(locale);
34
43
  return null;
35
44
  };
36
45
 
46
+ // Enhanced UserContext and Provider with state monitoring capabilities
47
+ const UserContext = createContext({ user_id: null, isLoggedIn: false });
48
+ const useUserContext = () => useContext(UserContext);
49
+
50
+ // Enhanced UserProvider with state change monitoring
51
+ const UserProvider = ({ children }) => {
52
+ const cartState = useCartState();
53
+
54
+ // Direct transformation of cart state to user context format
55
+ // No local state needed - context reactively updates when cartState changes
56
+ const userContextValue = {
57
+ user_id: cartState?.user_id || null,
58
+ isLoggedIn: cartState?.isLoggedIn || false,
59
+ };
60
+
61
+ // Show loading only when cart state is completely undefined (initial load)
62
+ // Prevents UI flicker by distinguishing between "loading" and "logged out" states
63
+ if (cartState === undefined) {
64
+ return (
65
+ <div className="loading-container">
66
+ <div className="loading-text">Loading...</div>
67
+ </div>
68
+ );
69
+ }
70
+
71
+ return (
72
+ <UserContext.Provider value={userContextValue}>
73
+ {children}
74
+ </UserContext.Provider>
75
+ );
76
+ };
77
+
78
+ // MapViewer State Monitor - Functional wrapper that monitors useCartState changes
79
+ // and ensures the class component always receives the latest authentication state
80
+ const MapViewerStateMonitor = ({ children, onUserStateChange }) => {
81
+ const cartState = useCartState();
82
+ const prevCartStateRef = useRef(null);
83
+
84
+ // Monitor cart state changes and notify parent component
85
+ useEffect(() => {
86
+ if (cartState !== undefined) {
87
+ const currentUserState = {
88
+ user_id: cartState?.user_id || null,
89
+ isLoggedIn: cartState?.isLoggedIn || false,
90
+ };
91
+
92
+ // Compare with previous state to detect changes
93
+ if (prevCartStateRef.current !== null) {
94
+ const hasUserIdChanged =
95
+ prevCartStateRef.current.user_id !== currentUserState.user_id;
96
+ const hasLoggedInChanged =
97
+ prevCartStateRef.current.isLoggedIn !== currentUserState.isLoggedIn;
98
+
99
+ if (hasUserIdChanged || hasLoggedInChanged) {
100
+ /* eslint-disable no-console */
101
+ console.log('[MapViewerStateMonitor] User state change detected:', {
102
+ previous: prevCartStateRef.current,
103
+ current: currentUserState,
104
+ userIdChanged: hasUserIdChanged,
105
+ loggedInChanged: hasLoggedInChanged,
106
+ });
107
+ /* eslint-enable no-console */
108
+
109
+ // Notify parent component of state change
110
+ if (onUserStateChange) {
111
+ onUserStateChange(currentUserState, prevCartStateRef.current);
112
+ }
113
+ }
114
+ }
115
+
116
+ // Update previous state using ref (doesn't cause re-renders)
117
+ prevCartStateRef.current = currentUserState;
118
+ }
119
+ }, [cartState, onUserStateChange]);
120
+
121
+ return children;
122
+ };
123
+
124
+ // UserStorageManager now consumes user state directly from UserContext
125
+ // Eliminates prop drilling and ensures consistent user state access
126
+ const UserStorageManager = ({ children, onStorageManaged }) => {
127
+ const { user_id: userID, isLoggedIn } = useUserContext();
128
+ const [storageManaged, setStorageManaged] = useState(false);
129
+ const [isInitialized, setIsInitialized] = useState(false);
130
+
131
+ // Generate unique instance ID to track operations and prevent race conditions
132
+ const instanceId = useRef(Math.random().toString(36).substring(7));
133
+
134
+ // Track if this instance has successfully restored data
135
+ const hasRestoredData = useRef(false);
136
+
137
+ const populateSessionFromUserData = (userKey) => {
138
+ const userData = localStorage.getItem(userKey);
139
+ if (userData) {
140
+ /* eslint-disable no-console */
141
+ console.log(
142
+ `[UserStorageManager:${instanceId.current}] Restoring from localStorage:`,
143
+ {
144
+ key: userKey,
145
+ dataPreview:
146
+ userData.substring(0, 100) + (userData.length > 100 ? '...' : ''),
147
+ dataLength: userData.length,
148
+ },
149
+ );
150
+ /* eslint-enable no-console */
151
+ const parsed = JSON.parse(userData);
152
+ const userIdFromKey = (key) => key.replace(/^user_/, '');
153
+ const hydratedFor = sessionStorage.getItem('mv_hydrated_for');
154
+ const alreadyHasLayers = (() => {
155
+ try {
156
+ const cl = JSON.parse(
157
+ sessionStorage.getItem('checkedLayers') || '[]',
158
+ );
159
+ return Array.isArray(cl) && cl.length > 0;
160
+ } catch {
161
+ return false;
162
+ }
163
+ })();
164
+ if (hydratedFor === userIdFromKey(userKey) || alreadyHasLayers) {
165
+ return true;
166
+ }
167
+ Object.entries(parsed).forEach(([k, v]) => {
168
+ const existing = sessionStorage.getItem(k);
169
+ if (existing == null) {
170
+ sessionStorage.setItem(
171
+ k,
172
+ typeof v === 'object' ? JSON.stringify(v) : v,
173
+ );
174
+ return;
175
+ }
176
+ if (k === 'checkedLayers') {
177
+ try {
178
+ const a = JSON.parse(existing || '[]');
179
+ const b = typeof v === 'string' ? JSON.parse(v) : v;
180
+ const union = [...new Set([...(b || []), ...(a || [])])];
181
+ sessionStorage.setItem('checkedLayers', JSON.stringify(union));
182
+ } catch {}
183
+ } else if (k === 'visibleLayers' || k === 'layerOpacities') {
184
+ try {
185
+ const a = JSON.parse(existing || '{}');
186
+ const b = typeof v === 'string' ? JSON.parse(v) : v || {};
187
+ sessionStorage.setItem(k, JSON.stringify({ ...b, ...a }));
188
+ } catch {}
189
+ }
190
+ });
191
+ sessionStorage.setItem('mv_hydrated_for', userIdFromKey(userKey));
192
+
193
+ // Mark that this instance has successfully restored data
194
+ hasRestoredData.current = true;
195
+
196
+ return true; // Indicate successful restoration
197
+ } else {
198
+ /* eslint-disable no-console */
199
+ console.log(
200
+ `[UserStorageManager:${instanceId.current}] No data found for key:`,
201
+ userKey,
202
+ );
203
+ /* eslint-enable no-console */
204
+ return false;
205
+ }
206
+ };
207
+
208
+ // Improved instance management with lock mechanism to prevent race conditions
209
+ const acquireStorageLock = (lockKey) => {
210
+ const existingLock = localStorage.getItem(lockKey);
211
+ if (existingLock && existingLock !== instanceId.current) {
212
+ return false; // Another instance has the lock
213
+ }
214
+ localStorage.setItem(lockKey, instanceId.current);
215
+ return true;
216
+ };
217
+
218
+ const releaseStorageLock = (lockKey) => {
219
+ const currentLock = localStorage.getItem(lockKey);
220
+ if (currentLock === instanceId.current) {
221
+ localStorage.removeItem(lockKey);
222
+ }
223
+ };
224
+
225
+ // Initialization effect - handles user state readiness
226
+ useEffect(() => {
227
+ if (userID !== undefined && isLoggedIn !== undefined) {
228
+ /* eslint-disable no-console */
229
+ console.log(
230
+ `[UserStorageManager:${instanceId.current}] User state initialized:`,
231
+ {
232
+ userID,
233
+ isLoggedIn,
234
+ },
235
+ );
236
+ /* eslint-enable no-console */
237
+ setIsInitialized(true);
238
+ }
239
+ }, [userID, isLoggedIn]);
240
+
241
+ // Initial storage management effect - runs once on mount with lock protection
242
+ useEffect(() => {
243
+ if (!isInitialized || storageManaged) {
244
+ return;
245
+ }
246
+
247
+ const lockKey = `storage_lock_${userID || 'anonymous'}`;
248
+
249
+ /* eslint-disable no-console */
250
+ console.log(
251
+ `[UserStorageManager:${instanceId.current}] Component mounting with state:`,
252
+ {
253
+ isLoggedIn,
254
+ userID,
255
+ storageManaged,
256
+ isInitialized,
257
+ },
258
+ );
259
+ /* eslint-enable no-console */
260
+
261
+ // Acquire lock to prevent concurrent operations
262
+ if (!acquireStorageLock(lockKey)) {
263
+ /* eslint-disable no-console */
264
+ console.log(
265
+ `[UserStorageManager:${instanceId.current}] Another instance is managing storage for this user`,
266
+ );
267
+ /* eslint-enable no-console */
268
+ return;
269
+ }
270
+ try {
271
+ if (isLoggedIn === true) {
272
+ /* eslint-disable no-console */
273
+ console.log(
274
+ `[UserStorageManager:${instanceId.current}] Detected signed-in user, processing storage...`,
275
+ );
276
+ /* eslint-enable no-console */
277
+
278
+ if (
279
+ userID &&
280
+ userID !== null &&
281
+ userID !== undefined &&
282
+ userID !== ''
283
+ ) {
284
+ const userKey = `user_${userID}`;
285
+ const userData = localStorage.getItem(userKey);
286
+ const anonymousData = localStorage.getItem('user_anonymous');
287
+
288
+ /* eslint-disable no-console */
289
+ console.log(
290
+ `[UserStorageManager:${instanceId.current}] Storage analysis:`,
291
+ {
292
+ userKey,
293
+ hasUserData: !!userData,
294
+ hasAnonymousData: !!anonymousData,
295
+ sessionEmpty: sessionStorage.length === 0,
296
+ sessionLength: sessionStorage.length,
297
+ },
298
+ );
299
+ /* eslint-enable no-console */
300
+
301
+ // Migration and restoration logic for signed-in users
302
+ if (!userData && anonymousData) {
303
+ /* eslint-disable no-console */
304
+ console.log(
305
+ `[UserStorageManager:${instanceId.current}] Migrating anonymous data to signed-in user`,
306
+ );
307
+ /* eslint-enable no-console */
308
+ // Move anonymous data to the signed-in user's localStorage key
309
+ localStorage.setItem(userKey, anonymousData);
310
+ // Delete the anonymous key from localStorage
311
+ localStorage.removeItem('user_anonymous');
312
+ // Populate sessionStorage with the migrated data from localStorage
313
+ populateSessionFromUserData(userKey);
314
+ } else if (userData) {
315
+ /* eslint-disable no-console */
316
+ console.log(
317
+ `[UserStorageManager:${instanceId.current}] Found existing user data - restoring to session storage`,
318
+ );
319
+ /* eslint-enable no-console */
320
+ // User has existing saved data - restore it to sessionStorage
321
+ populateSessionFromUserData(userKey);
322
+ } else {
323
+ /* eslint-disable no-console */
324
+ console.log(
325
+ `[UserStorageManager:${instanceId.current}] No saved user data found - preserving current session state`,
326
+ );
327
+ /* eslint-enable no-console */
328
+ }
329
+ setStorageManaged(true);
330
+ } else {
331
+ /* eslint-disable no-console */
332
+ console.log(
333
+ `[UserStorageManager:${instanceId.current}] valid userID unavailable: userID is ${userID}`,
334
+ );
335
+ /* eslint-enable no-console */
336
+ }
337
+ } else if (isLoggedIn === false) {
338
+ /* eslint-disable no-console */
339
+ console.log(
340
+ `[UserStorageManager:${instanceId.current}] Detected anonymous user, processing storage...`,
341
+ );
342
+ /* eslint-enable no-console */
343
+ // Handle anonymous user session - restore anonymous data if it exists
344
+ const anonymousKey = 'user_anonymous';
345
+ const anonymousData = localStorage.getItem(anonymousKey);
346
+ if (anonymousData) {
347
+ /* eslint-disable no-console */
348
+ console.log(
349
+ `[UserStorageManager:${instanceId.current}] Restoring anonymous data to session storage:`,
350
+ {
351
+ dataPreview:
352
+ anonymousData.substring(0, 100) +
353
+ (anonymousData.length > 100 ? '...' : ''),
354
+ dataLength: anonymousData.length,
355
+ },
356
+ );
357
+ /* eslint-enable no-console */
358
+ sessionStorage.clear();
359
+ const parsed = JSON.parse(anonymousData);
360
+ Object.keys(parsed).forEach((k) =>
361
+ sessionStorage.setItem(
362
+ k,
363
+ typeof parsed[k] === 'object'
364
+ ? JSON.stringify(parsed[k])
365
+ : parsed[k],
366
+ ),
367
+ );
368
+ hasRestoredData.current = true;
369
+ /* eslint-disable no-console */
370
+ console.log(
371
+ `[UserStorageManager:${instanceId.current}] Anonymous data restored to sessionStorage`,
372
+ );
373
+ /* eslint-enable no-console */
374
+ } else {
375
+ /* eslint-disable no-console */
376
+ console.log(
377
+ `[UserStorageManager:${instanceId.current}] No anonymous data found to restore`,
378
+ );
379
+ /* eslint-enable no-console */
380
+ }
381
+ setStorageManaged(true);
382
+ } else {
383
+ /* eslint-disable no-console */
384
+ console.log(
385
+ `[UserStorageManager:${instanceId.current}] User state does not match any processing condition - skipping storage logic`,
386
+ );
387
+ /* eslint-enable no-console */
388
+ }
389
+
390
+ if (onStorageManaged) {
391
+ onStorageManaged();
392
+ }
393
+ } finally {
394
+ // Always release the lock, even if an error occurred
395
+ releaseStorageLock(lockKey);
396
+ }
397
+
398
+ // Cleanup function to release lock on unmount
399
+ return () => {
400
+ releaseStorageLock(lockKey);
401
+ };
402
+ }, [isInitialized, isLoggedIn, userID, storageManaged, onStorageManaged]);
403
+
404
+ // Data-reactive effect - ensures sessionStorage mirrors localStorage whenever userData changes
405
+ // useEffect(() => {
406
+ // /* eslint-disable no-console */
407
+ // console.log(
408
+ // `[UserStorageManager:${instanceId.current}] Data-reactive effect triggered:`,
409
+ // {
410
+ // storageManaged,
411
+ // userKey: userKey,
412
+ // hasUserData: !!userData,
413
+ // userDataLength: userData ? userData.length : 0,
414
+ // },
415
+ // );
416
+ // /* eslint-enable no-console */
417
+
418
+ // if (storageManaged && userKey && userData) {
419
+ // /* eslint-disable no-console */
420
+ // console.log(
421
+ // `[UserStorageManager:${instanceId.current}] Re-syncing session storage with localStorage changes`,
422
+ // );
423
+ // /* eslint-enable no-console */
424
+ // populateSessionFromUserData(userKey);
425
+ // } else {
426
+ // /* eslint-disable no-console */
427
+ // console.log(
428
+ // `[UserStorageManager:${instanceId.current}] Skipping data sync - conditions not met`,
429
+ // );
430
+ // /* eslint-enable no-console */
431
+ // }
432
+ // }, [userData, userKey, storageManaged]);
433
+
434
+ // Render only when properly initialized to prevent premature mounting
435
+ return isInitialized &&
436
+ storageManaged &&
437
+ userID !== undefined &&
438
+ isLoggedIn !== undefined ? (
439
+ children
440
+ ) : (
441
+ <div className="loading-container">
442
+ <div className="loading-text">Loading...</div>
443
+ </div>
444
+ );
445
+ };
446
+
37
447
  class MapViewer extends React.Component {
448
+ static contextType = UserContext;
449
+
38
450
  /**
39
451
  * This method does the creation of the main component
40
452
  * @param {*} props
@@ -56,18 +468,28 @@ class MapViewer extends React.Component {
56
468
  this.mapClass = classNames('map-container', {
57
469
  [`${props.customClass}`]: props.customClass || null,
58
470
  });
471
+
472
+ // Generate unique instance ID for tracking operations
473
+ this.instanceId = Math.random().toString(36).substring(7);
474
+
59
475
  this.state = {
60
476
  layerLoading: false,
61
477
  layers: {},
62
478
  uploadedFile: true,
63
479
  wmsServiceUrl: '',
64
480
  uploadError: false,
481
+ // Track current user state for comparison in componentDidUpdate
482
+ currentUserState: props.initialUserState || {
483
+ user_id: null,
484
+ isLoggedIn: false,
485
+ },
65
486
  };
66
487
  this.activeLayersHandler = this.activeLayersHandler.bind(this);
67
488
  this.activeLayersArray = {};
68
489
  this.props.mapviewer_config.loading = true;
69
490
  this.cfgUrls = this.props.cfg.Urls;
70
- this.userID = null;
491
+ // User state change handler
492
+ this.handleUserStateChange = this.handleUserStateChange.bind(this);
71
493
  this.loadingHandler = this.loadingHandler.bind(this);
72
494
  this.hotspotDataHandler = this.hotspotDataHandler.bind(this);
73
495
  this.mapLayersHandler = this.mapLayersHandler.bind(this);
@@ -80,6 +502,285 @@ class MapViewer extends React.Component {
80
502
  this.tax = null;
81
503
  }
82
504
 
505
+ // Method to handle user state changes from the monitoring wrapper
506
+ handleUserStateChange(newUserState, previousUserState) {
507
+ /* eslint-disable no-console */
508
+ console.log('[MapViewer] Handling user state change:', {
509
+ newUserState,
510
+ previousUserState,
511
+ currentState: this.state.currentUserState,
512
+ });
513
+ /* eslint-enable no-console */
514
+
515
+ // Update current user state in component state
516
+ this.setState({ currentUserState: newUserState });
517
+
518
+ // Trigger session state update logic
519
+ this.handleSessionStateUpdate(newUserState, previousUserState);
520
+ }
521
+
522
+ // Enhanced method to save session data to localStorage with instance tracking
523
+ saveSessionToLocalStorage = () => {
524
+ const { user_id, isLoggedIn } = this.context;
525
+ const instanceId = this.instanceId || 'unknown';
526
+
527
+ /* eslint-disable no-console */
528
+ console.log(`[MapViewer:${instanceId}] Saving session data:`, {
529
+ hasUserContext:
530
+ user_id !== undefined && user_id !== null && user_id !== '',
531
+ isLoggedIn,
532
+ userId:
533
+ user_id !== undefined && user_id !== null && user_id !== ''
534
+ ? user_id
535
+ : 'anonymous',
536
+ sessionStorageLength: sessionStorage.length,
537
+ });
538
+ /* eslint-enable no-console */
539
+
540
+ if (sessionStorage.length === 0) {
541
+ /* eslint-disable no-console */
542
+ console.log(
543
+ `[MapViewer:${instanceId}] Skipping data save - session storage is empty`,
544
+ );
545
+ /* eslint-enable no-console */
546
+ return;
547
+ }
548
+
549
+ const saveToLocal = (key) => {
550
+ const data = {};
551
+ for (let i = 0; i < sessionStorage.length; i++) {
552
+ const k = sessionStorage.key(i);
553
+ data[k] = sessionStorage.getItem(k);
554
+ }
555
+
556
+ try {
557
+ localStorage.setItem(key, JSON.stringify(data));
558
+ /* eslint-disable no-console */
559
+ console.log(
560
+ `[MapViewer:${instanceId}] Successfully saved data to localStorage key: ${key}`,
561
+ );
562
+ /* eslint-enable no-console */
563
+ } catch (error) {
564
+ /* eslint-disable no-console */
565
+ console.error(
566
+ `[MapViewer:${instanceId}] Error saving to localStorage key ${key}:`,
567
+ error,
568
+ );
569
+ /* eslint-enable no-console */
570
+ }
571
+ };
572
+
573
+ // Save session data to appropriate localStorage key based on user state (always overwrite user key)
574
+ if (
575
+ isLoggedIn &&
576
+ user_id !== undefined &&
577
+ user_id !== null &&
578
+ user_id !== ''
579
+ ) {
580
+ if (localStorage.getItem(`user_${user_id}`)) {
581
+ /* eslint-disable no-console */
582
+ console.log(
583
+ `[MapViewer:${instanceId}] User session data already exists in localStorage`,
584
+ );
585
+ /* eslint-enable no-console */
586
+ }
587
+ saveToLocal(`user_${user_id}`);
588
+ } else if (!isLoggedIn) {
589
+ saveToLocal('user_anonymous');
590
+ }
591
+ sessionStorage.clear();
592
+ };
593
+
594
+ // Handle page unload events (navigation, refresh, close)
595
+ handlePageUnload = (event) => {
596
+ /* eslint-disable no-console */
597
+ console.log(
598
+ `[MapViewer:${this.instanceId}] Page unload detected, saving session data`,
599
+ );
600
+ /* eslint-enable no-console */
601
+
602
+ this.saveSessionToLocalStorage();
603
+ };
604
+
605
+ // Handle visibility changes (tab switches, window minimizing)
606
+ handleVisibilityChange = () => {
607
+ if (document.visibilityState === 'hidden') {
608
+ /* eslint-disable no-console */
609
+ console.log(
610
+ `[MapViewer:${this.instanceId}] Page hidden, saving session data`,
611
+ );
612
+ /* eslint-enable no-console */
613
+
614
+ this.saveSessionToLocalStorage();
615
+ }
616
+ };
617
+
618
+ // Method to handle session state updates when user authentication changes
619
+ handleSessionStateUpdate(newUserState, previousUserState) {
620
+ const { user_id: newUserId, isLoggedIn: newIsLoggedIn } = newUserState;
621
+ const {
622
+ user_id: prevUserId,
623
+ isLoggedIn: prevIsLoggedIn,
624
+ } = previousUserState;
625
+
626
+ /* eslint-disable no-console */
627
+ console.log('[MapViewer] Processing session state update:', {
628
+ userIdChanged: prevUserId !== newUserId,
629
+ loginStateChanged: prevIsLoggedIn !== newIsLoggedIn,
630
+ loginTransition: `${prevIsLoggedIn} -> ${newIsLoggedIn}`,
631
+ userIdTransition: `${prevUserId} -> ${newUserId}`,
632
+ });
633
+ /* eslint-enable no-console */
634
+
635
+ // Handle login/logout transitions
636
+ if (prevIsLoggedIn !== newIsLoggedIn) {
637
+ if (newIsLoggedIn && !prevIsLoggedIn) {
638
+ // User just logged in
639
+ this.handleUserLogin(newUserId);
640
+ } else if (!newIsLoggedIn && prevIsLoggedIn) {
641
+ // User just logged out
642
+ this.handleUserLogout(prevUserId);
643
+ }
644
+ }
645
+
646
+ // Handle user ID changes (e.g., switching accounts)
647
+ if (prevUserId !== newUserId && newIsLoggedIn) {
648
+ this.handleUserSwitch(prevUserId, newUserId);
649
+ }
650
+ }
651
+
652
+ // Handle user login - restore saved session or preserve current session
653
+ handleUserLogin(userId) {
654
+ /* eslint-disable no-console */
655
+ console.log('[MapViewer] User logged in:', userId);
656
+ /* eslint-enable no-console */
657
+
658
+ // Check if user has saved data
659
+ const userKey = `user_${userId}`;
660
+ const userData = localStorage.getItem(userKey);
661
+
662
+ if (userData) {
663
+ /* eslint-disable no-console */
664
+ console.log('[MapViewer] Restoring saved user session data');
665
+ /* eslint-enable no-console */
666
+ // Restore user's saved session
667
+ try {
668
+ const parsed = JSON.parse(userData);
669
+ Object.keys(parsed).forEach((k) =>
670
+ sessionStorage.setItem(
671
+ k,
672
+ typeof parsed[k] === 'object'
673
+ ? JSON.stringify(parsed[k])
674
+ : parsed[k],
675
+ ),
676
+ );
677
+ } catch (error) {
678
+ /* eslint-disable no-console */
679
+ console.error('[MapViewer] Error restoring user session:', error);
680
+ /* eslint-enable no-console */
681
+ }
682
+ } else {
683
+ /* eslint-disable no-console */
684
+ console.log(
685
+ '[MapViewer] No saved session found - preserving current session',
686
+ );
687
+ /* eslint-enable no-console */
688
+ // No saved data - preserve current session state
689
+ }
690
+ }
691
+
692
+ // Handle user logout - save current session to anonymous storage
693
+ handleUserLogout(prevUserId) {
694
+ /* eslint-disable no-console */
695
+ console.log('[MapViewer] User logged out:', prevUserId);
696
+ /* eslint-enable no-console */
697
+
698
+ // Save current session to anonymous storage
699
+ const sessionContents = {};
700
+ for (let i = 0; i < sessionStorage.length; i++) {
701
+ const key = sessionStorage.key(i);
702
+ sessionContents[key] = sessionStorage.getItem(key);
703
+ }
704
+
705
+ if (Object.keys(sessionContents).length > 0) {
706
+ // Persist snapshot under previous user key first (if available)
707
+ if (prevUserId) {
708
+ try {
709
+ localStorage.setItem(
710
+ `user_${prevUserId}`,
711
+ JSON.stringify(sessionContents),
712
+ );
713
+ /* eslint-disable no-console */
714
+ console.log(
715
+ '[MapViewer] Saved session to previous user key before logout',
716
+ );
717
+ /* eslint-enable no-console */
718
+ } catch (error) {
719
+ /* eslint-disable no-console */
720
+ console.error(
721
+ '[MapViewer] Error saving previous user session on logout:',
722
+ error,
723
+ );
724
+ /* eslint-enable no-console */
725
+ }
726
+ }
727
+ try {
728
+ localStorage.setItem('user_anonymous', JSON.stringify(sessionContents));
729
+ /* eslint-disable no-console */
730
+ console.log('[MapViewer] Saved session to anonymous storage');
731
+ /* eslint-enable no-console */
732
+ } catch (error) {
733
+ /* eslint-disable no-console */
734
+ console.error('[MapViewer] Error saving anonymous session:', error);
735
+ /* eslint-enable no-console */
736
+ }
737
+ }
738
+ }
739
+
740
+ // Handle user account switching
741
+ handleUserSwitch(prevUserId, newUserId) {
742
+ /* eslint-disable no-console */
743
+ console.log('[MapViewer] User switched accounts:', {
744
+ prevUserId,
745
+ newUserId,
746
+ });
747
+ /* eslint-enable no-console */
748
+
749
+ // Save current session to previous user's storage
750
+ if (prevUserId) {
751
+ const sessionContents = {};
752
+ for (let i = 0; i < sessionStorage.length; i++) {
753
+ const key = sessionStorage.key(i);
754
+ sessionContents[key] = sessionStorage.getItem(key);
755
+ }
756
+
757
+ if (Object.keys(sessionContents).length > 0) {
758
+ try {
759
+ localStorage.setItem(
760
+ `user_${prevUserId}`,
761
+ JSON.stringify(sessionContents),
762
+ );
763
+ /* eslint-disable no-console */
764
+ console.log(
765
+ '[MapViewer] Saved session for previous user:',
766
+ prevUserId,
767
+ );
768
+ /* eslint-enable no-console */
769
+ } catch (error) {
770
+ /* eslint-disable no-console */
771
+ console.error(
772
+ '[MapViewer] Error saving previous user session:',
773
+ error,
774
+ );
775
+ /* eslint-enable no-console */
776
+ }
777
+ }
778
+ }
779
+
780
+ // Load new user's session
781
+ this.handleUserLogin(newUserId);
782
+ }
783
+
83
784
  mapLayersHandler(newLayers) {
84
785
  this.setState({ layers: newLayers });
85
786
  }
@@ -249,13 +950,41 @@ class MapViewer extends React.Component {
249
950
  logo: false,
250
951
  });
251
952
 
953
+ /* eslint-disable no-console */
954
+ console.log(
955
+ `[MapViewer:${this.instanceId}] Attempting to recover map state from sessionStorage`,
956
+ );
957
+ /* eslint-enable no-console */
958
+
252
959
  mapStatus = this.recoverState();
253
960
 
961
+ /* eslint-disable no-console */
962
+ console.log(`[MapViewer:${this.instanceId}] Recovered mapStatus:`, {
963
+ isNull: mapStatus === null,
964
+ isUndefined: mapStatus === undefined,
965
+ type: typeof mapStatus,
966
+ hasZoom: mapStatus && mapStatus.zoom !== undefined,
967
+ hasCenter: mapStatus && mapStatus.center !== undefined,
968
+ entryCount: mapStatus ? Object.entries(mapStatus).length : 0,
969
+ mapStatus: mapStatus,
970
+ });
971
+ /* eslint-enable no-console */
972
+
973
+ // Improved condition check to prevent false positives that overwrite restored data
254
974
  if (
255
975
  mapStatus === null ||
256
- (mapStatus.zoom === null && mapStatus.center === null) ||
257
- Object.entries(mapStatus).length === 0
976
+ mapStatus === undefined ||
977
+ (typeof mapStatus === 'object' &&
978
+ mapStatus.zoom === undefined &&
979
+ mapStatus.center === undefined) ||
980
+ (typeof mapStatus === 'object' && Object.entries(mapStatus).length === 0)
258
981
  ) {
982
+ /* eslint-disable no-console */
983
+ console.log(
984
+ `[MapViewer:${this.instanceId}] No valid map state found - initializing with default configuration`,
985
+ );
986
+ /* eslint-enable no-console */
987
+
259
988
  mapStatus = {};
260
989
  mapStatus.zoom = this.mapCfg.zoom;
261
990
  mapStatus.center = this.mapCfg.center;
@@ -263,6 +992,12 @@ class MapViewer extends React.Component {
263
992
  this.setCenterState(this.mapCfg.center);
264
993
  this.setZoomState(this.mapCfg.zoom);
265
994
  this.activeLayersHandler(this.mapCfg.activeLayers);
995
+ } else {
996
+ /* eslint-disable no-console */
997
+ console.log(
998
+ `[MapViewer:${this.instanceId}] Using restored map state from sessionStorage`,
999
+ );
1000
+ /* eslint-enable no-console */
266
1001
  }
267
1002
 
268
1003
  this.view = new MapView({
@@ -320,15 +1055,26 @@ class MapViewer extends React.Component {
320
1055
  // we will have stored the json response here:
321
1056
  // this.props.mapviewer_config
322
1057
  this.props.MapViewerConfig(flattenToAppURL(this.props.url));
323
- //Once we have created the MapView, we need to ensure that the map div
324
- //is refreshed in order to show the map on it. To do so, we need to
325
- //trigger the renderization again, and to trigger the renderization
326
- //we invoke the setState method, that changes the state and forces a
327
- //react component to render itself again
328
- //this.setState({});
1058
+
1059
+ // Add event listeners for page unload events to ensure data is saved
1060
+ /* eslint-disable no-console */
1061
+ console.log(
1062
+ `[MapViewer:${this.instanceId}] Adding page unload event listeners`,
1063
+ );
1064
+ /* eslint-enable no-console */
1065
+
1066
+ // beforeunload - most reliable for catching navigation away from page
1067
+ window.addEventListener('beforeunload', this.handlePageUnload);
1068
+
1069
+ // pagehide - more reliable than unload, catches all page hiding scenarios
1070
+ window.addEventListener('pagehide', this.handlePageUnload);
1071
+
1072
+ // visibilitychange - catches tab switches and window minimizing
1073
+ document.addEventListener('visibilitychange', this.handleVisibilityChange);
329
1074
  }
330
1075
 
331
1076
  componentDidUpdate(prevProps, prevState) {
1077
+ // Handle Download/dataset URL changes (existing logic)
332
1078
  if (
333
1079
  this.props.Download ||
334
1080
  (this.location &&
@@ -341,17 +1087,63 @@ class MapViewer extends React.Component {
341
1087
  }
342
1088
  sessionStorage.setItem('toc_panel_scrolls', toc_panel_scrolls);
343
1089
  }
344
- // if (
345
- // prevState.wmsServiceUrl !== this.state.wmsServiceUrl &&
346
- // this.state.wmsServiceUrl === ''
347
- // ) {
348
- // // Reset wmsServiceUrl without causing a new update of the children
349
- // this.setState({ wmsServiceUrl: '' });
350
- // }
1090
+
1091
+ // Handle user state changes from context
1092
+ const {
1093
+ user_id: currentUserId,
1094
+ isLoggedIn: currentIsLoggedIn,
1095
+ } = this.context;
1096
+ const { currentUserState: prevUserState } = prevState;
1097
+
1098
+ // Compare current context values with previous state
1099
+ if (
1100
+ prevUserState.user_id !== currentUserId ||
1101
+ prevUserState.isLoggedIn !== currentIsLoggedIn
1102
+ ) {
1103
+ /* eslint-disable no-console */
1104
+ console.log(
1105
+ '[MapViewer] User state change detected in componentDidUpdate:',
1106
+ {
1107
+ previous: prevUserState,
1108
+ current: { user_id: currentUserId, isLoggedIn: currentIsLoggedIn },
1109
+ },
1110
+ );
1111
+ /* eslint-enable no-console */
1112
+
1113
+ // Update component state to reflect new user state
1114
+ const newUserState = {
1115
+ user_id: currentUserId,
1116
+ isLoggedIn: currentIsLoggedIn,
1117
+ };
1118
+ this.setState({ currentUserState: newUserState });
1119
+
1120
+ // Handle the user state change
1121
+ this.handleSessionStateUpdate(newUserState, prevUserState);
1122
+ }
351
1123
  }
352
1124
 
353
1125
  componentWillUnmount() {
354
- // clean up
1126
+ // Remove event listeners to prevent memory leaks
1127
+ /* eslint-disable no-console */
1128
+ console.log(`[MapViewer:${this.instanceId}] Removing event listeners`);
1129
+ /* eslint-enable no-console */
1130
+
1131
+ window.removeEventListener('beforeunload', this.handlePageUnload);
1132
+ window.removeEventListener('pagehide', this.handlePageUnload);
1133
+ document.removeEventListener(
1134
+ 'visibilitychange',
1135
+ this.handleVisibilityChange,
1136
+ );
1137
+
1138
+ // Save data using the extracted method
1139
+ this.saveSessionToLocalStorage();
1140
+
1141
+ /* eslint-disable no-console */
1142
+ console.log(
1143
+ `[MapViewer:${this.instanceId}] Clearing session storage and cleaning up view`,
1144
+ );
1145
+ /* eslint-enable no-console */
1146
+
355
1147
  if (this.view) {
356
1148
  this.view.container = null;
357
1149
  this.view.destroy();
@@ -554,7 +1346,7 @@ class MapViewer extends React.Component {
554
1346
  if ('loading' in this.props.mapviewer_config) {
555
1347
  return (
556
1348
  <div className={this.mapClass}>
557
- <div ref={this.mapdiv} className="map"></div>
1349
+ <div ref={this.mapdiv} className="map" />
558
1350
  </div>
559
1351
  );
560
1352
  } else {
@@ -572,10 +1364,8 @@ class MapViewer extends React.Component {
572
1364
  {this.renderScale()}
573
1365
  {this.renderInfo()}
574
1366
  {this.renderHotspot()}
575
- {/*this.renderMenu()*/}
576
- {/*this.renderBookmark()*/}
577
- <CheckUserID reference={this} />
578
1367
  {this.renderLoadingSpinner()}
1368
+ <CheckUserID reference={this} />
579
1369
  {this.renderUploadService()}
580
1370
  </div>
581
1371
  </div>
@@ -584,8 +1374,10 @@ class MapViewer extends React.Component {
584
1374
  }
585
1375
  }
586
1376
 
1377
+ // CheckLogin, CheckUserID now use useUserContext instead of useCartState
1378
+
587
1379
  export const CheckLogin = ({ reference }) => {
588
- let { isLoggedIn } = useCartState();
1380
+ const { isLoggedIn } = useUserContext();
589
1381
  return (
590
1382
  <>
591
1383
  {isLoggedIn && (
@@ -606,9 +1398,9 @@ export const CheckLogin = ({ reference }) => {
606
1398
  </>
607
1399
  );
608
1400
  };
609
- export const CheckUserID = ({ reference }) => {
610
- let { user_id } = useCartState();
611
1401
 
1402
+ export const CheckUserID = ({ reference }) => {
1403
+ const { user_id, isLoggedIn } = useUserContext();
612
1404
  return (
613
1405
  <>
614
1406
  {reference.view && (
@@ -653,6 +1445,7 @@ export const CheckUserID = ({ reference }) => {
653
1445
  onServiceChange={reference.serviceChangeHandler}
654
1446
  uploadFileErrorHandler={reference.uploadFileErrorHandler}
655
1447
  userID={user_id}
1448
+ isLoggedIn={isLoggedIn}
656
1449
  getTaxonomy={reference.getTaxonomy}
657
1450
  tax={reference.tax}
658
1451
  />
@@ -661,10 +1454,47 @@ export const CheckUserID = ({ reference }) => {
661
1454
  </>
662
1455
  );
663
1456
  };
664
- const mapDispatchToProps = (dispatch) => {
665
- return {
666
- getTaxonomy: (name) => dispatch(getTaxonomy(name)),
1457
+
1458
+ const mapDispatchToProps = (dispatch) => ({
1459
+ getTaxonomy: (name) => dispatch(getTaxonomy(name)),
1460
+ });
1461
+
1462
+ const MapViewerWithProvider = (props) => {
1463
+ const mapViewerRef = useRef(null);
1464
+ const cartState = useCartState();
1465
+
1466
+ // Get initial user state
1467
+ const initialUserState = {
1468
+ user_id: cartState?.user_id || null,
1469
+ isLoggedIn: cartState?.isLoggedIn || false,
667
1470
  };
1471
+
1472
+ // Handle user state changes from the monitor
1473
+ const handleUserStateChange = useCallback(
1474
+ (newUserState, previousUserState) => {
1475
+ if (mapViewerRef.current) {
1476
+ mapViewerRef.current.handleUserStateChange(
1477
+ newUserState,
1478
+ previousUserState,
1479
+ );
1480
+ }
1481
+ },
1482
+ [],
1483
+ );
1484
+
1485
+ return (
1486
+ <UserProvider>
1487
+ <UserStorageManager>
1488
+ <MapViewerStateMonitor onUserStateChange={handleUserStateChange}>
1489
+ <MapViewer
1490
+ {...props}
1491
+ ref={mapViewerRef}
1492
+ initialUserState={initialUserState}
1493
+ />
1494
+ </MapViewerStateMonitor>
1495
+ </UserStorageManager>
1496
+ </UserProvider>
1497
+ );
668
1498
  };
669
1499
 
670
1500
  export default compose(
@@ -676,4 +1506,4 @@ export default compose(
676
1506
  ),
677
1507
  connect(null, mapDispatchToProps),
678
1508
  injectLazyLibs('highcharts'),
679
- )(MapViewer, MenuWidget);
1509
+ )(MapViewerWithProvider, MenuWidget);
@@ -1093,6 +1093,7 @@ class MenuWidget extends React.Component {
1093
1093
  let button = familyDropdown.querySelector(
1094
1094
  '.ccl-expandable__button',
1095
1095
  );
1096
+ scrollPosition = familyDropdown.offsetTop;
1096
1097
  if (button) {
1097
1098
  button.setAttribute('aria-expanded', 'true');
1098
1099
  }
@@ -2317,6 +2318,7 @@ class MenuWidget extends React.Component {
2317
2318
  this.props.uploadFileErrorHandler();
2318
2319
  return;
2319
2320
  }
2321
+ } else {
2320
2322
  }
2321
2323
  }
2322
2324
 
@@ -2434,7 +2436,7 @@ class MenuWidget extends React.Component {
2434
2436
  }
2435
2437
 
2436
2438
  saveUserServicesToStorage(layers) {
2437
- if (this.userID == null) return;
2439
+ if (this.userID === null) return;
2438
2440
 
2439
2441
  try {
2440
2442
  const layersToSave = layers.map((layer) => {
@@ -2477,8 +2479,7 @@ class MenuWidget extends React.Component {
2477
2479
  }
2478
2480
 
2479
2481
  async loadUserServicesFromStorage() {
2480
- if (this.userID != null) {
2481
- sessionStorage.clear();
2482
+ if (this.userID !== null) {
2482
2483
  try {
2483
2484
  const savedServices = JSON.parse(
2484
2485
  localStorage.getItem(USER_SERVICES_KEY + '_' + this.userID),
@@ -3361,33 +3362,24 @@ class MenuWidget extends React.Component {
3361
3362
  try {
3362
3363
  const response = await fetch(`${subLayer.url}?f=pjson`);
3363
3364
  if (!response.ok) {
3364
- //console.log('no response from server');
3365
- continue; // Skip this iteration on error
3365
+ continue;
3366
3366
  }
3367
- const subLayerData = await response.json(); // Await JSON parsing
3367
+ const subLayerData = await response.json();
3368
3368
  if (subLayerData === null) {
3369
- //console.log('no data retrieved:', subLayerData);
3370
3369
  continue;
3371
3370
  } else {
3372
- // Convert bounding box data to correct extent values for map view
3373
-
3374
3371
  let extent = this.convertBBOXValues(subLayerData.extent);
3375
-
3376
- // Store sublayer extent
3377
-
3378
3372
  BBoxes[subLayerData.name] = {
3379
3373
  id: subLayerData.id,
3380
3374
  extent: extent,
3381
3375
  };
3382
3376
  }
3383
- } catch (error) {
3384
- //console.error('Error fetching sublayer:', error);
3385
- }
3377
+ } catch (error) {}
3386
3378
  }
3387
3379
 
3388
3380
  BBoxes['dataset'] = this.convertBBOXValues(layer?.fullExtent?.extent);
3389
3381
 
3390
- return BBoxes; // Return BBoxes after all fetches are completed
3382
+ return BBoxes;
3391
3383
  }
3392
3384
 
3393
3385
  parseBBOXWMS(xml) {