expo-web-browser 10.0.3 → 10.2.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.
@@ -0,0 +1,63 @@
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
2
+
3
+ import SafariServices
4
+ import ExpoModulesCore
5
+
6
+ internal class WebBrowserSession: NSObject, SFSafariViewControllerDelegate, UIAdaptivePresentationControllerDelegate {
7
+ let viewController: SFSafariViewController
8
+ var promise: Promise?
9
+ var isOpen: Bool {
10
+ promise != nil
11
+ }
12
+
13
+ init(url: URL, options: WebBrowserOptions) {
14
+ let configuration = SFSafariViewController.Configuration()
15
+ configuration.barCollapsingEnabled = options.enableBarCollapsing
16
+ configuration.entersReaderIfAvailable = options.readerMode
17
+
18
+ viewController = SFSafariViewController(url: url, configuration: configuration)
19
+ viewController.modalPresentationStyle = options.presentationStyle.toPresentationStyle()
20
+ viewController.dismissButtonStyle = options.dismissButtonStyle.toSafariDismissButtonStyle()
21
+ viewController.preferredBarTintColor = options.toolbarColor
22
+ viewController.preferredControlTintColor = options.controlsColor
23
+
24
+ super.init()
25
+ viewController.delegate = self
26
+ viewController.presentationController?.delegate = self
27
+ }
28
+
29
+ func open(_ promise: Promise) {
30
+ var currentViewController = UIApplication.shared.keyWindow?.rootViewController
31
+ while currentViewController?.presentedViewController != nil {
32
+ currentViewController = currentViewController?.presentedViewController
33
+ }
34
+ currentViewController?.present(viewController, animated: true, completion: nil)
35
+
36
+ self.promise = promise
37
+ }
38
+
39
+ func dismiss() {
40
+ viewController.dismiss(animated: true) {
41
+ self.finish(type: "dismiss")
42
+ }
43
+ }
44
+
45
+ // MARK: - SFSafariViewControllerDelegate
46
+
47
+ func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
48
+ finish(type: "cancel")
49
+ }
50
+
51
+ // MARK: - UIAdaptivePresentationControllerDelegate
52
+
53
+ func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
54
+ finish(type: "cancel")
55
+ }
56
+
57
+ // MARK: - Private
58
+
59
+ private func finish(type: String) {
60
+ promise?.resolve(["type": type])
61
+ promise = nil
62
+ }
63
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-web-browser",
3
- "version": "10.0.3",
3
+ "version": "10.2.0",
4
4
  "description": "Provides access to the system's web browser and supports handling redirects. On iOS, it uses SFSafariViewController or SFAuthenticationSession, depending on the method you call, and on Android it uses ChromeCustomTabs. As of iOS 11, SFSafariViewController no longer shares cookies with Safari, so if you are using WebBrowser for authentication you will want to use WebBrowser.openAuthSessionAsync, and if you just want to open a webpage (such as your app privacy policy), then use WebBrowser.openBrowserAsync.",
5
5
  "main": "build/WebBrowser.js",
6
6
  "types": "build/WebBrowser.d.ts",
@@ -36,11 +36,13 @@
36
36
  "preset": "expo-module-scripts"
37
37
  },
38
38
  "dependencies": {
39
- "compare-urls": "^2.0.0",
40
- "expo-modules-core": "~0.4.4"
39
+ "compare-urls": "^2.0.0"
41
40
  },
42
41
  "devDependencies": {
43
42
  "expo-module-scripts": "^2.0.0"
44
43
  },
45
- "gitHead": "4fa0497a180ae707fa860cb03858630ab7af19f4"
44
+ "peerDependencies": {
45
+ "expo": "*"
46
+ },
47
+ "gitHead": "22dce752354bb429c84851bc4389abe47a766b1f"
46
48
  }
@@ -133,7 +133,7 @@ export default {
133
133
  if (popupWindow) {
134
134
  try {
135
135
  popupWindow.focus();
136
- } catch (e) {}
136
+ } catch {}
137
137
  } else {
138
138
  throw new CodedError(
139
139
  'ERR_WEB_BROWSER_BLOCKED',
package/src/WebBrowser.ts CHANGED
@@ -16,6 +16,8 @@ import {
16
16
  WebBrowserResultType,
17
17
  WebBrowserWarmUpResult,
18
18
  WebBrowserWindowFeatures,
19
+ WebBrowserPresentationStyle,
20
+ AuthSessionOpenOptions,
19
21
  } from './WebBrowser.types';
20
22
 
21
23
  export {
@@ -31,6 +33,8 @@ export {
31
33
  WebBrowserResultType,
32
34
  WebBrowserWarmUpResult,
33
35
  WebBrowserWindowFeatures,
36
+ WebBrowserPresentationStyle,
37
+ AuthSessionOpenOptions,
34
38
  };
35
39
 
36
40
  const emptyCustomTabsPackages: WebBrowserCustomTabsResults = {
@@ -42,12 +46,13 @@ const emptyCustomTabsPackages: WebBrowserCustomTabsResults = {
42
46
 
43
47
  // @needsAudit
44
48
  /**
45
- * _Android only_. Returns a list of applications package names supporting Custom Tabs, Custom Tabs
49
+ * Returns a list of applications package names supporting Custom Tabs, Custom Tabs
46
50
  * service, user chosen and preferred one. This may not be fully reliable, since it uses
47
51
  * `PackageManager.getResolvingActivities` under the hood. (For example, some browsers might not be
48
52
  * present in browserPackages list once another browser is set to default.)
49
53
  *
50
54
  * @return The promise which fulfils with [`WebBrowserCustomTabsResults`](#webbrowsercustomtabsresults) object.
55
+ * @platform android
51
56
  */
52
57
  export async function getCustomTabsSupportingBrowsersAsync(): Promise<WebBrowserCustomTabsResults> {
53
58
  if (!ExponentWebBrowser.getCustomTabsSupportingBrowsersAsync) {
@@ -62,12 +67,13 @@ export async function getCustomTabsSupportingBrowsersAsync(): Promise<WebBrowser
62
67
 
63
68
  // @needsAudit
64
69
  /**
65
- * _Android only_. This method calls `warmUp` method on [CustomTabsClient](https://developer.android.com/reference/android/support/customtabs/CustomTabsClient.html#warmup(long))
70
+ * This method calls `warmUp` method on [CustomTabsClient](https://developer.android.com/reference/android/support/customtabs/CustomTabsClient.html#warmup(long))
66
71
  * for specified package.
67
72
  *
68
- * @param browserPackage _Optional_ - Package of browser to be warmed up. If not set, preferred browser will be warmed.
73
+ * @param browserPackage Package of browser to be warmed up. If not set, preferred browser will be warmed.
69
74
  *
70
- * @return A promise which fulfils with `{ package: string }` object.
75
+ * @return A promise which fulfils with `WebBrowserWarmUpResult` object.
76
+ * @platform android
71
77
  */
72
78
  export async function warmUpAsync(browserPackage?: string): Promise<WebBrowserWarmUpResult> {
73
79
  if (!ExponentWebBrowser.warmUpAsync) {
@@ -82,13 +88,15 @@ export async function warmUpAsync(browserPackage?: string): Promise<WebBrowserWa
82
88
 
83
89
  // @needsAudit
84
90
  /**
85
- * _Android only_. This method initiates (if needed) [CustomTabsSession](https://developer.android.com/reference/android/support/customtabs/CustomTabsSession.html#maylaunchurl)
91
+ * This method initiates (if needed) [CustomTabsSession](https://developer.android.com/reference/android/support/customtabs/CustomTabsSession.html#maylaunchurl)
86
92
  * and calls its `mayLaunchUrl` method for browser specified by the package.
87
93
  *
88
94
  * @param url The url of page that is likely to be loaded first when opening browser.
89
- * @param browserPackage _Optional_ - Package of browser to be informed. If not set, preferred browser will be used.
95
+ * @param browserPackage Package of browser to be informed. If not set, preferred
96
+ * browser will be used.
90
97
  *
91
- * @return A promise which fulfils with `{ package: string }` object.
98
+ * @return A promise which fulfils with `WebBrowserMayInitWithUrlResult` object.
99
+ * @platform android
92
100
  */
93
101
  export async function mayInitWithUrlAsync(
94
102
  url: string,
@@ -106,15 +114,16 @@ export async function mayInitWithUrlAsync(
106
114
 
107
115
  // @needsAudit
108
116
  /**
109
- * _Android only_. This methods removes all bindings to services created by [`warmUpAsync`](#webbrowserwarmupasyncbrowserpackage)
117
+ * This methods removes all bindings to services created by [`warmUpAsync`](#webbrowserwarmupasyncbrowserpackage)
110
118
  * or [`mayInitWithUrlAsync`](#webbrowsermayinitwithurlasyncurl-browserpackage). You should call
111
119
  * this method once you don't need them to avoid potential memory leaks. However, those binding
112
120
  * would be cleared once your application is destroyed, which might be sufficient in most cases.
113
121
  *
114
- * @param browserPackage _Optional_ - Package of browser to be cooled. If not set, preferred browser will be used.
122
+ * @param browserPackage Package of browser to be cooled. If not set, preferred browser will be used.
115
123
  *
116
- * @return The promise which fulfils with `{ package: string }` when cooling is performed, or
124
+ * @return The promise which fulfils with ` WebBrowserCoolDownResult` when cooling is performed, or
117
125
  * an empty object when there was no connection to be dismissed.
126
+ * @platform android
118
127
  */
119
128
  export async function coolDownAsync(browserPackage?: string): Promise<WebBrowserCoolDownResult> {
120
129
  if (!ExponentWebBrowser.coolDownAsync) {
@@ -179,9 +188,10 @@ export async function openBrowserAsync(
179
188
 
180
189
  // @needsAudit
181
190
  /**
182
- * _iOS only_. Dismisses the presented web browser.
191
+ * Dismisses the presented web browser.
183
192
  *
184
- * @return The promise which fulfils with `{ type: 'dismiss' }` object.
193
+ * @return The `void` on successful attempt, or throws error, if dismiss functionality is not avaiable.
194
+ * @platform ios
185
195
  */
186
196
  export function dismissBrowser(): void {
187
197
  if (!ExponentWebBrowser.dismissBrowser) {
@@ -225,7 +235,7 @@ export function dismissBrowser(): void {
225
235
  *
226
236
  * @param url The url to open in the web browser. This should be a login page.
227
237
  * @param redirectUrl _Optional_ - The url to deep link back into your app. By default, this will be [`Constants.linkingUrl`](./constants/#expoconstantslinkinguri).
228
- * @param browserParams _Optional_ - An object with the same keys as [`WebBrowserOpenOptions`](#webbrowseropenoptions).
238
+ * @param options _Optional_ - An object extending the [`WebBrowserOpenOptions`](#webbrowseropenoptions).
229
239
  * If there is no native AuthSession implementation available (which is the case on Android)
230
240
  * these params will be used in the browser polyfill. If there is a native AuthSession implementation,
231
241
  * these params will be ignored.
@@ -239,18 +249,18 @@ export function dismissBrowser(): void {
239
249
  export async function openAuthSessionAsync(
240
250
  url: string,
241
251
  redirectUrl: string,
242
- browserParams: WebBrowserOpenOptions = {}
252
+ options: AuthSessionOpenOptions = {}
243
253
  ): Promise<WebBrowserAuthSessionResult> {
244
254
  if (_authSessionIsNativelySupported()) {
245
255
  if (!ExponentWebBrowser.openAuthSessionAsync) {
246
256
  throw new UnavailabilityError('WebBrowser', 'openAuthSessionAsync');
247
257
  }
248
- if (Platform.OS === 'web') {
249
- return ExponentWebBrowser.openAuthSessionAsync(url, redirectUrl, browserParams);
258
+ if (['ios', 'web'].includes(Platform.OS)) {
259
+ return ExponentWebBrowser.openAuthSessionAsync(url, redirectUrl, options);
250
260
  }
251
261
  return ExponentWebBrowser.openAuthSessionAsync(url, redirectUrl);
252
262
  } else {
253
- return _openAuthSessionPolyfillAsync(url, redirectUrl, browserParams);
263
+ return _openAuthSessionPolyfillAsync(url, redirectUrl, options);
254
264
  }
255
265
  }
256
266
 
@@ -271,7 +281,7 @@ export function dismissAuthSession(): void {
271
281
 
272
282
  // @needsAudit
273
283
  /**
274
- * _Web only_. Possibly completes an authentication session on web in a window popup. The method
284
+ * Possibly completes an authentication session on web in a window popup. The method
275
285
  * should be invoked on the page that the window redirects to.
276
286
  *
277
287
  * @param options
@@ -295,6 +305,8 @@ export function dismissAuthSession(): void {
295
305
  *
296
306
  * If the error `ERR_WEB_BROWSER_REDIRECT` was thrown, it may mean that the parent window was
297
307
  * reloaded before the auth was completed. In this case you'll need to close the child window manually.
308
+ *
309
+ * @platform web
298
310
  */
299
311
  export function maybeCompleteAuthSession(
300
312
  options: WebBrowserCompleteAuthSessionOptions = {}
@@ -351,8 +363,8 @@ async function _openBrowserAndWaitAndroidAsync(
351
363
  ): Promise<WebBrowserResult> {
352
364
  const appStateChangedToActive = new Promise<void>((resolve) => {
353
365
  _onWebBrowserCloseAndroid = resolve;
354
- AppState.addEventListener('change', _onAppStateChangeAndroid);
355
366
  });
367
+ const stateChangeSubscription = AppState.addEventListener('change', _onAppStateChangeAndroid);
356
368
 
357
369
  let result: WebBrowserResult = { type: WebBrowserResultType.CANCEL };
358
370
  let type: string | null = null;
@@ -360,7 +372,7 @@ async function _openBrowserAndWaitAndroidAsync(
360
372
  try {
361
373
  ({ type } = await openBrowserAsync(startUrl, browserParams));
362
374
  } catch (e) {
363
- AppState.removeEventListener('change', _onAppStateChangeAndroid);
375
+ stateChangeSubscription.remove();
364
376
  _onWebBrowserCloseAndroid = null;
365
377
  throw e;
366
378
  }
@@ -370,7 +382,7 @@ async function _openBrowserAndWaitAndroidAsync(
370
382
  result = { type: WebBrowserResultType.DISMISS };
371
383
  }
372
384
 
373
- AppState.removeEventListener('change', _onAppStateChangeAndroid);
385
+ stateChangeSubscription.remove();
374
386
  _onWebBrowserCloseAndroid = null;
375
387
  return result;
376
388
  }
@@ -12,8 +12,9 @@ export type WebBrowserOpenOptions = {
12
12
  */
13
13
  toolbarColor?: string;
14
14
  /**
15
- * __(Android only)__. Package name of a browser to be used to handle Custom Tabs. List of
15
+ * Package name of a browser to be used to handle Custom Tabs. List of
16
16
  * available packages is to be queried by [`getCustomTabsSupportingBrowsers`](#webbrowsergetcustomtabssupportingbrowsersasync) method.
17
+ * @platform android
17
18
  */
18
19
  browserPackage?: string;
19
20
  /**
@@ -21,51 +22,84 @@ export type WebBrowserOpenOptions = {
21
22
  */
22
23
  enableBarCollapsing?: boolean;
23
24
  /**
24
- * __(Android only)__ Color of the secondary toolbar in either `#AARRGGBB` or `#RRGGBB` format.
25
+ * Color of the secondary toolbar in either `#AARRGGBB` or `#RRGGBB` format.
26
+ * @platform android
25
27
  */
26
28
  secondaryToolbarColor?: string;
27
29
  /**
28
- * __(Android only)__ A boolean determining whether the browser should show the title of website on the toolbar.
30
+ * A boolean determining whether the browser should show the title of website on the toolbar.
31
+ * @platform android
29
32
  */
30
33
  showTitle?: boolean;
31
34
  /**
32
- * __(Android only)__ A boolean determining whether a default share item should be added to the menu.
35
+ * A boolean determining whether a default share item should be added to the menu.
36
+ * @platform android
33
37
  */
34
38
  enableDefaultShareMenuItem?: boolean;
35
39
  /**
36
- * __(Android only)__ A boolean determining whether browsed website should be shown as separate
40
+ * A boolean determining whether browsed website should be shown as separate
37
41
  * entry in Android recents/multitasking view. Requires `createTask` to be `true` (default).
38
- * @default `false`
42
+ * @default false
43
+ * @platform android
39
44
  */
40
45
  showInRecents?: boolean;
41
46
  /**
42
- * __(Android only)__ A boolean determining whether the browser should open in a new task or in
47
+ * A boolean determining whether the browser should open in a new task or in
43
48
  * the same task as your app.
44
- * @default `true`
49
+ * @default true
50
+ * @platform android
45
51
  */
46
52
  createTask?: boolean;
47
53
  /**
48
- * __(iOS only)__ Tint color for controls in SKSafariViewController in `#AARRGGBB` or `#RRGGBB` format.
54
+ * Tint color for controls in SKSafariViewController in `#AARRGGBB` or `#RRGGBB` format.
55
+ * @platform ios
49
56
  */
50
57
  controlsColor?: string;
51
58
  /**
52
- * __(iOS only)__ The style of the dismiss button. Should be one of: `done`, `close`, or `cancel`.
59
+ * The style of the dismiss button. Should be one of: `done`, `close`, or `cancel`.
60
+ * @platform ios
53
61
  */
54
62
  dismissButtonStyle?: 'done' | 'close' | 'cancel';
55
63
  /**
56
- * __(iOS only)__ A boolean determining whether Safari should enter Reader mode, if it is available.
64
+ * A boolean determining whether Safari should enter Reader mode, if it is available.
65
+ * @platform ios
57
66
  */
58
67
  readerMode?: boolean;
59
68
  /**
60
- * __(Web only)__ Name to assign to the popup window.
69
+ * The [presentation style](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621355-modalpresentationstyle)
70
+ * of the browser window.
71
+ * @default WebBrowser.WebBrowserPresentationStyle.OverFullScreen
72
+ * @platform ios
73
+ */
74
+ presentationStyle?: WebBrowserPresentationStyle;
75
+ /**
76
+ * Name to assign to the popup window.
77
+ * @platform web
61
78
  */
62
79
  windowName?: string;
63
80
  /**
64
- * __(Web only)__ Features to use with `window.open()`.
81
+ * Features to use with `window.open()`.
82
+ * @platform web
65
83
  */
66
84
  windowFeatures?: string | WebBrowserWindowFeatures;
67
85
  };
68
86
 
87
+ /**
88
+ * If there is no native AuthSession implementation available (which is the case on Android) the params inherited from
89
+ * [`WebBrowserOpenOptions`](#webbrowseropenoptions) will be used in the browser polyfill. Otherwise, the browser parameters will be ignored.
90
+ */
91
+ export type AuthSessionOpenOptions = WebBrowserOpenOptions & {
92
+ /**
93
+ * Determines whether the session should ask the browser for a private authentication session.
94
+ * Set this to `true` to request that the browser doesn’t share cookies or other browsing data between the authentication session and the user’s normal browser session.
95
+ * Whether the request is honored depends on the user’s default web browser.
96
+ *
97
+ * @default false
98
+ * @platform ios 13+
99
+ */
100
+ preferEphemeralSession?: boolean;
101
+ };
102
+
69
103
  export type WebBrowserAuthSessionResult = WebBrowserRedirectResult | WebBrowserResult;
70
104
 
71
105
  // @needsAudit
@@ -99,20 +133,64 @@ export type WebBrowserCustomTabsResults = {
99
133
  // @needsAudit @docsMissing
100
134
  export enum WebBrowserResultType {
101
135
  /**
102
- * iOS only.
136
+ * @platform ios
103
137
  */
104
138
  CANCEL = 'cancel',
105
139
  /**
106
- * iOS only.
140
+ * @platform ios
107
141
  */
108
142
  DISMISS = 'dismiss',
109
143
  /**
110
- * Android only.
144
+ * @platform android
111
145
  */
112
146
  OPENED = 'opened',
113
147
  LOCKED = 'locked',
114
148
  }
115
149
 
150
+ // @needsAudit
151
+ /**
152
+ * A browser presentation style. Its values are directly mapped to the [`UIModalPresentationStyle`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621355-modalpresentationstyle).
153
+ *
154
+ * @platform ios
155
+ */
156
+ export enum WebBrowserPresentationStyle {
157
+ /**
158
+ * A presentation style in which the presented browser covers the screen.
159
+ */
160
+ FULL_SCREEN = 'fullScreen',
161
+ /**
162
+ * A presentation style that partially covers the underlying content.
163
+ */
164
+ PAGE_SHEET = 'pageSheet',
165
+ /**
166
+ * A presentation style that displays the browser centered in the screen.
167
+ */
168
+ FORM_SHEET = 'formSheet',
169
+ /**
170
+ * A presentation style where the browser is displayed over the app's content.
171
+ */
172
+ CURRENT_CONTEXT = 'currentContext',
173
+ /**
174
+ * A presentation style in which the browser view covers the screen.
175
+ */
176
+ OVER_FULL_SCREEN = 'overFullScreen',
177
+ /**
178
+ * A presentation style where the browser is displayed over the app's content.
179
+ */
180
+ OVER_CURRENT_CONTEXT = 'overCurrentContext',
181
+ /**
182
+ * A presentation style where the browser is displayed in a popover view.
183
+ */
184
+ POPOVER = 'popover',
185
+ /**
186
+ * The default presentation style chosen by the system.
187
+ * On older iOS versions, falls back to `WebBrowserPresentationStyle.FullScreen`.
188
+ *
189
+ * @platform ios 13+
190
+ */
191
+ AUTOMATIC = 'automatic',
192
+ }
193
+
116
194
  // @needsAudit
117
195
  export type WebBrowserResult = {
118
196
  /**
@@ -1,9 +0,0 @@
1
- // Copyright © 2018 650 Industries. All rights reserved.
2
-
3
- #import <ExpoModulesCore/EXExportedModule.h>
4
- #import <ExpoModulesCore/EXModuleRegistryConsumer.h>
5
-
6
- @interface EXWebBrowser : EXExportedModule <EXModuleRegistryConsumer>
7
-
8
-
9
- @end