@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
|
@@ -27,25 +27,28 @@
|
|
|
27
27
|
* </PowerAppsProvider>
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
-
import React, {
|
|
30
|
+
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
31
31
|
import { AppState, Platform, ScrollView, StyleSheet, Text } from 'react-native';
|
|
32
32
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
33
33
|
import { createTamagui, TamaguiProvider } from '@tamagui/core';
|
|
34
34
|
import { PortalProvider } from '@tamagui/portal';
|
|
35
35
|
import { defaultConfig as tamaguiDefaultConfig } from '@tamagui/config/v5';
|
|
36
36
|
import { ToastProvider, ToastViewport } from '@tamagui/toast';
|
|
37
|
-
import {
|
|
37
|
+
import { QueryClientProvider, focusManager, onlineManager } from '@tanstack/react-query';
|
|
38
38
|
import NetInfo from '@react-native-community/netinfo';
|
|
39
39
|
import { AuthProvider, useAuth } from '../auth/AuthContext';
|
|
40
40
|
import { PowerAppsHostProvider } from './PowerAppsHostProvider';
|
|
41
|
-
import {
|
|
41
|
+
import { powerPlatformScope } from "../platform/PowerPlatformEnvironmentApi.js";
|
|
42
42
|
import { ThemeProvider } from "../theme/ThemeProvider.js";
|
|
43
43
|
import { ErrorBoundary } from "../ErrorBoundary.js";
|
|
44
44
|
import { isDevPlayer } from "../utils/isDevPlayer.js";
|
|
45
45
|
import Constants from 'expo-constants';
|
|
46
|
-
import { configureAppLogger, getAppLogger, Logger, OneDsTelemetryProvider, resolveDeviceId, resolveTelemetryLocation, regionToClusterCategory, suppressKnownWarnings } from '@microsoft/power-apps-native-common';
|
|
46
|
+
import { ApplicationInsightsTelemetryProvider, configureAppLogger, configureCustomerTelemetryLogger, getAppLogger, Logger, OneDsTelemetryProvider, resolveDeviceId, resolveTelemetryLocation, regionToClusterCategory, suppressKnownWarnings } from '@microsoft/power-apps-native-common';
|
|
47
47
|
suppressKnownWarnings();
|
|
48
|
-
import {
|
|
48
|
+
import { createHostQueryClient } from "../offline/createOfflineHostActivationCallbacks.js";
|
|
49
|
+
import { useOfflineIntegrationWithStartupGate } from '../offline/useOfflineIntegrationWithStartupGate';
|
|
50
|
+
import { SessionDetailsGesture } from "./SessionDetailsGesture.js";
|
|
51
|
+
import { configureOfflineSdkTelemetry } from '../telemetry/OfflineSdkTelemetry';
|
|
49
52
|
|
|
50
53
|
// ---- React-Query wiring (runs once at module load) ----
|
|
51
54
|
|
|
@@ -56,7 +59,6 @@ onlineManager.setEventListener(setOnline => {
|
|
|
56
59
|
if (Platform.OS === 'web' && typeof window !== 'undefined') {
|
|
57
60
|
const updateOnlineState = () => setOnline(window.navigator.onLine);
|
|
58
61
|
window.addEventListener('online', updateOnlineState);
|
|
59
|
-
window.addEventListener('offline', updateOnlineState);
|
|
60
62
|
updateOnlineState();
|
|
61
63
|
return () => {
|
|
62
64
|
window.removeEventListener('online', updateOnlineState);
|
|
@@ -81,86 +83,14 @@ focusManager.setEventListener(handleFocus => {
|
|
|
81
83
|
// The bare @tamagui/config/v5 preset is only a last-resort fallback for tests
|
|
82
84
|
// and storybook — it will not resolve custom $surface* tokens.
|
|
83
85
|
const defaultTamaguiConfig = createTamagui(tamaguiDefaultConfig);
|
|
84
|
-
|
|
85
|
-
// Whether the offline SDK will actually orchestrate for this app. Mirrors the
|
|
86
|
-
// resolution that @microsoft/power-apps-native-offline's useOfflineIntegration
|
|
87
|
-
// performs (offlineProfile prop → expo manifest `extra.__offlineProfile`;
|
|
88
|
-
// enabled defaults true unless the profile's `appConfig.enabled` is explicitly
|
|
89
|
-
// false), but computed synchronously here because the QueryClient's networkMode
|
|
90
|
-
// must be fixed BEFORE the first query runs. The SDK's own `isEnabled` is only
|
|
91
|
-
// known deep inside the tree (below QueryClientProvider), which is too late to
|
|
92
|
-
// configure the client without a race. Kept deliberately narrow: it only
|
|
93
|
-
// decides networkMode below.
|
|
94
|
-
function isOfflineEnabled(offlineProfile) {
|
|
95
|
-
if (Platform.OS === 'web') return false;
|
|
96
|
-
// Only honour the prop when it carries a profileId — this mirrors the SDK's
|
|
97
|
-
// useOfflineIntegration, which ignores a profileId-less prop and falls through
|
|
98
|
-
// to the manifest. Without this guard a truthy-but-invalid prop (no profileId)
|
|
99
|
-
// short-circuits to `false` here while the SDK still resolves enabled from the
|
|
100
|
-
// manifest, leaving networkMode on 'online' (blank offline lists).
|
|
101
|
-
const propProfile = offlineProfile;
|
|
102
|
-
let profile = propProfile && propProfile.profileId ? propProfile : undefined;
|
|
103
|
-
if (!profile) {
|
|
104
|
-
const fromManifest = Constants.expoConfig?.extra?.__offlineProfile;
|
|
105
|
-
if (fromManifest && typeof fromManifest === 'object' && fromManifest.profileId) {
|
|
106
|
-
profile = fromManifest;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
if (profile) {
|
|
110
|
-
// Honour an explicit `appConfig.enabled: false`. This mirrors the SDK's
|
|
111
|
-
// useOfflineIntegration, whose validateConfig classifies `enabled === false`
|
|
112
|
-
// as "explicitly-disabled" and stays inert. A host shell's placeholder
|
|
113
|
-
// profile (e.g. Power Apps Dev Player's offline-profile.json, enabled:false)
|
|
114
|
-
// must NOT flip networkMode to 'always' for the loaded app — otherwise every
|
|
115
|
-
// scanned app gets paused queries / blank lists. Any other resolved profile
|
|
116
|
-
// (enabled true, or omitted → SDK defaults to true) enables offline.
|
|
117
|
-
const appConfig = profile.appConfig;
|
|
118
|
-
if (appConfig && appConfig.enabled === false) {
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Builds the app's QueryClient. networkMode is gated on whether offline is
|
|
127
|
-
// actually enabled:
|
|
128
|
-
// • Offline enabled → 'always': the host is offline-first, so reads/writes are
|
|
129
|
-
// served by the offline SDK from local SQLite regardless of connectivity.
|
|
130
|
-
// onlineManager is wired to NetInfo (above) for refetchOnReconnect, but that
|
|
131
|
-
// same wiring makes the default 'online' mode PAUSE every query/mutation while
|
|
132
|
-
// offline (queryFn never runs → blank lists with WiFi off). 'always' makes
|
|
133
|
-
// React Query ignore connectivity for both the initial attempt AND retries, so
|
|
134
|
-
// local-SQLite reads/writes never get paused. ('offlineFirst' would still pause
|
|
135
|
-
// *retries* offline — wrong for a purely-local SDK where a transient first
|
|
136
|
-
// attempt error, e.g. SDK-not-ready at cold boot, would leave the query stuck
|
|
137
|
-
// 'paused' until reconnect.)
|
|
138
|
-
// • Offline disabled → undefined: React Query keeps its default 'online' mode,
|
|
139
|
-
// i.e. the exact pre-offline behaviour for online-only apps.
|
|
140
|
-
function createQueryClient(offlineEnabled) {
|
|
141
|
-
const networkMode = offlineEnabled ? 'always' : undefined;
|
|
142
|
-
return new QueryClient({
|
|
143
|
-
defaultOptions: {
|
|
144
|
-
queries: {
|
|
145
|
-
staleTime: 60_000,
|
|
146
|
-
retry: 2,
|
|
147
|
-
networkMode
|
|
148
|
-
},
|
|
149
|
-
mutations: {
|
|
150
|
-
retry: 0,
|
|
151
|
-
networkMode
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
86
|
function hasConfiguredValue(value) {
|
|
157
87
|
return typeof value === 'string' && value.trim().length > 0;
|
|
158
88
|
}
|
|
159
89
|
function hasEntries(value) {
|
|
160
90
|
return Boolean(value && typeof value === 'object' && Object.keys(value).length > 0);
|
|
161
91
|
}
|
|
162
|
-
function requiresPowerPlatformAuth(powerConfig
|
|
163
|
-
return Boolean(hasConfiguredValue(powerConfig.appId) || hasConfiguredValue(powerConfig.environmentId) || hasEntries(powerConfig.connectionReferences) || hasEntries(powerConfig.databaseReferences)
|
|
92
|
+
function requiresPowerPlatformAuth(powerConfig) {
|
|
93
|
+
return Boolean(hasConfiguredValue(powerConfig.appId) || hasConfiguredValue(powerConfig.environmentId) || hasEntries(powerConfig.connectionReferences) || hasEntries(powerConfig.databaseReferences));
|
|
164
94
|
}
|
|
165
95
|
function MissingClientIdErrorScreen() {
|
|
166
96
|
return /*#__PURE__*/_jsxs(ScrollView, {
|
|
@@ -177,11 +107,58 @@ function MissingClientIdErrorScreen() {
|
|
|
177
107
|
})]
|
|
178
108
|
});
|
|
179
109
|
}
|
|
110
|
+
function MissingAppIdErrorScreen() {
|
|
111
|
+
return /*#__PURE__*/_jsxs(ScrollView, {
|
|
112
|
+
contentContainerStyle: styles.errorContainer,
|
|
113
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
114
|
+
style: styles.errorTitle,
|
|
115
|
+
children: "App ID is missing"
|
|
116
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
117
|
+
style: styles.errorBody,
|
|
118
|
+
children: "This mobile app does not include an app ID. Push the mobile app to the Power Platform environment, rebuild it, and try again."
|
|
119
|
+
})]
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function MissingEnvironmentIdErrorScreen() {
|
|
123
|
+
return /*#__PURE__*/_jsxs(ScrollView, {
|
|
124
|
+
contentContainerStyle: styles.errorContainer,
|
|
125
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
126
|
+
style: styles.errorTitle,
|
|
127
|
+
children: "Environment ID is missing"
|
|
128
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
129
|
+
style: styles.errorBody,
|
|
130
|
+
children: "This mobile app does not include an environment ID. Push the mobile app to the Power Platform environment, rebuild it, and try again."
|
|
131
|
+
})]
|
|
132
|
+
});
|
|
133
|
+
}
|
|
180
134
|
|
|
181
135
|
// ---- Types ----
|
|
182
136
|
|
|
183
137
|
/** Shape of power.config.json consumed by the provider. */
|
|
184
138
|
|
|
139
|
+
function readAppInsightsConfig(value) {
|
|
140
|
+
if (!value || typeof value !== 'object') return undefined;
|
|
141
|
+
const config = value;
|
|
142
|
+
const minSeverity = config.minSeverity;
|
|
143
|
+
return {
|
|
144
|
+
enabled: config.enabled === true,
|
|
145
|
+
connectionString: typeof config.connectionString === 'string' ? config.connectionString : '',
|
|
146
|
+
appId: typeof config.appId === 'string' ? config.appId : '',
|
|
147
|
+
...(typeof config.appVersion === 'string' ? {
|
|
148
|
+
appVersion: config.appVersion
|
|
149
|
+
} : {}),
|
|
150
|
+
...(typeof config.environment === 'string' ? {
|
|
151
|
+
environment: config.environment
|
|
152
|
+
} : {}),
|
|
153
|
+
...(minSeverity === 'verbose' || minSeverity === 'info' || minSeverity === 'warning' || minSeverity === 'error' ? {
|
|
154
|
+
minSeverity
|
|
155
|
+
} : {}),
|
|
156
|
+
...(typeof config.includeUserId === 'boolean' ? {
|
|
157
|
+
includeUserId: config.includeUserId
|
|
158
|
+
} : {})
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
185
162
|
// ---- Inner component ----
|
|
186
163
|
// Separate from PowerAppsProvider only because useAuth() must be called inside
|
|
187
164
|
// AuthProvider. All auth-dependent wiring lives here.
|
|
@@ -192,53 +169,49 @@ function InnerProviders({
|
|
|
192
169
|
schemaMap,
|
|
193
170
|
renderOAuthWebView,
|
|
194
171
|
onDisambiguationNeeded,
|
|
172
|
+
onConnectionConsent,
|
|
173
|
+
onResourceConsent,
|
|
174
|
+
onConnectionConsentDeclined,
|
|
195
175
|
offlineProfile,
|
|
176
|
+
onOfflineEnabledChange,
|
|
196
177
|
children
|
|
197
178
|
}) {
|
|
198
179
|
const {
|
|
199
180
|
acquireToken,
|
|
200
181
|
isSignedIn,
|
|
201
|
-
hasRealAccount
|
|
182
|
+
hasRealAccount,
|
|
183
|
+
isAuthReady,
|
|
184
|
+
signOut
|
|
202
185
|
} = useAuth();
|
|
203
|
-
const connectionReferences = useMemo(() => powerConfig.connectionReferences ?? {}, [powerConfig.connectionReferences]);
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
// Adapter over the host's fetchInstanceUrl: the offline SDK's contract passes a
|
|
207
|
-
// 4th `signal?: AbortSignal` arg (which fetchInstanceUrl treats as bearerToken)
|
|
208
|
-
// and expects a `string | null` return, so drop the extra arg and normalise
|
|
209
|
-
// `undefined` -> `null`.
|
|
210
|
-
const resolveInstanceUrlForOffline = useCallback(async (environmentId, region, acqToken, _signal) => {
|
|
211
|
-
const url = await fetchInstanceUrl(environmentId, region, acqToken);
|
|
212
|
-
return url ?? null;
|
|
213
|
-
}, []);
|
|
214
|
-
|
|
215
|
-
// Gate offline engagement on a REAL account (`hasRealAccount`), not the
|
|
216
|
-
// `isSignedIn` flag — `isSignedIn` is inflated by noAuthMode (login-bypassed /
|
|
217
|
-
// demo sessions report signed-in but have no account, so `acquireToken` always
|
|
218
|
-
// returns null). Passing the inflated flag makes the SDK orchestrator engage
|
|
219
|
-
// and then spin in a perpetual "waiting-for-auth" 3s retry loop (each retry
|
|
220
|
-
// re-renders the provider subtree). With no obtainable token offline cannot
|
|
221
|
-
// sync anyway, so we only engage once a usable token exists.
|
|
222
|
-
const offline = useOfflineIntegration({
|
|
186
|
+
const connectionReferences = useMemo(() => isDevPlayer ? powerConfig.connectionReferences ?? {} : {}, [powerConfig.connectionReferences, isDevPlayer]);
|
|
187
|
+
const runtimeSchemaMap = useMemo(() => isDevPlayer ? schemaMap : {}, [schemaMap, isDevPlayer]);
|
|
188
|
+
const offline = useOfflineIntegrationWithStartupGate({
|
|
223
189
|
powerConfig,
|
|
224
190
|
offlineProfile,
|
|
225
191
|
acquireToken,
|
|
226
|
-
|
|
227
|
-
|
|
192
|
+
hasRealAccount,
|
|
193
|
+
isAuthReady
|
|
228
194
|
});
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
onOfflineEnabledChange(offline.isEnabled);
|
|
197
|
+
}, [offline.isEnabled, onOfflineEnabledChange]);
|
|
229
198
|
return /*#__PURE__*/_jsxs(PowerAppsHostProvider, {
|
|
230
199
|
acquireToken: acquireToken,
|
|
231
200
|
isSignedIn: isSignedIn,
|
|
201
|
+
onSignOut: signOut,
|
|
232
202
|
appId: powerConfig.appId ?? undefined,
|
|
233
203
|
environmentId: powerConfig.environmentId ?? '',
|
|
234
204
|
region: powerConfig.region ?? 'prod',
|
|
235
205
|
connectionReferences: connectionReferences,
|
|
236
|
-
schemaMap:
|
|
237
|
-
databaseReferences: powerConfig.databaseReferences,
|
|
206
|
+
schemaMap: runtimeSchemaMap,
|
|
207
|
+
databaseReferences: isDevPlayer ? powerConfig.databaseReferences : undefined,
|
|
238
208
|
isvClientId: msalConfig.clientId,
|
|
239
209
|
renderOAuthWebView: renderOAuthWebView,
|
|
240
210
|
onDisambiguationNeeded: onDisambiguationNeeded,
|
|
241
|
-
|
|
211
|
+
onConnectionConsent: onConnectionConsent,
|
|
212
|
+
onResourceConsent: onResourceConsent,
|
|
213
|
+
onConnectionConsentDeclined: onConnectionConsentDeclined,
|
|
214
|
+
children: [offline.shouldMountChildren && children, offline.StatusOverlay && /*#__PURE__*/_jsx(offline.StatusOverlay, {})]
|
|
242
215
|
});
|
|
243
216
|
}
|
|
244
217
|
|
|
@@ -265,8 +238,13 @@ export function PowerAppsProvider({
|
|
|
265
238
|
followSystemTheme = true,
|
|
266
239
|
renderOAuthWebView,
|
|
267
240
|
onDisambiguationNeeded,
|
|
241
|
+
onConnectionConsent,
|
|
242
|
+
onResourceConsent,
|
|
243
|
+
onConnectionConsentDeclined,
|
|
268
244
|
appName,
|
|
269
245
|
logger: loggerProp,
|
|
246
|
+
showSessionDetailsOnSwipe = true,
|
|
247
|
+
appInsightsConfig,
|
|
270
248
|
offlineProfile,
|
|
271
249
|
children
|
|
272
250
|
}) {
|
|
@@ -274,14 +252,17 @@ export function PowerAppsProvider({
|
|
|
274
252
|
// is stable (loggerProp is an object reference; re-running on every render
|
|
275
253
|
// would reconfigure and leak the previous provider).
|
|
276
254
|
const loggerPropRef = useRef(loggerProp);
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
const
|
|
255
|
+
const appInsightsConfigRef = useRef(appInsightsConfig ?? readAppInsightsConfig(Constants.expoConfig?.extra?.appInsightsConfig));
|
|
256
|
+
const [offlineTelemetryEnabled, setOfflineTelemetryEnabled] = useState();
|
|
257
|
+
const missingClientIdInDevPlayer = Platform.OS !== 'web' && isDevPlayer && !hasConfiguredValue(msalConfig.clientId) && requiresPowerPlatformAuth(powerConfig);
|
|
258
|
+
const missingAppId = !isDevPlayer && !hasConfiguredValue(powerConfig.appId);
|
|
259
|
+
const missingEnvironmentId = !isDevPlayer && !hasConfiguredValue(powerConfig.environmentId);
|
|
260
|
+
const appId = typeof powerConfig.appId === 'string' ? powerConfig.appId.trim() : '';
|
|
261
|
+
const bundleId = Platform.OS === 'ios' ? Constants.expoConfig?.ios?.bundleIdentifier : Platform.OS === 'android' ? Constants.expoConfig?.android?.package : undefined;
|
|
262
|
+
const appInstanceId = Constants.expoConfig?.extra?.telemetry?.appInstanceId;
|
|
263
|
+
const skillCorrelationId = typeof appInstanceId === 'string' ? appInstanceId.trim() : '';
|
|
264
|
+
const requiredAuthScopes = useMemo(() => [powerPlatformScope(powerConfig.region ?? 'prod')], [powerConfig.region]);
|
|
265
|
+
const queryClient = useMemo(() => createHostQueryClient(), []);
|
|
285
266
|
|
|
286
267
|
// Configure the app logger exactly once per mount inside useEffect so that
|
|
287
268
|
// side effects (provider construction, async resolveDeviceId) never run
|
|
@@ -289,6 +270,9 @@ export function PowerAppsProvider({
|
|
|
289
270
|
// silent no-op Logger, so any telemetry emitted before the first commit is
|
|
290
271
|
// silently dropped rather than crashing.
|
|
291
272
|
useEffect(() => {
|
|
273
|
+
// Customer telemetry is a no-op until a valid Application Insights
|
|
274
|
+
// provider is configured below.
|
|
275
|
+
configureCustomerTelemetryLogger(new Logger());
|
|
292
276
|
const lp = loggerPropRef.current;
|
|
293
277
|
if (lp === null) {
|
|
294
278
|
// Explicit opt-out — replace with a silent no-op Logger.
|
|
@@ -304,26 +288,63 @@ export function PowerAppsProvider({
|
|
|
304
288
|
clientType: appName ?? 'PowerAppsNative',
|
|
305
289
|
appVersion: Constants.expoConfig?.version,
|
|
306
290
|
minSeverity: __DEV__ ? 'verbose' : 'info',
|
|
307
|
-
telemetryLocation: resolveTelemetryLocation(powerConfig.geoRegion)
|
|
291
|
+
telemetryLocation: resolveTelemetryLocation(powerConfig.geoRegion),
|
|
292
|
+
commonProperties: {
|
|
293
|
+
...(appId ? {
|
|
294
|
+
appId
|
|
295
|
+
} : {}),
|
|
296
|
+
...(bundleId ? {
|
|
297
|
+
bundleId
|
|
298
|
+
} : {}),
|
|
299
|
+
...(skillCorrelationId ? {
|
|
300
|
+
skillCorrelationId
|
|
301
|
+
} : {})
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
const configureDefaultLogger = (deviceId = 'react-native') => {
|
|
305
|
+
const oneDsProvider = new OneDsTelemetryProvider({
|
|
306
|
+
...providerConfig,
|
|
307
|
+
deviceId
|
|
308
|
+
});
|
|
309
|
+
const providers = [oneDsProvider];
|
|
310
|
+
let appInsightsEnabled = false;
|
|
311
|
+
const configuredAppInsights = appInsightsConfigRef.current;
|
|
312
|
+
if (configuredAppInsights?.enabled) {
|
|
313
|
+
try {
|
|
314
|
+
const customerProvider = new ApplicationInsightsTelemetryProvider({
|
|
315
|
+
...configuredAppInsights,
|
|
316
|
+
appVersion: configuredAppInsights.appVersion ?? Constants.expoConfig?.version,
|
|
317
|
+
deviceId,
|
|
318
|
+
sessionId: oneDsProvider.sessionId
|
|
319
|
+
});
|
|
320
|
+
providers.push(customerProvider);
|
|
321
|
+
configureCustomerTelemetryLogger(new Logger({
|
|
322
|
+
providers: [customerProvider]
|
|
323
|
+
}));
|
|
324
|
+
appInsightsEnabled = true;
|
|
325
|
+
} catch (error) {
|
|
326
|
+
console.error('[PowerAppsProvider] Application Insights configuration is invalid.', error);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const logger = new Logger({
|
|
330
|
+
providers
|
|
331
|
+
});
|
|
332
|
+
configureAppLogger(logger);
|
|
333
|
+
if (appInsightsEnabled) {
|
|
334
|
+
logger.trackActivityEvent('PowerAppsNative.ApplicationStarted', {
|
|
335
|
+
app_id: configuredAppInsights?.appId
|
|
336
|
+
});
|
|
337
|
+
}
|
|
308
338
|
};
|
|
309
339
|
// Resolve the stable device ID first so every event — including the
|
|
310
340
|
// very first — carries the real GUID rather than the 'react-native'
|
|
311
341
|
// fallback. Events emitted while this resolves are silently dropped
|
|
312
342
|
// by the no-op Logger that is active until configureAppLogger is called.
|
|
313
343
|
resolveDeviceId().then(deviceId => {
|
|
314
|
-
|
|
315
|
-
...providerConfig,
|
|
316
|
-
deviceId
|
|
317
|
-
});
|
|
318
|
-
configureAppLogger(new Logger({
|
|
319
|
-
providers: [provider]
|
|
320
|
-
}));
|
|
344
|
+
configureDefaultLogger(deviceId);
|
|
321
345
|
}).catch(_err => {
|
|
322
346
|
// SecureStore unavailable — fall back to a provider without a stable ID.
|
|
323
|
-
|
|
324
|
-
configureAppLogger(new Logger({
|
|
325
|
-
providers: [provider]
|
|
326
|
-
}));
|
|
347
|
+
configureDefaultLogger();
|
|
327
348
|
});
|
|
328
349
|
}
|
|
329
350
|
|
|
@@ -336,6 +357,11 @@ export function PowerAppsProvider({
|
|
|
336
357
|
};
|
|
337
358
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
338
359
|
}, []); // intentionally empty — logger is configured once per mount
|
|
360
|
+
|
|
361
|
+
useEffect(() => {
|
|
362
|
+
if (offlineTelemetryEnabled === undefined) return undefined;
|
|
363
|
+
return configureOfflineSdkTelemetry(offlineTelemetryEnabled);
|
|
364
|
+
}, [offlineTelemetryEnabled]);
|
|
339
365
|
return /*#__PURE__*/_jsx(ErrorBoundary, {
|
|
340
366
|
children: /*#__PURE__*/_jsx(GestureHandlerRootView, {
|
|
341
367
|
style: {
|
|
@@ -353,19 +379,32 @@ export function PowerAppsProvider({
|
|
|
353
379
|
children: /*#__PURE__*/_jsxs(ToastProvider, {
|
|
354
380
|
swipeDirection: "horizontal",
|
|
355
381
|
duration: 3000,
|
|
356
|
-
children: [/*#__PURE__*/_jsx(
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
382
|
+
children: [/*#__PURE__*/_jsx(SessionDetailsGesture, {
|
|
383
|
+
enabled: showSessionDetailsOnSwipe,
|
|
384
|
+
appName: appName ?? Constants.expoConfig?.name ?? 'Power Apps',
|
|
385
|
+
appId: appId || undefined,
|
|
386
|
+
environmentId: powerConfig.environmentId,
|
|
387
|
+
region: powerConfig.region ?? 'prod',
|
|
388
|
+
appVersion: Constants.expoConfig?.version,
|
|
389
|
+
children: /*#__PURE__*/_jsx(QueryClientProvider, {
|
|
390
|
+
client: queryClient,
|
|
391
|
+
children: missingAppId ? /*#__PURE__*/_jsx(MissingAppIdErrorScreen, {}) : missingEnvironmentId ? /*#__PURE__*/_jsx(MissingEnvironmentIdErrorScreen, {}) : missingClientIdInDevPlayer ? /*#__PURE__*/_jsx(MissingClientIdErrorScreen, {}) : /*#__PURE__*/_jsx(AuthProvider, {
|
|
362
392
|
msalConfig: msalConfig,
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
393
|
+
intuneConfig: intuneConfig,
|
|
394
|
+
requiredScopes: requiredAuthScopes,
|
|
395
|
+
children: /*#__PURE__*/_jsx(InnerProviders, {
|
|
396
|
+
msalConfig: msalConfig,
|
|
397
|
+
powerConfig: powerConfig,
|
|
398
|
+
schemaMap: schemaMap,
|
|
399
|
+
renderOAuthWebView: renderOAuthWebView,
|
|
400
|
+
onDisambiguationNeeded: onDisambiguationNeeded,
|
|
401
|
+
onConnectionConsent: onConnectionConsent,
|
|
402
|
+
onResourceConsent: onResourceConsent,
|
|
403
|
+
onConnectionConsentDeclined: onConnectionConsentDeclined,
|
|
404
|
+
offlineProfile: offlineProfile,
|
|
405
|
+
onOfflineEnabledChange: setOfflineTelemetryEnabled,
|
|
406
|
+
children: children
|
|
407
|
+
})
|
|
369
408
|
})
|
|
370
409
|
})
|
|
371
410
|
}), /*#__PURE__*/_jsx(ToastViewport, {
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
4
|
+
import { Modal, Platform, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
|
+
import * as Clipboard from 'expo-clipboard';
|
|
6
|
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
7
|
+
import { getAppLogger } from "../telemetry/index.js";
|
|
8
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
9
|
+
const SWIPE_DISTANCE = 80;
|
|
10
|
+
export function SessionDetailsGesture({
|
|
11
|
+
enabled,
|
|
12
|
+
appName,
|
|
13
|
+
appId,
|
|
14
|
+
environmentId,
|
|
15
|
+
region,
|
|
16
|
+
appVersion,
|
|
17
|
+
children
|
|
18
|
+
}) {
|
|
19
|
+
const [visible, setVisible] = useState(false);
|
|
20
|
+
const [sessionId, setSessionId] = useState();
|
|
21
|
+
const [copied, setCopied] = useState(false);
|
|
22
|
+
const showSessionDetails = useCallback(() => {
|
|
23
|
+
setSessionId(getAppLogger().sessionId);
|
|
24
|
+
setCopied(false);
|
|
25
|
+
setVisible(true);
|
|
26
|
+
}, []);
|
|
27
|
+
const gesture = useMemo(() => Gesture.Pan().enabled(enabled && Platform.OS !== 'web').minPointers(3).maxPointers(3).runOnJS(true).onEnd(({
|
|
28
|
+
translationX,
|
|
29
|
+
translationY
|
|
30
|
+
}) => {
|
|
31
|
+
if (translationY >= SWIPE_DISTANCE && translationY > Math.abs(translationX)) {
|
|
32
|
+
showSessionDetails();
|
|
33
|
+
}
|
|
34
|
+
}), [enabled, showSessionDetails]);
|
|
35
|
+
const close = useCallback(() => setVisible(false), []);
|
|
36
|
+
const copySessionId = useCallback(async () => {
|
|
37
|
+
if (!sessionId) return;
|
|
38
|
+
await Clipboard.setStringAsync(sessionId);
|
|
39
|
+
setCopied(true);
|
|
40
|
+
}, [sessionId]);
|
|
41
|
+
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
42
|
+
children: [/*#__PURE__*/_jsx(GestureDetector, {
|
|
43
|
+
gesture: gesture,
|
|
44
|
+
children: /*#__PURE__*/_jsx(View, {
|
|
45
|
+
style: styles.content,
|
|
46
|
+
children: children
|
|
47
|
+
})
|
|
48
|
+
}), /*#__PURE__*/_jsx(Modal, {
|
|
49
|
+
animationType: "fade",
|
|
50
|
+
onRequestClose: close,
|
|
51
|
+
transparent: true,
|
|
52
|
+
visible: visible,
|
|
53
|
+
children: /*#__PURE__*/_jsx(Pressable, {
|
|
54
|
+
accessibilityRole: "button",
|
|
55
|
+
accessibilityLabel: "Close session details",
|
|
56
|
+
onPress: close,
|
|
57
|
+
style: styles.backdrop,
|
|
58
|
+
children: /*#__PURE__*/_jsxs(Pressable, {
|
|
59
|
+
accessible: false,
|
|
60
|
+
onPress: event => event.stopPropagation(),
|
|
61
|
+
style: styles.panel,
|
|
62
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
63
|
+
style: styles.title,
|
|
64
|
+
children: "Session details"
|
|
65
|
+
}), /*#__PURE__*/_jsx(SessionDetail, {
|
|
66
|
+
label: "Telemetry session ID",
|
|
67
|
+
value: sessionId ?? 'Not available',
|
|
68
|
+
selectable: true,
|
|
69
|
+
singleLine: true
|
|
70
|
+
}), /*#__PURE__*/_jsx(Pressable, {
|
|
71
|
+
accessibilityRole: "button",
|
|
72
|
+
disabled: !sessionId,
|
|
73
|
+
onPress: () => {
|
|
74
|
+
void copySessionId();
|
|
75
|
+
},
|
|
76
|
+
style: ({
|
|
77
|
+
pressed
|
|
78
|
+
}) => [styles.copyButton, !sessionId && styles.disabledButton, pressed && styles.pressedButton],
|
|
79
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
80
|
+
style: styles.copyButtonText,
|
|
81
|
+
children: copied ? 'Copied' : 'Copy session ID'
|
|
82
|
+
})
|
|
83
|
+
}), /*#__PURE__*/_jsx(SessionDetail, {
|
|
84
|
+
label: "App",
|
|
85
|
+
value: appName
|
|
86
|
+
}), /*#__PURE__*/_jsx(SessionDetail, {
|
|
87
|
+
label: "App ID",
|
|
88
|
+
value: appId ?? 'Not available',
|
|
89
|
+
selectable: true
|
|
90
|
+
}), /*#__PURE__*/_jsx(SessionDetail, {
|
|
91
|
+
label: "Environment ID",
|
|
92
|
+
value: environmentId ?? 'Not available',
|
|
93
|
+
selectable: true
|
|
94
|
+
}), /*#__PURE__*/_jsx(SessionDetail, {
|
|
95
|
+
label: "Region",
|
|
96
|
+
value: region
|
|
97
|
+
}), /*#__PURE__*/_jsx(SessionDetail, {
|
|
98
|
+
label: "App version",
|
|
99
|
+
value: appVersion ?? 'Not available'
|
|
100
|
+
}), /*#__PURE__*/_jsx(Pressable, {
|
|
101
|
+
accessibilityRole: "button",
|
|
102
|
+
onPress: close,
|
|
103
|
+
style: styles.closeButton,
|
|
104
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
105
|
+
style: styles.closeButtonText,
|
|
106
|
+
children: "Close"
|
|
107
|
+
})
|
|
108
|
+
})]
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
})]
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function SessionDetail({
|
|
115
|
+
label,
|
|
116
|
+
value,
|
|
117
|
+
selectable = false,
|
|
118
|
+
singleLine = false
|
|
119
|
+
}) {
|
|
120
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
121
|
+
style: styles.row,
|
|
122
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
123
|
+
style: styles.label,
|
|
124
|
+
children: label
|
|
125
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
126
|
+
adjustsFontSizeToFit: singleLine,
|
|
127
|
+
minimumFontScale: 0.85,
|
|
128
|
+
numberOfLines: singleLine ? 1 : undefined,
|
|
129
|
+
selectable: selectable,
|
|
130
|
+
style: styles.value,
|
|
131
|
+
children: value
|
|
132
|
+
})]
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const styles = StyleSheet.create({
|
|
136
|
+
content: {
|
|
137
|
+
flex: 1
|
|
138
|
+
},
|
|
139
|
+
backdrop: {
|
|
140
|
+
alignItems: 'center',
|
|
141
|
+
backgroundColor: 'rgba(0, 0, 0, 0.48)',
|
|
142
|
+
flex: 1,
|
|
143
|
+
justifyContent: 'center',
|
|
144
|
+
paddingHorizontal: 12,
|
|
145
|
+
paddingVertical: 24
|
|
146
|
+
},
|
|
147
|
+
panel: {
|
|
148
|
+
backgroundColor: '#FFFFFF',
|
|
149
|
+
borderRadius: 8,
|
|
150
|
+
maxWidth: 520,
|
|
151
|
+
paddingHorizontal: 16,
|
|
152
|
+
paddingVertical: 24,
|
|
153
|
+
width: '100%'
|
|
154
|
+
},
|
|
155
|
+
title: {
|
|
156
|
+
color: '#242424',
|
|
157
|
+
fontSize: 20,
|
|
158
|
+
fontWeight: '700',
|
|
159
|
+
marginBottom: 20
|
|
160
|
+
},
|
|
161
|
+
row: {
|
|
162
|
+
marginBottom: 14
|
|
163
|
+
},
|
|
164
|
+
label: {
|
|
165
|
+
color: '#616161',
|
|
166
|
+
fontSize: 12,
|
|
167
|
+
fontWeight: '600',
|
|
168
|
+
marginBottom: 3
|
|
169
|
+
},
|
|
170
|
+
value: {
|
|
171
|
+
color: '#242424',
|
|
172
|
+
fontSize: 15,
|
|
173
|
+
lineHeight: 21
|
|
174
|
+
},
|
|
175
|
+
copyButton: {
|
|
176
|
+
alignSelf: 'flex-start',
|
|
177
|
+
borderColor: '#0F6CBD',
|
|
178
|
+
borderRadius: 4,
|
|
179
|
+
borderWidth: 1,
|
|
180
|
+
marginBottom: 14,
|
|
181
|
+
marginTop: -4,
|
|
182
|
+
paddingHorizontal: 12,
|
|
183
|
+
paddingVertical: 7
|
|
184
|
+
},
|
|
185
|
+
copyButtonText: {
|
|
186
|
+
color: '#0F6CBD',
|
|
187
|
+
fontSize: 13,
|
|
188
|
+
fontWeight: '600'
|
|
189
|
+
},
|
|
190
|
+
disabledButton: {
|
|
191
|
+
opacity: 0.45
|
|
192
|
+
},
|
|
193
|
+
pressedButton: {
|
|
194
|
+
opacity: 0.7
|
|
195
|
+
},
|
|
196
|
+
closeButton: {
|
|
197
|
+
alignItems: 'center',
|
|
198
|
+
alignSelf: 'flex-end',
|
|
199
|
+
backgroundColor: '#0F6CBD',
|
|
200
|
+
borderRadius: 4,
|
|
201
|
+
marginTop: 8,
|
|
202
|
+
minWidth: 88,
|
|
203
|
+
paddingHorizontal: 18,
|
|
204
|
+
paddingVertical: 10
|
|
205
|
+
},
|
|
206
|
+
closeButtonText: {
|
|
207
|
+
color: '#FFFFFF',
|
|
208
|
+
fontSize: 14,
|
|
209
|
+
fontWeight: '600'
|
|
210
|
+
}
|
|
211
|
+
});
|