@microsoft/power-apps-native-host 0.2.25 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -2
- package/compatibility/current.json +0 -1
- package/lib/NOTICE.txt +6 -12
- package/lib/commonjs/auth/AuthContext.js +74 -18
- package/lib/commonjs/bridge/NativePowerAppsBridge.js +11 -18
- package/lib/commonjs/bridge/PowerPagesBridge.js +27 -8
- package/lib/commonjs/bridge/bridgeState.js +40 -0
- package/lib/commonjs/connections/ConnectionSetupScreen.js +459 -80
- package/lib/commonjs/connections/PowerPlatformConnectionApi.js +946 -0
- package/lib/commonjs/connections/connectionMetadata.js +127 -0
- package/lib/commonjs/connections/connectionState.js +96 -0
- package/lib/commonjs/connections/disambiguate.js +19 -11
- package/lib/commonjs/connections/resolveConnections.js +347 -0
- package/lib/commonjs/connections/types.js +106 -0
- package/lib/commonjs/{platform → connections}/useConnectionRefs.js +128 -65
- package/lib/commonjs/connections/useConnectionSetup.js +111 -52
- package/lib/commonjs/controls/FilePicker.js +6 -0
- package/lib/commonjs/controls/ImagePicker.js +8 -0
- package/lib/commonjs/index.js +166 -4
- package/lib/commonjs/logger.js +9 -6
- package/lib/commonjs/offline/createOfflineHostActivationCallbacks.js +61 -0
- package/lib/commonjs/offline/useOfflineIntegrationWithStartupGate.js +46 -0
- package/lib/commonjs/offline/useOfflineIntegrationWithStartupGate.web.js +13 -0
- package/lib/commonjs/platform/DataverseDataSources.js +14 -0
- package/lib/commonjs/platform/DataverseGlobalDiscoveryApi.js +49 -0
- package/lib/commonjs/platform/GlobalDiscovery.js +51 -0
- package/lib/commonjs/platform/PowerPlatformEnvironmentApi.js +6 -106
- package/lib/commonjs/provider/PowerAppsHostProvider.js +156 -25
- package/lib/commonjs/provider/PowerAppsProvider.js +173 -134
- package/lib/commonjs/provider/SessionDetailsGesture.js +216 -0
- package/lib/commonjs/telemetry/OfflineSdkTelemetry.js +95 -0
- package/lib/commonjs/telemetry/OfflineSdkTelemetry.web.js +9 -0
- package/lib/commonjs/telemetry/index.js +22 -3
- package/lib/module/auth/AuthContext.js +74 -18
- package/lib/module/bridge/NativePowerAppsBridge.js +12 -19
- package/lib/module/bridge/PowerPagesBridge.js +27 -8
- package/lib/module/bridge/bridgeState.js +34 -0
- package/lib/module/connections/ConnectionSetupScreen.js +460 -81
- package/lib/module/connections/PowerPlatformConnectionApi.js +932 -0
- package/lib/module/connections/connectionMetadata.js +112 -0
- package/lib/module/connections/connectionState.js +82 -0
- package/lib/module/connections/disambiguate.js +19 -12
- package/lib/module/connections/resolveConnections.js +342 -0
- package/lib/module/connections/types.js +91 -0
- package/lib/module/{platform → connections}/useConnectionRefs.js +129 -66
- package/lib/module/connections/useConnectionSetup.js +113 -54
- package/lib/module/controls/FilePicker.js +6 -0
- package/lib/module/controls/ImagePicker.js +8 -0
- package/lib/module/index.js +10 -4
- package/lib/module/logger.js +8 -6
- package/lib/module/offline/createOfflineHostActivationCallbacks.js +55 -0
- package/lib/module/offline/useOfflineIntegrationWithStartupGate.js +42 -0
- package/lib/module/offline/useOfflineIntegrationWithStartupGate.web.js +9 -0
- package/lib/module/platform/DataverseDataSources.js +10 -0
- package/lib/module/platform/DataverseGlobalDiscoveryApi.js +45 -0
- package/lib/module/platform/GlobalDiscovery.js +45 -0
- package/lib/module/platform/PowerPlatformEnvironmentApi.js +5 -104
- package/lib/module/provider/PowerAppsHostProvider.js +159 -28
- package/lib/module/provider/PowerAppsProvider.js +177 -138
- package/lib/module/provider/SessionDetailsGesture.js +211 -0
- package/lib/module/telemetry/OfflineSdkTelemetry.js +91 -0
- package/lib/module/telemetry/OfflineSdkTelemetry.web.js +5 -0
- package/lib/module/telemetry/index.js +15 -4
- package/lib/typescript/commonjs/auth/AuthContext.d.ts +3 -1
- package/lib/typescript/commonjs/auth/AuthContext.web.d.ts +1 -0
- package/lib/typescript/commonjs/bridge/bridgeState.d.ts +5 -0
- package/lib/typescript/commonjs/connections/ConnectionSetupScreen.d.ts +28 -7
- package/lib/typescript/commonjs/{platform → connections}/PowerPlatformConnectionApi.d.ts +52 -43
- package/lib/typescript/commonjs/connections/connectionMetadata.d.ts +66 -0
- package/lib/typescript/commonjs/connections/connectionState.d.ts +16 -0
- package/lib/typescript/commonjs/connections/disambiguate.d.ts +4 -2
- package/lib/typescript/commonjs/{platform → connections}/resolveConnections.d.ts +19 -19
- package/lib/typescript/commonjs/connections/types.d.ts +129 -1
- package/lib/typescript/commonjs/{platform → connections}/useConnectionRefs.d.ts +33 -24
- package/lib/typescript/commonjs/connections/useConnectionSetup.d.ts +12 -8
- package/lib/typescript/commonjs/controls/FilePicker.d.ts +7 -1
- package/lib/typescript/commonjs/controls/ImagePicker.d.ts +9 -1
- package/lib/typescript/commonjs/index.d.ts +17 -10
- package/lib/typescript/commonjs/logger.d.ts +4 -4
- package/lib/typescript/commonjs/offline/createOfflineHostActivationCallbacks.d.ts +12 -0
- package/lib/typescript/commonjs/offline/useOfflineIntegrationWithStartupGate.d.ts +15 -0
- package/lib/typescript/commonjs/offline/useOfflineIntegrationWithStartupGate.web.d.ts +6 -0
- package/lib/typescript/commonjs/platform/DataverseDataSources.d.ts +27 -0
- package/lib/typescript/commonjs/platform/DataverseGlobalDiscoveryApi.d.ts +3 -0
- package/lib/typescript/commonjs/platform/GlobalDiscovery.d.ts +4 -0
- package/lib/typescript/commonjs/platform/PowerPlatformEnvironmentApi.d.ts +3 -74
- package/lib/typescript/commonjs/provider/PowerAppsHostProvider.d.ts +29 -18
- package/lib/typescript/commonjs/provider/PowerAppsProvider.d.ts +30 -8
- package/lib/typescript/commonjs/provider/SessionDetailsGesture.d.ts +13 -0
- package/lib/typescript/commonjs/telemetry/OfflineSdkTelemetry.d.ts +15 -0
- package/lib/typescript/commonjs/telemetry/OfflineSdkTelemetry.web.d.ts +2 -0
- package/lib/typescript/commonjs/telemetry/index.d.ts +13 -1
- package/lib/typescript/module/auth/AuthContext.d.ts +3 -1
- package/lib/typescript/module/auth/AuthContext.web.d.ts +1 -0
- package/lib/typescript/module/bridge/bridgeState.d.ts +5 -0
- package/lib/typescript/module/connections/ConnectionSetupScreen.d.ts +28 -7
- package/lib/typescript/module/{platform → connections}/PowerPlatformConnectionApi.d.ts +52 -43
- package/lib/typescript/module/connections/connectionMetadata.d.ts +66 -0
- package/lib/typescript/module/connections/connectionState.d.ts +16 -0
- package/lib/typescript/module/connections/disambiguate.d.ts +4 -2
- package/lib/typescript/module/{platform → connections}/resolveConnections.d.ts +19 -19
- package/lib/typescript/module/connections/types.d.ts +129 -1
- package/lib/typescript/module/{platform → connections}/useConnectionRefs.d.ts +33 -24
- package/lib/typescript/module/connections/useConnectionSetup.d.ts +12 -8
- package/lib/typescript/module/controls/FilePicker.d.ts +7 -1
- package/lib/typescript/module/controls/ImagePicker.d.ts +9 -1
- package/lib/typescript/module/index.d.ts +17 -10
- package/lib/typescript/module/logger.d.ts +4 -4
- package/lib/typescript/module/offline/createOfflineHostActivationCallbacks.d.ts +12 -0
- package/lib/typescript/module/offline/useOfflineIntegrationWithStartupGate.d.ts +15 -0
- package/lib/typescript/module/offline/useOfflineIntegrationWithStartupGate.web.d.ts +6 -0
- package/lib/typescript/module/platform/DataverseDataSources.d.ts +27 -0
- package/lib/typescript/module/platform/DataverseGlobalDiscoveryApi.d.ts +3 -0
- package/lib/typescript/module/platform/GlobalDiscovery.d.ts +4 -0
- package/lib/typescript/module/platform/PowerPlatformEnvironmentApi.d.ts +3 -74
- package/lib/typescript/module/provider/PowerAppsHostProvider.d.ts +29 -18
- package/lib/typescript/module/provider/PowerAppsProvider.d.ts +30 -8
- package/lib/typescript/module/provider/SessionDetailsGesture.d.ts +13 -0
- package/lib/typescript/module/telemetry/OfflineSdkTelemetry.d.ts +15 -0
- package/lib/typescript/module/telemetry/OfflineSdkTelemetry.web.d.ts +2 -0
- package/lib/typescript/module/telemetry/index.d.ts +13 -1
- package/package.json +11 -6
- package/scripts/metro-logger.js +213 -0
- package/scripts/upgrade/runtime.js +13 -7
- package/scripts/upgrade-authoring.js +5 -6
- package/lib/commonjs/platform/PowerPlatformConnectionApi.js +0 -606
- package/lib/commonjs/platform/resolveConnections.js +0 -264
- package/lib/module/platform/PowerPlatformConnectionApi.js +0 -595
- package/lib/module/platform/resolveConnections.js +0 -259
|
@@ -10,9 +10,11 @@ var WebBrowser = _interopRequireWildcard(require("expo-web-browser"));
|
|
|
10
10
|
var Linking = _interopRequireWildcard(require("expo-linking"));
|
|
11
11
|
var _reactNativeSafeAreaContext = require("react-native-safe-area-context");
|
|
12
12
|
var _useConnectionSetup = require("../connections/useConnectionSetup.js");
|
|
13
|
-
var _PowerPlatformConnectionApi = require("
|
|
13
|
+
var _PowerPlatformConnectionApi = require("./PowerPlatformConnectionApi.js");
|
|
14
14
|
var _PowerPlatformEnvironmentApi = require("../platform/PowerPlatformEnvironmentApi.js");
|
|
15
15
|
var _types = require("../connections/types.js");
|
|
16
|
+
var _types2 = require("./types.js");
|
|
17
|
+
var _disambiguate = require("./disambiguate.js");
|
|
16
18
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
17
19
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
18
20
|
/*!
|
|
@@ -66,12 +68,17 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
66
68
|
// ---------------------------------------------------------------------------
|
|
67
69
|
|
|
68
70
|
function ConnectorSetupStep({
|
|
71
|
+
localRefId,
|
|
69
72
|
connectorId,
|
|
70
73
|
apiId,
|
|
71
74
|
environmentId,
|
|
72
75
|
region,
|
|
73
76
|
acquireToken,
|
|
77
|
+
onSignOut,
|
|
74
78
|
isvClientId,
|
|
79
|
+
gatewayObjectIdHint,
|
|
80
|
+
parameterSetName,
|
|
81
|
+
sharedConnectionId,
|
|
75
82
|
onConnected,
|
|
76
83
|
renderOAuthWebView
|
|
77
84
|
}) {
|
|
@@ -85,18 +92,24 @@ function ConnectorSetupStep({
|
|
|
85
92
|
// useConnectionSetup) so the hook stays focused on connection flow only.
|
|
86
93
|
const [isLoadingMetadata, setIsLoadingMetadata] = (0, _react.useState)(true);
|
|
87
94
|
const [metadata, setMetadata] = (0, _react.useState)(null);
|
|
95
|
+
const [metadataError, setMetadataError] = (0, _react.useState)(null);
|
|
96
|
+
const [metadataReload, setMetadataReload] = (0, _react.useState)(0);
|
|
88
97
|
const [showOAuthModal, setShowOAuthModal] = (0, _react.useState)(false);
|
|
89
|
-
|
|
90
|
-
const [
|
|
98
|
+
const [existingConnected, setExistingConnected] = (0, _react.useState)([]);
|
|
99
|
+
const [existingRepairable, setExistingRepairable] = (0, _react.useState)([]);
|
|
100
|
+
const [selectedParameterSet, setSelectedParameterSet] = (0, _react.useState)(parameterSetName ?? '');
|
|
101
|
+
const [parameterValues, setParameterValues] = (0, _react.useState)({});
|
|
102
|
+
const [gatewayObjectId, setGatewayObjectId] = (0, _react.useState)(gatewayObjectIdHint ?? '');
|
|
91
103
|
const setup = (0, _useConnectionSetup.useConnectionSetup)({
|
|
92
104
|
connectorId,
|
|
93
105
|
environmentId,
|
|
94
106
|
region,
|
|
95
107
|
acquireToken,
|
|
96
108
|
isvClientId,
|
|
109
|
+
sharedConnectionId,
|
|
97
110
|
onConnected: instance => {
|
|
98
111
|
setShowOAuthModal(false);
|
|
99
|
-
onConnected(
|
|
112
|
+
onConnected(localRefId, instance);
|
|
100
113
|
},
|
|
101
114
|
oauthRedirectUrl,
|
|
102
115
|
polling: {
|
|
@@ -108,46 +121,94 @@ function ConnectorSetupStep({
|
|
|
108
121
|
// Fetch connector metadata and check for existing non-connected connections.
|
|
109
122
|
(0, _react.useEffect)(() => {
|
|
110
123
|
let cancelled = false;
|
|
124
|
+
setIsLoadingMetadata(true);
|
|
125
|
+
setMetadata(null);
|
|
126
|
+
setMetadataError(null);
|
|
127
|
+
setExistingConnected([]);
|
|
128
|
+
setExistingRepairable([]);
|
|
129
|
+
setSelectedParameterSet(parameterSetName ?? '');
|
|
130
|
+
setParameterValues({});
|
|
131
|
+
setGatewayObjectId(gatewayObjectIdHint ?? '');
|
|
111
132
|
async function load() {
|
|
112
133
|
const token = await acquireToken([(0, _PowerPlatformEnvironmentApi.powerPlatformScope)(region)]);
|
|
113
134
|
if (!token || cancelled) {
|
|
114
135
|
if (!cancelled) {
|
|
115
|
-
|
|
116
|
-
displayName: connectorId,
|
|
117
|
-
iconUri: '',
|
|
118
|
-
description: ''
|
|
119
|
-
});
|
|
136
|
+
setMetadataError('Unable to acquire a token to load connector setup.');
|
|
120
137
|
setIsLoadingMetadata(false);
|
|
121
138
|
}
|
|
122
139
|
return;
|
|
123
140
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
141
|
+
try {
|
|
142
|
+
const [meta, instances] = await Promise.all([(0, _PowerPlatformConnectionApi.fetchConnectorMetadata)(connectorId, environmentId, region, token), (0, _PowerPlatformConnectionApi.fetchConnectionsForConnector)(connectorId, environmentId, region, token)]);
|
|
143
|
+
if (cancelled) return;
|
|
144
|
+
if (!meta) throw new Error('Connector metadata is unavailable.');
|
|
145
|
+
const resolvedMetadata = meta;
|
|
146
|
+
setMetadata(resolvedMetadata);
|
|
147
|
+
const preferredSet = resolvedMetadata.parameterSets.find(set => set.name === parameterSetName) ?? resolvedMetadata.parameterSets[0];
|
|
148
|
+
setSelectedParameterSet(preferredSet?.name ?? '');
|
|
149
|
+
setParameterValues(Object.fromEntries((preferredSet?.parameters ?? []).filter(parameter => parameter.defaultValue !== undefined).map(parameter => [parameter.name, parameter.defaultValue])));
|
|
131
150
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
151
|
+
// If there's already a Connected instance the disambiguation didn't pick
|
|
152
|
+
// (e.g., appeared after the initial load), use it directly.
|
|
153
|
+
const sharedId = sharedConnectionId ? (0, _disambiguate.extractConnectionId)(sharedConnectionId) : undefined;
|
|
154
|
+
const eligibleInstances = sharedId ? instances.filter(instance => (0, _disambiguate.connectionIdsEqual)(instance.id, sharedId)) : instances;
|
|
155
|
+
setExistingConnected(eligibleInstances.filter(instance => instance.status === _types.ConnectionStatus.Connected));
|
|
156
|
+
setExistingRepairable(sharedId ? [] : eligibleInstances.filter(instance => instance.status !== _types.ConnectionStatus.Connected));
|
|
157
|
+
} catch (error) {
|
|
158
|
+
if (!cancelled) {
|
|
159
|
+
setMetadataError(error instanceof Error ? error.message : 'Unable to load connector setup.');
|
|
160
|
+
}
|
|
161
|
+
} finally {
|
|
162
|
+
if (!cancelled) setIsLoadingMetadata(false);
|
|
163
|
+
}
|
|
137
164
|
}
|
|
138
165
|
void load();
|
|
139
166
|
return () => {
|
|
140
167
|
cancelled = true;
|
|
141
168
|
};
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
169
|
+
}, [acquireToken, connectorId, environmentId, gatewayObjectIdHint, parameterSetName, region, sharedConnectionId, metadataReload]);
|
|
170
|
+
const handleUseExisting = (0, _react.useCallback)(async instance => {
|
|
171
|
+
await setup.useExisting(instance);
|
|
172
|
+
}, [setup]);
|
|
173
|
+
const activeParameterSet = metadata?.parameterSets.find(set => (set.name ?? '') === selectedParameterSet) ?? metadata?.parameterSets[0];
|
|
174
|
+
const selectedCapability = gatewayObjectIdHint ? 'gateway' : 'cloud';
|
|
175
|
+
const visibleParameters = activeParameterSet?.parameters.filter(parameter => !parameter.hidden && (!parameter.capabilities?.length || parameter.capabilities.some(capability => capability.toLowerCase() === selectedCapability))) ?? [];
|
|
176
|
+
const requiredParametersComplete = visibleParameters.every(parameter => {
|
|
177
|
+
if (!parameter.required) return true;
|
|
178
|
+
const value = parameterValues[parameter.name];
|
|
179
|
+
if (typeof value === 'object' && value && 'pfx' in value) {
|
|
180
|
+
return Boolean(value.pfx && value.password);
|
|
147
181
|
}
|
|
148
|
-
|
|
182
|
+
return value !== undefined && value !== '';
|
|
183
|
+
});
|
|
184
|
+
const buildInput = (0, _react.useCallback)(() => ({
|
|
185
|
+
parameterSetName: activeParameterSet?.name,
|
|
186
|
+
gatewayObjectId: gatewayObjectId || undefined,
|
|
187
|
+
gatewayParameterName: activeParameterSet?.gatewayParameterName,
|
|
188
|
+
parameters: parameterValues
|
|
189
|
+
}), [activeParameterSet?.name, gatewayObjectId, parameterValues]);
|
|
149
190
|
const handleConnect = (0, _react.useCallback)(async () => {
|
|
150
|
-
await setup.startCreate();
|
|
191
|
+
await setup.startCreate(buildInput());
|
|
192
|
+
}, [setup, buildInput]);
|
|
193
|
+
const handleRepair = (0, _react.useCallback)(async instance => {
|
|
194
|
+
const instanceParameterSet = metadata?.parameterSets.find(parameterSet => parameterSet.name === instance.parameterSetName) ?? activeParameterSet;
|
|
195
|
+
const requiresApiConsent = instanceParameterSet?.requiresApiConsent ?? metadata?.hasOAuthParameter ?? false;
|
|
196
|
+
const input = buildInput();
|
|
197
|
+
const hasEditableInput = Object.keys(input.parameters).length > 0 || Boolean(input.gatewayObjectId);
|
|
198
|
+
await setup.startRepair(instance, !requiresApiConsent || hasEditableInput ? input : undefined, requiresApiConsent);
|
|
199
|
+
}, [setup, metadata, activeParameterSet, buildInput]);
|
|
200
|
+
const completeOAuthRedirect = (0, _react.useCallback)(async redirectUrl => {
|
|
201
|
+
let code;
|
|
202
|
+
if (redirectUrl) {
|
|
203
|
+
try {
|
|
204
|
+
const raw = new URL(redirectUrl).searchParams.get('code') ?? '';
|
|
205
|
+
if (/^[0-9a-f]{32}$/i.test(raw)) code = raw;
|
|
206
|
+
} catch {
|
|
207
|
+
setup.reset();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
await setup.completeConsent(code);
|
|
151
212
|
}, [setup]);
|
|
152
213
|
|
|
153
214
|
// Open OAuth UI when consentUrl becomes available (set by startCreate in the hook).
|
|
@@ -161,29 +222,24 @@ function ConnectorSetupStep({
|
|
|
161
222
|
// custom-scheme redirect (e.g. expo-app://oauth-callback).
|
|
162
223
|
void (async () => {
|
|
163
224
|
const result = await WebBrowser.openAuthSessionAsync(setup.consentUrl, oauthRedirectUrl);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const raw = params.get('code') ?? '';
|
|
169
|
-
// CommitTokenCode is a 32-char lowercase hex string.
|
|
170
|
-
if (/^[0-9a-f]{32}$/i.test(raw)) {
|
|
171
|
-
code = raw;
|
|
172
|
-
}
|
|
225
|
+
if (result.type === 'success') {
|
|
226
|
+
await completeOAuthRedirect(result.url);
|
|
227
|
+
} else {
|
|
228
|
+
setup.reset();
|
|
173
229
|
}
|
|
174
|
-
await setup.completeConsent(code);
|
|
175
230
|
})();
|
|
176
231
|
}
|
|
177
232
|
}
|
|
178
|
-
}, [setup.phase, setup.consentUrl, renderOAuthWebView, oauthRedirectUrl]);
|
|
179
|
-
const handleWebViewDismiss = (0, _react.useCallback)(async
|
|
233
|
+
}, [setup.phase, setup.consentUrl, renderOAuthWebView, oauthRedirectUrl, completeOAuthRedirect]);
|
|
234
|
+
const handleWebViewDismiss = (0, _react.useCallback)(async redirectUrl => {
|
|
180
235
|
setShowOAuthModal(false);
|
|
181
|
-
await setup.checkNow();
|
|
182
|
-
}, [setup]);
|
|
236
|
+
if (redirectUrl) await completeOAuthRedirect(redirectUrl);else await setup.checkNow();
|
|
237
|
+
}, [completeOAuthRedirect, setup]);
|
|
183
238
|
const displayName = metadata?.displayName ?? connectorId;
|
|
184
239
|
const description = metadata?.description ?? '';
|
|
185
240
|
if (isLoadingMetadata) {
|
|
186
241
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
242
|
+
testID: `powerApps.connectionSetup.connector.${connectorId}`,
|
|
187
243
|
style: styles.connectorCard,
|
|
188
244
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
189
245
|
style: styles.metadataLoading,
|
|
@@ -193,7 +249,36 @@ function ConnectorSetupStep({
|
|
|
193
249
|
})
|
|
194
250
|
});
|
|
195
251
|
}
|
|
252
|
+
if (metadataError || !metadata) {
|
|
253
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
254
|
+
style: styles.connectorCard,
|
|
255
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
256
|
+
style: styles.connectorName,
|
|
257
|
+
children: connectorId
|
|
258
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
259
|
+
style: styles.errorText,
|
|
260
|
+
children: metadataError ?? 'Unable to load connector setup.'
|
|
261
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
262
|
+
style: styles.button,
|
|
263
|
+
onPress: () => setMetadataReload(value => value + 1),
|
|
264
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
265
|
+
style: styles.buttonText,
|
|
266
|
+
children: "Retry"
|
|
267
|
+
})
|
|
268
|
+
}), onSignOut ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
269
|
+
style: [styles.button, styles.buttonSecondary],
|
|
270
|
+
onPress: () => {
|
|
271
|
+
void onSignOut();
|
|
272
|
+
},
|
|
273
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
274
|
+
style: styles.buttonTextSecondary,
|
|
275
|
+
children: "Sign out"
|
|
276
|
+
})
|
|
277
|
+
}) : null]
|
|
278
|
+
});
|
|
279
|
+
}
|
|
196
280
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
281
|
+
testID: `powerApps.connectionSetup.connector.${connectorId}`,
|
|
197
282
|
style: styles.connectorCard,
|
|
198
283
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
199
284
|
style: styles.connectorName,
|
|
@@ -201,33 +286,140 @@ function ConnectorSetupStep({
|
|
|
201
286
|
}), description ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
202
287
|
style: styles.connectorDescription,
|
|
203
288
|
children: description
|
|
204
|
-
}) : null, setup.phase === 'idle' && /*#__PURE__*/(0, _jsxRuntime.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
289
|
+
}) : null, setup.phase === 'idle' && !sharedConnectionId && (metadata?.parameterSets.length ?? 0) > 1 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
290
|
+
style: styles.fieldGroup,
|
|
291
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
292
|
+
style: styles.fieldLabel,
|
|
293
|
+
children: "Authentication type"
|
|
294
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
295
|
+
style: styles.choiceRow,
|
|
296
|
+
children: metadata?.parameterSets.map(parameterSet => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
297
|
+
style: [styles.choiceButton, selectedParameterSet === (parameterSet.name ?? '') && styles.choiceButtonSelected],
|
|
298
|
+
onPress: () => {
|
|
299
|
+
setSelectedParameterSet(parameterSet.name ?? '');
|
|
300
|
+
setParameterValues(Object.fromEntries(parameterSet.parameters.filter(parameter => parameter.defaultValue !== undefined).map(parameter => [parameter.name, parameter.defaultValue])));
|
|
301
|
+
},
|
|
210
302
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
211
|
-
style: styles.
|
|
212
|
-
children:
|
|
303
|
+
style: selectedParameterSet === (parameterSet.name ?? '') ? styles.choiceTextSelected : styles.choiceText,
|
|
304
|
+
children: parameterSet.displayName
|
|
213
305
|
})
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
|
|
306
|
+
}, parameterSet.name ?? 'default'))
|
|
307
|
+
})]
|
|
308
|
+
}) : null, setup.phase === 'idle' && !sharedConnectionId && visibleParameters.map(parameter => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
309
|
+
style: styles.fieldGroup,
|
|
310
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
|
|
311
|
+
style: styles.fieldLabel,
|
|
312
|
+
children: [parameter.displayName, parameter.required ? ' *' : '']
|
|
313
|
+
}), parameter.allowedValues?.length ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
314
|
+
style: styles.choiceRow,
|
|
315
|
+
children: parameter.allowedValues.map(option => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
316
|
+
style: [styles.choiceButton, parameterValues[parameter.name] === option.value && styles.choiceButtonSelected],
|
|
317
|
+
onPress: () => setParameterValues(values => ({
|
|
318
|
+
...values,
|
|
319
|
+
[parameter.name]: option.value
|
|
320
|
+
})),
|
|
217
321
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
218
|
-
style: styles.
|
|
219
|
-
children:
|
|
322
|
+
style: parameterValues[parameter.name] === option.value ? styles.choiceTextSelected : styles.choiceText,
|
|
323
|
+
children: option.displayName
|
|
220
324
|
})
|
|
325
|
+
}, option.value))
|
|
326
|
+
}) : parameter.type === 'boolean' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Switch, {
|
|
327
|
+
value: Boolean(parameterValues[parameter.name]),
|
|
328
|
+
onValueChange: value => setParameterValues(values => ({
|
|
329
|
+
...values,
|
|
330
|
+
[parameter.name]: value
|
|
331
|
+
}))
|
|
332
|
+
}) : parameter.type === 'clientcertificate' ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
333
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
|
|
334
|
+
style: styles.textInput,
|
|
335
|
+
placeholder: "PFX certificate (base64)",
|
|
336
|
+
autoCapitalize: "none",
|
|
337
|
+
value: parameterValues[parameter.name]?.pfx ?? '',
|
|
338
|
+
onChangeText: pfx => setParameterValues(values => ({
|
|
339
|
+
...values,
|
|
340
|
+
[parameter.name]: {
|
|
341
|
+
pfx,
|
|
342
|
+
password: values[parameter.name]?.password ?? ''
|
|
343
|
+
}
|
|
344
|
+
}))
|
|
345
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
|
|
346
|
+
style: styles.textInput,
|
|
347
|
+
placeholder: "Certificate password",
|
|
348
|
+
secureTextEntry: true,
|
|
349
|
+
value: parameterValues[parameter.name]?.password ?? '',
|
|
350
|
+
onChangeText: password => setParameterValues(values => ({
|
|
351
|
+
...values,
|
|
352
|
+
[parameter.name]: {
|
|
353
|
+
pfx: values[parameter.name]?.pfx ?? '',
|
|
354
|
+
password
|
|
355
|
+
}
|
|
356
|
+
}))
|
|
221
357
|
})]
|
|
222
|
-
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.
|
|
223
|
-
style: styles.
|
|
358
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
|
|
359
|
+
style: styles.textInput,
|
|
360
|
+
secureTextEntry: parameter.type === 'securestring',
|
|
361
|
+
keyboardType: parameter.type === 'integer' ? 'number-pad' : 'default',
|
|
362
|
+
autoCapitalize: "none",
|
|
363
|
+
value: String(parameterValues[parameter.name] ?? ''),
|
|
364
|
+
onChangeText: value => setParameterValues(values => ({
|
|
365
|
+
...values,
|
|
366
|
+
[parameter.name]: parameter.type === 'integer' && value !== '' ? /^-?\d+$/.test(value) ? parseInt(value, 10) : '' : value
|
|
367
|
+
}))
|
|
368
|
+
})]
|
|
369
|
+
}, parameter.name)), setup.phase === 'idle' && !sharedConnectionId && gatewayObjectIdHint ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
370
|
+
style: styles.fieldGroup,
|
|
371
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
372
|
+
style: styles.fieldLabel,
|
|
373
|
+
children: "Gateway"
|
|
374
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
|
|
375
|
+
style: styles.textInput,
|
|
376
|
+
value: gatewayObjectId,
|
|
377
|
+
onChangeText: setGatewayObjectId,
|
|
378
|
+
autoCapitalize: "none"
|
|
379
|
+
})]
|
|
380
|
+
}) : null, setup.phase === 'idle' && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
381
|
+
children: [existingConnected.map((instance, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
382
|
+
testID: index === 0 ? `powerApps.connectionSetup.connector.${connectorId}.useExistingButton` : `powerApps.connectionSetup.connector.${connectorId}.useExistingButton.${instance.id}`,
|
|
383
|
+
style: [styles.button, styles.buttonSecondary],
|
|
384
|
+
onPress: () => handleUseExisting(instance),
|
|
385
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
|
|
386
|
+
style: styles.buttonTextSecondary,
|
|
387
|
+
children: ["Use ", instance.displayName || instance.id]
|
|
388
|
+
})
|
|
389
|
+
}, instance.id)), existingRepairable.map(instance => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
390
|
+
style: [styles.button, styles.buttonSecondary],
|
|
391
|
+
disabled: !requiredParametersComplete,
|
|
392
|
+
onPress: () => handleRepair(instance),
|
|
393
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
|
|
394
|
+
style: styles.buttonTextSecondary,
|
|
395
|
+
children: ["Fix ", instance.displayName || instance.id]
|
|
396
|
+
})
|
|
397
|
+
}, instance.id)), sharedConnectionId && existingConnected.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
398
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
399
|
+
style: styles.errorText,
|
|
400
|
+
children: "You do not have access to the shared connection required by this app."
|
|
401
|
+
}), onSignOut ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
402
|
+
style: [styles.button, styles.buttonSecondary],
|
|
403
|
+
onPress: () => {
|
|
404
|
+
void onSignOut();
|
|
405
|
+
},
|
|
406
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
407
|
+
style: styles.buttonTextSecondary,
|
|
408
|
+
children: "Sign out"
|
|
409
|
+
})
|
|
410
|
+
}) : null]
|
|
411
|
+
}) : !sharedConnectionId ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
412
|
+
testID: `powerApps.connectionSetup.connector.${connectorId}.${existingConnected.length ? 'createButton' : 'connectButton'}`,
|
|
413
|
+
style: [styles.button, !requiredParametersComplete && styles.buttonDisabled],
|
|
414
|
+
disabled: !requiredParametersComplete,
|
|
224
415
|
onPress: handleConnect,
|
|
225
416
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
226
417
|
style: styles.buttonText,
|
|
227
|
-
children:
|
|
418
|
+
children: existingConnected.length ? 'Create new' : metadata?.hasOAuthParameter ? 'Sign in' : 'Connect'
|
|
228
419
|
})
|
|
229
|
-
})
|
|
420
|
+
}) : null]
|
|
230
421
|
}), (setup.phase === 'creating' || setup.phase === 'confirming') && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
422
|
+
testID: `powerApps.connectionSetup.connector.${connectorId}.${setup.phase}`,
|
|
231
423
|
style: styles.statusRow,
|
|
232
424
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
|
|
233
425
|
size: "small",
|
|
@@ -237,6 +429,7 @@ function ConnectorSetupStep({
|
|
|
237
429
|
children: setup.phase === 'creating' ? 'Creating connection…' : 'Confirming…'
|
|
238
430
|
})]
|
|
239
431
|
}), setup.phase === 'oauth' && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
432
|
+
testID: `powerApps.connectionSetup.connector.${connectorId}.oauth`,
|
|
240
433
|
style: styles.statusRow,
|
|
241
434
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
|
|
242
435
|
size: "small",
|
|
@@ -245,30 +438,43 @@ function ConnectorSetupStep({
|
|
|
245
438
|
style: styles.statusText,
|
|
246
439
|
children: "Waiting for sign-in\u2026"
|
|
247
440
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
248
|
-
|
|
441
|
+
testID: `powerApps.connectionSetup.connector.${connectorId}.doneButton`,
|
|
442
|
+
onPress: () => handleWebViewDismiss(),
|
|
249
443
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
250
444
|
style: styles.linkText,
|
|
251
445
|
children: "Done"
|
|
252
446
|
})
|
|
253
447
|
})]
|
|
254
448
|
}), setup.phase === 'connected' && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
449
|
+
testID: `powerApps.connectionSetup.connector.${connectorId}.connected`,
|
|
255
450
|
style: styles.statusRow,
|
|
256
451
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
257
452
|
style: styles.successText,
|
|
258
453
|
children: "\u2713 Connected"
|
|
259
454
|
})
|
|
260
455
|
}), setup.phase === 'error' && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
456
|
+
testID: `powerApps.connectionSetup.connector.${connectorId}.error`,
|
|
261
457
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
262
458
|
style: styles.errorText,
|
|
263
459
|
children: setup.errorMessage ?? 'Connection failed'
|
|
264
460
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
461
|
+
testID: `powerApps.connectionSetup.connector.${connectorId}.retryButton`,
|
|
265
462
|
style: [styles.button, styles.buttonDanger],
|
|
266
463
|
onPress: setup.reset,
|
|
267
464
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
268
465
|
style: styles.buttonText,
|
|
269
466
|
children: "Retry"
|
|
270
467
|
})
|
|
271
|
-
})
|
|
468
|
+
}), onSignOut ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
469
|
+
style: [styles.button, styles.buttonSecondary],
|
|
470
|
+
onPress: () => {
|
|
471
|
+
void onSignOut();
|
|
472
|
+
},
|
|
473
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
474
|
+
style: styles.buttonTextSecondary,
|
|
475
|
+
children: "Sign out"
|
|
476
|
+
})
|
|
477
|
+
}) : null]
|
|
272
478
|
}), showOAuthModal && setup.consentUrl && renderOAuthWebView ? renderOAuthWebView(setup.consentUrl, handleWebViewDismiss) : null]
|
|
273
479
|
});
|
|
274
480
|
}
|
|
@@ -291,15 +497,31 @@ function ConnectionSetupScreen({
|
|
|
291
497
|
environmentId,
|
|
292
498
|
region = 'prod',
|
|
293
499
|
acquireToken,
|
|
500
|
+
onSignOut,
|
|
294
501
|
isvClientId,
|
|
295
502
|
onAllConnected,
|
|
296
503
|
onSkip,
|
|
504
|
+
consentDetails = [],
|
|
505
|
+
resourceConsents = [],
|
|
506
|
+
onConsentDeclined,
|
|
507
|
+
showReview = true,
|
|
297
508
|
renderOAuthWebView,
|
|
298
509
|
title = 'Set up connections'
|
|
299
510
|
}) {
|
|
300
511
|
// resolvedIds accumulates as each connector is connected.
|
|
301
512
|
const [resolvedIds, setResolvedIds] = (0, _react.useState)({});
|
|
513
|
+
const [isSubmittingConsent, setIsSubmittingConsent] = (0, _react.useState)(false);
|
|
514
|
+
const [consentError, setConsentError] = (0, _react.useState)(null);
|
|
515
|
+
const [consentDeclined, setConsentDeclined] = (0, _react.useState)(false);
|
|
516
|
+
const autoCompletionStartedRef = _react.default.useRef(false);
|
|
302
517
|
const insets = (0, _reactNativeSafeAreaContext.useSafeAreaInsets)();
|
|
518
|
+
const missingReferencesKey = _react.default.useMemo(() => missingConnectors.map(connector => `${connector.localRefId}:${(0, _types2.normalizeApiId)(connector.apiId)}`).sort().join('|'), [missingConnectors]);
|
|
519
|
+
(0, _react.useEffect)(() => {
|
|
520
|
+
setResolvedIds({});
|
|
521
|
+
setConsentError(null);
|
|
522
|
+
setConsentDeclined(false);
|
|
523
|
+
autoCompletionStartedRef.current = false;
|
|
524
|
+
}, [missingReferencesKey]);
|
|
303
525
|
|
|
304
526
|
// Build connectorId (short name) for each missing entry from the schemaMap.
|
|
305
527
|
// schemaMap keys are data source names; we need to find the ConnectorSchema
|
|
@@ -315,26 +537,38 @@ function ConnectionSetupScreen({
|
|
|
315
537
|
}
|
|
316
538
|
return map;
|
|
317
539
|
}, [schemaMap]);
|
|
318
|
-
const handleConnected = (0, _react.useCallback)((
|
|
540
|
+
const handleConnected = (0, _react.useCallback)((localRefId, instance) => {
|
|
319
541
|
setResolvedIds(prev => ({
|
|
320
542
|
...prev,
|
|
321
|
-
[
|
|
543
|
+
[localRefId]: instance.id
|
|
322
544
|
}));
|
|
323
545
|
}, []);
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
546
|
+
const allResolved = missingConnectors.every(mc => resolvedIds[mc.localRefId] !== undefined);
|
|
547
|
+
const handleAllow = (0, _react.useCallback)(async () => {
|
|
548
|
+
if (!allResolved || isSubmittingConsent) return;
|
|
549
|
+
setIsSubmittingConsent(true);
|
|
550
|
+
setConsentError(null);
|
|
551
|
+
try {
|
|
552
|
+
await onAllConnected(resolvedIds);
|
|
553
|
+
} catch (error) {
|
|
554
|
+
setConsentError(error instanceof Error ? error.message : 'Unable to save connection consent.');
|
|
555
|
+
} finally {
|
|
556
|
+
setIsSubmittingConsent(false);
|
|
333
557
|
}
|
|
334
|
-
}, [
|
|
558
|
+
}, [allResolved, isSubmittingConsent, onAllConnected, resolvedIds]);
|
|
559
|
+
const handleDecline = (0, _react.useCallback)(async () => {
|
|
560
|
+
setConsentDeclined(true);
|
|
561
|
+
await onConsentDeclined?.();
|
|
562
|
+
}, [onConsentDeclined]);
|
|
563
|
+
(0, _react.useEffect)(() => {
|
|
564
|
+
if (showReview || !allResolved || autoCompletionStartedRef.current) return;
|
|
565
|
+
autoCompletionStartedRef.current = true;
|
|
566
|
+
void handleAllow();
|
|
567
|
+
}, [allResolved, handleAllow, showReview]);
|
|
335
568
|
const resolvedCount = Object.keys(resolvedIds).length;
|
|
336
569
|
const total = missingConnectors.length;
|
|
337
570
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
571
|
+
testID: "powerApps.connectionSetup.root",
|
|
338
572
|
style: [styles.container, {
|
|
339
573
|
paddingTop: insets.top,
|
|
340
574
|
paddingBottom: insets.bottom
|
|
@@ -342,41 +576,121 @@ function ConnectionSetupScreen({
|
|
|
342
576
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
|
|
343
577
|
contentContainerStyle: styles.scrollContent,
|
|
344
578
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
579
|
+
testID: "powerApps.connectionSetup.title",
|
|
345
580
|
style: styles.title,
|
|
346
581
|
children: title
|
|
347
|
-
}), /*#__PURE__*/(0, _jsxRuntime.
|
|
582
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
348
583
|
style: styles.subtitle,
|
|
349
|
-
children:
|
|
584
|
+
children: total > 0 ? `This app needs ${total === 1 ? 'a connection' : `${total} connections`} to work. Sign in to each service below${showReview ? ', then review access' : ''}.` : 'Review the connections and resources this app will use.'
|
|
350
585
|
}), resolvedCount > 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
|
|
586
|
+
testID: "powerApps.connectionSetup.progress",
|
|
351
587
|
style: styles.progressText,
|
|
352
588
|
children: [resolvedCount, " of ", total, " connected"]
|
|
353
589
|
}), missingConnectors.map(mc => {
|
|
354
|
-
const connectorId = connectorIdByApiId[mc.apiId];
|
|
590
|
+
const connectorId = mc.connectorId ?? connectorIdByApiId[(0, _types2.normalizeApiId)(mc.apiId)];
|
|
355
591
|
if (!connectorId) {
|
|
356
592
|
// Unknown connector — skip rendering.
|
|
357
593
|
return null;
|
|
358
594
|
}
|
|
359
|
-
const isResolved = resolvedIds[mc.
|
|
595
|
+
const isResolved = resolvedIds[mc.localRefId] !== undefined;
|
|
360
596
|
if (isResolved) {
|
|
361
597
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
598
|
+
testID: `powerApps.connectionSetup.connector.${connectorId}.resolved`,
|
|
362
599
|
style: [styles.connectorCard, styles.connectorCardResolved],
|
|
363
600
|
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
|
|
364
601
|
style: styles.successText,
|
|
365
602
|
children: ["\u2713 ", connectorId.replace('shared_', '')]
|
|
366
603
|
})
|
|
367
|
-
}, mc.
|
|
604
|
+
}, mc.localRefId);
|
|
368
605
|
}
|
|
369
606
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(ConnectorSetupStep, {
|
|
607
|
+
localRefId: mc.localRefId,
|
|
370
608
|
connectorId: connectorId,
|
|
371
609
|
apiId: mc.apiId,
|
|
372
610
|
environmentId: environmentId,
|
|
373
611
|
region: region,
|
|
374
612
|
acquireToken: acquireToken,
|
|
613
|
+
onSignOut: onSignOut,
|
|
375
614
|
isvClientId: isvClientId,
|
|
615
|
+
gatewayObjectIdHint: mc.gatewayObjectIdHint,
|
|
616
|
+
parameterSetName: mc.parameterSetName,
|
|
617
|
+
sharedConnectionId: mc.sharedConnectionId,
|
|
376
618
|
onConnected: handleConnected,
|
|
377
619
|
renderOAuthWebView: renderOAuthWebView
|
|
378
|
-
}, mc.
|
|
379
|
-
})
|
|
620
|
+
}, mc.localRefId);
|
|
621
|
+
}), showReview && allResolved && !consentDeclined ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
622
|
+
style: styles.consentSection,
|
|
623
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
624
|
+
style: styles.consentTitle,
|
|
625
|
+
children: "Review access"
|
|
626
|
+
}), consentDetails.map(detail => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
627
|
+
style: styles.consentItem,
|
|
628
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
629
|
+
style: styles.connectorName,
|
|
630
|
+
children: detail.displayName
|
|
631
|
+
}), detail.description ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
632
|
+
style: styles.connectorDescription,
|
|
633
|
+
children: detail.description
|
|
634
|
+
}) : null, detail.appActions.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
|
|
635
|
+
style: styles.consentActions,
|
|
636
|
+
children: ["Uses: ", detail.appActions.join(', ')]
|
|
637
|
+
}) : null]
|
|
638
|
+
}, detail.localRefId)), resourceConsents.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
639
|
+
style: styles.consentItem,
|
|
640
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
641
|
+
style: styles.connectorName,
|
|
642
|
+
children: "Protected resources"
|
|
643
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
644
|
+
style: styles.connectorDescription,
|
|
645
|
+
children: resourceConsents.map(resource => resource.displayName).join(', ')
|
|
646
|
+
})]
|
|
647
|
+
}) : null, consentError ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
648
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
649
|
+
style: styles.errorText,
|
|
650
|
+
children: consentError
|
|
651
|
+
}), onSignOut ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
652
|
+
style: [styles.button, styles.buttonSecondary],
|
|
653
|
+
onPress: () => {
|
|
654
|
+
void onSignOut();
|
|
655
|
+
},
|
|
656
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
657
|
+
style: styles.buttonTextSecondary,
|
|
658
|
+
children: "Sign out"
|
|
659
|
+
})
|
|
660
|
+
}) : null]
|
|
661
|
+
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
662
|
+
style: [styles.button, isSubmittingConsent && styles.buttonDisabled],
|
|
663
|
+
disabled: isSubmittingConsent,
|
|
664
|
+
onPress: handleAllow,
|
|
665
|
+
children: isSubmittingConsent ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
|
|
666
|
+
size: "small",
|
|
667
|
+
color: "#FFF"
|
|
668
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
669
|
+
style: styles.buttonText,
|
|
670
|
+
children: "Allow"
|
|
671
|
+
})
|
|
672
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
673
|
+
style: styles.skipButton,
|
|
674
|
+
onPress: handleDecline,
|
|
675
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
676
|
+
style: styles.skipText,
|
|
677
|
+
children: "Do not allow"
|
|
678
|
+
})
|
|
679
|
+
})]
|
|
680
|
+
}) : null, showReview && consentDeclined ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
681
|
+
style: styles.consentSection,
|
|
682
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
683
|
+
style: styles.consentTitle,
|
|
684
|
+
children: "Access was not granted"
|
|
685
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
686
|
+
style: styles.button,
|
|
687
|
+
onPress: () => setConsentDeclined(false),
|
|
688
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
689
|
+
style: styles.buttonText,
|
|
690
|
+
children: "Review again"
|
|
691
|
+
})
|
|
692
|
+
})]
|
|
693
|
+
}) : null]
|
|
380
694
|
}), onSkip ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
381
695
|
style: styles.footer,
|
|
382
696
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
@@ -472,6 +786,9 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
472
786
|
buttonDanger: {
|
|
473
787
|
backgroundColor: '#C4262E'
|
|
474
788
|
},
|
|
789
|
+
buttonDisabled: {
|
|
790
|
+
backgroundColor: '#A8A8A8'
|
|
791
|
+
},
|
|
475
792
|
buttonText: {
|
|
476
793
|
color: '#FFF',
|
|
477
794
|
fontWeight: '600',
|
|
@@ -527,5 +844,67 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
527
844
|
alignItems: 'center',
|
|
528
845
|
justifyContent: 'center',
|
|
529
846
|
minHeight: 48
|
|
847
|
+
},
|
|
848
|
+
fieldGroup: {
|
|
849
|
+
marginTop: 12,
|
|
850
|
+
gap: 6
|
|
851
|
+
},
|
|
852
|
+
fieldLabel: {
|
|
853
|
+
color: '#333',
|
|
854
|
+
fontSize: 13,
|
|
855
|
+
fontWeight: '600'
|
|
856
|
+
},
|
|
857
|
+
textInput: {
|
|
858
|
+
minHeight: 42,
|
|
859
|
+
borderWidth: 1,
|
|
860
|
+
borderColor: '#C8C8C8',
|
|
861
|
+
borderRadius: 6,
|
|
862
|
+
paddingHorizontal: 10,
|
|
863
|
+
color: '#1A1A1A',
|
|
864
|
+
backgroundColor: '#FFF'
|
|
865
|
+
},
|
|
866
|
+
choiceRow: {
|
|
867
|
+
flexDirection: 'row',
|
|
868
|
+
flexWrap: 'wrap',
|
|
869
|
+
gap: 8
|
|
870
|
+
},
|
|
871
|
+
choiceButton: {
|
|
872
|
+
borderWidth: 1,
|
|
873
|
+
borderColor: '#C8C8C8',
|
|
874
|
+
borderRadius: 6,
|
|
875
|
+
paddingHorizontal: 10,
|
|
876
|
+
paddingVertical: 8
|
|
877
|
+
},
|
|
878
|
+
choiceButtonSelected: {
|
|
879
|
+
borderColor: '#0078D4',
|
|
880
|
+
backgroundColor: '#EFF6FC'
|
|
881
|
+
},
|
|
882
|
+
choiceText: {
|
|
883
|
+
color: '#333',
|
|
884
|
+
fontSize: 13
|
|
885
|
+
},
|
|
886
|
+
choiceTextSelected: {
|
|
887
|
+
color: '#0078D4',
|
|
888
|
+
fontSize: 13,
|
|
889
|
+
fontWeight: '600'
|
|
890
|
+
},
|
|
891
|
+
consentSection: {
|
|
892
|
+
marginTop: 12,
|
|
893
|
+
paddingTop: 16,
|
|
894
|
+
borderTopWidth: _reactNative.StyleSheet.hairlineWidth,
|
|
895
|
+
borderTopColor: '#D8D8D8'
|
|
896
|
+
},
|
|
897
|
+
consentTitle: {
|
|
898
|
+
fontSize: 18,
|
|
899
|
+
fontWeight: '700',
|
|
900
|
+
color: '#1A1A1A',
|
|
901
|
+
marginBottom: 12
|
|
902
|
+
},
|
|
903
|
+
consentItem: {
|
|
904
|
+
marginBottom: 12
|
|
905
|
+
},
|
|
906
|
+
consentActions: {
|
|
907
|
+
fontSize: 13,
|
|
908
|
+
color: '#444'
|
|
530
909
|
}
|
|
531
910
|
});
|