@opengeoweb/workspace 19.3.0 → 19.5.0
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/dist/index.esm.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createSlice, createEntityAdapter, createSelector, createListenerMiddleware, isAnyOf, combineReducers, configureStore } from '@reduxjs/toolkit';
|
|
2
2
|
import { handleResponse, getHeaders, getQueryClient, QueryClientProviderWrapper, useAuthenticationContext as useAuthenticationContext$1, GEOWEB_ROLE_PRESETS_ADMIN as GEOWEB_ROLE_PRESETS_ADMIN$1 } from '@opengeoweb/api';
|
|
3
3
|
import { soundingsActions, SoundingsTypes, soundingsSelectors, soundingsReducer } from '@opengeoweb/soundings';
|
|
4
|
-
import { syncGroupsTypes, FEATURE_GROUP_PREFIX, syncGroupsActions, layerActions, mapActions, uiActions, genericActions, layerTypes, mapEnums, selectorMemoizationOptions, genericSelectors, mapSelectors, syncGroupsSelectors, routerActions, createViewObjectForNewMap, syncConstants, mapListener, serviceListener, layersListener, syncGroupsListener, openlayersListener, uiReducer, loadingIndicatorReducer, syncGroupsReducer, layerReducer, serviceReducer, mapReducer, uiTypes, isWarningsTimelineLinkSourceAllowed, getTakenWarningTimelineIds, linkComponentTypesConstants, constructSyncSourceDisplayName,
|
|
4
|
+
import { syncGroupsTypes, FEATURE_GROUP_PREFIX, syncGroupsActions, layerActions, mapActions, uiActions, genericActions, layerTypes, mapEnums, selectorMemoizationOptions, genericSelectors, mapSelectors, syncGroupsSelectors, routerActions, createViewObjectForNewMap, syncConstants, mapListener, serviceListener, layersListener, syncGroupsListener, openlayersListener, uiReducer, loadingIndicatorReducer, syncGroupsReducer, layerReducer, serviceReducer, mapReducer, uiTypes, isWarningsTimelineLinkSourceAllowed, getTakenWarningTimelineIds, linkComponentTypesConstants, constructSyncSourceDisplayName, uiSelectors, loadingIndicatorSelectors } from '@opengeoweb/store';
|
|
5
5
|
import { timeSeriesActions, TimeSeriesTypes, timeSeriesSelectors, timeSeriesReducer } from '@opengeoweb/time-series';
|
|
6
6
|
import { chunk, cloneDeep, isEqual, isString } from 'lodash';
|
|
7
7
|
import { getLeaves, createBalancedTreeFromLeaves, MosaicContext, getAndAssertNodeAtPathExists, MosaicWindow, Mosaic } from 'react-mosaic-component';
|
|
8
8
|
import { queryOptions, useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
9
|
-
import { sessionStorageProvider, isAxiosError, getAxiosErrorMessage, isAuthError, isAxiosAuthError, SHARED_NAMESPACE, safelyInitI18nForTestsAndStories, sharedTranslations, ConfirmationServiceProvider, CustomIconButton, useConfirmationDialog, usePrevious, AlertBanner, CustomAccordion, CustomTooltip, dateUtils, SystemScope, SearchHighlight, ToggleMenu, SearchField, Filter, LastUpdateTime, renderCounter, CustomDialog, ToolContainer, CustomToggleButton, ConfirmationButtons } from '@opengeoweb/shared';
|
|
9
|
+
import { sessionStorageProvider, isAxiosError, getAxiosErrorMessage, isAuthError, isAxiosAuthError, SHARED_NAMESPACE, safelyInitI18nForTestsAndStories, sharedTranslations, ConfirmationServiceProvider, CustomIconButton, useConfirmationDialog, usePrevious, AlertBanner, CustomAccordion, CustomTooltip, linkifyText, dateUtils, SystemScope, SearchHighlight, ToggleMenu, SearchField, Filter, LastUpdateTime, renderCounter, CustomDialog, ToolContainer, CustomToggleButton, ConfirmationButtons } from '@opengeoweb/shared';
|
|
10
10
|
import { pluginsActions, getPluginState, pluginsReducer, PluginProvider, PluginDebugProvider, OverlayPluginMenuButton, usePluginCatalog } from '@opengeoweb/plugins';
|
|
11
11
|
import { warningsFiltersActions, publicWarningsSelectors, warningsTranslations, WARN_NAMESPACE } from '@opengeoweb/warnings';
|
|
12
12
|
import { snackbarTypes, snackbarActions, snackbarListener, snackbarReducer, SnackbarWrapperConnect } from '@opengeoweb/snackbar';
|
|
@@ -1547,7 +1547,7 @@ var SUPPORTED_TIMESERIES_COMPONENT_TYPES = ['TimeSeries'];
|
|
|
1547
1547
|
// 3) Warning list components
|
|
1548
1548
|
var WARNING_LIST_COMPONENT_TYPES = ['WarningList', 'MaritimeWarnings', 'WarningsTimeline'];
|
|
1549
1549
|
// TODO: expand it and move to a more centralized place
|
|
1550
|
-
var ALL_COMPONENT_TYPES = [].concat(SUPPORTED_COMPONENT_TYPES, SUPPORTED_TIMESERIES_COMPONENT_TYPES, WARNING_LIST_COMPONENT_TYPES, AVIATION_COMPONENT_TYPES, CAP_COMPONENT_TYPES, ['TafModule', 'TrajksModule', 'SpaceweatherModule', 'Plugin']);
|
|
1550
|
+
var ALL_COMPONENT_TYPES = [].concat(SUPPORTED_COMPONENT_TYPES, SUPPORTED_TIMESERIES_COMPONENT_TYPES, WARNING_LIST_COMPONENT_TYPES, AVIATION_COMPONENT_TYPES, CAP_COMPONENT_TYPES, ['TafModule', 'TrajksModule', 'SpaceweatherModule', 'Plugin', 'Soundings']);
|
|
1551
1551
|
var ViewPresetErrorComponent;
|
|
1552
1552
|
(function (ViewPresetErrorComponent) {
|
|
1553
1553
|
ViewPresetErrorComponent["PRESET_DETAIL"] = "PRESET_DETAIL";
|
|
@@ -1818,8 +1818,8 @@ var warningsPresetChangeActions = [warningsFiltersActions.setWarningFilters, war
|
|
|
1818
1818
|
var syncGroupChangeActions = [syncGroupsActions.syncGroupRemoveTarget, syncGroupsActions.syncGroupAddTarget];
|
|
1819
1819
|
var mapPresetChangeActions = [layerActions.setBaseLayers, layerActions.layerDelete, layerActions.addLayer, layerActions.duplicateMapLayer, layerActions.layerChangeEnabled, layerActions.layerChangeName, layerActions.layerChangeStyle, layerActions.layerChangeOpacity, layerActions.layerChangeDimension, layerActions.layerChangeAcceptanceTime, layerActions.SetLayerLegendVisibilityPayload, mapActions.layerMoveLayer, mapActions.setAutoLayerId, mapActions.setAutoTimestepLayerId, mapActions.setAutoUpdateLayerId, mapActions.toggleAutoUpdate, mapActions.mapStartAnimation, mapActions.mapStopAnimation, mapActions.toggleTimeSliderIsVisible, mapActions.setTimeStep, mapActions.setTimeSliderSpan, mapActions.toggleTimeSpanAuto, mapActions.setAnimationDelay, mapActions.toggleTimestepAuto, mapActions.setAnimationStartTime, mapActions.setAnimationEndTime, mapActions.changeOverlayer, uiActions.setActiveMapIdForDialog, uiActions.setToggleOpenDialog, genericActions.setBbox, mapActions.setBbox, mapActions.toggleClock, mapActions.toggleClockOptions, mapActions.setClockPositionSize, mapActions.setDockedLayerManagerSize, mapActions.setDockedLayerManagerPosition, mapActions.setDockedLayerManagerDimensions];
|
|
1820
1820
|
var pluginPresetChangeActions = [pluginsActions.setGeoWebPluginState];
|
|
1821
|
-
var timeSeriesPresetChangeActions = [timeSeriesActions.addParameter, timeSeriesActions.addPlot, timeSeriesActions.deleteParameter, timeSeriesActions.deletePlot, timeSeriesActions.moveParameter, timeSeriesActions.movePlot, timeSeriesActions.toggleParameter, timeSeriesActions.togglePlot, timeSeriesActions.patchParameter, timeSeriesActions.updateTitle];
|
|
1822
|
-
var soundingsChangeActions = [soundingsActions.updateSelectedSource, soundingsActions.updateSelectedLocation, soundingsActions.updateDiagramType, soundingsActions.toggleLocationMarkers, soundingsActions.updateProfileDataPreferences, soundingsActions.changeSoundingPanelOrder, soundingsActions.toggleSoundingPanelVisibility, soundingsActions.setPanelIsOpen, soundingsActions.setPanelCustomWidth];
|
|
1821
|
+
var timeSeriesPresetChangeActions = [timeSeriesActions.addParameter, timeSeriesActions.addPlot, timeSeriesActions.deleteParameter, timeSeriesActions.deletePlot, timeSeriesActions.moveParameter, timeSeriesActions.movePlot, timeSeriesActions.toggleParameter, timeSeriesActions.togglePlot, timeSeriesActions.patchParameter, timeSeriesActions.updateTitle, timeSeriesActions.setLocationMenuSizeAndPosition, timeSeriesActions.setLocationsDialogOpen];
|
|
1822
|
+
var soundingsChangeActions = [soundingsActions.updateSelectedSource, soundingsActions.updateSelectedLocation, soundingsActions.updateDiagramType, soundingsActions.toggleLocationMarkers, soundingsActions.updateProfileDataPreferences, soundingsActions.changeSoundingPanelOrder, soundingsActions.toggleSoundingPanelVisibility, soundingsActions.setPanelIsOpen, soundingsActions.setPanelCustomWidth, soundingsActions.toggleLegendVisibility, soundingsActions.updateSelectedTime, soundingsActions.toggleTimeSliderVisibility, soundingsActions.setLocationMenuSizeAndPosition, soundingsActions.setLocationsMenuOpen];
|
|
1823
1823
|
var supportedMapActionOrigins = [layerTypes.LayerActionOrigin.layerManager, layerTypes.LayerActionOrigin.legend, layerTypes.LayerActionOrigin.wmsLoader, mapEnums.MapActionOrigin.map];
|
|
1824
1824
|
var supportedTimeSeriesActionOrigins = [TimeSeriesTypes.TimeSeriesActionOrigin.timeSeriesManager, TimeSeriesTypes.TimeSeriesActionOrigin.timeSeriesSelect];
|
|
1825
1825
|
var supportedSoundingsActionOrigins = [SoundingsTypes.SoundingsActionOrigin.soundingsComponent, SoundingsTypes.SoundingsActionOrigin.soundingsManager];
|
|
@@ -3115,6 +3115,22 @@ var getViewPresetToSendToBackend = createSelector(getViewById, getViewComponentT
|
|
|
3115
3115
|
initialProps: (_initialPropsByCompon = initialPropsByComponentType[viewComponentType]) != null ? _initialPropsByCompon : defaultInitialProps
|
|
3116
3116
|
});
|
|
3117
3117
|
}, selectorMemoizationOptions);
|
|
3118
|
+
/**
|
|
3119
|
+
* Gets workspace data with each view's current state included in initialProps.
|
|
3120
|
+
*/
|
|
3121
|
+
var getWorkspaceToSendToBackend = function getWorkspaceToSendToBackend(store) {
|
|
3122
|
+
var workspaceData = getWorkspaceData(store);
|
|
3123
|
+
return Object.assign({}, workspaceData, {
|
|
3124
|
+
views: workspaceData.views.map(function (view) {
|
|
3125
|
+
var _presetLike$component, _presetLike$initialPr;
|
|
3126
|
+
var presetLike = getViewPresetToSendToBackend(store, view.mosaicNodeId);
|
|
3127
|
+
return Object.assign({}, view, {
|
|
3128
|
+
componentType: (_presetLike$component = presetLike.componentType) != null ? _presetLike$component : view.componentType,
|
|
3129
|
+
initialProps: (_presetLike$initialPr = presetLike.initialProps) != null ? _presetLike$initialPr : {}
|
|
3130
|
+
});
|
|
3131
|
+
})
|
|
3132
|
+
});
|
|
3133
|
+
};
|
|
3118
3134
|
|
|
3119
3135
|
var selectors$1 = /*#__PURE__*/Object.freeze({
|
|
3120
3136
|
__proto__: null,
|
|
@@ -3133,6 +3149,7 @@ var selectors$1 = /*#__PURE__*/Object.freeze({
|
|
|
3133
3149
|
getWorkspaceError: getWorkspaceError,
|
|
3134
3150
|
getWorkspaceId: getWorkspaceId,
|
|
3135
3151
|
getWorkspaceState: getWorkspaceState,
|
|
3152
|
+
getWorkspaceToSendToBackend: getWorkspaceToSendToBackend,
|
|
3136
3153
|
hasWorkspaceChanges: hasWorkspaceChanges,
|
|
3137
3154
|
isWorkspaceLoading: isWorkspaceLoading
|
|
3138
3155
|
});
|
|
@@ -3766,7 +3783,7 @@ workspaceListListener.startListening({
|
|
|
3766
3783
|
effect: function () {
|
|
3767
3784
|
var _effect2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(_ref2, listenerApi) {
|
|
3768
3785
|
var _listenerApi$getState2;
|
|
3769
|
-
var payload, workspaceState, credentials, presetId, presetAction, data, emptyMapPresetTitle, emptyWorkspacePresetTitle, emptySoundingsPresetTitle, scope, originalData, workspaceDataNewTitle, _originalData$views, _data$views2, _originalData, mergedViews, payloadToSave, isSaveWorkspaceAction, isSaveAsWorkspaceAction, hasWorkspaceAlreadyBeenSaved, workspaceId, workspaceData,
|
|
3786
|
+
var payload, workspaceState, credentials, presetId, presetAction, data, emptyMapPresetTitle, emptyWorkspacePresetTitle, emptySoundingsPresetTitle, scope, originalData, workspaceDataNewTitle, _originalData$views, _data$views2, _originalData, mergedViews, payloadToSave, isSaveWorkspaceAction, isSaveAsWorkspaceAction, hasWorkspaceAlreadyBeenSaved, workspaceId, workspaceData, newWorkspace, _listenerApi$getState3, _credentials, hasChanges, message, _listenerApi$getState4, category, _t;
|
|
3770
3787
|
return _regenerator().w(function (_context2) {
|
|
3771
3788
|
while (1) switch (_context2.p = _context2.n) {
|
|
3772
3789
|
case 0:
|
|
@@ -3868,15 +3885,7 @@ workspaceListListener.startListening({
|
|
|
3868
3885
|
}
|
|
3869
3886
|
hasWorkspaceAlreadyBeenSaved = getWorkspaceDialogIsSaved(workspaceState);
|
|
3870
3887
|
workspaceId = presetId;
|
|
3871
|
-
workspaceData =
|
|
3872
|
-
viewsWithInitialProps = workspaceData.views.map(function (view) {
|
|
3873
|
-
var _presetLike$component, _presetLike$initialPr;
|
|
3874
|
-
var presetLike = getViewPresetToSendToBackend(listenerApi.getState(), view.mosaicNodeId);
|
|
3875
|
-
return Object.assign({}, view, {
|
|
3876
|
-
componentType: (_presetLike$component = presetLike.componentType) != null ? _presetLike$component : view.componentType,
|
|
3877
|
-
initialProps: (_presetLike$initialPr = presetLike.initialProps) != null ? _presetLike$initialPr : {}
|
|
3878
|
-
});
|
|
3879
|
-
});
|
|
3888
|
+
workspaceData = getWorkspaceToSendToBackend(listenerApi.getState());
|
|
3880
3889
|
newWorkspace = Object.assign({}, workspaceData, {
|
|
3881
3890
|
id: workspaceId,
|
|
3882
3891
|
title: data.title,
|
|
@@ -3884,7 +3893,7 @@ workspaceListListener.startListening({
|
|
|
3884
3893
|
description: data.description,
|
|
3885
3894
|
labels: data.labels,
|
|
3886
3895
|
scope: scope,
|
|
3887
|
-
views:
|
|
3896
|
+
views: workspaceData.views,
|
|
3888
3897
|
username: (credentials == null ? void 0 : credentials.username) || ''
|
|
3889
3898
|
});
|
|
3890
3899
|
if (!(!hasWorkspaceAlreadyBeenSaved && isSaveAsWorkspaceAction)) {
|
|
@@ -6908,7 +6917,7 @@ var WorkspacePresetInfo = function WorkspacePresetInfo(_ref) {
|
|
|
6908
6917
|
color: 'text.secondary'
|
|
6909
6918
|
},
|
|
6910
6919
|
variant: "body2",
|
|
6911
|
-
children: shortDescription
|
|
6920
|
+
children: shortDescription ? linkifyText(shortDescription) : t('workspace-info-no-description')
|
|
6912
6921
|
})]
|
|
6913
6922
|
}), jsxs(Box, {
|
|
6914
6923
|
sx: {
|
|
@@ -6953,7 +6962,7 @@ var WorkspacePresetInfo = function WorkspacePresetInfo(_ref) {
|
|
|
6953
6962
|
overflowWrap: 'anywhere'
|
|
6954
6963
|
},
|
|
6955
6964
|
variant: "body2",
|
|
6956
|
-
children: abstractText
|
|
6965
|
+
children: abstractText ? linkifyText(abstractText) : t('workspace-info-no-abstract')
|
|
6957
6966
|
})
|
|
6958
6967
|
})]
|
|
6959
6968
|
}), jsxs(Box, {
|
|
@@ -7680,7 +7689,13 @@ var ViewPresetMenuListConnect = function ViewPresetMenuListConnect(_ref) {
|
|
|
7680
7689
|
case 'WarningsTimeline':
|
|
7681
7690
|
return currentWarningsPreset;
|
|
7682
7691
|
case 'TimeSeries':
|
|
7683
|
-
|
|
7692
|
+
{
|
|
7693
|
+
var state = store.getState();
|
|
7694
|
+
var isLocationsDialogOpen = uiSelectors.getisDialogOpen(state, uiTypes.DialogTypes.TimeseriesLocations) && uiSelectors.getSourcePanelId(state, uiTypes.DialogTypes.TimeseriesLocations) === panelId;
|
|
7695
|
+
return Object.assign({}, currentTimeSeriesPreset, {
|
|
7696
|
+
isLocationsDialogOpen: isLocationsDialogOpen
|
|
7697
|
+
});
|
|
7698
|
+
}
|
|
7684
7699
|
case 'Map':
|
|
7685
7700
|
return {
|
|
7686
7701
|
mapPreset: currentMapPreset,
|
|
@@ -9161,7 +9176,14 @@ var _WorkspaceViewTitleConnectNoMemo = function WorkspaceViewTitleConnectNoMemo(
|
|
|
9161
9176
|
case 'WarningsTimeline':
|
|
9162
9177
|
return currentWarningsPreset;
|
|
9163
9178
|
case 'TimeSeries':
|
|
9164
|
-
|
|
9179
|
+
{
|
|
9180
|
+
var state = store.getState();
|
|
9181
|
+
var preset = timeSeriesSelectors.getTimeSeriesPreset(state, panelId);
|
|
9182
|
+
var isLocationsDialogOpen = uiSelectors.getisDialogOpen(state, uiTypes.DialogTypes.TimeseriesLocations) && uiSelectors.getSourcePanelId(state, uiTypes.DialogTypes.TimeseriesLocations) === panelId;
|
|
9183
|
+
return Object.assign({}, preset, {
|
|
9184
|
+
isLocationsDialogOpen: isLocationsDialogOpen
|
|
9185
|
+
});
|
|
9186
|
+
}
|
|
9165
9187
|
case 'Soundings':
|
|
9166
9188
|
return soundingsSelectors.getSoundingsPreset(store.getState(), panelId);
|
|
9167
9189
|
case 'Map':
|
|
@@ -9751,6 +9773,11 @@ var WorkspaceView = function WorkspaceView(_ref) {
|
|
|
9751
9773
|
onClick: function onClick(event) {
|
|
9752
9774
|
return handleClickOnWindow(event, viewId);
|
|
9753
9775
|
},
|
|
9776
|
+
onFocus: function onFocus() {
|
|
9777
|
+
if (viewId !== activeWindowId) {
|
|
9778
|
+
setActiveWindowId(viewId);
|
|
9779
|
+
}
|
|
9780
|
+
},
|
|
9754
9781
|
sx: function sx(theme) {
|
|
9755
9782
|
return {
|
|
9756
9783
|
'> *': {
|
|
@@ -10100,42 +10127,35 @@ var WorkspaceOptionsConnect = function WorkspaceOptionsConnect(_ref) {
|
|
|
10100
10127
|
isPasteDialogOpen = _React$useState[0],
|
|
10101
10128
|
setIsPasteDialogOpen = _React$useState[1];
|
|
10102
10129
|
var onCopyToClipboard = React.useCallback(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
10103
|
-
var
|
|
10130
|
+
var workspaceWithLiveViews, sanitized, json;
|
|
10104
10131
|
return _regenerator().w(function (_context) {
|
|
10105
10132
|
while (1) switch (_context.p = _context.n) {
|
|
10106
10133
|
case 0:
|
|
10107
10134
|
_context.p = 0;
|
|
10108
|
-
|
|
10109
|
-
|
|
10110
|
-
case 1:
|
|
10111
|
-
preset = _context.v;
|
|
10112
|
-
_context.n = 2;
|
|
10113
|
-
return expandWorkspaceViewPresets(preset, auth);
|
|
10114
|
-
case 2:
|
|
10115
|
-
expanded = _context.v;
|
|
10116
|
-
sanitized = sanitizeWorkspaceForClipboard(expanded);
|
|
10135
|
+
workspaceWithLiveViews = getWorkspaceToSendToBackend(store.getState());
|
|
10136
|
+
sanitized = sanitizeWorkspaceForClipboard(workspaceWithLiveViews);
|
|
10117
10137
|
json = JSON.stringify(sanitized, null, 2);
|
|
10118
|
-
_context.n =
|
|
10138
|
+
_context.n = 1;
|
|
10119
10139
|
return writeToClipboard(json);
|
|
10120
|
-
case
|
|
10140
|
+
case 1:
|
|
10121
10141
|
dispatch(snackbarActions.openSnackbar({
|
|
10122
10142
|
type: snackbarTypes.SnackbarMessageType.VERBATIM_MESSAGE,
|
|
10123
10143
|
message: t('workspace-copy-success')
|
|
10124
10144
|
}));
|
|
10125
|
-
_context.n =
|
|
10145
|
+
_context.n = 3;
|
|
10126
10146
|
break;
|
|
10127
|
-
case
|
|
10128
|
-
_context.p =
|
|
10147
|
+
case 2:
|
|
10148
|
+
_context.p = 2;
|
|
10129
10149
|
_context.v;
|
|
10130
10150
|
dispatch(snackbarActions.openSnackbar({
|
|
10131
10151
|
type: snackbarTypes.SnackbarMessageType.VERBATIM_MESSAGE,
|
|
10132
10152
|
message: t('workspace-error-generic')
|
|
10133
10153
|
}));
|
|
10134
|
-
case
|
|
10154
|
+
case 3:
|
|
10135
10155
|
return _context.a(2);
|
|
10136
10156
|
}
|
|
10137
|
-
}, _callee, null, [[0,
|
|
10138
|
-
})), [
|
|
10157
|
+
}, _callee, null, [[0, 2]]);
|
|
10158
|
+
})), [dispatch, store, t]);
|
|
10139
10159
|
var onPasteFromClipboard = React.useCallback(function () {
|
|
10140
10160
|
setIsPasteDialogOpen(true);
|
|
10141
10161
|
}, []);
|
|
@@ -403,4 +403,8 @@ export declare const getViewPresetToSendToBackend: ((state: any, ...params: any[
|
|
|
403
403
|
memoize: typeof import("reselect").weakMapMemoize;
|
|
404
404
|
argsMemoize: typeof import("reselect").weakMapMemoize;
|
|
405
405
|
};
|
|
406
|
+
/**
|
|
407
|
+
* Gets workspace data with each view's current state included in initialProps.
|
|
408
|
+
*/
|
|
409
|
+
export declare const getWorkspaceToSendToBackend: (store: AppStore) => WorkspaceData;
|
|
406
410
|
export {};
|