@symbiote-native/web-browser 0.0.1
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/LICENSE +21 -0
- package/README.md +160 -0
- package/build/angular/index.d.ts +1 -0
- package/build/angular/index.js +4 -0
- package/build/core/index.d.ts +3 -0
- package/build/core/index.js +2 -0
- package/build/core/native-module.d.ts +12 -0
- package/build/core/native-module.js +3 -0
- package/build/core/types.d.ts +193 -0
- package/build/core/types.js +37 -0
- package/build/core/web-browser.d.ts +83 -0
- package/build/core/web-browser.js +256 -0
- package/build/react/index.d.ts +1 -0
- package/build/react/index.js +7 -0
- package/build/vue/index.d.ts +1 -0
- package/build/vue/index.js +4 -0
- package/build-ngc/angular/index.d.ts +1 -0
- package/build-ngc/angular/index.js +5 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/core/index.d.ts +3 -0
- package/build-ngc/core/index.js +3 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/native-module.d.ts +12 -0
- package/build-ngc/core/native-module.js +4 -0
- package/build-ngc/core/native-module.js.map +1 -0
- package/build-ngc/core/types.d.ts +193 -0
- package/build-ngc/core/types.js +38 -0
- package/build-ngc/core/types.js.map +1 -0
- package/build-ngc/core/web-browser.d.ts +83 -0
- package/build-ngc/core/web-browser.js +257 -0
- package/build-ngc/core/web-browser.js.map +1 -0
- package/native-link.json +12 -0
- package/package.json +107 -0
- package/src/angular/index.ts +4 -0
- package/src/core/index.ts +25 -0
- package/src/core/native-module.ts +43 -0
- package/src/core/types.ts +208 -0
- package/src/core/web-browser.test.ts +295 -0
- package/src/core/web-browser.ts +316 -0
- package/src/react/index.ts +7 -0
- package/src/vue/index.ts +4 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// Hand-ported from .vendors/expo/packages/expo-web-browser/src/WebBrowser.ts (sdk-57). Two
|
|
2
|
+
// deliberate departures from upstream, both because SymbioteNative has no web target:
|
|
3
|
+
// `maybeCompleteAuthSession` is not ported (it exists only to close a `window.open` popup; on a
|
|
4
|
+
// native platform it can do nothing but return `{ type: 'failed' }`), and the web-only open
|
|
5
|
+
// options are gone from IWebBrowserOpenOptions.
|
|
6
|
+
import { Platform, UnavailabilityError } from 'expo-modules-core';
|
|
7
|
+
import { AppState, Linking, processColor } from 'react-native';
|
|
8
|
+
import { expoWebBrowser } from './native-module';
|
|
9
|
+
import { WebBrowserResultType, } from './types';
|
|
10
|
+
const NATIVE_MODULE_NAME = 'expo-web-browser';
|
|
11
|
+
const EMPTY_CUSTOM_TABS_PACKAGES = {
|
|
12
|
+
defaultBrowserPackage: undefined,
|
|
13
|
+
preferredBrowserPackage: undefined,
|
|
14
|
+
browserPackages: [],
|
|
15
|
+
servicePackages: [],
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* The packages that support Custom Tabs, the Custom Tabs service, and the user's chosen and
|
|
19
|
+
* preferred browser. Only as reliable as `PackageManager.getResolvingActivities` underneath — a
|
|
20
|
+
* browser can be missing from `browserPackages` once another one is set as default.
|
|
21
|
+
*
|
|
22
|
+
* @platform android
|
|
23
|
+
*/
|
|
24
|
+
export async function getCustomTabsSupportingBrowsersAsync() {
|
|
25
|
+
// iOS registers its no-op stub as `getCustomTabsSupportingBrowsers`, without the `Async`
|
|
26
|
+
// suffix, so this guard — kept in upstream's order deliberately — fires there before the
|
|
27
|
+
// non-Android early return below can be reached.
|
|
28
|
+
if (!expoWebBrowser.getCustomTabsSupportingBrowsersAsync) {
|
|
29
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getCustomTabsSupportingBrowsersAsync');
|
|
30
|
+
}
|
|
31
|
+
if (Platform.OS !== 'android') {
|
|
32
|
+
return EMPTY_CUSTOM_TABS_PACKAGES;
|
|
33
|
+
}
|
|
34
|
+
return expoWebBrowser.getCustomTabsSupportingBrowsersAsync();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Calls `warmUp` on the [`CustomTabsClient`](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsClient#warmup(long))
|
|
38
|
+
* for the given package.
|
|
39
|
+
*
|
|
40
|
+
* @param browserPackage Browser to warm up. Defaults to the preferred one.
|
|
41
|
+
* @platform android
|
|
42
|
+
*/
|
|
43
|
+
export async function warmUpAsync(browserPackage) {
|
|
44
|
+
if (!expoWebBrowser.warmUpAsync) {
|
|
45
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'warmUpAsync');
|
|
46
|
+
}
|
|
47
|
+
if (Platform.OS !== 'android') {
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
return expoWebBrowser.warmUpAsync(browserPackage);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Starts a [`CustomTabsSession`](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsSession#mayLaunchUrl(android.net.Uri,android.os.Bundle,java.util.List%3Candroid.os.Bundle%3E))
|
|
54
|
+
* if needed and calls its `mayLaunchUrl`, so the browser can prefetch the page.
|
|
55
|
+
*
|
|
56
|
+
* @param url The page most likely to be opened first.
|
|
57
|
+
* @param browserPackage Browser to inform. Defaults to the preferred one.
|
|
58
|
+
* @platform android
|
|
59
|
+
*/
|
|
60
|
+
export async function mayInitWithUrlAsync(url, browserPackage) {
|
|
61
|
+
if (!expoWebBrowser.mayInitWithUrlAsync) {
|
|
62
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'mayInitWithUrlAsync');
|
|
63
|
+
}
|
|
64
|
+
if (Platform.OS !== 'android') {
|
|
65
|
+
return {};
|
|
66
|
+
}
|
|
67
|
+
return expoWebBrowser.mayInitWithUrlAsync(url, browserPackage);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Drops every binding `warmUpAsync` and `mayInitWithUrlAsync` created. Call it once the bindings
|
|
71
|
+
* are no longer needed to avoid leaking them — though they are also released when the app is
|
|
72
|
+
* destroyed, which is often good enough.
|
|
73
|
+
*
|
|
74
|
+
* @param browserPackage Browser to cool down. Defaults to the preferred one.
|
|
75
|
+
* @returns The cooled service, or an empty object when there was no connection to dismiss.
|
|
76
|
+
* @platform android
|
|
77
|
+
*/
|
|
78
|
+
export async function coolDownAsync(browserPackage) {
|
|
79
|
+
if (!expoWebBrowser.coolDownAsync) {
|
|
80
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'coolDownAsync');
|
|
81
|
+
}
|
|
82
|
+
if (Platform.OS !== 'android') {
|
|
83
|
+
return {};
|
|
84
|
+
}
|
|
85
|
+
return expoWebBrowser.coolDownAsync(browserPackage);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Opens the url in a modal [`SFSafariViewController`](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller)
|
|
89
|
+
* on iOS and a [Custom Tab](https://developer.chrome.com/docs/android/custom-tabs) on Android. The
|
|
90
|
+
* modal Safari does not share cookies with the system Safari — for a login flow that needs them,
|
|
91
|
+
* use `openAuthSessionAsync`.
|
|
92
|
+
*
|
|
93
|
+
* @returns Android resolves with `{ type: 'opened' }` as soon as the tab launches, without waiting
|
|
94
|
+
* for the user to close it. iOS resolves with `{ type: 'cancel' }` when the user dismissed the
|
|
95
|
+
* browser and `{ type: 'dismiss' }` when `dismissBrowser` closed it.
|
|
96
|
+
*/
|
|
97
|
+
export async function openBrowserAsync(url, browserParams = {}) {
|
|
98
|
+
if (!expoWebBrowser.openBrowserAsync) {
|
|
99
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'openBrowserAsync');
|
|
100
|
+
}
|
|
101
|
+
return expoWebBrowser.openBrowserAsync(url, processOptions(browserParams));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Dismisses the presented browser. Throws where dismissing isn't available — Android's Custom Tabs
|
|
105
|
+
* offer no way to close a tab programmatically, so there the user has to press the tab's own close
|
|
106
|
+
* button.
|
|
107
|
+
*
|
|
108
|
+
* @platform ios
|
|
109
|
+
*/
|
|
110
|
+
export function dismissBrowser() {
|
|
111
|
+
if (!expoWebBrowser.dismissBrowser) {
|
|
112
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'dismissBrowser');
|
|
113
|
+
}
|
|
114
|
+
return expoWebBrowser.dismissBrowser();
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Opens a login page and resolves once the authentication provider redirects back to the app.
|
|
118
|
+
*
|
|
119
|
+
* On iOS this is `ASWebAuthenticationSession`, which asks the user whether the app may
|
|
120
|
+
* authenticate with the given url; the redirect URI registered with the authentication server has
|
|
121
|
+
* to use the app's own scheme (`demo://…`, not `https://…`). Adding a `Linking` listener of your
|
|
122
|
+
* own is unnecessary there and can have side effects.
|
|
123
|
+
*
|
|
124
|
+
* Android has no equivalent native API, so it is polyfilled: a Custom Tab plus `AppState` and
|
|
125
|
+
* `Linking`, racing "the deep link came back" against "the app returned to the foreground". Only
|
|
126
|
+
* one such session can be open at a time.
|
|
127
|
+
*
|
|
128
|
+
* @param url The login page to open.
|
|
129
|
+
* @param redirectUrl The url that deep-links back into the app. Without it the session can only
|
|
130
|
+
* end by the user closing the browser.
|
|
131
|
+
* @returns `{ type: 'success', url }` with the redirect url on a completed login,
|
|
132
|
+
* `{ type: 'cancel' }` when the user declined or closed the browser, and `{ type: 'dismiss' }` when
|
|
133
|
+
* `dismissBrowser` closed it.
|
|
134
|
+
*/
|
|
135
|
+
export async function openAuthSessionAsync(url, redirectUrl, options = {}) {
|
|
136
|
+
if (!isAuthSessionNativelySupported()) {
|
|
137
|
+
return openAuthSessionPolyfillAsync(url, redirectUrl, options);
|
|
138
|
+
}
|
|
139
|
+
if (!expoWebBrowser.openAuthSessionAsync) {
|
|
140
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'openAuthSessionAsync');
|
|
141
|
+
}
|
|
142
|
+
return expoWebBrowser.openAuthSessionAsync(url, redirectUrl, processOptions(options));
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Dismisses the current authentication session. Falls back to dismissing the plain browser where
|
|
146
|
+
* there is no native auth session, which on Android throws for the same reason `dismissBrowser`
|
|
147
|
+
* does.
|
|
148
|
+
*
|
|
149
|
+
* @platform ios
|
|
150
|
+
*/
|
|
151
|
+
export function dismissAuthSession() {
|
|
152
|
+
if (isAuthSessionNativelySupported()) {
|
|
153
|
+
if (!expoWebBrowser.dismissAuthSession) {
|
|
154
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'dismissAuthSession');
|
|
155
|
+
}
|
|
156
|
+
expoWebBrowser.dismissAuthSession();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (!expoWebBrowser.dismissBrowser) {
|
|
160
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'dismissBrowser');
|
|
161
|
+
}
|
|
162
|
+
void expoWebBrowser.dismissBrowser();
|
|
163
|
+
}
|
|
164
|
+
function processOptions(options) {
|
|
165
|
+
return {
|
|
166
|
+
...options,
|
|
167
|
+
controlsColor: processColor(options.controlsColor),
|
|
168
|
+
toolbarColor: processColor(options.toolbarColor),
|
|
169
|
+
secondaryToolbarColor: processColor(options.secondaryToolbarColor),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/* Android polyfill for the ASWebAuthenticationSession flow */
|
|
173
|
+
function isAuthSessionNativelySupported() {
|
|
174
|
+
return Platform.OS !== 'android';
|
|
175
|
+
}
|
|
176
|
+
let redirectSubscription = null;
|
|
177
|
+
// `openBrowserAsync` on Android resolves the moment the Custom Tab launches, not when the user
|
|
178
|
+
// closes it, so the polyfill has to watch AppState instead. This holds the `resolve` of the
|
|
179
|
+
// promise that waits for the app to come back to the foreground.
|
|
180
|
+
let onWebBrowserCloseAndroid = null;
|
|
181
|
+
// A null initial `AppState.currentState` means the first `change` event is the bridge reporting
|
|
182
|
+
// the state it captured, not a real transition — that one has to be ignored, or the session would
|
|
183
|
+
// end the instant it started. See https://reactnative.dev/docs/appstate#basic-usage.
|
|
184
|
+
let isAppStateAvailable = AppState.currentState !== null;
|
|
185
|
+
function onAppStateChangeAndroid(state) {
|
|
186
|
+
if (!isAppStateAvailable) {
|
|
187
|
+
isAppStateAvailable = true;
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (state === 'active' && onWebBrowserCloseAndroid) {
|
|
191
|
+
onWebBrowserCloseAndroid();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async function openBrowserAndWaitAndroidAsync(startUrl, browserParams = {}) {
|
|
195
|
+
const appStateChangedToActive = new Promise(resolve => {
|
|
196
|
+
onWebBrowserCloseAndroid = resolve;
|
|
197
|
+
});
|
|
198
|
+
const stateChangeSubscription = AppState.addEventListener('change', onAppStateChangeAndroid);
|
|
199
|
+
let launched;
|
|
200
|
+
try {
|
|
201
|
+
launched = await openBrowserAsync(startUrl, browserParams);
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
stateChangeSubscription.remove();
|
|
205
|
+
onWebBrowserCloseAndroid = null;
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
let result = { type: WebBrowserResultType.CANCEL };
|
|
209
|
+
if (launched.type === WebBrowserResultType.OPENED) {
|
|
210
|
+
await appStateChangedToActive;
|
|
211
|
+
result = { type: WebBrowserResultType.DISMISS };
|
|
212
|
+
}
|
|
213
|
+
stateChangeSubscription.remove();
|
|
214
|
+
onWebBrowserCloseAndroid = null;
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
async function openAuthSessionPolyfillAsync(startUrl, returnUrl, browserParams = {}) {
|
|
218
|
+
if (redirectSubscription) {
|
|
219
|
+
throw new Error("The WebBrowser's auth session is in an invalid state with a redirect handler set when it should not be");
|
|
220
|
+
}
|
|
221
|
+
if (onWebBrowserCloseAndroid) {
|
|
222
|
+
throw new Error('WebBrowser is already open, only one can be open at a time');
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
return await Promise.race([
|
|
226
|
+
openBrowserAndWaitAndroidAsync(startUrl, browserParams),
|
|
227
|
+
waitForRedirectAsync(returnUrl),
|
|
228
|
+
]);
|
|
229
|
+
}
|
|
230
|
+
finally {
|
|
231
|
+
// Only reachable on a platform that can dismiss a browser at all — Android users have to
|
|
232
|
+
// close the Custom Tab themselves.
|
|
233
|
+
if (expoWebBrowser.dismissBrowser) {
|
|
234
|
+
void expoWebBrowser.dismissBrowser();
|
|
235
|
+
}
|
|
236
|
+
stopWaitingForRedirect();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function stopWaitingForRedirect() {
|
|
240
|
+
if (!redirectSubscription) {
|
|
241
|
+
throw new Error('The WebBrowser auth session is in an invalid state with no redirect handler when one should be set');
|
|
242
|
+
}
|
|
243
|
+
redirectSubscription.remove();
|
|
244
|
+
redirectSubscription = null;
|
|
245
|
+
}
|
|
246
|
+
function waitForRedirectAsync(returnUrl) {
|
|
247
|
+
// Deliberately never resolves when `returnUrl` is nullish: the browser promise it is raced
|
|
248
|
+
// against is then the only thing that can settle the session.
|
|
249
|
+
return new Promise(resolve => {
|
|
250
|
+
redirectSubscription = Linking.addEventListener('url', (event) => {
|
|
251
|
+
if (returnUrl && event.url.startsWith(returnUrl)) {
|
|
252
|
+
resolve({ url: event.url, type: 'success' });
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=web-browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-browser.js","sourceRoot":"","sources":["../../src/core/web-browser.ts"],"names":[],"mappings":"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"}
|
package/native-link.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@symbiote-native/web-browser",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "expo-web-browser wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, and Angular adapters. In-app browser (SFSafariViewController / Android Custom Tabs) and the OAuth auth-session flow.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/OneEyed1366/symbiote-native.git",
|
|
9
|
+
"directory": "packages/web-browser"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/OneEyed1366/symbiote-native/tree/master/packages/web-browser#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/OneEyed1366/symbiote-native/issues"
|
|
14
|
+
},
|
|
15
|
+
"author": "Andrey Prokopenko <psevdoproger@gmail.com>",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"main": "./build/core/index.js",
|
|
18
|
+
"module": "./build/core/index.js",
|
|
19
|
+
"types": "./build/core/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./build/core/index.d.ts",
|
|
23
|
+
"default": "./build/core/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./vue": {
|
|
26
|
+
"types": "./build/vue/index.d.ts",
|
|
27
|
+
"default": "./build/vue/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./react": {
|
|
30
|
+
"types": "./build/react/index.d.ts",
|
|
31
|
+
"default": "./build/react/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./angular": {
|
|
34
|
+
"types": "./build-ngc/angular/index.d.ts",
|
|
35
|
+
"react-native": "./build-ngc/angular/index.js",
|
|
36
|
+
"default": "./src/angular/index.ts"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"src",
|
|
41
|
+
"build",
|
|
42
|
+
"build-ngc",
|
|
43
|
+
"native-link.json"
|
|
44
|
+
],
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"expo-web-browser": "57.0.2",
|
|
50
|
+
"expo-modules-core": "57.0.5"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@symbiote-native/engine": ">=0.1.7",
|
|
54
|
+
"@angular/core": ">=20",
|
|
55
|
+
"@vue/runtime-core": "^3.5.13",
|
|
56
|
+
"react": ">=19.0.0",
|
|
57
|
+
"react-native": ">=0.86",
|
|
58
|
+
"vue": ">=3.5.0",
|
|
59
|
+
"@symbiote-native/angular": "0.6.1",
|
|
60
|
+
"@symbiote-native/react": "0.2.8",
|
|
61
|
+
"@symbiote-native/vue": "0.3.8"
|
|
62
|
+
},
|
|
63
|
+
"peerDependenciesMeta": {
|
|
64
|
+
"@symbiote-native/angular": {
|
|
65
|
+
"optional": true
|
|
66
|
+
},
|
|
67
|
+
"@symbiote-native/react": {
|
|
68
|
+
"optional": true
|
|
69
|
+
},
|
|
70
|
+
"@symbiote-native/vue": {
|
|
71
|
+
"optional": true
|
|
72
|
+
},
|
|
73
|
+
"@angular/core": {
|
|
74
|
+
"optional": true
|
|
75
|
+
},
|
|
76
|
+
"@vue/runtime-core": {
|
|
77
|
+
"optional": true
|
|
78
|
+
},
|
|
79
|
+
"react": {
|
|
80
|
+
"optional": true
|
|
81
|
+
},
|
|
82
|
+
"vue": {
|
|
83
|
+
"optional": true
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"devDependencies": {
|
|
87
|
+
"@angular/compiler": "^22",
|
|
88
|
+
"@angular/compiler-cli": "^22",
|
|
89
|
+
"@angular/core": "^22",
|
|
90
|
+
"@types/node": "^26.0.0",
|
|
91
|
+
"@types/react": "^19.2.0",
|
|
92
|
+
"@vue/runtime-core": "^3.5.13",
|
|
93
|
+
"react": "19.2.3",
|
|
94
|
+
"typescript": "~6.0.0",
|
|
95
|
+
"@symbiote-native/test-utils": "0.1.6",
|
|
96
|
+
"@symbiote-native/angular": "0.6.1",
|
|
97
|
+
"@symbiote-native/vue": "0.3.8",
|
|
98
|
+
"@symbiote-native/engine": "0.1.7",
|
|
99
|
+
"@symbiote-native/react": "0.2.8"
|
|
100
|
+
},
|
|
101
|
+
"scripts": {
|
|
102
|
+
"typecheck": "tsc --build",
|
|
103
|
+
"clean": "rm -rf build-ngc",
|
|
104
|
+
"ng:build": "pnpm run clean && ngc -p tsconfig.angular.json",
|
|
105
|
+
"format": "prettier --write \"src/**/*.{ts,tsx}\""
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// @symbiote-native/web-browser/angular: the Angular entry over the framework-agnostic core.
|
|
2
|
+
// Nothing here holds per-instance state or hands back a subscription, so there is no lifecycle for
|
|
3
|
+
// an injectable service to own — see the React entry for the full reasoning.
|
|
4
|
+
export * from '../core';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export {
|
|
2
|
+
getCustomTabsSupportingBrowsersAsync,
|
|
3
|
+
warmUpAsync,
|
|
4
|
+
mayInitWithUrlAsync,
|
|
5
|
+
coolDownAsync,
|
|
6
|
+
openBrowserAsync,
|
|
7
|
+
dismissBrowser,
|
|
8
|
+
openAuthSessionAsync,
|
|
9
|
+
dismissAuthSession,
|
|
10
|
+
} from './web-browser';
|
|
11
|
+
export { WebBrowserResultType, WebBrowserPresentationStyle } from './types';
|
|
12
|
+
export type {
|
|
13
|
+
IAuthSessionOpenOptions,
|
|
14
|
+
IRedirectEvent,
|
|
15
|
+
IServiceActionResult,
|
|
16
|
+
IWebBrowserAuthSessionResult,
|
|
17
|
+
IWebBrowserCoolDownResult,
|
|
18
|
+
IWebBrowserCustomTabsResults,
|
|
19
|
+
IWebBrowserDismissResult,
|
|
20
|
+
IWebBrowserMayInitWithUrlResult,
|
|
21
|
+
IWebBrowserOpenOptions,
|
|
22
|
+
IWebBrowserRedirectResult,
|
|
23
|
+
IWebBrowserResult,
|
|
24
|
+
IWebBrowserWarmUpResult,
|
|
25
|
+
} from './types';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { requireNativeModule } from 'expo-modules-core';
|
|
2
|
+
import type {
|
|
3
|
+
IProcessedOpenOptions,
|
|
4
|
+
IWebBrowserAuthSessionResult,
|
|
5
|
+
IWebBrowserCoolDownResult,
|
|
6
|
+
IWebBrowserCustomTabsResults,
|
|
7
|
+
IWebBrowserDismissResult,
|
|
8
|
+
IWebBrowserMayInitWithUrlResult,
|
|
9
|
+
IWebBrowserResult,
|
|
10
|
+
IWebBrowserWarmUpResult,
|
|
11
|
+
} from './types';
|
|
12
|
+
|
|
13
|
+
const EXPO_WEB_BROWSER_MODULE_NAME = 'ExpoWebBrowser';
|
|
14
|
+
|
|
15
|
+
// Every member is optional — the two platforms register genuinely different sets, so each call
|
|
16
|
+
// site checks for presence and throws an UnavailabilityError itself rather than assuming the
|
|
17
|
+
// whole surface exists:
|
|
18
|
+
// iOS — openBrowserAsync, dismissBrowser, openAuthSessionAsync, dismissAuthSession, plus
|
|
19
|
+
// no-op warmUpAsync/coolDownAsync/mayInitWithUrlAsync stubs.
|
|
20
|
+
// Android — openBrowserAsync, warmUpAsync, coolDownAsync, mayInitWithUrlAsync,
|
|
21
|
+
// getCustomTabsSupportingBrowsersAsync. There is no way to dismiss a Custom Tab and no
|
|
22
|
+
// native auth session; the user has to press the tab's own close button.
|
|
23
|
+
export type INativeWebBrowserModule = {
|
|
24
|
+
openBrowserAsync?(url: string, options: IProcessedOpenOptions): Promise<IWebBrowserResult>;
|
|
25
|
+
dismissBrowser?(): Promise<IWebBrowserDismissResult>;
|
|
26
|
+
openAuthSessionAsync?(
|
|
27
|
+
url: string,
|
|
28
|
+
redirectUrl: string | null | undefined,
|
|
29
|
+
options: IProcessedOpenOptions,
|
|
30
|
+
): Promise<IWebBrowserAuthSessionResult>;
|
|
31
|
+
dismissAuthSession?(): void;
|
|
32
|
+
warmUpAsync?(browserPackage?: string): Promise<IWebBrowserWarmUpResult>;
|
|
33
|
+
coolDownAsync?(browserPackage?: string): Promise<IWebBrowserCoolDownResult>;
|
|
34
|
+
mayInitWithUrlAsync?(
|
|
35
|
+
url: string,
|
|
36
|
+
browserPackage?: string,
|
|
37
|
+
): Promise<IWebBrowserMayInitWithUrlResult>;
|
|
38
|
+
getCustomTabsSupportingBrowsersAsync?(): Promise<IWebBrowserCustomTabsResults>;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const expoWebBrowser = requireNativeModule<INativeWebBrowserModule>(
|
|
42
|
+
EXPO_WEB_BROWSER_MODULE_NAME,
|
|
43
|
+
);
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type { ProcessedColorValue } from 'react-native';
|
|
2
|
+
|
|
3
|
+
/** Payload of the deep-link event the Android auth-session polyfill listens for. */
|
|
4
|
+
export type IRedirectEvent = {
|
|
5
|
+
url: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Options for [`openBrowserAsync`](#openbrowserasync).
|
|
10
|
+
*
|
|
11
|
+
* Upstream's two `@platform web` fields (`windowName`, `windowFeatures`) are not part of this
|
|
12
|
+
* type — SymbioteNative has no web target, so they could never be read.
|
|
13
|
+
*/
|
|
14
|
+
export type IWebBrowserOpenOptions = {
|
|
15
|
+
/** Color of the toolbar. Accepts any React Native color format. */
|
|
16
|
+
toolbarColor?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Package name of the browser that should handle the Custom Tab. Pick one from
|
|
19
|
+
* `getCustomTabsSupportingBrowsersAsync`.
|
|
20
|
+
* @platform android
|
|
21
|
+
*/
|
|
22
|
+
browserPackage?: string;
|
|
23
|
+
/** Whether the toolbar hides as the user scrolls the page. */
|
|
24
|
+
enableBarCollapsing?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Color of the secondary toolbar. Accepts any React Native color format.
|
|
27
|
+
* @platform android
|
|
28
|
+
*/
|
|
29
|
+
secondaryToolbarColor?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Whether the toolbar shows the website's title.
|
|
32
|
+
* @platform android
|
|
33
|
+
*/
|
|
34
|
+
showTitle?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Whether a default share item is added to the browser's menu.
|
|
37
|
+
* @platform android
|
|
38
|
+
*/
|
|
39
|
+
enableDefaultShareMenuItem?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Whether the browsed website appears as its own entry in the recents/multitasking view.
|
|
42
|
+
* Requires `createTask` to stay `true`.
|
|
43
|
+
* @default false
|
|
44
|
+
* @platform android
|
|
45
|
+
*/
|
|
46
|
+
showInRecents?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Whether the browser opens in its own task rather than the app's.
|
|
49
|
+
* @default true
|
|
50
|
+
* @platform android
|
|
51
|
+
*/
|
|
52
|
+
createTask?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Whether to launch the browser through a transparent proxy activity with a different task
|
|
55
|
+
* affinity, which keeps the browser alive while the app is backgrounded. Only read when
|
|
56
|
+
* `createTask` is `true`, and forces `showInRecents` on. Set `false` for the legacy direct
|
|
57
|
+
* launch.
|
|
58
|
+
* @default true
|
|
59
|
+
* @platform android
|
|
60
|
+
*/
|
|
61
|
+
useProxyActivity?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Tint color for the controls in `SFSafariViewController`. Accepts any React Native color
|
|
64
|
+
* format.
|
|
65
|
+
* @platform ios
|
|
66
|
+
*/
|
|
67
|
+
controlsColor?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Style of the dismiss button.
|
|
70
|
+
* @platform ios
|
|
71
|
+
*/
|
|
72
|
+
dismissButtonStyle?: 'done' | 'close' | 'cancel';
|
|
73
|
+
/**
|
|
74
|
+
* Whether Safari should enter Reader mode when the page supports it.
|
|
75
|
+
* @platform ios
|
|
76
|
+
*/
|
|
77
|
+
readerMode?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* [Presentation style](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621355-modalpresentationstyle)
|
|
80
|
+
* of the browser window.
|
|
81
|
+
* @default WebBrowserPresentationStyle.OVER_FULL_SCREEN
|
|
82
|
+
* @platform ios
|
|
83
|
+
*/
|
|
84
|
+
presentationStyle?: WebBrowserPresentationStyle;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Options for [`openAuthSessionAsync`](#openauthsessionasync). Android has no native auth-session
|
|
89
|
+
* API, so there the inherited `IWebBrowserOpenOptions` fields are passed to the Custom Tab the
|
|
90
|
+
* polyfill opens; on iOS `ASWebAuthenticationSession` ignores them and reads only the two fields
|
|
91
|
+
* below.
|
|
92
|
+
*/
|
|
93
|
+
export type IAuthSessionOpenOptions = IWebBrowserOpenOptions & {
|
|
94
|
+
/**
|
|
95
|
+
* Ask the browser for a private authentication session, so it shares no cookies or browsing
|
|
96
|
+
* data with the user's normal session. Whether the request is honored is up to the user's
|
|
97
|
+
* default browser.
|
|
98
|
+
* @default false
|
|
99
|
+
* @platform ios
|
|
100
|
+
*/
|
|
101
|
+
preferEphemeralSession?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Use HTTPS universal-link callbacks instead of custom-URL-scheme callbacks. Needs the
|
|
104
|
+
* Associated Domains entitlement configured for the redirect URL's host, and iOS 17.4+. Left
|
|
105
|
+
* `false`, the legacy `callbackURLScheme` API is used, which needs no entitlement.
|
|
106
|
+
* @default false
|
|
107
|
+
* @platform ios
|
|
108
|
+
*/
|
|
109
|
+
preferUniversalLinks?: boolean;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The open options after the three color fields have been run through `processColor`. This is the
|
|
114
|
+
* shape the native module actually receives — a color reaches native as a platform integer, never
|
|
115
|
+
* as the CSS string the caller wrote.
|
|
116
|
+
*/
|
|
117
|
+
export type IProcessedOpenOptions = Omit<
|
|
118
|
+
IAuthSessionOpenOptions,
|
|
119
|
+
'toolbarColor' | 'secondaryToolbarColor' | 'controlsColor'
|
|
120
|
+
> & {
|
|
121
|
+
toolbarColor?: ProcessedColorValue | null;
|
|
122
|
+
secondaryToolbarColor?: ProcessedColorValue | null;
|
|
123
|
+
controlsColor?: ProcessedColorValue | null;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export type IWebBrowserCustomTabsResults = {
|
|
127
|
+
/**
|
|
128
|
+
* The browser the user picked as their device default, or `undefined` when there is none — which
|
|
129
|
+
* usually means the user will be prompted to choose.
|
|
130
|
+
*/
|
|
131
|
+
defaultBrowserPackage?: string;
|
|
132
|
+
/**
|
|
133
|
+
* The browser `CustomTabsClient` prefers for Custom Tabs. Favors the user's default as long as
|
|
134
|
+
* it appears in both `browserPackages` and `servicePackages`; only such browsers fully support
|
|
135
|
+
* Custom Tabs. `undefined` when no installed browser qualifies.
|
|
136
|
+
*/
|
|
137
|
+
preferredBrowserPackage?: string;
|
|
138
|
+
/**
|
|
139
|
+
* Every package `PackageManager` recognizes as able to handle Custom Tabs. Empty when the device
|
|
140
|
+
* has no supporting browser.
|
|
141
|
+
*/
|
|
142
|
+
browserPackages: string[];
|
|
143
|
+
/**
|
|
144
|
+
* Every package `PackageManager` recognizes as able to handle the Custom Tabs *Service* — the
|
|
145
|
+
* one `warmUpAsync`, `mayInitWithUrlAsync` and `coolDownAsync` talk to.
|
|
146
|
+
*/
|
|
147
|
+
servicePackages: string[];
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/** How a browser session ended. */
|
|
151
|
+
export enum WebBrowserResultType {
|
|
152
|
+
/** The user dismissed the browser themselves. @platform ios */
|
|
153
|
+
CANCEL = 'cancel',
|
|
154
|
+
/** The browser was closed by a `dismissBrowser` call. @platform ios */
|
|
155
|
+
DISMISS = 'dismiss',
|
|
156
|
+
/** The browser was launched. Android resolves here without waiting for it to close. @platform android */
|
|
157
|
+
OPENED = 'opened',
|
|
158
|
+
/** Another browser session is already in progress. */
|
|
159
|
+
LOCKED = 'locked',
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* A browser presentation style, mapped directly onto
|
|
164
|
+
* [`UIModalPresentationStyle`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621355-modalpresentationstyle).
|
|
165
|
+
*
|
|
166
|
+
* @platform ios
|
|
167
|
+
*/
|
|
168
|
+
export enum WebBrowserPresentationStyle {
|
|
169
|
+
/** The browser covers the screen. */
|
|
170
|
+
FULL_SCREEN = 'fullScreen',
|
|
171
|
+
/** The browser partially covers the underlying content. */
|
|
172
|
+
PAGE_SHEET = 'pageSheet',
|
|
173
|
+
/** The browser is centered on the screen. */
|
|
174
|
+
FORM_SHEET = 'formSheet',
|
|
175
|
+
/** The browser is displayed over the app's content. */
|
|
176
|
+
CURRENT_CONTEXT = 'currentContext',
|
|
177
|
+
/** The browser view covers the screen. */
|
|
178
|
+
OVER_FULL_SCREEN = 'overFullScreen',
|
|
179
|
+
/** The browser is displayed over the app's content. */
|
|
180
|
+
OVER_CURRENT_CONTEXT = 'overCurrentContext',
|
|
181
|
+
/** The browser is displayed in a popover view. */
|
|
182
|
+
POPOVER = 'popover',
|
|
183
|
+
/** The system picks the style. Older iOS versions fall back to `FULL_SCREEN`. */
|
|
184
|
+
AUTOMATIC = 'automatic',
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export type IWebBrowserResult = {
|
|
188
|
+
type: WebBrowserResultType;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/** What `dismissBrowser` resolves with once the presented browser has been torn down. */
|
|
192
|
+
export type IWebBrowserDismissResult = {
|
|
193
|
+
type: WebBrowserResultType.DISMISS;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export type IWebBrowserRedirectResult = {
|
|
197
|
+
type: 'success';
|
|
198
|
+
url: string;
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
export type IServiceActionResult = {
|
|
202
|
+
servicePackage?: string;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
export type IWebBrowserAuthSessionResult = IWebBrowserRedirectResult | IWebBrowserResult;
|
|
206
|
+
export type IWebBrowserMayInitWithUrlResult = IServiceActionResult;
|
|
207
|
+
export type IWebBrowserWarmUpResult = IServiceActionResult;
|
|
208
|
+
export type IWebBrowserCoolDownResult = IServiceActionResult;
|