@itwin/map-layers 3.5.3 → 3.5.4

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.
Files changed (156) hide show
  1. package/CHANGELOG.md +6 -1
  2. package/lib/cjs/MapLayerPreferences.d.ts +88 -88
  3. package/lib/cjs/MapLayerPreferences.js +312 -312
  4. package/lib/cjs/MapLayerPreferences.js.map +1 -1
  5. package/lib/cjs/map-layers.d.ts +6 -6
  6. package/lib/cjs/map-layers.js +22 -22
  7. package/lib/cjs/mapLayers.d.ts +44 -44
  8. package/lib/cjs/mapLayers.js +63 -63
  9. package/lib/cjs/mapLayers.js.map +1 -1
  10. package/lib/cjs/ui/FeatureInfoUiItemsProvider.d.ts +10 -10
  11. package/lib/cjs/ui/FeatureInfoUiItemsProvider.js +50 -50
  12. package/lib/cjs/ui/FeatureInfoUiItemsProvider.js.map +1 -1
  13. package/lib/cjs/ui/Interfaces.d.ts +47 -47
  14. package/lib/cjs/ui/Interfaces.js +2 -2
  15. package/lib/cjs/ui/Interfaces.js.map +1 -1
  16. package/lib/cjs/ui/MapFeatureInfoTool.d.ts +13 -13
  17. package/lib/cjs/ui/MapFeatureInfoTool.js +50 -50
  18. package/lib/cjs/ui/MapFeatureInfoTool.js.map +1 -1
  19. package/lib/cjs/ui/MapLayersUiItemsProvider.d.ts +8 -8
  20. package/lib/cjs/ui/MapLayersUiItemsProvider.js +38 -38
  21. package/lib/cjs/ui/MapLayersUiItemsProvider.js.map +1 -1
  22. package/lib/cjs/ui/widget/AttachLayerPopupButton.d.ts +14 -14
  23. package/lib/cjs/ui/widget/AttachLayerPopupButton.js +338 -338
  24. package/lib/cjs/ui/widget/BasemapPanel.d.ts +8 -8
  25. package/lib/cjs/ui/widget/BasemapPanel.js +156 -156
  26. package/lib/cjs/ui/widget/BasemapPanel.js.map +1 -1
  27. package/lib/cjs/ui/widget/BasemapPanel.scss +87 -87
  28. package/lib/cjs/ui/widget/ConfirmMessageDialog.d.ts +21 -21
  29. package/lib/cjs/ui/widget/ConfirmMessageDialog.js +25 -25
  30. package/lib/cjs/ui/widget/ConfirmMessageDialog.js.map +1 -1
  31. package/lib/cjs/ui/widget/FeatureInfoDataProvider.d.ts +40 -40
  32. package/lib/cjs/ui/widget/FeatureInfoDataProvider.js +139 -139
  33. package/lib/cjs/ui/widget/FeatureInfoDataProvider.js.map +1 -1
  34. package/lib/cjs/ui/widget/FeatureInfoWidget.d.ts +7 -7
  35. package/lib/cjs/ui/widget/FeatureInfoWidget.js +71 -71
  36. package/lib/cjs/ui/widget/FeatureInfoWidget.js.map +1 -1
  37. package/lib/cjs/ui/widget/MapLayerDroppable.d.ts +19 -19
  38. package/lib/cjs/ui/widget/MapLayerDroppable.js +86 -86
  39. package/lib/cjs/ui/widget/MapLayerDroppable.js.map +1 -1
  40. package/lib/cjs/ui/widget/MapLayerManager.d.ts +26 -26
  41. package/lib/cjs/ui/widget/MapLayerManager.js +375 -375
  42. package/lib/cjs/ui/widget/MapLayerManager.js.map +1 -1
  43. package/lib/cjs/ui/widget/MapLayerManager.scss +409 -409
  44. package/lib/cjs/ui/widget/MapLayerSettingsMenu.d.ts +12 -12
  45. package/lib/cjs/ui/widget/MapLayerSettingsMenu.js +83 -83
  46. package/lib/cjs/ui/widget/MapLayerSettingsMenu.js.map +1 -1
  47. package/lib/cjs/ui/widget/MapLayerSettingsPopupButton.d.ts +7 -7
  48. package/lib/cjs/ui/widget/MapLayerSettingsPopupButton.js +65 -65
  49. package/lib/cjs/ui/widget/MapLayerSettingsPopupButton.js.map +1 -1
  50. package/lib/cjs/ui/widget/MapLayerSettingsPopupButton.scss +20 -20
  51. package/lib/cjs/ui/widget/MapLayersWidget.d.ts +11 -11
  52. package/lib/cjs/ui/widget/MapLayersWidget.js +26 -26
  53. package/lib/cjs/ui/widget/MapLayersWidget.js.map +1 -1
  54. package/lib/cjs/ui/widget/MapManagerSettings.d.ts +3 -3
  55. package/lib/cjs/ui/widget/MapManagerSettings.js +200 -200
  56. package/lib/cjs/ui/widget/MapManagerSettings.js.map +1 -1
  57. package/lib/cjs/ui/widget/MapManagerSettings.scss +29 -29
  58. package/lib/cjs/ui/widget/MapUrlDialog.d.ts +23 -23
  59. package/lib/cjs/ui/widget/MapUrlDialog.js +522 -522
  60. package/lib/cjs/ui/widget/MapUrlDialog.js.map +1 -1
  61. package/lib/cjs/ui/widget/MapUrlDialog.scss +100 -100
  62. package/lib/cjs/ui/widget/SelectMapFormat.d.ts +18 -18
  63. package/lib/cjs/ui/widget/SelectMapFormat.js +59 -59
  64. package/lib/cjs/ui/widget/SelectMapFormat.js.map +1 -1
  65. package/lib/cjs/ui/widget/SubLayersDataProvider.d.ts +20 -20
  66. package/lib/cjs/ui/widget/SubLayersDataProvider.js +76 -76
  67. package/lib/cjs/ui/widget/SubLayersDataProvider.js.map +1 -1
  68. package/lib/cjs/ui/widget/SubLayersPopupButton.d.ts +10 -10
  69. package/lib/cjs/ui/widget/SubLayersPopupButton.js +40 -40
  70. package/lib/cjs/ui/widget/SubLayersPopupButton.js.map +1 -1
  71. package/lib/cjs/ui/widget/SubLayersTree.d.ts +15 -15
  72. package/lib/cjs/ui/widget/SubLayersTree.js +414 -414
  73. package/lib/cjs/ui/widget/SubLayersTree.js.map +1 -1
  74. package/lib/cjs/ui/widget/SubLayersTree.scss +69 -69
  75. package/lib/cjs/ui/widget/TransparencyPopupButton.d.ts +14 -14
  76. package/lib/cjs/ui/widget/TransparencyPopupButton.js +47 -47
  77. package/lib/cjs/ui/widget/TransparencyPopupButton.js.map +1 -1
  78. package/lib/cjs/ui/widget/TransparencyPopupButton.scss +36 -36
  79. package/lib/esm/MapLayerPreferences.d.ts +88 -88
  80. package/lib/esm/MapLayerPreferences.js +308 -308
  81. package/lib/esm/MapLayerPreferences.js.map +1 -1
  82. package/lib/esm/map-layers.d.ts +6 -6
  83. package/lib/esm/map-layers.js +10 -10
  84. package/lib/esm/mapLayers.d.ts +44 -44
  85. package/lib/esm/mapLayers.js +59 -59
  86. package/lib/esm/mapLayers.js.map +1 -1
  87. package/lib/esm/ui/FeatureInfoUiItemsProvider.d.ts +10 -10
  88. package/lib/esm/ui/FeatureInfoUiItemsProvider.js +46 -46
  89. package/lib/esm/ui/FeatureInfoUiItemsProvider.js.map +1 -1
  90. package/lib/esm/ui/Interfaces.d.ts +47 -47
  91. package/lib/esm/ui/Interfaces.js +1 -1
  92. package/lib/esm/ui/Interfaces.js.map +1 -1
  93. package/lib/esm/ui/MapFeatureInfoTool.d.ts +13 -13
  94. package/lib/esm/ui/MapFeatureInfoTool.js +45 -45
  95. package/lib/esm/ui/MapFeatureInfoTool.js.map +1 -1
  96. package/lib/esm/ui/MapLayersUiItemsProvider.d.ts +8 -8
  97. package/lib/esm/ui/MapLayersUiItemsProvider.js +34 -34
  98. package/lib/esm/ui/MapLayersUiItemsProvider.js.map +1 -1
  99. package/lib/esm/ui/widget/AttachLayerPopupButton.d.ts +14 -14
  100. package/lib/esm/ui/widget/AttachLayerPopupButton.js +334 -334
  101. package/lib/esm/ui/widget/BasemapPanel.d.ts +8 -8
  102. package/lib/esm/ui/widget/BasemapPanel.js +152 -152
  103. package/lib/esm/ui/widget/BasemapPanel.js.map +1 -1
  104. package/lib/esm/ui/widget/BasemapPanel.scss +87 -87
  105. package/lib/esm/ui/widget/ConfirmMessageDialog.d.ts +21 -21
  106. package/lib/esm/ui/widget/ConfirmMessageDialog.js +21 -21
  107. package/lib/esm/ui/widget/ConfirmMessageDialog.js.map +1 -1
  108. package/lib/esm/ui/widget/FeatureInfoDataProvider.d.ts +40 -40
  109. package/lib/esm/ui/widget/FeatureInfoDataProvider.js +135 -135
  110. package/lib/esm/ui/widget/FeatureInfoDataProvider.js.map +1 -1
  111. package/lib/esm/ui/widget/FeatureInfoWidget.d.ts +7 -7
  112. package/lib/esm/ui/widget/FeatureInfoWidget.js +67 -67
  113. package/lib/esm/ui/widget/FeatureInfoWidget.js.map +1 -1
  114. package/lib/esm/ui/widget/MapLayerDroppable.d.ts +19 -19
  115. package/lib/esm/ui/widget/MapLayerDroppable.js +82 -82
  116. package/lib/esm/ui/widget/MapLayerDroppable.js.map +1 -1
  117. package/lib/esm/ui/widget/MapLayerManager.d.ts +26 -26
  118. package/lib/esm/ui/widget/MapLayerManager.js +370 -370
  119. package/lib/esm/ui/widget/MapLayerManager.js.map +1 -1
  120. package/lib/esm/ui/widget/MapLayerManager.scss +409 -409
  121. package/lib/esm/ui/widget/MapLayerSettingsMenu.d.ts +12 -12
  122. package/lib/esm/ui/widget/MapLayerSettingsMenu.js +79 -79
  123. package/lib/esm/ui/widget/MapLayerSettingsMenu.js.map +1 -1
  124. package/lib/esm/ui/widget/MapLayerSettingsPopupButton.d.ts +7 -7
  125. package/lib/esm/ui/widget/MapLayerSettingsPopupButton.js +61 -61
  126. package/lib/esm/ui/widget/MapLayerSettingsPopupButton.js.map +1 -1
  127. package/lib/esm/ui/widget/MapLayerSettingsPopupButton.scss +20 -20
  128. package/lib/esm/ui/widget/MapLayersWidget.d.ts +11 -11
  129. package/lib/esm/ui/widget/MapLayersWidget.js +22 -22
  130. package/lib/esm/ui/widget/MapLayersWidget.js.map +1 -1
  131. package/lib/esm/ui/widget/MapManagerSettings.d.ts +3 -3
  132. package/lib/esm/ui/widget/MapManagerSettings.js +196 -196
  133. package/lib/esm/ui/widget/MapManagerSettings.js.map +1 -1
  134. package/lib/esm/ui/widget/MapManagerSettings.scss +29 -29
  135. package/lib/esm/ui/widget/MapUrlDialog.d.ts +23 -23
  136. package/lib/esm/ui/widget/MapUrlDialog.js +518 -518
  137. package/lib/esm/ui/widget/MapUrlDialog.js.map +1 -1
  138. package/lib/esm/ui/widget/MapUrlDialog.scss +100 -100
  139. package/lib/esm/ui/widget/SelectMapFormat.d.ts +18 -18
  140. package/lib/esm/ui/widget/SelectMapFormat.js +55 -55
  141. package/lib/esm/ui/widget/SelectMapFormat.js.map +1 -1
  142. package/lib/esm/ui/widget/SubLayersDataProvider.d.ts +20 -20
  143. package/lib/esm/ui/widget/SubLayersDataProvider.js +72 -72
  144. package/lib/esm/ui/widget/SubLayersDataProvider.js.map +1 -1
  145. package/lib/esm/ui/widget/SubLayersPopupButton.d.ts +10 -10
  146. package/lib/esm/ui/widget/SubLayersPopupButton.js +36 -36
  147. package/lib/esm/ui/widget/SubLayersPopupButton.js.map +1 -1
  148. package/lib/esm/ui/widget/SubLayersTree.d.ts +15 -15
  149. package/lib/esm/ui/widget/SubLayersTree.js +409 -409
  150. package/lib/esm/ui/widget/SubLayersTree.js.map +1 -1
  151. package/lib/esm/ui/widget/SubLayersTree.scss +69 -69
  152. package/lib/esm/ui/widget/TransparencyPopupButton.d.ts +14 -14
  153. package/lib/esm/ui/widget/TransparencyPopupButton.js +43 -43
  154. package/lib/esm/ui/widget/TransparencyPopupButton.js.map +1 -1
  155. package/lib/esm/ui/widget/TransparencyPopupButton.scss +36 -36
  156. package/package.json +26 -26
@@ -1,519 +1,519 @@
1
- /*---------------------------------------------------------------------------------------------
2
- * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
- * See LICENSE.md in the project root for license terms and full copyright notice.
4
- *--------------------------------------------------------------------------------------------*/
5
- // cSpell:ignore Modeless WMTS
6
- import { DialogButtonType, SpecialKey } from "@itwin/appui-abstract";
7
- import { ModalDialogManager } from "@itwin/appui-react";
8
- import { Button, Input, LabeledInput, ProgressLinear, Radio } from "@itwin/itwinui-react";
9
- import { IModelApp, MapLayerImageryProviderStatus, MapLayerSource, MapLayerSourceStatus, NotifyMessageDetails, OutputMessagePriority, } from "@itwin/core-frontend";
10
- import { Dialog, Icon, useCrossOriginPopup } from "@itwin/core-react";
11
- import * as React from "react";
12
- import { MapLayerPreferences } from "../../MapLayerPreferences";
13
- import { MapLayersUI } from "../../mapLayers";
14
- import "./MapUrlDialog.scss";
15
- import { BeEvent, Guid } from "@itwin/core-bentley";
16
- import { SelectMapFormat } from "./SelectMapFormat";
17
- export const MAP_TYPES = {
18
- wms: "WMS",
19
- arcGis: "ArcGIS",
20
- wmts: "WMTS",
21
- tileUrl: "TileURL",
22
- arcGisFeature: "ArcGISFeature",
23
- };
24
- // eslint-disable-next-line @typescript-eslint/naming-convention
25
- export function MapUrlDialog(props) {
26
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
27
- const { isOverlay, onOkResult, mapTypesOptions } = props;
28
- const getMapUrlFromProps = React.useCallback(() => {
29
- var _a;
30
- if (props.mapLayerSourceToEdit) {
31
- return props.mapLayerSourceToEdit.url;
32
- }
33
- else if ((_a = props.layerRequiringCredentials) === null || _a === void 0 ? void 0 : _a.url) {
34
- return props.layerRequiringCredentials.url;
35
- }
36
- return "";
37
- }, [props.layerRequiringCredentials, props.mapLayerSourceToEdit]);
38
- const getMapNameFromProps = React.useCallback(() => {
39
- var _a;
40
- if (props.mapLayerSourceToEdit) {
41
- return props.mapLayerSourceToEdit.name;
42
- }
43
- else if ((_a = props.layerRequiringCredentials) === null || _a === void 0 ? void 0 : _a.name) {
44
- return props.layerRequiringCredentials.name;
45
- }
46
- return "";
47
- }, [props.layerRequiringCredentials, props.mapLayerSourceToEdit]);
48
- const getFormatFromProps = React.useCallback(() => {
49
- var _a;
50
- if (props.mapLayerSourceToEdit) {
51
- return props.mapLayerSourceToEdit.formatId;
52
- }
53
- else if ((_a = props.layerRequiringCredentials) === null || _a === void 0 ? void 0 : _a.formatId) {
54
- return props.layerRequiringCredentials.formatId;
55
- }
56
- return undefined;
57
- }, [props.layerRequiringCredentials, props.mapLayerSourceToEdit]);
58
- const [dialogTitle] = React.useState(MapLayersUI.localization.getLocalizedString(props.layerRequiringCredentials || props.mapLayerSourceToEdit ? "mapLayers:CustomAttach.EditCustomLayer" : "mapLayers:CustomAttach.AttachCustomLayer"));
59
- const [typeLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.Type"));
60
- const [nameLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.Name"));
61
- const [nameInputPlaceHolder] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.NameInputPlaceHolder"));
62
- const [urlLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.URL"));
63
- const [urlInputPlaceHolder] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.UrlInputPlaceHolder"));
64
- const [iTwinSettingsLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.StoreOnITwinSettings"));
65
- const [modelSettingsLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.StoreOnModelSettings"));
66
- const [missingCredentialsLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.MissingCredentials"));
67
- const [invalidCredentialsLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.InvalidCredentials"));
68
- const [externalLoginTitle] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.ExternalLogin"));
69
- const [externalLoginFailedMsg] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.ExternalLoginFailed"));
70
- const [externalLoginSucceededMsg] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.ExternalLoginSucceeded"));
71
- const [externalLoginWaitingMsg] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.ExternalLoginWaiting"));
72
- const [externalLoginTryAgainLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.ExternalLoginTryAgain"));
73
- const [serverRequireCredentials, setServerRequireCredentials] = React.useState(false);
74
- const [invalidCredentialsProvided, setInvalidCredentialsProvided] = React.useState(false);
75
- const [layerAttachPending, setLayerAttachPending] = React.useState(false);
76
- const [layerAuthPending, setLayerAuthPending] = React.useState(false);
77
- const [mapUrl, setMapUrl] = React.useState(getMapUrlFromProps());
78
- const [mapName, setMapName] = React.useState(getMapNameFromProps());
79
- const [userName, setUserName] = React.useState("");
80
- const [password, setPassword] = React.useState("");
81
- const [noSaveSettingsWarning] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.NoSaveSettingsWarning"));
82
- const [passwordLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:AuthenticationInputs.Password"));
83
- const [passwordRequiredLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:AuthenticationInputs.PasswordRequired"));
84
- const [userNameLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:AuthenticationInputs.Username"));
85
- const [userNameRequiredLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:AuthenticationInputs.UsernameRequired"));
86
- const [settingsStorage, setSettingsStorageRadio] = React.useState("iTwin");
87
- const [oauthProcessSucceeded, setOAuthProcessSucceeded] = React.useState(undefined);
88
- const [showOauthPopup, setShowOauthPopup] = React.useState(false);
89
- const [externalLoginUrl, setExternalLoginUrl] = React.useState();
90
- const [onOauthProcessEnd] = React.useState(new BeEvent());
91
- const [accessClient, setAccessClient] = React.useState();
92
- const [isAccessClientInitialized, setAccessClientInitialized] = React.useState(false);
93
- const [mapType, setMapType] = React.useState((_a = getFormatFromProps()) !== null && _a !== void 0 ? _a : "ArcGIS");
94
- // 'isMounted' is used to prevent any async operation once the hook has been
95
- // unloaded. Otherwise we get a 'Can't perform a React state update on an unmounted component.' warning in the console.
96
- const isMounted = React.useRef(false);
97
- React.useEffect(() => {
98
- isMounted.current = true;
99
- return () => {
100
- isMounted.current = false;
101
- };
102
- }, []);
103
- 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));
104
- const [hasImodelContext] = React.useState(((_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.iTwinId) !== undefined
105
- && props.activeViewport.iModel.iTwinId !== Guid.empty
106
- && ((_g = (_f = props === null || props === void 0 ? void 0 : props.activeViewport) === null || _f === void 0 ? void 0 : _f.iModel) === null || _g === void 0 ? void 0 : _g.iModelId) !== undefined
107
- && (props === null || props === void 0 ? void 0 : props.activeViewport.iModel.iModelId) !== Guid.empty);
108
- // Even though the settings storage is available,
109
- // we don't always want to enable it in the UI.
110
- const [settingsStorageDisabled] = React.useState(!isSettingsStorageAvailable || props.mapLayerSourceToEdit !== undefined || props.layerRequiringCredentials !== undefined);
111
- const [layerRequiringCredentialsIdx] = React.useState(() => {
112
- var _a;
113
- if (props.layerRequiringCredentials === undefined || !props.layerRequiringCredentials.name || !props.layerRequiringCredentials.url) {
114
- return undefined;
115
- }
116
- const indexInDisplayStyle = (_a = props.activeViewport) === null || _a === void 0 ? void 0 : _a.displayStyle.findMapLayerIndexByNameAndSource(props.layerRequiringCredentials.name, props.layerRequiringCredentials.url, isOverlay);
117
- if (indexInDisplayStyle === undefined || indexInDisplayStyle < 0) {
118
- return undefined;
119
- }
120
- else {
121
- return indexInDisplayStyle;
122
- }
123
- });
124
- const handleCancel = React.useCallback(() => {
125
- if (props.onCancelResult) {
126
- props.onCancelResult();
127
- return;
128
- }
129
- ModalDialogManager.closeDialog();
130
- }, [props]);
131
- const onUsernameChange = React.useCallback((event) => {
132
- setUserName(event.target.value);
133
- if (invalidCredentialsProvided)
134
- setInvalidCredentialsProvided(false);
135
- }, [setUserName, invalidCredentialsProvided, setInvalidCredentialsProvided]);
136
- const onPasswordChange = React.useCallback((event) => {
137
- setPassword(event.target.value);
138
- if (invalidCredentialsProvided)
139
- setInvalidCredentialsProvided(false);
140
- }, [setPassword, invalidCredentialsProvided, setInvalidCredentialsProvided]);
141
- const handleArcGisLogin = React.useCallback(() => {
142
- setLayerAuthPending(true);
143
- setShowOauthPopup(true);
144
- if (oauthProcessSucceeded === false) {
145
- setOAuthProcessSucceeded(undefined);
146
- }
147
- }, [oauthProcessSucceeded]);
148
- // return true if authorization is needed
149
- const updateAuthState = React.useCallback(async (source, sourceValidation) => {
150
- const sourceRequireAuth = (sourceValidation.status === MapLayerSourceStatus.RequireAuth);
151
- const invalidCredentials = (sourceValidation.status === MapLayerSourceStatus.InvalidCredentials);
152
- if (sourceRequireAuth) {
153
- const settings = source.toLayerSettings();
154
- if (accessClient !== undefined && accessClient.getTokenServiceEndPoint !== undefined && settings !== undefined) {
155
- try {
156
- const tokenEndpoint = await accessClient.getTokenServiceEndPoint(settings.url);
157
- if (tokenEndpoint !== undefined) {
158
- const loginUrl = tokenEndpoint.getLoginUrl();
159
- setExternalLoginUrl(loginUrl);
160
- }
161
- }
162
- catch (_error) {
163
- }
164
- }
165
- }
166
- setServerRequireCredentials(sourceRequireAuth || invalidCredentials);
167
- if (invalidCredentials) {
168
- setInvalidCredentialsProvided(true);
169
- }
170
- else if (invalidCredentialsProvided) {
171
- setInvalidCredentialsProvided(false); // flag reset
172
- }
173
- return sourceRequireAuth || invalidCredentials;
174
- }, [accessClient, invalidCredentialsProvided]);
175
- const updateAttachedLayer = React.useCallback(async (source, validation) => {
176
- const vp = props === null || props === void 0 ? void 0 : props.activeViewport;
177
- if (vp === undefined || source === undefined || layerRequiringCredentialsIdx === undefined) {
178
- const error = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachMissingViewOrSource");
179
- const msg = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachError", { error, sourceUrl: source.url });
180
- IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
181
- return true;
182
- }
183
- // Layer is already attached,
184
- vp.displayStyle.changeMapLayerProps({
185
- subLayers: validation.subLayers,
186
- }, layerRequiringCredentialsIdx, isOverlay);
187
- vp.displayStyle.changeMapLayerCredentials(layerRequiringCredentialsIdx, isOverlay, source.userName, source.password);
188
- // Reset the provider's status
189
- const provider = vp.getMapLayerImageryProvider(layerRequiringCredentialsIdx, isOverlay);
190
- if (provider && provider.status !== MapLayerImageryProviderStatus.Valid) {
191
- provider.status = MapLayerImageryProviderStatus.Valid;
192
- }
193
- vp.invalidateRenderPlan();
194
- // This handler will close the layer source handler, and therefore the MapUrl dialog.
195
- // don't call it if the dialog needs to remains open.
196
- onOkResult();
197
- return true;
198
- }, [isOverlay, layerRequiringCredentialsIdx, onOkResult, props.activeViewport]);
199
- // Returns true if no further input is needed from end-user.
200
- const doAttach = React.useCallback(async (source, validation) => {
201
- const vp = props === null || props === void 0 ? void 0 : props.activeViewport;
202
- if (vp === undefined || source === undefined) {
203
- const error = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachMissingViewOrSource");
204
- const msg = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachError", { error, sourceUrl: source.url });
205
- IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
206
- return true;
207
- }
208
- // Update service settings if storage is available and we are not prompting user for credentials
209
- if (!settingsStorageDisabled && !props.layerRequiringCredentials) {
210
- const storeOnIModel = (hasImodelContext ? "Model" === settingsStorage : undefined);
211
- if (vp.iModel.iTwinId && !(await MapLayerPreferences.storeSource(source, vp.iModel.iTwinId, vp.iModel.iModelId, storeOnIModel))) {
212
- const msgError = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerPreferencesStoreFailed");
213
- IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msgError));
214
- }
215
- }
216
- const settings = source.toLayerSettings(validation.subLayers);
217
- if (settings) {
218
- vp.displayStyle.attachMapLayer({ settings, isOverlay });
219
- const msg = IModelApp.localization.getLocalizedString("mapLayers:Messages.MapLayerAttached", { sourceName: source.name, sourceUrl: source.url });
220
- IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Info, msg));
221
- }
222
- else {
223
- const msgError = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerLayerSettingsConversionError");
224
- const msg = MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.MapLayerAttachError", { error: msgError, sourceUrl: source.url });
225
- IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
226
- }
227
- vp.invalidateRenderPlan();
228
- // This handler will close the layer source handler, and therefore the MapUrl dialog.
229
- // don't call it if the dialog needs to remains open.
230
- onOkResult();
231
- return true;
232
- }, [hasImodelContext, isOverlay, onOkResult, props === null || props === void 0 ? void 0 : props.activeViewport, props.layerRequiringCredentials, settingsStorage, settingsStorageDisabled]);
233
- // Validate the layer source and attempt to attach (or update) the layer.
234
- // Returns true if no further input is needed from end-user (i.e. close the dialog)
235
- const attemptAttachSource = React.useCallback(async (source) => {
236
- try {
237
- const validation = await source.validateSource(true);
238
- if (validation.status === MapLayerSourceStatus.Valid) {
239
- if (layerRequiringCredentialsIdx === undefined) {
240
- return await doAttach(source, validation);
241
- }
242
- else {
243
- return await updateAttachedLayer(source, validation);
244
- }
245
- }
246
- else {
247
- const authNeeded = await updateAuthState(source, validation);
248
- if (authNeeded) {
249
- return false;
250
- }
251
- else {
252
- const msg = MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.ValidationError");
253
- IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, `${msg} ${source.url}`));
254
- return true;
255
- }
256
- }
257
- return false;
258
- }
259
- catch (error) {
260
- const msg = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachError", { error, sourceUrl: source.url });
261
- IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
262
- return true;
263
- }
264
- }, [updateAuthState, doAttach, layerRequiringCredentialsIdx, updateAttachedLayer]);
265
- const onNameChange = React.useCallback((event) => {
266
- setMapName(event.target.value);
267
- }, [setMapName]);
268
- const onRadioChange = React.useCallback((event) => {
269
- setSettingsStorageRadio(event.target.value);
270
- }, [setSettingsStorageRadio]);
271
- const onUrlChange = React.useCallback((event) => {
272
- setMapUrl(event.target.value);
273
- }, [setMapUrl]);
274
- const createSource = React.useCallback(() => {
275
- let source;
276
- if (mapUrl && mapName) {
277
- source = MapLayerSource.fromJSON({
278
- url: mapUrl,
279
- name: mapName,
280
- formatId: mapType
281
- });
282
- // Set credentials separately since they are not part of JSON
283
- if (source) {
284
- source.userName = userName || undefined; // When there is no value, empty string is always returned, in this case force it to undefined,
285
- source.password = password || undefined;
286
- }
287
- }
288
- return source;
289
- }, [mapName, mapType, mapUrl, password, userName]);
290
- const handleOk = React.useCallback(() => {
291
- const source = createSource();
292
- if (source === undefined || props.mapLayerSourceToEdit) {
293
- ModalDialogManager.closeDialog();
294
- onOkResult();
295
- if (source === undefined) {
296
- // Close the dialog and inform end user something went wrong.
297
- const msgError = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerLayerSourceCreationFailed");
298
- const msg = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachError", { error: msgError, sourceUrl: mapUrl });
299
- IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
300
- return;
301
- }
302
- // Simply change the source definition in the setting service
303
- if (props.mapLayerSourceToEdit !== undefined) {
304
- const vp = props.activeViewport;
305
- void (async () => {
306
- var _a, _b;
307
- if (isSettingsStorageAvailable && ((_a = vp === null || vp === void 0 ? void 0 : vp.iModel) === null || _a === void 0 ? void 0 : _a.iTwinId)) {
308
- try {
309
- await MapLayerPreferences.replaceSource(props.mapLayerSourceToEdit, source, vp.iModel.iTwinId, vp === null || vp === void 0 ? void 0 : vp.iModel.iModelId);
310
- }
311
- catch (err) {
312
- const errorMessage = IModelApp.localization.getLocalizedString("mapLayers:Messages.MapLayerEditError", { layerName: (_b = props.mapLayerSourceToEdit) === null || _b === void 0 ? void 0 : _b.name });
313
- IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, errorMessage));
314
- return;
315
- }
316
- }
317
- })();
318
- return;
319
- }
320
- }
321
- setLayerAttachPending(true);
322
- // Attach source asynchronously.
323
- void (async () => {
324
- try {
325
- const closeDialog = await attemptAttachSource(source);
326
- if (isMounted.current) {
327
- setLayerAttachPending(false);
328
- }
329
- // In theory the modal dialog should always get closed by the parent
330
- // AttachLayerPanel's 'onOkResult' handler. We close it here just in case.
331
- if (closeDialog) {
332
- ModalDialogManager.closeDialog();
333
- onOkResult();
334
- }
335
- }
336
- catch (_error) {
337
- onOkResult();
338
- ModalDialogManager.closeDialog();
339
- }
340
- })();
341
- }, [createSource, props.mapLayerSourceToEdit, props.activeViewport, onOkResult, mapUrl, isSettingsStorageAvailable, attemptAttachSource]);
342
- React.useEffect(() => {
343
- const handleOAuthProcessEnd = (success, _state) => {
344
- onOauthProcessEnd.raiseEvent(success, _state);
345
- };
346
- // Currently only arcgis support AccessClient
347
- const ac = IModelApp.mapLayerFormatRegistry.getAccessClient(MAP_TYPES.arcGis);
348
- if (ac === null || ac === void 0 ? void 0 : ac.onOAuthProcessEnd) {
349
- setAccessClient(ac); // cache it, so we dont need to make another lookup;
350
- ac.onOAuthProcessEnd.addListener(handleOAuthProcessEnd);
351
- }
352
- setAccessClientInitialized(true);
353
- return () => {
354
- if (ac === null || ac === void 0 ? void 0 : ac.onOAuthProcessEnd) {
355
- ac.onOAuthProcessEnd.removeListener(handleOAuthProcessEnd);
356
- }
357
- setAccessClient(undefined);
358
- setAccessClientInitialized(false);
359
- };
360
- }, [mapType, onOauthProcessEnd, setAccessClient]);
361
- // After a map type change, make sure the different Oauth states are reset.
362
- React.useEffect(() => {
363
- // Reset few states
364
- setServerRequireCredentials(false);
365
- setInvalidCredentialsProvided(false);
366
- setShowOauthPopup(false);
367
- setOAuthProcessSucceeded(undefined);
368
- setExternalLoginUrl(undefined);
369
- }, [mapType]);
370
- // The first time the dialog is loaded and we already know the layer requires auth. (i.e ImageryProvider already made an attempt)
371
- // makes a request to discover the authentification types and adjust UI accordingly (i.e. username/password fields, Oauth popup)
372
- // Without this effect, user would have to manually click the 'OK' button in order to trigger the layer connection.
373
- React.useEffect(() => {
374
- // Attach source asynchronously.
375
- void (async () => {
376
- var _a, _b;
377
- if (isAccessClientInitialized && ((_a = props.layerRequiringCredentials) === null || _a === void 0 ? void 0 : _a.url) !== undefined && ((_b = props.layerRequiringCredentials) === null || _b === void 0 ? void 0 : _b.name) !== undefined) {
378
- try {
379
- const source = MapLayerSource.fromJSON({
380
- url: props.layerRequiringCredentials.url,
381
- name: props.layerRequiringCredentials.name,
382
- formatId: props.layerRequiringCredentials.formatId
383
- });
384
- if (source !== undefined) {
385
- setLayerAttachPending(true);
386
- const validation = await source.validateSource(true);
387
- if (isMounted.current) {
388
- setLayerAttachPending(false);
389
- }
390
- await updateAuthState(source, validation);
391
- }
392
- }
393
- catch (_error) { }
394
- }
395
- })();
396
- }, [isAccessClientInitialized, (_h = props.layerRequiringCredentials) === null || _h === void 0 ? void 0 : _h.formatId, (_j = props.layerRequiringCredentials) === null || _j === void 0 ? void 0 : _j.name, (_k = props.layerRequiringCredentials) === null || _k === void 0 ? void 0 : _k.url, updateAuthState]);
397
- const dialogContainer = React.useRef(null);
398
- const readyToSave = React.useCallback(() => {
399
- const credentialsSet = !!userName && !!password;
400
- return (!!mapUrl && !!mapName)
401
- && !layerAttachPending
402
- && (!serverRequireCredentials || credentialsSet)
403
- && !invalidCredentialsProvided
404
- && (externalLoginUrl === undefined || (externalLoginUrl !== undefined && oauthProcessSucceeded));
405
- }, [userName, password, mapUrl, mapName, serverRequireCredentials, layerAttachPending, invalidCredentialsProvided, externalLoginUrl, oauthProcessSucceeded]);
406
- const buttonCluster = React.useMemo(() => [
407
- { type: DialogButtonType.OK, onClick: handleOk, disabled: !readyToSave() },
408
- { type: DialogButtonType.Cancel, onClick: handleCancel },
409
- ], [readyToSave, handleCancel, handleOk]);
410
- const handleOnKeyDown = React.useCallback((event) => {
411
- if (event.key === SpecialKey.Enter) {
412
- if (readyToSave())
413
- handleOk();
414
- }
415
- }, [handleOk, readyToSave]);
416
- // onOauthProcessEnd events handler
417
- React.useEffect(() => {
418
- const handleOauthProcess = (success, _state) => {
419
- setLayerAuthPending(false);
420
- if (success) {
421
- setOAuthProcessSucceeded(true);
422
- setShowOauthPopup(false);
423
- setLayerAttachPending(false);
424
- handleOk(); // Add the layer the same way the user would do by clicking 'ok'
425
- }
426
- else {
427
- setShowOauthPopup(false);
428
- setLayerAttachPending(false);
429
- setOAuthProcessSucceeded(false);
430
- }
431
- };
432
- onOauthProcessEnd.addListener(handleOauthProcess);
433
- return () => {
434
- onOauthProcessEnd.removeListener(handleOauthProcess);
435
- };
436
- }, [handleOk, onOauthProcessEnd]);
437
- //
438
- // Monitors authentication method changes
439
- React.useEffect(() => {
440
- if (serverRequireCredentials && oauthProcessSucceeded === undefined && externalLoginUrl !== undefined) {
441
- handleArcGisLogin();
442
- }
443
- }, [oauthProcessSucceeded, externalLoginUrl, handleArcGisLogin, serverRequireCredentials]);
444
- // Monitors Oauth2 popup was closed
445
- const handleOAuthPopupClose = React.useCallback(() => {
446
- setShowOauthPopup(false);
447
- setLayerAuthPending(false);
448
- if (oauthProcessSucceeded === undefined)
449
- setOAuthProcessSucceeded(false); // indicates there was a failed attempt
450
- }, [oauthProcessSucceeded]);
451
- // Utility function to get warning message section
452
- function renderWarningMessage() {
453
- let node;
454
- let warningMessage;
455
- // Get the proper warning message
456
- if (showOauthPopup) {
457
- warningMessage = externalLoginWaitingMsg;
458
- }
459
- else if (oauthProcessSucceeded === false) {
460
- warningMessage = externalLoginFailedMsg;
461
- }
462
- else if (oauthProcessSucceeded === true) {
463
- warningMessage = externalLoginSucceededMsg;
464
- }
465
- else if (invalidCredentialsProvided) {
466
- warningMessage = invalidCredentialsLabel;
467
- }
468
- else if (serverRequireCredentials && (!userName || !password)) {
469
- warningMessage = missingCredentialsLabel;
470
- }
471
- // Sometimes we want to add an extra node, such as a button
472
- let extraNode;
473
- if (oauthProcessSucceeded === false) {
474
- extraNode = React.createElement("div", null,
475
- React.createElement(Button, { onClick: handleArcGisLogin }, externalLoginTryAgainLabel));
476
- }
477
- if (warningMessage !== undefined) {
478
- return (React.createElement("div", { className: "map-layer-source-warnMessage" },
479
- React.createElement(Icon, { className: "map-layer-source-warnMessage-icon", iconSpec: "icon-status-warning" }),
480
- React.createElement("span", { className: "map-layer-source-warnMessage-label" }, warningMessage),
481
- extraNode));
482
- }
483
- else {
484
- return (React.createElement("span", { className: "map-layer-source-placeholder" }, "\u00A0"));
485
- }
486
- return node;
487
- }
488
- // Use a hook to display the popup.
489
- // The display of the popup is controlled by the 'showOauthPopup' state variable.
490
- useCrossOriginPopup(showOauthPopup, externalLoginUrl, externalLoginTitle, 450, 450, handleOAuthPopupClose);
491
- return (React.createElement("div", { ref: dialogContainer },
492
- 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 },
493
- React.createElement("div", { className: "map-layer-url-dialog-content" },
494
- React.createElement("div", { className: "map-layer-source-url" },
495
- React.createElement("span", { className: "map-layer-source-label" }, typeLabel),
496
- React.createElement(SelectMapFormat, { value: mapType, disabled: props.layerRequiringCredentials !== undefined || props.mapLayerSourceToEdit !== undefined || layerAttachPending || layerAuthPending, onChange: setMapType, mapTypesOptions: mapTypesOptions }),
497
- React.createElement("span", { className: "map-layer-source-label" }, nameLabel),
498
- React.createElement(Input, { className: "map-layer-source-input", placeholder: nameInputPlaceHolder, onChange: onNameChange, value: mapName, disabled: props.layerRequiringCredentials !== undefined || layerAttachPending || layerAuthPending }),
499
- React.createElement("span", { className: "map-layer-source-label" }, urlLabel),
500
- React.createElement(Input, { className: "map-layer-source-input", placeholder: urlInputPlaceHolder, onKeyPress: handleOnKeyDown, onChange: onUrlChange, disabled: props.mapLayerSourceToEdit !== undefined || layerAttachPending || layerAuthPending, value: mapUrl }),
501
- serverRequireCredentials
502
- && externalLoginUrl === undefined // external login is handled in popup
503
- && props.mapLayerSourceToEdit === undefined &&
504
- React.createElement(React.Fragment, null,
505
- React.createElement("span", { className: "map-layer-source-label" }, userNameLabel),
506
- React.createElement(LabeledInput, { className: "map-layer-source-input", displayStyle: "inline", placeholder: serverRequireCredentials ? userNameRequiredLabel : userNameLabel, status: (!userName && serverRequireCredentials) || invalidCredentialsProvided ? "warning" : undefined, disabled: layerAttachPending || layerAuthPending, onChange: onUsernameChange, value: userName, size: "small" }),
507
- React.createElement("span", { className: "map-layer-source-label" }, passwordLabel),
508
- React.createElement(LabeledInput, { className: "map-layer-source-input", displayStyle: "inline", type: "password", placeholder: serverRequireCredentials ? passwordRequiredLabel : passwordLabel, status: (!password && serverRequireCredentials) || invalidCredentialsProvided ? "warning" : undefined, disabled: layerAttachPending || layerAuthPending, onChange: onPasswordChange, onKeyPress: handleOnKeyDown, value: password, size: "small" })),
509
- isSettingsStorageAvailable &&
510
- React.createElement("div", { title: settingsStorageDisabled ? noSaveSettingsWarning : "" }, hasImodelContext &&
511
- React.createElement("div", null,
512
- React.createElement(Radio, { disabled: settingsStorageDisabled, name: "settingsStorage", value: "iTwin", label: iTwinSettingsLabel, checked: settingsStorage === "iTwin", onChange: onRadioChange }),
513
- React.createElement(Radio, { disabled: settingsStorageDisabled, name: "settingsStorage", value: "Model", label: modelSettingsLabel, checked: settingsStorage === "Model", onChange: onRadioChange }))))),
514
- renderWarningMessage(),
515
- (layerAttachPending || layerAuthPending) &&
516
- React.createElement("div", { className: "map-layer-source-progressBar" },
517
- React.createElement(ProgressLinear, { indeterminate: true })))));
518
- }
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
+ * See LICENSE.md in the project root for license terms and full copyright notice.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ // cSpell:ignore Modeless WMTS
6
+ import { DialogButtonType, SpecialKey } from "@itwin/appui-abstract";
7
+ import { ModalDialogManager } from "@itwin/appui-react";
8
+ import { Button, Input, LabeledInput, ProgressLinear, Radio } from "@itwin/itwinui-react";
9
+ import { IModelApp, MapLayerImageryProviderStatus, MapLayerSource, MapLayerSourceStatus, NotifyMessageDetails, OutputMessagePriority, } from "@itwin/core-frontend";
10
+ import { Dialog, Icon, useCrossOriginPopup } from "@itwin/core-react";
11
+ import * as React from "react";
12
+ import { MapLayerPreferences } from "../../MapLayerPreferences";
13
+ import { MapLayersUI } from "../../mapLayers";
14
+ import "./MapUrlDialog.scss";
15
+ import { BeEvent, Guid } from "@itwin/core-bentley";
16
+ import { SelectMapFormat } from "./SelectMapFormat";
17
+ export const MAP_TYPES = {
18
+ wms: "WMS",
19
+ arcGis: "ArcGIS",
20
+ wmts: "WMTS",
21
+ tileUrl: "TileURL",
22
+ arcGisFeature: "ArcGISFeature",
23
+ };
24
+ // eslint-disable-next-line @typescript-eslint/naming-convention
25
+ export function MapUrlDialog(props) {
26
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
27
+ const { isOverlay, onOkResult, mapTypesOptions } = props;
28
+ const getMapUrlFromProps = React.useCallback(() => {
29
+ var _a;
30
+ if (props.mapLayerSourceToEdit) {
31
+ return props.mapLayerSourceToEdit.url;
32
+ }
33
+ else if ((_a = props.layerRequiringCredentials) === null || _a === void 0 ? void 0 : _a.url) {
34
+ return props.layerRequiringCredentials.url;
35
+ }
36
+ return "";
37
+ }, [props.layerRequiringCredentials, props.mapLayerSourceToEdit]);
38
+ const getMapNameFromProps = React.useCallback(() => {
39
+ var _a;
40
+ if (props.mapLayerSourceToEdit) {
41
+ return props.mapLayerSourceToEdit.name;
42
+ }
43
+ else if ((_a = props.layerRequiringCredentials) === null || _a === void 0 ? void 0 : _a.name) {
44
+ return props.layerRequiringCredentials.name;
45
+ }
46
+ return "";
47
+ }, [props.layerRequiringCredentials, props.mapLayerSourceToEdit]);
48
+ const getFormatFromProps = React.useCallback(() => {
49
+ var _a;
50
+ if (props.mapLayerSourceToEdit) {
51
+ return props.mapLayerSourceToEdit.formatId;
52
+ }
53
+ else if ((_a = props.layerRequiringCredentials) === null || _a === void 0 ? void 0 : _a.formatId) {
54
+ return props.layerRequiringCredentials.formatId;
55
+ }
56
+ return undefined;
57
+ }, [props.layerRequiringCredentials, props.mapLayerSourceToEdit]);
58
+ const [dialogTitle] = React.useState(MapLayersUI.localization.getLocalizedString(props.layerRequiringCredentials || props.mapLayerSourceToEdit ? "mapLayers:CustomAttach.EditCustomLayer" : "mapLayers:CustomAttach.AttachCustomLayer"));
59
+ const [typeLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.Type"));
60
+ const [nameLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.Name"));
61
+ const [nameInputPlaceHolder] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.NameInputPlaceHolder"));
62
+ const [urlLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.URL"));
63
+ const [urlInputPlaceHolder] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.UrlInputPlaceHolder"));
64
+ const [iTwinSettingsLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.StoreOnITwinSettings"));
65
+ const [modelSettingsLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.StoreOnModelSettings"));
66
+ const [missingCredentialsLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.MissingCredentials"));
67
+ const [invalidCredentialsLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.InvalidCredentials"));
68
+ const [externalLoginTitle] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.ExternalLogin"));
69
+ const [externalLoginFailedMsg] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.ExternalLoginFailed"));
70
+ const [externalLoginSucceededMsg] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.ExternalLoginSucceeded"));
71
+ const [externalLoginWaitingMsg] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.ExternalLoginWaiting"));
72
+ const [externalLoginTryAgainLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.ExternalLoginTryAgain"));
73
+ const [serverRequireCredentials, setServerRequireCredentials] = React.useState(false);
74
+ const [invalidCredentialsProvided, setInvalidCredentialsProvided] = React.useState(false);
75
+ const [layerAttachPending, setLayerAttachPending] = React.useState(false);
76
+ const [layerAuthPending, setLayerAuthPending] = React.useState(false);
77
+ const [mapUrl, setMapUrl] = React.useState(getMapUrlFromProps());
78
+ const [mapName, setMapName] = React.useState(getMapNameFromProps());
79
+ const [userName, setUserName] = React.useState("");
80
+ const [password, setPassword] = React.useState("");
81
+ const [noSaveSettingsWarning] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.NoSaveSettingsWarning"));
82
+ const [passwordLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:AuthenticationInputs.Password"));
83
+ const [passwordRequiredLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:AuthenticationInputs.PasswordRequired"));
84
+ const [userNameLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:AuthenticationInputs.Username"));
85
+ const [userNameRequiredLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:AuthenticationInputs.UsernameRequired"));
86
+ const [settingsStorage, setSettingsStorageRadio] = React.useState("iTwin");
87
+ const [oauthProcessSucceeded, setOAuthProcessSucceeded] = React.useState(undefined);
88
+ const [showOauthPopup, setShowOauthPopup] = React.useState(false);
89
+ const [externalLoginUrl, setExternalLoginUrl] = React.useState();
90
+ const [onOauthProcessEnd] = React.useState(new BeEvent());
91
+ const [accessClient, setAccessClient] = React.useState();
92
+ const [isAccessClientInitialized, setAccessClientInitialized] = React.useState(false);
93
+ const [mapType, setMapType] = React.useState((_a = getFormatFromProps()) !== null && _a !== void 0 ? _a : "ArcGIS");
94
+ // 'isMounted' is used to prevent any async operation once the hook has been
95
+ // unloaded. Otherwise we get a 'Can't perform a React state update on an unmounted component.' warning in the console.
96
+ const isMounted = React.useRef(false);
97
+ React.useEffect(() => {
98
+ isMounted.current = true;
99
+ return () => {
100
+ isMounted.current = false;
101
+ };
102
+ }, []);
103
+ 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));
104
+ const [hasImodelContext] = React.useState(((_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.iTwinId) !== undefined
105
+ && props.activeViewport.iModel.iTwinId !== Guid.empty
106
+ && ((_g = (_f = props === null || props === void 0 ? void 0 : props.activeViewport) === null || _f === void 0 ? void 0 : _f.iModel) === null || _g === void 0 ? void 0 : _g.iModelId) !== undefined
107
+ && (props === null || props === void 0 ? void 0 : props.activeViewport.iModel.iModelId) !== Guid.empty);
108
+ // Even though the settings storage is available,
109
+ // we don't always want to enable it in the UI.
110
+ const [settingsStorageDisabled] = React.useState(!isSettingsStorageAvailable || props.mapLayerSourceToEdit !== undefined || props.layerRequiringCredentials !== undefined);
111
+ const [layerRequiringCredentialsIdx] = React.useState(() => {
112
+ var _a;
113
+ if (props.layerRequiringCredentials === undefined || !props.layerRequiringCredentials.name || !props.layerRequiringCredentials.url) {
114
+ return undefined;
115
+ }
116
+ const indexInDisplayStyle = (_a = props.activeViewport) === null || _a === void 0 ? void 0 : _a.displayStyle.findMapLayerIndexByNameAndSource(props.layerRequiringCredentials.name, props.layerRequiringCredentials.url, isOverlay);
117
+ if (indexInDisplayStyle === undefined || indexInDisplayStyle < 0) {
118
+ return undefined;
119
+ }
120
+ else {
121
+ return indexInDisplayStyle;
122
+ }
123
+ });
124
+ const handleCancel = React.useCallback(() => {
125
+ if (props.onCancelResult) {
126
+ props.onCancelResult();
127
+ return;
128
+ }
129
+ ModalDialogManager.closeDialog();
130
+ }, [props]);
131
+ const onUsernameChange = React.useCallback((event) => {
132
+ setUserName(event.target.value);
133
+ if (invalidCredentialsProvided)
134
+ setInvalidCredentialsProvided(false);
135
+ }, [setUserName, invalidCredentialsProvided, setInvalidCredentialsProvided]);
136
+ const onPasswordChange = React.useCallback((event) => {
137
+ setPassword(event.target.value);
138
+ if (invalidCredentialsProvided)
139
+ setInvalidCredentialsProvided(false);
140
+ }, [setPassword, invalidCredentialsProvided, setInvalidCredentialsProvided]);
141
+ const handleArcGisLogin = React.useCallback(() => {
142
+ setLayerAuthPending(true);
143
+ setShowOauthPopup(true);
144
+ if (oauthProcessSucceeded === false) {
145
+ setOAuthProcessSucceeded(undefined);
146
+ }
147
+ }, [oauthProcessSucceeded]);
148
+ // return true if authorization is needed
149
+ const updateAuthState = React.useCallback(async (source, sourceValidation) => {
150
+ const sourceRequireAuth = (sourceValidation.status === MapLayerSourceStatus.RequireAuth);
151
+ const invalidCredentials = (sourceValidation.status === MapLayerSourceStatus.InvalidCredentials);
152
+ if (sourceRequireAuth) {
153
+ const settings = source.toLayerSettings();
154
+ if (accessClient !== undefined && accessClient.getTokenServiceEndPoint !== undefined && settings !== undefined) {
155
+ try {
156
+ const tokenEndpoint = await accessClient.getTokenServiceEndPoint(settings.url);
157
+ if (tokenEndpoint !== undefined) {
158
+ const loginUrl = tokenEndpoint.getLoginUrl();
159
+ setExternalLoginUrl(loginUrl);
160
+ }
161
+ }
162
+ catch (_error) {
163
+ }
164
+ }
165
+ }
166
+ setServerRequireCredentials(sourceRequireAuth || invalidCredentials);
167
+ if (invalidCredentials) {
168
+ setInvalidCredentialsProvided(true);
169
+ }
170
+ else if (invalidCredentialsProvided) {
171
+ setInvalidCredentialsProvided(false); // flag reset
172
+ }
173
+ return sourceRequireAuth || invalidCredentials;
174
+ }, [accessClient, invalidCredentialsProvided]);
175
+ const updateAttachedLayer = React.useCallback(async (source, validation) => {
176
+ const vp = props === null || props === void 0 ? void 0 : props.activeViewport;
177
+ if (vp === undefined || source === undefined || layerRequiringCredentialsIdx === undefined) {
178
+ const error = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachMissingViewOrSource");
179
+ const msg = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachError", { error, sourceUrl: source.url });
180
+ IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
181
+ return true;
182
+ }
183
+ // Layer is already attached,
184
+ vp.displayStyle.changeMapLayerProps({
185
+ subLayers: validation.subLayers,
186
+ }, layerRequiringCredentialsIdx, isOverlay);
187
+ vp.displayStyle.changeMapLayerCredentials(layerRequiringCredentialsIdx, isOverlay, source.userName, source.password);
188
+ // Reset the provider's status
189
+ const provider = vp.getMapLayerImageryProvider(layerRequiringCredentialsIdx, isOverlay);
190
+ if (provider && provider.status !== MapLayerImageryProviderStatus.Valid) {
191
+ provider.status = MapLayerImageryProviderStatus.Valid;
192
+ }
193
+ vp.invalidateRenderPlan();
194
+ // This handler will close the layer source handler, and therefore the MapUrl dialog.
195
+ // don't call it if the dialog needs to remains open.
196
+ onOkResult();
197
+ return true;
198
+ }, [isOverlay, layerRequiringCredentialsIdx, onOkResult, props.activeViewport]);
199
+ // Returns true if no further input is needed from end-user.
200
+ const doAttach = React.useCallback(async (source, validation) => {
201
+ const vp = props === null || props === void 0 ? void 0 : props.activeViewport;
202
+ if (vp === undefined || source === undefined) {
203
+ const error = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachMissingViewOrSource");
204
+ const msg = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachError", { error, sourceUrl: source.url });
205
+ IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
206
+ return true;
207
+ }
208
+ // Update service settings if storage is available and we are not prompting user for credentials
209
+ if (!settingsStorageDisabled && !props.layerRequiringCredentials) {
210
+ const storeOnIModel = (hasImodelContext ? "Model" === settingsStorage : undefined);
211
+ if (vp.iModel.iTwinId && !(await MapLayerPreferences.storeSource(source, vp.iModel.iTwinId, vp.iModel.iModelId, storeOnIModel))) {
212
+ const msgError = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerPreferencesStoreFailed");
213
+ IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msgError));
214
+ }
215
+ }
216
+ const settings = source.toLayerSettings(validation.subLayers);
217
+ if (settings) {
218
+ vp.displayStyle.attachMapLayer({ settings, isOverlay });
219
+ const msg = IModelApp.localization.getLocalizedString("mapLayers:Messages.MapLayerAttached", { sourceName: source.name, sourceUrl: source.url });
220
+ IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Info, msg));
221
+ }
222
+ else {
223
+ const msgError = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerLayerSettingsConversionError");
224
+ const msg = MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.MapLayerAttachError", { error: msgError, sourceUrl: source.url });
225
+ IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
226
+ }
227
+ vp.invalidateRenderPlan();
228
+ // This handler will close the layer source handler, and therefore the MapUrl dialog.
229
+ // don't call it if the dialog needs to remains open.
230
+ onOkResult();
231
+ return true;
232
+ }, [hasImodelContext, isOverlay, onOkResult, props === null || props === void 0 ? void 0 : props.activeViewport, props.layerRequiringCredentials, settingsStorage, settingsStorageDisabled]);
233
+ // Validate the layer source and attempt to attach (or update) the layer.
234
+ // Returns true if no further input is needed from end-user (i.e. close the dialog)
235
+ const attemptAttachSource = React.useCallback(async (source) => {
236
+ try {
237
+ const validation = await source.validateSource(true);
238
+ if (validation.status === MapLayerSourceStatus.Valid) {
239
+ if (layerRequiringCredentialsIdx === undefined) {
240
+ return await doAttach(source, validation);
241
+ }
242
+ else {
243
+ return await updateAttachedLayer(source, validation);
244
+ }
245
+ }
246
+ else {
247
+ const authNeeded = await updateAuthState(source, validation);
248
+ if (authNeeded) {
249
+ return false;
250
+ }
251
+ else {
252
+ const msg = MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.ValidationError");
253
+ IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, `${msg} ${source.url}`));
254
+ return true;
255
+ }
256
+ }
257
+ return false;
258
+ }
259
+ catch (error) {
260
+ const msg = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachError", { error, sourceUrl: source.url });
261
+ IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
262
+ return true;
263
+ }
264
+ }, [updateAuthState, doAttach, layerRequiringCredentialsIdx, updateAttachedLayer]);
265
+ const onNameChange = React.useCallback((event) => {
266
+ setMapName(event.target.value);
267
+ }, [setMapName]);
268
+ const onRadioChange = React.useCallback((event) => {
269
+ setSettingsStorageRadio(event.target.value);
270
+ }, [setSettingsStorageRadio]);
271
+ const onUrlChange = React.useCallback((event) => {
272
+ setMapUrl(event.target.value);
273
+ }, [setMapUrl]);
274
+ const createSource = React.useCallback(() => {
275
+ let source;
276
+ if (mapUrl && mapName) {
277
+ source = MapLayerSource.fromJSON({
278
+ url: mapUrl,
279
+ name: mapName,
280
+ formatId: mapType
281
+ });
282
+ // Set credentials separately since they are not part of JSON
283
+ if (source) {
284
+ source.userName = userName || undefined; // When there is no value, empty string is always returned, in this case force it to undefined,
285
+ source.password = password || undefined;
286
+ }
287
+ }
288
+ return source;
289
+ }, [mapName, mapType, mapUrl, password, userName]);
290
+ const handleOk = React.useCallback(() => {
291
+ const source = createSource();
292
+ if (source === undefined || props.mapLayerSourceToEdit) {
293
+ ModalDialogManager.closeDialog();
294
+ onOkResult();
295
+ if (source === undefined) {
296
+ // Close the dialog and inform end user something went wrong.
297
+ const msgError = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerLayerSourceCreationFailed");
298
+ const msg = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachError", { error: msgError, sourceUrl: mapUrl });
299
+ IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
300
+ return;
301
+ }
302
+ // Simply change the source definition in the setting service
303
+ if (props.mapLayerSourceToEdit !== undefined) {
304
+ const vp = props.activeViewport;
305
+ void (async () => {
306
+ var _a, _b;
307
+ if (isSettingsStorageAvailable && ((_a = vp === null || vp === void 0 ? void 0 : vp.iModel) === null || _a === void 0 ? void 0 : _a.iTwinId)) {
308
+ try {
309
+ await MapLayerPreferences.replaceSource(props.mapLayerSourceToEdit, source, vp.iModel.iTwinId, vp === null || vp === void 0 ? void 0 : vp.iModel.iModelId);
310
+ }
311
+ catch (err) {
312
+ const errorMessage = IModelApp.localization.getLocalizedString("mapLayers:Messages.MapLayerEditError", { layerName: (_b = props.mapLayerSourceToEdit) === null || _b === void 0 ? void 0 : _b.name });
313
+ IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, errorMessage));
314
+ return;
315
+ }
316
+ }
317
+ })();
318
+ return;
319
+ }
320
+ }
321
+ setLayerAttachPending(true);
322
+ // Attach source asynchronously.
323
+ void (async () => {
324
+ try {
325
+ const closeDialog = await attemptAttachSource(source);
326
+ if (isMounted.current) {
327
+ setLayerAttachPending(false);
328
+ }
329
+ // In theory the modal dialog should always get closed by the parent
330
+ // AttachLayerPanel's 'onOkResult' handler. We close it here just in case.
331
+ if (closeDialog) {
332
+ ModalDialogManager.closeDialog();
333
+ onOkResult();
334
+ }
335
+ }
336
+ catch (_error) {
337
+ onOkResult();
338
+ ModalDialogManager.closeDialog();
339
+ }
340
+ })();
341
+ }, [createSource, props.mapLayerSourceToEdit, props.activeViewport, onOkResult, mapUrl, isSettingsStorageAvailable, attemptAttachSource]);
342
+ React.useEffect(() => {
343
+ const handleOAuthProcessEnd = (success, _state) => {
344
+ onOauthProcessEnd.raiseEvent(success, _state);
345
+ };
346
+ // Currently only arcgis support AccessClient
347
+ const ac = IModelApp.mapLayerFormatRegistry.getAccessClient(MAP_TYPES.arcGis);
348
+ if (ac === null || ac === void 0 ? void 0 : ac.onOAuthProcessEnd) {
349
+ setAccessClient(ac); // cache it, so we dont need to make another lookup;
350
+ ac.onOAuthProcessEnd.addListener(handleOAuthProcessEnd);
351
+ }
352
+ setAccessClientInitialized(true);
353
+ return () => {
354
+ if (ac === null || ac === void 0 ? void 0 : ac.onOAuthProcessEnd) {
355
+ ac.onOAuthProcessEnd.removeListener(handleOAuthProcessEnd);
356
+ }
357
+ setAccessClient(undefined);
358
+ setAccessClientInitialized(false);
359
+ };
360
+ }, [mapType, onOauthProcessEnd, setAccessClient]);
361
+ // After a map type change, make sure the different Oauth states are reset.
362
+ React.useEffect(() => {
363
+ // Reset few states
364
+ setServerRequireCredentials(false);
365
+ setInvalidCredentialsProvided(false);
366
+ setShowOauthPopup(false);
367
+ setOAuthProcessSucceeded(undefined);
368
+ setExternalLoginUrl(undefined);
369
+ }, [mapType]);
370
+ // The first time the dialog is loaded and we already know the layer requires auth. (i.e ImageryProvider already made an attempt)
371
+ // makes a request to discover the authentification types and adjust UI accordingly (i.e. username/password fields, Oauth popup)
372
+ // Without this effect, user would have to manually click the 'OK' button in order to trigger the layer connection.
373
+ React.useEffect(() => {
374
+ // Attach source asynchronously.
375
+ void (async () => {
376
+ var _a, _b;
377
+ if (isAccessClientInitialized && ((_a = props.layerRequiringCredentials) === null || _a === void 0 ? void 0 : _a.url) !== undefined && ((_b = props.layerRequiringCredentials) === null || _b === void 0 ? void 0 : _b.name) !== undefined) {
378
+ try {
379
+ const source = MapLayerSource.fromJSON({
380
+ url: props.layerRequiringCredentials.url,
381
+ name: props.layerRequiringCredentials.name,
382
+ formatId: props.layerRequiringCredentials.formatId
383
+ });
384
+ if (source !== undefined) {
385
+ setLayerAttachPending(true);
386
+ const validation = await source.validateSource(true);
387
+ if (isMounted.current) {
388
+ setLayerAttachPending(false);
389
+ }
390
+ await updateAuthState(source, validation);
391
+ }
392
+ }
393
+ catch (_error) { }
394
+ }
395
+ })();
396
+ }, [isAccessClientInitialized, (_h = props.layerRequiringCredentials) === null || _h === void 0 ? void 0 : _h.formatId, (_j = props.layerRequiringCredentials) === null || _j === void 0 ? void 0 : _j.name, (_k = props.layerRequiringCredentials) === null || _k === void 0 ? void 0 : _k.url, updateAuthState]);
397
+ const dialogContainer = React.useRef(null);
398
+ const readyToSave = React.useCallback(() => {
399
+ const credentialsSet = !!userName && !!password;
400
+ return (!!mapUrl && !!mapName)
401
+ && !layerAttachPending
402
+ && (!serverRequireCredentials || credentialsSet)
403
+ && !invalidCredentialsProvided
404
+ && (externalLoginUrl === undefined || (externalLoginUrl !== undefined && oauthProcessSucceeded));
405
+ }, [userName, password, mapUrl, mapName, serverRequireCredentials, layerAttachPending, invalidCredentialsProvided, externalLoginUrl, oauthProcessSucceeded]);
406
+ const buttonCluster = React.useMemo(() => [
407
+ { type: DialogButtonType.OK, onClick: handleOk, disabled: !readyToSave() },
408
+ { type: DialogButtonType.Cancel, onClick: handleCancel },
409
+ ], [readyToSave, handleCancel, handleOk]);
410
+ const handleOnKeyDown = React.useCallback((event) => {
411
+ if (event.key === SpecialKey.Enter) {
412
+ if (readyToSave())
413
+ handleOk();
414
+ }
415
+ }, [handleOk, readyToSave]);
416
+ // onOauthProcessEnd events handler
417
+ React.useEffect(() => {
418
+ const handleOauthProcess = (success, _state) => {
419
+ setLayerAuthPending(false);
420
+ if (success) {
421
+ setOAuthProcessSucceeded(true);
422
+ setShowOauthPopup(false);
423
+ setLayerAttachPending(false);
424
+ handleOk(); // Add the layer the same way the user would do by clicking 'ok'
425
+ }
426
+ else {
427
+ setShowOauthPopup(false);
428
+ setLayerAttachPending(false);
429
+ setOAuthProcessSucceeded(false);
430
+ }
431
+ };
432
+ onOauthProcessEnd.addListener(handleOauthProcess);
433
+ return () => {
434
+ onOauthProcessEnd.removeListener(handleOauthProcess);
435
+ };
436
+ }, [handleOk, onOauthProcessEnd]);
437
+ //
438
+ // Monitors authentication method changes
439
+ React.useEffect(() => {
440
+ if (serverRequireCredentials && oauthProcessSucceeded === undefined && externalLoginUrl !== undefined) {
441
+ handleArcGisLogin();
442
+ }
443
+ }, [oauthProcessSucceeded, externalLoginUrl, handleArcGisLogin, serverRequireCredentials]);
444
+ // Monitors Oauth2 popup was closed
445
+ const handleOAuthPopupClose = React.useCallback(() => {
446
+ setShowOauthPopup(false);
447
+ setLayerAuthPending(false);
448
+ if (oauthProcessSucceeded === undefined)
449
+ setOAuthProcessSucceeded(false); // indicates there was a failed attempt
450
+ }, [oauthProcessSucceeded]);
451
+ // Utility function to get warning message section
452
+ function renderWarningMessage() {
453
+ let node;
454
+ let warningMessage;
455
+ // Get the proper warning message
456
+ if (showOauthPopup) {
457
+ warningMessage = externalLoginWaitingMsg;
458
+ }
459
+ else if (oauthProcessSucceeded === false) {
460
+ warningMessage = externalLoginFailedMsg;
461
+ }
462
+ else if (oauthProcessSucceeded === true) {
463
+ warningMessage = externalLoginSucceededMsg;
464
+ }
465
+ else if (invalidCredentialsProvided) {
466
+ warningMessage = invalidCredentialsLabel;
467
+ }
468
+ else if (serverRequireCredentials && (!userName || !password)) {
469
+ warningMessage = missingCredentialsLabel;
470
+ }
471
+ // Sometimes we want to add an extra node, such as a button
472
+ let extraNode;
473
+ if (oauthProcessSucceeded === false) {
474
+ extraNode = React.createElement("div", null,
475
+ React.createElement(Button, { onClick: handleArcGisLogin }, externalLoginTryAgainLabel));
476
+ }
477
+ if (warningMessage !== undefined) {
478
+ return (React.createElement("div", { className: "map-layer-source-warnMessage" },
479
+ React.createElement(Icon, { className: "map-layer-source-warnMessage-icon", iconSpec: "icon-status-warning" }),
480
+ React.createElement("span", { className: "map-layer-source-warnMessage-label" }, warningMessage),
481
+ extraNode));
482
+ }
483
+ else {
484
+ return (React.createElement("span", { className: "map-layer-source-placeholder" }, "\u00A0"));
485
+ }
486
+ return node;
487
+ }
488
+ // Use a hook to display the popup.
489
+ // The display of the popup is controlled by the 'showOauthPopup' state variable.
490
+ useCrossOriginPopup(showOauthPopup, externalLoginUrl, externalLoginTitle, 450, 450, handleOAuthPopupClose);
491
+ return (React.createElement("div", { ref: dialogContainer },
492
+ 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 },
493
+ React.createElement("div", { className: "map-layer-url-dialog-content" },
494
+ React.createElement("div", { className: "map-layer-source-url" },
495
+ React.createElement("span", { className: "map-layer-source-label" }, typeLabel),
496
+ React.createElement(SelectMapFormat, { value: mapType, disabled: props.layerRequiringCredentials !== undefined || props.mapLayerSourceToEdit !== undefined || layerAttachPending || layerAuthPending, onChange: setMapType, mapTypesOptions: mapTypesOptions }),
497
+ React.createElement("span", { className: "map-layer-source-label" }, nameLabel),
498
+ React.createElement(Input, { className: "map-layer-source-input", placeholder: nameInputPlaceHolder, onChange: onNameChange, value: mapName, disabled: props.layerRequiringCredentials !== undefined || layerAttachPending || layerAuthPending }),
499
+ React.createElement("span", { className: "map-layer-source-label" }, urlLabel),
500
+ React.createElement(Input, { className: "map-layer-source-input", placeholder: urlInputPlaceHolder, onKeyPress: handleOnKeyDown, onChange: onUrlChange, disabled: props.mapLayerSourceToEdit !== undefined || layerAttachPending || layerAuthPending, value: mapUrl }),
501
+ serverRequireCredentials
502
+ && externalLoginUrl === undefined // external login is handled in popup
503
+ && props.mapLayerSourceToEdit === undefined &&
504
+ React.createElement(React.Fragment, null,
505
+ React.createElement("span", { className: "map-layer-source-label" }, userNameLabel),
506
+ React.createElement(LabeledInput, { className: "map-layer-source-input", displayStyle: "inline", placeholder: serverRequireCredentials ? userNameRequiredLabel : userNameLabel, status: (!userName && serverRequireCredentials) || invalidCredentialsProvided ? "warning" : undefined, disabled: layerAttachPending || layerAuthPending, onChange: onUsernameChange, value: userName, size: "small" }),
507
+ React.createElement("span", { className: "map-layer-source-label" }, passwordLabel),
508
+ React.createElement(LabeledInput, { className: "map-layer-source-input", displayStyle: "inline", type: "password", placeholder: serverRequireCredentials ? passwordRequiredLabel : passwordLabel, status: (!password && serverRequireCredentials) || invalidCredentialsProvided ? "warning" : undefined, disabled: layerAttachPending || layerAuthPending, onChange: onPasswordChange, onKeyPress: handleOnKeyDown, value: password, size: "small" })),
509
+ isSettingsStorageAvailable &&
510
+ React.createElement("div", { title: settingsStorageDisabled ? noSaveSettingsWarning : "" }, hasImodelContext &&
511
+ React.createElement("div", null,
512
+ React.createElement(Radio, { disabled: settingsStorageDisabled, name: "settingsStorage", value: "iTwin", label: iTwinSettingsLabel, checked: settingsStorage === "iTwin", onChange: onRadioChange }),
513
+ React.createElement(Radio, { disabled: settingsStorageDisabled, name: "settingsStorage", value: "Model", label: modelSettingsLabel, checked: settingsStorage === "Model", onChange: onRadioChange }))))),
514
+ renderWarningMessage(),
515
+ (layerAttachPending || layerAuthPending) &&
516
+ React.createElement("div", { className: "map-layer-source-progressBar" },
517
+ React.createElement(ProgressLinear, { indeterminate: true })))));
518
+ }
519
519
  //# sourceMappingURL=MapUrlDialog.js.map