@itwin/map-layers 3.0.0-extension.0 → 3.1.0-dev.5
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 +82 -1
- package/README.md +1 -1
- package/lib/cjs/MapLayerPreferences.d.ts +89 -0
- package/lib/cjs/MapLayerPreferences.d.ts.map +1 -0
- package/lib/cjs/MapLayerPreferences.js +312 -0
- package/lib/cjs/MapLayerPreferences.js.map +1 -0
- package/lib/cjs/mapLayers.d.ts +18 -13
- package/lib/cjs/mapLayers.d.ts.map +1 -1
- package/lib/cjs/mapLayers.js +25 -17
- package/lib/cjs/mapLayers.js.map +1 -1
- package/lib/cjs/public/locales/en/mapLayers.json +5 -2
- package/lib/cjs/ui/MapLayersUiItemsProvider.js +1 -1
- package/lib/cjs/ui/MapLayersUiItemsProvider.js.map +1 -1
- package/lib/cjs/ui/widget/AttachLayerPopupButton.d.ts.map +1 -1
- package/lib/cjs/ui/widget/AttachLayerPopupButton.js +5 -3
- package/lib/cjs/ui/widget/AttachLayerPopupButton.js.map +1 -1
- package/lib/cjs/ui/widget/BasemapPanel.d.ts.map +1 -1
- package/lib/cjs/ui/widget/BasemapPanel.js +5 -4
- package/lib/cjs/ui/widget/BasemapPanel.js.map +1 -1
- package/lib/cjs/ui/widget/MapLayerManager.d.ts +2 -2
- package/lib/cjs/ui/widget/MapLayerManager.d.ts.map +1 -1
- package/lib/cjs/ui/widget/MapLayerManager.js +24 -10
- package/lib/cjs/ui/widget/MapLayerManager.js.map +1 -1
- package/lib/cjs/ui/widget/MapLayerManager.scss +0 -7
- package/lib/cjs/ui/widget/MapManagerSettings.d.ts.map +1 -1
- package/lib/cjs/ui/widget/MapManagerSettings.js +51 -22
- package/lib/cjs/ui/widget/MapManagerSettings.js.map +1 -1
- package/lib/cjs/ui/widget/MapManagerSettings.scss +1 -1
- package/lib/cjs/ui/widget/MapUrlDialog.d.ts.map +1 -1
- package/lib/cjs/ui/widget/MapUrlDialog.js +231 -161
- package/lib/cjs/ui/widget/MapUrlDialog.js.map +1 -1
- package/lib/cjs/ui/widget/SubLayersTree.d.ts.map +1 -1
- package/lib/cjs/ui/widget/SubLayersTree.js +1 -1
- package/lib/cjs/ui/widget/SubLayersTree.js.map +1 -1
- package/lib/cjs/ui/widget/TransparencyPopupButton.js +2 -2
- package/lib/cjs/ui/widget/TransparencyPopupButton.js.map +1 -1
- package/lib/esm/MapLayerPreferences.d.ts +89 -0
- package/lib/esm/MapLayerPreferences.d.ts.map +1 -0
- package/lib/esm/MapLayerPreferences.js +308 -0
- package/lib/esm/MapLayerPreferences.js.map +1 -0
- package/lib/esm/mapLayers.d.ts +18 -13
- package/lib/esm/mapLayers.d.ts.map +1 -1
- package/lib/esm/mapLayers.js +26 -18
- package/lib/esm/mapLayers.js.map +1 -1
- package/lib/esm/public/locales/en/mapLayers.json +5 -2
- package/lib/esm/ui/MapLayersUiItemsProvider.js +1 -1
- package/lib/esm/ui/MapLayersUiItemsProvider.js.map +1 -1
- package/lib/esm/ui/widget/AttachLayerPopupButton.d.ts.map +1 -1
- package/lib/esm/ui/widget/AttachLayerPopupButton.js +6 -4
- package/lib/esm/ui/widget/AttachLayerPopupButton.js.map +1 -1
- package/lib/esm/ui/widget/BasemapPanel.d.ts.map +1 -1
- package/lib/esm/ui/widget/BasemapPanel.js +6 -5
- package/lib/esm/ui/widget/BasemapPanel.js.map +1 -1
- package/lib/esm/ui/widget/MapLayerManager.d.ts +2 -2
- package/lib/esm/ui/widget/MapLayerManager.d.ts.map +1 -1
- package/lib/esm/ui/widget/MapLayerManager.js +21 -7
- package/lib/esm/ui/widget/MapLayerManager.js.map +1 -1
- package/lib/esm/ui/widget/MapLayerManager.scss +0 -7
- package/lib/esm/ui/widget/MapManagerSettings.d.ts.map +1 -1
- package/lib/esm/ui/widget/MapManagerSettings.js +53 -24
- package/lib/esm/ui/widget/MapManagerSettings.js.map +1 -1
- package/lib/esm/ui/widget/MapManagerSettings.scss +1 -1
- package/lib/esm/ui/widget/MapUrlDialog.d.ts.map +1 -1
- package/lib/esm/ui/widget/MapUrlDialog.js +233 -163
- package/lib/esm/ui/widget/MapUrlDialog.js.map +1 -1
- package/lib/esm/ui/widget/SubLayersTree.d.ts.map +1 -1
- package/lib/esm/ui/widget/SubLayersTree.js +1 -1
- package/lib/esm/ui/widget/SubLayersTree.js.map +1 -1
- package/lib/esm/ui/widget/TransparencyPopupButton.js +2 -2
- package/lib/esm/ui/widget/TransparencyPopupButton.js.map +1 -1
- package/lib/public/locales/en/mapLayers.json +5 -2
- package/package.json +34 -39
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
5
|
// cSpell:ignore Modeless WMTS
|
|
6
6
|
import * as React from "react";
|
|
7
|
-
import {
|
|
8
|
-
import { Dialog, Icon, InputStatus } from "@itwin/core-react";
|
|
7
|
+
import { Dialog, Icon } from "@itwin/core-react";
|
|
9
8
|
import { ModalDialogManager } from "@itwin/appui-react";
|
|
9
|
+
import { Input, LabeledInput, ProgressLinear, Radio, Select } from "@itwin/itwinui-react";
|
|
10
10
|
import { MapLayersUiItemsProvider } from "../MapLayersUiItemsProvider";
|
|
11
|
-
import { IModelApp,
|
|
11
|
+
import { IModelApp, MapLayerAuthType, MapLayerImageryProviderStatus, MapLayerSource, MapLayerSourceStatus, NotifyMessageDetails, OutputMessagePriority, } from "@itwin/core-frontend";
|
|
12
12
|
import "./MapUrlDialog.scss";
|
|
13
13
|
import { DialogButtonType, SpecialKey } from "@itwin/appui-abstract";
|
|
14
|
+
import { MapLayerPreferences } from "../../MapLayerPreferences";
|
|
15
|
+
import { MapLayersUI } from "../../mapLayers";
|
|
14
16
|
export const MAP_TYPES = {
|
|
15
17
|
wms: "WMS",
|
|
16
18
|
arcGis: "ArcGIS",
|
|
@@ -19,9 +21,8 @@ export const MAP_TYPES = {
|
|
|
19
21
|
};
|
|
20
22
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
21
23
|
export function MapUrlDialog(props) {
|
|
22
|
-
var _a, _b, _c, _d, _e
|
|
24
|
+
var _a, _b, _c, _d, _e;
|
|
23
25
|
const { isOverlay, onOkResult, mapTypesOptions } = props;
|
|
24
|
-
const supportWmsAuthentication = ((mapTypesOptions === null || mapTypesOptions === void 0 ? void 0 : mapTypesOptions.supportWmsAuthentication) ? true : false);
|
|
25
26
|
const getMapUrlFromProps = React.useCallback(() => {
|
|
26
27
|
var _a;
|
|
27
28
|
if (props.mapLayerSourceToEdit) {
|
|
@@ -62,10 +63,9 @@ export function MapUrlDialog(props) {
|
|
|
62
63
|
const [modelSettingsLabel] = React.useState(MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:CustomAttach.StoreOnModelSettings"));
|
|
63
64
|
const [missingCredentialsLabel] = React.useState(MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:CustomAttach.MissingCredentials"));
|
|
64
65
|
const [invalidCredentialsLabel] = React.useState(MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:CustomAttach.InvalidCredentials"));
|
|
65
|
-
const [serverRequireCredentials, setServerRequireCredentials] = React.useState(
|
|
66
|
+
const [serverRequireCredentials, setServerRequireCredentials] = React.useState(false);
|
|
66
67
|
const [invalidCredentialsProvided, setInvalidCredentialsProvided] = React.useState(false);
|
|
67
68
|
const [layerAttachPending, setLayerAttachPending] = React.useState(false);
|
|
68
|
-
const [warningMessage, setWarningMessage] = React.useState(props.layerRequiringCredentials ? missingCredentialsLabel : undefined);
|
|
69
69
|
const [mapUrl, setMapUrl] = React.useState(getMapUrlFromProps());
|
|
70
70
|
const [mapName, setMapName] = React.useState(getMapNameFromProps());
|
|
71
71
|
const [userName, setUserName] = React.useState("");
|
|
@@ -75,8 +75,9 @@ export function MapUrlDialog(props) {
|
|
|
75
75
|
const [passwordRequiredLabel] = React.useState(MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:AuthenticationInputs.PasswordRequired"));
|
|
76
76
|
const [userNameLabel] = React.useState(MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:AuthenticationInputs.Username"));
|
|
77
77
|
const [userNameRequiredLabel] = React.useState(MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:AuthenticationInputs.UsernameRequired"));
|
|
78
|
-
const [settingsStorage, setSettingsStorageRadio] = React.useState("
|
|
79
|
-
const [
|
|
78
|
+
const [settingsStorage, setSettingsStorageRadio] = React.useState("iTwin");
|
|
79
|
+
const [layerAuthMethod, setLayerAuthMethod] = React.useState(MapLayerAuthType.None);
|
|
80
|
+
const [mapType, setMapType] = React.useState((_a = getFormatFromProps()) !== null && _a !== void 0 ? _a : MAP_TYPES.arcGis);
|
|
80
81
|
// 'isMounted' is used to prevent any async operation once the hook has been
|
|
81
82
|
// unloaded. Otherwise we get a 'Can't perform a React state update on an unmounted component.' warning in the console.
|
|
82
83
|
const isMounted = React.useRef(false);
|
|
@@ -96,15 +97,10 @@ export function MapUrlDialog(props) {
|
|
|
96
97
|
types.push({ value: MAP_TYPES.tileUrl, label: MAP_TYPES.tileUrl });
|
|
97
98
|
return types;
|
|
98
99
|
});
|
|
99
|
-
const [isSettingsStorageAvailable] = React.useState(((
|
|
100
|
+
const [isSettingsStorageAvailable] = React.useState(MapLayersUI.iTwinConfig && ((_c = (_b = props === null || props === void 0 ? void 0 : props.activeViewport) === null || _b === void 0 ? void 0 : _b.iModel) === null || _c === void 0 ? void 0 : _c.iTwinId) && ((_e = (_d = props === null || props === void 0 ? void 0 : props.activeViewport) === null || _d === void 0 ? void 0 : _d.iModel) === null || _e === void 0 ? void 0 : _e.iModelId));
|
|
100
101
|
// Even though the settings storage is available,
|
|
101
102
|
// we don't always want to enable it in the UI.
|
|
102
103
|
const [settingsStorageDisabled] = React.useState(!isSettingsStorageAvailable || props.mapLayerSourceToEdit !== undefined || props.layerRequiringCredentials !== undefined);
|
|
103
|
-
const isAuthSupported = React.useCallback(() => {
|
|
104
|
-
return ((mapType === MAP_TYPES.wms || mapType === MAP_TYPES.wms) && supportWmsAuthentication)
|
|
105
|
-
|| mapType === MAP_TYPES.arcGis;
|
|
106
|
-
}, [mapType, supportWmsAuthentication]);
|
|
107
|
-
// const [layerIdxToEdit] = React.useState((): number | undefined => {
|
|
108
104
|
const [layerRequiringCredentialsIdx] = React.useState(() => {
|
|
109
105
|
var _a;
|
|
110
106
|
if (props.layerRequiringCredentials === undefined || !props.layerRequiringCredentials.name || !props.layerRequiringCredentials.url) {
|
|
@@ -119,20 +115,15 @@ export function MapUrlDialog(props) {
|
|
|
119
115
|
}
|
|
120
116
|
});
|
|
121
117
|
// Update warning message based on the dialog state and server response
|
|
122
|
-
React.useEffect(() => {
|
|
123
|
-
if (invalidCredentialsProvided) {
|
|
124
|
-
setWarningMessage(invalidCredentialsLabel);
|
|
125
|
-
}
|
|
126
|
-
else if (serverRequireCredentials && (!userName || !password)) {
|
|
127
|
-
setWarningMessage(missingCredentialsLabel);
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
setWarningMessage(undefined);
|
|
131
|
-
}
|
|
132
|
-
}, [invalidCredentialsProvided, invalidCredentialsLabel, missingCredentialsLabel, serverRequireCredentials, userName, password, setWarningMessage]);
|
|
133
118
|
const handleMapTypeSelection = React.useCallback((newValue) => {
|
|
134
119
|
setMapType(newValue);
|
|
135
|
-
|
|
120
|
+
// Reset few states
|
|
121
|
+
if (invalidCredentialsProvided)
|
|
122
|
+
setInvalidCredentialsProvided(false);
|
|
123
|
+
if (layerAuthMethod !== MapLayerAuthType.None) {
|
|
124
|
+
setLayerAuthMethod(MapLayerAuthType.None);
|
|
125
|
+
}
|
|
126
|
+
}, [invalidCredentialsProvided, layerAuthMethod]);
|
|
136
127
|
const handleCancel = React.useCallback(() => {
|
|
137
128
|
if (props.onCancelResult) {
|
|
138
129
|
props.onCancelResult();
|
|
@@ -150,86 +141,109 @@ export function MapUrlDialog(props) {
|
|
|
150
141
|
if (invalidCredentialsProvided)
|
|
151
142
|
setInvalidCredentialsProvided(false);
|
|
152
143
|
}, [setPassword, invalidCredentialsProvided, setInvalidCredentialsProvided]);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
144
|
+
// return true if authorization is needed
|
|
145
|
+
const updateAuthState = React.useCallback((sourceValidation) => {
|
|
146
|
+
var _a, _b;
|
|
147
|
+
const sourceRequireAuth = (sourceValidation.status === MapLayerSourceStatus.RequireAuth);
|
|
148
|
+
const invalidCredentials = (sourceValidation.status === MapLayerSourceStatus.InvalidCredentials);
|
|
149
|
+
if (sourceRequireAuth && ((_a = sourceValidation.authInfo) === null || _a === void 0 ? void 0 : _a.authMethod) !== undefined) {
|
|
150
|
+
setLayerAuthMethod((_b = sourceValidation.authInfo) === null || _b === void 0 ? void 0 : _b.authMethod);
|
|
151
|
+
}
|
|
152
|
+
if (invalidCredentials) {
|
|
153
|
+
setInvalidCredentialsProvided(true);
|
|
154
|
+
}
|
|
155
|
+
else if (invalidCredentialsProvided) {
|
|
156
|
+
setInvalidCredentialsProvided(false); // flag reset
|
|
157
|
+
}
|
|
158
|
+
return sourceRequireAuth || invalidCredentials;
|
|
159
|
+
}, [invalidCredentialsProvided]);
|
|
160
|
+
const updateAttachedLayer = React.useCallback(async (source, validation) => {
|
|
161
|
+
const vp = props === null || props === void 0 ? void 0 : props.activeViewport;
|
|
162
|
+
if (vp === undefined || source === undefined || layerRequiringCredentialsIdx === undefined) {
|
|
163
|
+
const error = MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachMissingViewOrSource");
|
|
164
|
+
const msg = MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachError", { error, sourceUrl: source.url });
|
|
165
|
+
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
// Layer is already attached,
|
|
169
|
+
vp.displayStyle.changeMapLayerProps({
|
|
170
|
+
subLayers: validation.subLayers,
|
|
171
|
+
}, layerRequiringCredentialsIdx, isOverlay);
|
|
172
|
+
vp.displayStyle.changeMapLayerCredentials(layerRequiringCredentialsIdx, isOverlay, source.userName, source.password);
|
|
173
|
+
// Reset the provider's status
|
|
174
|
+
const provider = vp.getMapLayerImageryProvider(layerRequiringCredentialsIdx, isOverlay);
|
|
175
|
+
if (provider && provider.status !== MapLayerImageryProviderStatus.Valid) {
|
|
176
|
+
provider.status = MapLayerImageryProviderStatus.Valid;
|
|
177
|
+
}
|
|
178
|
+
vp.invalidateRenderPlan();
|
|
179
|
+
// This handler will close the layer source handler, and therefore the MapUrl dialog.
|
|
180
|
+
// don't call it if the dialog needs to remains open.
|
|
181
|
+
onOkResult();
|
|
182
|
+
return true;
|
|
183
|
+
}, [isOverlay, layerRequiringCredentialsIdx, onOkResult, props.activeViewport]);
|
|
184
|
+
// Returns true if no further input is needed from end-user.
|
|
185
|
+
const doAttach = React.useCallback(async (source, validation) => {
|
|
186
|
+
const vp = props === null || props === void 0 ? void 0 : props.activeViewport;
|
|
187
|
+
if (vp === undefined || source === undefined) {
|
|
188
|
+
const error = MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachMissingViewOrSource");
|
|
189
|
+
const msg = MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachError", { error, sourceUrl: source.url });
|
|
190
|
+
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
// Update service settings if storage is available and we are not prompting user for credentials
|
|
194
|
+
if (!settingsStorageDisabled && !props.layerRequiringCredentials) {
|
|
161
195
|
const storeOnIModel = "Model" === settingsStorage;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
else {
|
|
195
|
-
// Update service settings if storage is available and we are not prompting user for credentials
|
|
196
|
-
if (!settingsStorageDisabled && !props.layerRequiringCredentials) {
|
|
197
|
-
if (!(await MapLayerSettingsService.storeSourceInSettingsService(source, storeOnIModel, vp.iModel.iTwinId, vp.iModel.iModelId)))
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
const layerSettings = source.toLayerSettings(validation.subLayers);
|
|
201
|
-
if (layerSettings) {
|
|
202
|
-
vp.displayStyle.attachMapLayerSettings(layerSettings, isOverlay, undefined);
|
|
203
|
-
const msg = IModelApp.localization.getLocalizedString("mapLayers:Messages.MapLayerAttached", { sourceName: source.name, sourceUrl: source.url });
|
|
204
|
-
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Info, msg));
|
|
205
|
-
}
|
|
206
|
-
else {
|
|
207
|
-
const msgError = MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:Messages.MapLayerLayerSettingsConversionError");
|
|
208
|
-
const msg = MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:CustomAttach.MapLayerAttachError", { error: msgError, sourceUrl: source.url });
|
|
209
|
-
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
vp.invalidateRenderPlan();
|
|
213
|
-
}
|
|
214
|
-
if (closeDialog) {
|
|
215
|
-
// This handler will close the layer source handler, and therefore the MapUrl dialog.
|
|
216
|
-
// don't call it if the dialog needs to remains open.
|
|
217
|
-
onOkResult();
|
|
218
|
-
}
|
|
196
|
+
if (!(await MapLayerPreferences.storeSource(source, storeOnIModel, vp.iModel.iTwinId, vp.iModel.iModelId))) {
|
|
197
|
+
const msgError = MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:Messages.MapLayerPreferencesStoreFailed");
|
|
198
|
+
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msgError));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const layerSettings = source.toLayerSettings(validation.subLayers);
|
|
202
|
+
if (layerSettings) {
|
|
203
|
+
vp.displayStyle.attachMapLayerSettings(layerSettings, isOverlay, undefined);
|
|
204
|
+
const msg = IModelApp.localization.getLocalizedString("mapLayers:Messages.MapLayerAttached", { sourceName: source.name, sourceUrl: source.url });
|
|
205
|
+
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Info, msg));
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
const msgError = MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:Messages.MapLayerLayerSettingsConversionError");
|
|
209
|
+
const msg = MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:CustomAttach.MapLayerAttachError", { error: msgError, sourceUrl: source.url });
|
|
210
|
+
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
|
|
211
|
+
}
|
|
212
|
+
vp.invalidateRenderPlan();
|
|
213
|
+
// This handler will close the layer source handler, and therefore the MapUrl dialog.
|
|
214
|
+
// don't call it if the dialog needs to remains open.
|
|
215
|
+
onOkResult();
|
|
216
|
+
return true;
|
|
217
|
+
}, [isOverlay, onOkResult, props === null || props === void 0 ? void 0 : props.activeViewport, props.layerRequiringCredentials, settingsStorage, settingsStorageDisabled]);
|
|
218
|
+
// Validate the layer source and attempt to attach (or update) the layer.
|
|
219
|
+
// Returns true if no further input is needed from end-user (i.e. close the dialog)
|
|
220
|
+
const attemptAttachSource = React.useCallback(async (source) => {
|
|
221
|
+
try {
|
|
222
|
+
const validation = await source.validateSource(true);
|
|
223
|
+
if (validation.status === MapLayerSourceStatus.Valid) {
|
|
224
|
+
if (layerRequiringCredentialsIdx === undefined) {
|
|
225
|
+
return await doAttach(source, validation);
|
|
219
226
|
}
|
|
220
227
|
else {
|
|
221
|
-
|
|
222
|
-
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, `${msg} ${source.url}`));
|
|
223
|
-
resolve(true);
|
|
228
|
+
return await updateAttachedLayer(source, validation);
|
|
224
229
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
230
|
+
}
|
|
231
|
+
else if (updateAuthState(validation)) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
const msg = MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:CustomAttach.ValidationError");
|
|
236
|
+
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, `${msg} ${source.url}`));
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
const msg = MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachError", { error, sourceUrl: source.url });
|
|
243
|
+
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
}, [updateAuthState, doAttach, layerRequiringCredentialsIdx, updateAttachedLayer]);
|
|
233
247
|
const onNameChange = React.useCallback((event) => {
|
|
234
248
|
setMapName(event.target.value);
|
|
235
249
|
}, [setMapName]);
|
|
@@ -239,70 +253,101 @@ export function MapUrlDialog(props) {
|
|
|
239
253
|
const onUrlChange = React.useCallback((event) => {
|
|
240
254
|
setMapUrl(event.target.value);
|
|
241
255
|
}, [setMapUrl]);
|
|
242
|
-
const
|
|
256
|
+
const createSource = React.useCallback(() => {
|
|
243
257
|
let source;
|
|
244
258
|
if (mapUrl && mapName) {
|
|
245
259
|
source = MapLayerSource.fromJSON({
|
|
246
260
|
url: mapUrl,
|
|
247
261
|
name: mapName,
|
|
248
262
|
formatId: mapType,
|
|
249
|
-
userName,
|
|
250
|
-
password
|
|
263
|
+
userName: userName || undefined,
|
|
264
|
+
password: password || undefined
|
|
251
265
|
});
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
266
|
+
}
|
|
267
|
+
return source;
|
|
268
|
+
}, [mapName, mapType, mapUrl, password, userName]);
|
|
269
|
+
const handleOk = React.useCallback(() => {
|
|
270
|
+
const source = createSource();
|
|
271
|
+
if (source === undefined || props.mapLayerSourceToEdit) {
|
|
272
|
+
ModalDialogManager.closeDialog();
|
|
273
|
+
if (source === undefined) {
|
|
274
|
+
// Close the dialog and inform end user something went wrong.
|
|
275
|
+
const msgError = MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:Messages.MapLayerLayerSourceCreationFailed");
|
|
276
|
+
const msg = MapLayersUiItemsProvider.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachError", { error: msgError, sourceUrl: mapUrl });
|
|
277
|
+
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
// Simply change the source definition in the setting service
|
|
281
|
+
if (props.mapLayerSourceToEdit !== undefined) {
|
|
282
|
+
const vp = props.activeViewport;
|
|
283
|
+
void (async () => {
|
|
284
|
+
var _a;
|
|
285
|
+
if (isSettingsStorageAvailable && vp) {
|
|
286
|
+
try {
|
|
287
|
+
await MapLayerPreferences.replaceSource(props.mapLayerSourceToEdit, source, vp.iModel.iTwinId, vp.iModel.iModelId);
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
const errorMessage = IModelApp.localization.getLocalizedString("mapLayers:Messages.MapLayerEditError", { layerName: (_a = props.mapLayerSourceToEdit) === null || _a === void 0 ? void 0 : _a.name });
|
|
291
|
+
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, errorMessage));
|
|
292
|
+
return;
|
|
272
293
|
}
|
|
273
|
-
}
|
|
274
|
-
|
|
294
|
+
}
|
|
295
|
+
})();
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
setLayerAttachPending(true);
|
|
300
|
+
// Attach source asynchronously.
|
|
301
|
+
void (async () => {
|
|
302
|
+
try {
|
|
303
|
+
const closeDialog = await attemptAttachSource(source);
|
|
304
|
+
if (isMounted.current) {
|
|
305
|
+
setLayerAttachPending(false);
|
|
306
|
+
}
|
|
307
|
+
// In theory the modal dialog should always get closed by the parent
|
|
308
|
+
// AttachLayerPanel's 'onOkResult' handler. We close it here just in case.
|
|
309
|
+
if (closeDialog) {
|
|
310
|
+
ModalDialogManager.closeDialog();
|
|
275
311
|
}
|
|
276
312
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
313
|
+
catch (_error) {
|
|
314
|
+
ModalDialogManager.closeDialog();
|
|
315
|
+
}
|
|
316
|
+
})();
|
|
317
|
+
}, [createSource, props.mapLayerSourceToEdit, props.activeViewport, mapUrl, isSettingsStorageAvailable, attemptAttachSource]);
|
|
318
|
+
// The first time the dialog is loaded and we already know the layer requires auth. (i.e ImageryProvider already made an attempt)
|
|
319
|
+
// makes a request to discover the authentification types and adjust UI accordingly (i.e. username/password fields, Oauth popup)
|
|
320
|
+
// Without this effect, user would have to manually click the 'OK' button in order to trigger the layer connection.
|
|
321
|
+
React.useEffect(() => {
|
|
322
|
+
// Attach source asynchronously.
|
|
323
|
+
void (async () => {
|
|
324
|
+
var _a, _b;
|
|
325
|
+
if (((_a = props.layerRequiringCredentials) === null || _a === void 0 ? void 0 : _a.url) !== undefined && ((_b = props.layerRequiringCredentials) === null || _b === void 0 ? void 0 : _b.name) !== undefined) {
|
|
282
326
|
try {
|
|
283
|
-
const
|
|
284
|
-
if (
|
|
285
|
-
setLayerAttachPending(
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
327
|
+
const source = MapLayerSource.fromJSON({ url: props.layerRequiringCredentials.url, name: props.layerRequiringCredentials.name, formatId: props.layerRequiringCredentials.formatId });
|
|
328
|
+
if (source !== undefined) {
|
|
329
|
+
setLayerAttachPending(true);
|
|
330
|
+
const validation = await source.validateSource(true);
|
|
331
|
+
if (isMounted.current) {
|
|
332
|
+
setLayerAttachPending(false);
|
|
333
|
+
}
|
|
334
|
+
updateAuthState(validation);
|
|
291
335
|
}
|
|
292
336
|
}
|
|
293
|
-
catch (_error) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}, [
|
|
337
|
+
catch (_error) { }
|
|
338
|
+
}
|
|
339
|
+
})();
|
|
340
|
+
// Only run this effect when the dialog is initialized, otherwise it will it creates undesirable side-effects when 'OK' button is clicked.
|
|
341
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
342
|
+
}, []);
|
|
299
343
|
const dialogContainer = React.useRef(null);
|
|
300
344
|
const readyToSave = React.useCallback(() => {
|
|
301
345
|
const credentialsSet = !!userName && !!password;
|
|
302
346
|
return (!!mapUrl && !!mapName)
|
|
303
|
-
&&
|
|
347
|
+
&& !layerAttachPending
|
|
348
|
+
&& (!serverRequireCredentials || credentialsSet)
|
|
304
349
|
&& !invalidCredentialsProvided;
|
|
305
|
-
}, [mapUrl, mapName,
|
|
350
|
+
}, [userName, password, mapUrl, mapName, serverRequireCredentials, layerAttachPending, invalidCredentialsProvided]);
|
|
306
351
|
const buttonCluster = React.useMemo(() => [
|
|
307
352
|
{ type: DialogButtonType.OK, onClick: handleOk, disabled: !readyToSave() },
|
|
308
353
|
{ type: DialogButtonType.Cancel, onClick: handleCancel },
|
|
@@ -313,33 +358,58 @@ export function MapUrlDialog(props) {
|
|
|
313
358
|
handleOk();
|
|
314
359
|
}
|
|
315
360
|
}, [handleOk, readyToSave]);
|
|
361
|
+
//
|
|
362
|
+
// Monitors authentication method changes
|
|
363
|
+
React.useEffect(() => {
|
|
364
|
+
setServerRequireCredentials(layerAuthMethod === MapLayerAuthType.Basic || layerAuthMethod === MapLayerAuthType.EsriToken);
|
|
365
|
+
}, [layerAuthMethod]);
|
|
366
|
+
// Utility function to get warning message section
|
|
367
|
+
function renderWarningMessage() {
|
|
368
|
+
let node;
|
|
369
|
+
let warningMessage;
|
|
370
|
+
// Get the proper warning message
|
|
371
|
+
if (invalidCredentialsProvided) {
|
|
372
|
+
warningMessage = invalidCredentialsLabel;
|
|
373
|
+
}
|
|
374
|
+
else if (serverRequireCredentials && (!userName || !password)) {
|
|
375
|
+
warningMessage = missingCredentialsLabel;
|
|
376
|
+
}
|
|
377
|
+
// Sometimes we want to add an extra node, such as a button
|
|
378
|
+
let extraNode;
|
|
379
|
+
if (warningMessage !== undefined) {
|
|
380
|
+
return (React.createElement("div", { className: "map-layer-source-warnMessage" },
|
|
381
|
+
React.createElement(Icon, { className: "map-layer-source-warnMessage-icon", iconSpec: "icon-status-warning" }),
|
|
382
|
+
React.createElement("span", { className: "map-layer-source-warnMessage-label" }, warningMessage),
|
|
383
|
+
extraNode));
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
return (React.createElement("span", { className: "map-layer-source-placeholder" }, "\u00A0"));
|
|
387
|
+
}
|
|
388
|
+
return node;
|
|
389
|
+
}
|
|
316
390
|
return (React.createElement("div", { ref: dialogContainer },
|
|
317
391
|
React.createElement(Dialog, { className: "map-layer-url-dialog", title: dialogTitle, opened: true, resizable: true, movable: true, modal: true, buttonCluster: buttonCluster, onClose: handleCancel, onEscape: handleCancel, minHeight: 120, maxWidth: 600, titleStyle: { paddingLeft: "10px" }, footerStyle: { paddingBottom: "10px", paddingRight: "10px" }, trapFocus: false },
|
|
318
392
|
React.createElement("div", { className: "map-layer-url-dialog-content" },
|
|
319
393
|
React.createElement("div", { className: "map-layer-source-url" },
|
|
320
394
|
React.createElement("span", { className: "map-layer-source-label" }, typeLabel),
|
|
321
|
-
React.createElement(Select, { className: "map-layer-source-select", options: mapTypes, value: mapType, disabled: props.layerRequiringCredentials !== undefined || props.mapLayerSourceToEdit !== undefined, onChange: handleMapTypeSelection }),
|
|
395
|
+
React.createElement(Select, { className: "map-layer-source-select", options: mapTypes, value: mapType, disabled: props.layerRequiringCredentials !== undefined || props.mapLayerSourceToEdit !== undefined || layerAttachPending, onChange: handleMapTypeSelection, size: "small" }),
|
|
322
396
|
React.createElement("span", { className: "map-layer-source-label" }, nameLabel),
|
|
323
|
-
React.createElement(Input, { className: "map-layer-source-input", placeholder: nameInputPlaceHolder, onChange: onNameChange, value: mapName, disabled: props.layerRequiringCredentials !== undefined }),
|
|
397
|
+
React.createElement(Input, { className: "map-layer-source-input", placeholder: nameInputPlaceHolder, onChange: onNameChange, value: mapName, disabled: props.layerRequiringCredentials !== undefined || layerAttachPending }),
|
|
324
398
|
React.createElement("span", { className: "map-layer-source-label" }, urlLabel),
|
|
325
|
-
React.createElement(Input, { className: "map-layer-source-input", placeholder: urlInputPlaceHolder, onKeyPress: handleOnKeyDown, onChange: onUrlChange, disabled: props.mapLayerSourceToEdit !== undefined, value: mapUrl }),
|
|
326
|
-
|
|
399
|
+
React.createElement(Input, { className: "map-layer-source-input", placeholder: urlInputPlaceHolder, onKeyPress: handleOnKeyDown, onChange: onUrlChange, disabled: props.mapLayerSourceToEdit !== undefined || layerAttachPending, value: mapUrl }),
|
|
400
|
+
serverRequireCredentials
|
|
401
|
+
&& (layerAuthMethod === MapLayerAuthType.Basic || layerAuthMethod === MapLayerAuthType.EsriToken)
|
|
402
|
+
&& props.mapLayerSourceToEdit === undefined &&
|
|
327
403
|
React.createElement(React.Fragment, null,
|
|
328
404
|
React.createElement("span", { className: "map-layer-source-label" }, userNameLabel),
|
|
329
|
-
React.createElement(LabeledInput, { className: "map-layer-source-input", displayStyle: "inline", placeholder: serverRequireCredentials ? userNameRequiredLabel : userNameLabel, status: !userName && serverRequireCredentials ?
|
|
405
|
+
React.createElement(LabeledInput, { className: "map-layer-source-input", displayStyle: "inline", placeholder: serverRequireCredentials ? userNameRequiredLabel : userNameLabel, status: !userName && serverRequireCredentials ? "warning" : undefined, disabled: layerAttachPending, onChange: onUsernameChange, size: "small" }),
|
|
330
406
|
React.createElement("span", { className: "map-layer-source-label" }, passwordLabel),
|
|
331
|
-
React.createElement(LabeledInput, { className: "map-layer-source-input", displayStyle: "inline", type: "password", placeholder: serverRequireCredentials ? passwordRequiredLabel : passwordLabel, status: !password && serverRequireCredentials ?
|
|
407
|
+
React.createElement(LabeledInput, { className: "map-layer-source-input", displayStyle: "inline", type: "password", placeholder: serverRequireCredentials ? passwordRequiredLabel : passwordLabel, status: !password && serverRequireCredentials ? "warning" : undefined, disabled: layerAttachPending, onChange: onPasswordChange, onKeyPress: handleOnKeyDown, size: "small" })),
|
|
332
408
|
isSettingsStorageAvailable && React.createElement("div", { title: settingsStorageDisabled ? noSaveSettingsWarning : "" },
|
|
333
409
|
React.createElement(Radio, { disabled: settingsStorageDisabled, name: "settingsStorage", value: "iTwin", label: iTwinSettingsLabel, checked: settingsStorage === "iTwin", onChange: onRadioChange }),
|
|
334
410
|
React.createElement(Radio, { disabled: settingsStorageDisabled, name: "settingsStorage", value: "Model", label: modelSettingsLabel, checked: settingsStorage === "Model", onChange: onRadioChange })))),
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
React.createElement(Icon, { className: "map-layer-source-warnMessage-icon", iconSpec: "icon-status-warning" }),
|
|
338
|
-
React.createElement("span", { className: "map-layer-source-warnMessage-label" }, warningMessage))
|
|
339
|
-
:
|
|
340
|
-
// Place holder to avoid dialog resize
|
|
341
|
-
React.createElement("span", { className: "map-layer-source-placeholder" }, "\u00A0")),
|
|
342
|
-
layerAttachPending &&
|
|
411
|
+
renderWarningMessage(),
|
|
412
|
+
(layerAttachPending) &&
|
|
343
413
|
React.createElement("div", { className: "map-layer-source-progressBar" },
|
|
344
414
|
React.createElement(ProgressLinear, { indeterminate: true })))));
|
|
345
415
|
}
|