expo-web-browser 13.0.1 → 13.0.3

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/CHANGELOG.md CHANGED
@@ -10,6 +10,16 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 13.0.3 — 2024-04-29
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - On `iOS`, fix an issue where rapidly opening and closing the browser would leave the module in a bad state, preventing opening the browser again. ([#28452](https://github.com/expo/expo/pull/28452) by [@alanjhughes](https://github.com/alanjhughes))
18
+
19
+ ## 13.0.2 — 2024-04-24
20
+
21
+ _This version does not introduce any user-facing changes._
22
+
13
23
  ## 13.0.1 — 2024-04-23
14
24
 
15
25
  _This version does not introduce any user-facing changes._
@@ -1,7 +1,7 @@
1
1
  apply plugin: 'com.android.library'
2
2
 
3
3
  group = 'host.exp.exponent'
4
- version = '13.0.1'
4
+ version = '13.0.3'
5
5
 
6
6
 
7
7
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
@@ -15,7 +15,7 @@ android {
15
15
  namespace "expo.modules.webbrowser"
16
16
  defaultConfig {
17
17
  versionCode 18
18
- versionName '13.0.1'
18
+ versionName '13.0.3'
19
19
  }
20
20
  }
21
21
 
@@ -49,13 +49,13 @@ export declare function coolDownAsync(browserPackage?: string): Promise<WebBrows
49
49
  * Opens the url with Safari in a modal on iOS using [`SFSafariViewController`](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller),
50
50
  * and Chrome in a new [custom tab](https://developer.chrome.com/multidevice/android/customtabs)
51
51
  * on Android. On iOS, the modal Safari will not share cookies with the system Safari. If you need
52
- * this, use [`openAuthSessionAsync`](#webbrowseropenauthsessionasyncurl-redirecturl-browserparams).
52
+ * this, use [`openAuthSessionAsync`](#webbrowseropenauthsessionasyncurl-redirecturl-options).
53
53
  *
54
54
  * @param url The url to open in the web browser.
55
55
  * @param browserParams A dictionary of key-value pairs.
56
56
  *
57
57
  * @return The promise behaves differently based on the platform.
58
- * On Android promise resolves with `{type: 'opened'}` if we were able to open browser.
58
+ * On Android promise resolves with `{ type: 'opened' }` if we were able to open browser.
59
59
  * On iOS:
60
60
  * - If the user closed the web browser, the Promise resolves with `{ type: 'cancel' }`.
61
61
  * - If the browser is closed using [`dismissBrowser`](#webbrowserdismissbrowser), the Promise resolves with `{ type: 'dismiss' }`.
@@ -83,7 +83,7 @@ export declare function dismissBrowser(): void;
83
83
  *
84
84
  * # On web:
85
85
  * > This API can only be used in a secure environment (localhost/https).
86
- * to test this. Otherwise, an error with code [`ERR_WEB_BROWSER_CRYPTO`](#errwebbrowsercrypto) will be thrown.
86
+ * to test this. Otherwise, an error with code [`ERR_WEB_BROWSER_CRYPTO`](#err_web_browser_crypto) will be thrown.
87
87
  * This will use the browser's [`window.open()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) API.
88
88
  * - _Desktop_: This will create a new web popup window in the browser that can be closed later using `WebBrowser.maybeCompleteAuthSession()`.
89
89
  * - _Mobile_: This will open a new tab in the browser which can be closed using `WebBrowser.maybeCompleteAuthSession()`.
@@ -101,7 +101,7 @@ export declare function dismissBrowser(): void;
101
101
  *
102
102
  * > On mobile web, Chrome and Safari will block any call to [`window.open()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open)
103
103
  * which takes too long to fire after a user interaction. This method must be invoked immediately
104
- * after a user interaction. If the event is blocked, an error with code [`ERR_WEB_BROWSER_BLOCKED`](#errwebbrowserblocked) will be thrown.
104
+ * after a user interaction. If the event is blocked, an error with code [`ERR_WEB_BROWSER_BLOCKED`](#err_web_browser_blocked) will be thrown.
105
105
  *
106
106
  * @param url The url to open in the web browser. This should be a login page.
107
107
  * @param redirectUrl _Optional_ - The url to deep link back into your app.
@@ -1 +1 @@
1
- {"version":3,"file":"WebBrowser.d.ts","sourceRoot":"","sources":["../src/WebBrowser.ts"],"names":[],"mappings":"AAWA,OAAO,EAEL,2BAA2B,EAC3B,oCAAoC,EACpC,mCAAmC,EACnC,wBAAwB,EACxB,2BAA2B,EAC3B,8BAA8B,EAC9B,qBAAqB,EACrB,wBAAwB,EACxB,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EACtB,wBAAwB,EACxB,2BAA2B,EAC3B,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,2BAA2B,EAC3B,oCAAoC,EACpC,mCAAmC,EACnC,wBAAwB,EACxB,2BAA2B,EAC3B,8BAA8B,EAC9B,qBAAqB,EACrB,wBAAwB,EACxB,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EACtB,wBAAwB,EACxB,2BAA2B,EAC3B,sBAAsB,GACvB,CAAC;AAUF;;;;;;;;GAQG;AACH,wBAAsB,oCAAoC,IAAI,OAAO,CAAC,2BAA2B,CAAC,CASjG;AAGD;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAS1F;AAGD;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,MAAM,EACX,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,8BAA8B,CAAC,CASzC;AAGD;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAS9F;AAKD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,aAAa,GAAE,qBAA0B,GACxC,OAAO,CAAC,gBAAgB,CAAC,CA2B3B;AAGD;;;;;GAKG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAKrC;AAGD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,EAC3B,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,2BAA2B,CAAC,CAYtC;AAGD,wBAAgB,kBAAkB,IAAI,IAAI,CAYzC;AAGD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,oCAAyC,GACjD,mCAAmC,CAKrC"}
1
+ {"version":3,"file":"WebBrowser.d.ts","sourceRoot":"","sources":["../src/WebBrowser.ts"],"names":[],"mappings":"AAWA,OAAO,EAEL,2BAA2B,EAC3B,oCAAoC,EACpC,mCAAmC,EACnC,wBAAwB,EACxB,2BAA2B,EAC3B,8BAA8B,EAC9B,qBAAqB,EACrB,wBAAwB,EACxB,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EACtB,wBAAwB,EACxB,2BAA2B,EAC3B,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,2BAA2B,EAC3B,oCAAoC,EACpC,mCAAmC,EACnC,wBAAwB,EACxB,2BAA2B,EAC3B,8BAA8B,EAC9B,qBAAqB,EACrB,wBAAwB,EACxB,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EACtB,wBAAwB,EACxB,2BAA2B,EAC3B,sBAAsB,GACvB,CAAC;AAUF;;;;;;;;GAQG;AACH,wBAAsB,oCAAoC,IAAI,OAAO,CAAC,2BAA2B,CAAC,CASjG;AAGD;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAS1F;AAGD;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,MAAM,EACX,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,8BAA8B,CAAC,CASzC;AAGD;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAS9F;AAGD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,aAAa,GAAE,qBAA0B,GACxC,OAAO,CAAC,gBAAgB,CAAC,CAa3B;AAGD;;;;;GAKG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAGD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,EAC3B,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,2BAA2B,CAAC,CAYtC;AAGD,wBAAgB,kBAAkB,IAAI,IAAI,CAYzC;AAGD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,oCAAyC,GACjD,mCAAmC,CAKrC"}
@@ -98,19 +98,18 @@ export async function coolDownAsync(browserPackage) {
98
98
  return await ExponentWebBrowser.coolDownAsync(browserPackage);
99
99
  }
100
100
  }
101
- let browserLocked = false;
102
101
  // @needsAudit
103
102
  /**
104
103
  * Opens the url with Safari in a modal on iOS using [`SFSafariViewController`](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller),
105
104
  * and Chrome in a new [custom tab](https://developer.chrome.com/multidevice/android/customtabs)
106
105
  * on Android. On iOS, the modal Safari will not share cookies with the system Safari. If you need
107
- * this, use [`openAuthSessionAsync`](#webbrowseropenauthsessionasyncurl-redirecturl-browserparams).
106
+ * this, use [`openAuthSessionAsync`](#webbrowseropenauthsessionasyncurl-redirecturl-options).
108
107
  *
109
108
  * @param url The url to open in the web browser.
110
109
  * @param browserParams A dictionary of key-value pairs.
111
110
  *
112
111
  * @return The promise behaves differently based on the platform.
113
- * On Android promise resolves with `{type: 'opened'}` if we were able to open browser.
112
+ * On Android promise resolves with `{ type: 'opened' }` if we were able to open browser.
114
113
  * On iOS:
115
114
  * - If the user closed the web browser, the Promise resolves with `{ type: 'cancel' }`.
116
115
  * - If the browser is closed using [`dismissBrowser`](#webbrowserdismissbrowser), the Promise resolves with `{ type: 'dismiss' }`.
@@ -119,22 +118,12 @@ export async function openBrowserAsync(url, browserParams = {}) {
119
118
  if (!ExponentWebBrowser.openBrowserAsync) {
120
119
  throw new UnavailabilityError('WebBrowser', 'openBrowserAsync');
121
120
  }
122
- if (browserLocked) {
123
- // Prevent multiple sessions from running at the same time, WebBrowser doesn't
124
- // support it this makes the behavior predictable.
125
- if (__DEV__) {
126
- console.warn('Attempted to call WebBrowser.openBrowserAsync multiple times while already active. Only one WebBrowser controller can be active at any given time.');
127
- }
128
- return { type: WebBrowserResultType.LOCKED };
129
- }
130
- browserLocked = true;
131
121
  let result;
132
122
  try {
133
123
  result = await ExponentWebBrowser.openBrowserAsync(url, _processOptions(browserParams));
134
124
  }
135
- finally {
136
- // WebBrowser session complete, unset lock
137
- browserLocked = false;
125
+ catch {
126
+ return { type: WebBrowserResultType.LOCKED };
138
127
  }
139
128
  return result;
140
129
  }
@@ -146,10 +135,7 @@ export async function openBrowserAsync(url, browserParams = {}) {
146
135
  * @platform ios
147
136
  */
148
137
  export function dismissBrowser() {
149
- if (!ExponentWebBrowser.dismissBrowser) {
150
- throw new UnavailabilityError('WebBrowser', 'dismissBrowser');
151
- }
152
- ExponentWebBrowser.dismissBrowser();
138
+ ExponentWebBrowser.dismissBrowser?.();
153
139
  }
154
140
  // @needsAudit
155
141
  /**
@@ -167,7 +153,7 @@ export function dismissBrowser() {
167
153
  *
168
154
  * # On web:
169
155
  * > This API can only be used in a secure environment (localhost/https).
170
- * to test this. Otherwise, an error with code [`ERR_WEB_BROWSER_CRYPTO`](#errwebbrowsercrypto) will be thrown.
156
+ * to test this. Otherwise, an error with code [`ERR_WEB_BROWSER_CRYPTO`](#err_web_browser_crypto) will be thrown.
171
157
  * This will use the browser's [`window.open()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) API.
172
158
  * - _Desktop_: This will create a new web popup window in the browser that can be closed later using `WebBrowser.maybeCompleteAuthSession()`.
173
159
  * - _Mobile_: This will open a new tab in the browser which can be closed using `WebBrowser.maybeCompleteAuthSession()`.
@@ -185,7 +171,7 @@ export function dismissBrowser() {
185
171
  *
186
172
  * > On mobile web, Chrome and Safari will block any call to [`window.open()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open)
187
173
  * which takes too long to fire after a user interaction. This method must be invoked immediately
188
- * after a user interaction. If the event is blocked, an error with code [`ERR_WEB_BROWSER_BLOCKED`](#errwebbrowserblocked) will be thrown.
174
+ * after a user interaction. If the event is blocked, an error with code [`ERR_WEB_BROWSER_BLOCKED`](#err_web_browser_blocked) will be thrown.
189
175
  *
190
176
  * @param url The url to open in the web browser. This should be a login page.
191
177
  * @param redirectUrl _Optional_ - The url to deep link back into your app.
@@ -1 +1 @@
1
- {"version":3,"file":"WebBrowser.js","sourceRoot":"","sources":["../src/WebBrowser.ts"],"names":[],"mappings":"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","sourcesContent":["import { UnavailabilityError } from 'expo-modules-core';\nimport {\n AppState,\n AppStateStatus,\n Linking,\n Platform,\n EmitterSubscription,\n processColor,\n} from 'react-native';\n\nimport ExponentWebBrowser from './ExpoWebBrowser';\nimport {\n RedirectEvent,\n WebBrowserAuthSessionResult,\n WebBrowserCompleteAuthSessionOptions,\n WebBrowserCompleteAuthSessionResult,\n WebBrowserCoolDownResult,\n WebBrowserCustomTabsResults,\n WebBrowserMayInitWithUrlResult,\n WebBrowserOpenOptions,\n WebBrowserRedirectResult,\n WebBrowserResult,\n WebBrowserResultType,\n WebBrowserWarmUpResult,\n WebBrowserWindowFeatures,\n WebBrowserPresentationStyle,\n AuthSessionOpenOptions,\n} from './WebBrowser.types';\n\nexport {\n WebBrowserAuthSessionResult,\n WebBrowserCompleteAuthSessionOptions,\n WebBrowserCompleteAuthSessionResult,\n WebBrowserCoolDownResult,\n WebBrowserCustomTabsResults,\n WebBrowserMayInitWithUrlResult,\n WebBrowserOpenOptions,\n WebBrowserRedirectResult,\n WebBrowserResult,\n WebBrowserResultType,\n WebBrowserWarmUpResult,\n WebBrowserWindowFeatures,\n WebBrowserPresentationStyle,\n AuthSessionOpenOptions,\n};\n\nconst emptyCustomTabsPackages: WebBrowserCustomTabsResults = {\n defaultBrowserPackage: undefined,\n preferredBrowserPackage: undefined,\n browserPackages: [],\n servicePackages: [],\n};\n\n// @needsAudit\n/**\n * Returns a list of applications package names supporting Custom Tabs, Custom Tabs\n * service, user chosen and preferred one. This may not be fully reliable, since it uses\n * `PackageManager.getResolvingActivities` under the hood. (For example, some browsers might not be\n * present in browserPackages list once another browser is set to default.)\n *\n * @return The promise which fulfils with [`WebBrowserCustomTabsResults`](#webbrowsercustomtabsresults) object.\n * @platform android\n */\nexport async function getCustomTabsSupportingBrowsersAsync(): Promise<WebBrowserCustomTabsResults> {\n if (!ExponentWebBrowser.getCustomTabsSupportingBrowsersAsync) {\n throw new UnavailabilityError('WebBrowser', 'getCustomTabsSupportingBrowsersAsync');\n }\n if (Platform.OS !== 'android') {\n return emptyCustomTabsPackages;\n } else {\n return await ExponentWebBrowser.getCustomTabsSupportingBrowsersAsync();\n }\n}\n\n// @needsAudit\n/**\n * This method calls `warmUp` method on [CustomTabsClient](https://developer.android.com/reference/android/support/customtabs/CustomTabsClient.html#warmup(long))\n * for specified package.\n *\n * @param browserPackage Package of browser to be warmed up. If not set, preferred browser will be warmed.\n *\n * @return A promise which fulfils with `WebBrowserWarmUpResult` object.\n * @platform android\n */\nexport async function warmUpAsync(browserPackage?: string): Promise<WebBrowserWarmUpResult> {\n if (!ExponentWebBrowser.warmUpAsync) {\n throw new UnavailabilityError('WebBrowser', 'warmUpAsync');\n }\n if (Platform.OS !== 'android') {\n return {};\n } else {\n return await ExponentWebBrowser.warmUpAsync(browserPackage);\n }\n}\n\n// @needsAudit\n/**\n * This method initiates (if needed) [CustomTabsSession](https://developer.android.com/reference/android/support/customtabs/CustomTabsSession.html#maylaunchurl)\n * and calls its `mayLaunchUrl` method for browser specified by the package.\n *\n * @param url The url of page that is likely to be loaded first when opening browser.\n * @param browserPackage Package of browser to be informed. If not set, preferred\n * browser will be used.\n *\n * @return A promise which fulfils with `WebBrowserMayInitWithUrlResult` object.\n * @platform android\n */\nexport async function mayInitWithUrlAsync(\n url: string,\n browserPackage?: string\n): Promise<WebBrowserMayInitWithUrlResult> {\n if (!ExponentWebBrowser.mayInitWithUrlAsync) {\n throw new UnavailabilityError('WebBrowser', 'mayInitWithUrlAsync');\n }\n if (Platform.OS !== 'android') {\n return {};\n } else {\n return await ExponentWebBrowser.mayInitWithUrlAsync(url, browserPackage);\n }\n}\n\n// @needsAudit\n/**\n * This methods removes all bindings to services created by [`warmUpAsync`](#webbrowserwarmupasyncbrowserpackage)\n * or [`mayInitWithUrlAsync`](#webbrowsermayinitwithurlasyncurl-browserpackage). You should call\n * this method once you don't need them to avoid potential memory leaks. However, those binding\n * would be cleared once your application is destroyed, which might be sufficient in most cases.\n *\n * @param browserPackage Package of browser to be cooled. If not set, preferred browser will be used.\n *\n * @return The promise which fulfils with ` WebBrowserCoolDownResult` when cooling is performed, or\n * an empty object when there was no connection to be dismissed.\n * @platform android\n */\nexport async function coolDownAsync(browserPackage?: string): Promise<WebBrowserCoolDownResult> {\n if (!ExponentWebBrowser.coolDownAsync) {\n throw new UnavailabilityError('WebBrowser', 'coolDownAsync');\n }\n if (Platform.OS !== 'android') {\n return {};\n } else {\n return await ExponentWebBrowser.coolDownAsync(browserPackage);\n }\n}\n\nlet browserLocked = false;\n\n// @needsAudit\n/**\n * Opens the url with Safari in a modal on iOS using [`SFSafariViewController`](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller),\n * and Chrome in a new [custom tab](https://developer.chrome.com/multidevice/android/customtabs)\n * on Android. On iOS, the modal Safari will not share cookies with the system Safari. If you need\n * this, use [`openAuthSessionAsync`](#webbrowseropenauthsessionasyncurl-redirecturl-browserparams).\n *\n * @param url The url to open in the web browser.\n * @param browserParams A dictionary of key-value pairs.\n *\n * @return The promise behaves differently based on the platform.\n * On Android promise resolves with `{type: 'opened'}` if we were able to open browser.\n * On iOS:\n * - If the user closed the web browser, the Promise resolves with `{ type: 'cancel' }`.\n * - If the browser is closed using [`dismissBrowser`](#webbrowserdismissbrowser), the Promise resolves with `{ type: 'dismiss' }`.\n */\nexport async function openBrowserAsync(\n url: string,\n browserParams: WebBrowserOpenOptions = {}\n): Promise<WebBrowserResult> {\n if (!ExponentWebBrowser.openBrowserAsync) {\n throw new UnavailabilityError('WebBrowser', 'openBrowserAsync');\n }\n\n if (browserLocked) {\n // Prevent multiple sessions from running at the same time, WebBrowser doesn't\n // support it this makes the behavior predictable.\n if (__DEV__) {\n console.warn(\n 'Attempted to call WebBrowser.openBrowserAsync multiple times while already active. Only one WebBrowser controller can be active at any given time.'\n );\n }\n\n return { type: WebBrowserResultType.LOCKED };\n }\n browserLocked = true;\n\n let result: WebBrowserResult;\n try {\n result = await ExponentWebBrowser.openBrowserAsync(url, _processOptions(browserParams));\n } finally {\n // WebBrowser session complete, unset lock\n browserLocked = false;\n }\n\n return result;\n}\n\n// @needsAudit\n/**\n * Dismisses the presented web browser.\n *\n * @return The `void` on successful attempt, or throws error, if dismiss functionality is not avaiable.\n * @platform ios\n */\nexport function dismissBrowser(): void {\n if (!ExponentWebBrowser.dismissBrowser) {\n throw new UnavailabilityError('WebBrowser', 'dismissBrowser');\n }\n ExponentWebBrowser.dismissBrowser();\n}\n\n// @needsAudit\n/**\n * # On Android:\n * This will be done using a \"custom Chrome tabs\" browser, [AppState](https://reactnative.dev/docs/appstate),\n * and [Linking](./linking/) APIs.\n *\n * # On iOS:\n * Opens the url with Safari in a modal using `ASWebAuthenticationSession`. The user will be asked\n * whether to allow the app to authenticate using the given url.\n * To handle redirection back to the mobile application, the redirect URI set in the authentication server\n * has to use the protocol provided as the scheme in **app.json** [`expo.scheme`](./../config/app/#scheme).\n * For example, `demo://` not `https://` protocol.\n * Using `Linking.addEventListener` is not needed and can have side effects.\n *\n * # On web:\n * > This API can only be used in a secure environment (localhost/https).\n * to test this. Otherwise, an error with code [`ERR_WEB_BROWSER_CRYPTO`](#errwebbrowsercrypto) will be thrown.\n * This will use the browser's [`window.open()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) API.\n * - _Desktop_: This will create a new web popup window in the browser that can be closed later using `WebBrowser.maybeCompleteAuthSession()`.\n * - _Mobile_: This will open a new tab in the browser which can be closed using `WebBrowser.maybeCompleteAuthSession()`.\n *\n * How this works on web:\n * - A crypto state will be created for verifying the redirect.\n * - This means you need to run with `npx expo start --https`\n * - The state will be added to the window's `localstorage`. This ensures that auth cannot complete\n * unless it's done from a page running with the same origin as it was started.\n * Ex: if `openAuthSessionAsync` is invoked on `https://localhost:19006`, then `maybeCompleteAuthSession`\n * must be invoked on a page hosted from the origin `https://localhost:19006`. Using a different\n * website, or even a different host like `https://128.0.0.*:19006` for example will not work.\n * - A timer will be started to check for every 1000 milliseconds (1 second) to detect if the window\n * has been closed by the user. If this happens then a promise will resolve with `{ type: 'dismiss' }`.\n *\n * > On mobile web, Chrome and Safari will block any call to [`window.open()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open)\n * which takes too long to fire after a user interaction. This method must be invoked immediately\n * after a user interaction. If the event is blocked, an error with code [`ERR_WEB_BROWSER_BLOCKED`](#errwebbrowserblocked) will be thrown.\n *\n * @param url The url to open in the web browser. This should be a login page.\n * @param redirectUrl _Optional_ - The url to deep link back into your app.\n * On web, this defaults to the output of [`Linking.createURL(\"\")`](./linking/#linkingcreateurlpath-namedparameters).\n * @param options _Optional_ - An object extending the [`WebBrowserOpenOptions`](#webbrowseropenoptions).\n * If there is no native AuthSession implementation available (which is the case on Android)\n * these params will be used in the browser polyfill. If there is a native AuthSession implementation,\n * these params will be ignored.\n *\n * @return\n * - If the user does not permit the application to authenticate with the given url, the Promise fulfills with `{ type: 'cancel' }` object.\n * - If the user closed the web browser, the Promise fulfills with `{ type: 'cancel' }` object.\n * - If the browser is closed using [`dismissBrowser`](#webbrowserdismissbrowser),\n * the Promise fulfills with `{ type: 'dismiss' }` object.\n */\nexport async function openAuthSessionAsync(\n url: string,\n redirectUrl?: string | null,\n options: AuthSessionOpenOptions = {}\n): Promise<WebBrowserAuthSessionResult> {\n if (_authSessionIsNativelySupported()) {\n if (!ExponentWebBrowser.openAuthSessionAsync) {\n throw new UnavailabilityError('WebBrowser', 'openAuthSessionAsync');\n }\n if (['ios', 'web'].includes(Platform.OS)) {\n return ExponentWebBrowser.openAuthSessionAsync(url, redirectUrl, _processOptions(options));\n }\n return ExponentWebBrowser.openAuthSessionAsync(url, redirectUrl);\n } else {\n return _openAuthSessionPolyfillAsync(url, redirectUrl, options);\n }\n}\n\n// @docsMissing\nexport function dismissAuthSession(): void {\n if (_authSessionIsNativelySupported()) {\n if (!ExponentWebBrowser.dismissAuthSession) {\n throw new UnavailabilityError('WebBrowser', 'dismissAuthSession');\n }\n ExponentWebBrowser.dismissAuthSession();\n } else {\n if (!ExponentWebBrowser.dismissBrowser) {\n throw new UnavailabilityError('WebBrowser', 'dismissAuthSession');\n }\n ExponentWebBrowser.dismissBrowser();\n }\n}\n\n// @needsAudit\n/**\n * Possibly completes an authentication session on web in a window popup. The method\n * should be invoked on the page that the window redirects to.\n *\n * @param options\n *\n * @return Returns an object with message about why the redirect failed or succeeded:\n *\n * If `type` is set to `failed`, the reason depends on the message:\n * - `Not supported on this platform`: If the platform doesn't support this method (iOS, Android).\n * - `Cannot use expo-web-browser in a non-browser environment`: If the code was executed in an SSR\n * or node environment.\n * - `No auth session is currently in progress`: (the cached state wasn't found in local storage).\n * This can happen if the window redirects to an origin (website) that is different to the initial\n * website origin. If this happens in development, it may be because the auth started on localhost\n * and finished on your computer port (Ex: `128.0.0.*`). This is controlled by the `redirectUrl`\n * and `returnUrl`.\n * - `Current URL \"<URL>\" and original redirect URL \"<URL>\" do not match`: This can occur when the\n * redirect URL doesn't match what was initial defined as the `returnUrl`. You can skip this test\n * in development by passing `{ skipRedirectCheck: true }` to the function.\n *\n * If `type` is set to `success`, the parent window will attempt to close the child window immediately.\n *\n * If the error `ERR_WEB_BROWSER_REDIRECT` was thrown, it may mean that the parent window was\n * reloaded before the auth was completed. In this case you'll need to close the child window manually.\n *\n * @platform web\n */\nexport function maybeCompleteAuthSession(\n options: WebBrowserCompleteAuthSessionOptions = {}\n): WebBrowserCompleteAuthSessionResult {\n if (ExponentWebBrowser.maybeCompleteAuthSession) {\n return ExponentWebBrowser.maybeCompleteAuthSession(options);\n }\n return { type: 'failed', message: 'Not supported on this platform' };\n}\n\nfunction _processOptions(options: WebBrowserOpenOptions) {\n return {\n ...options,\n controlsColor: processColor(options.controlsColor),\n toolbarColor: processColor(options.toolbarColor),\n secondaryToolbarColor: processColor(options.secondaryToolbarColor),\n };\n}\n\n/* iOS <= 10 and Android polyfill for SFAuthenticationSession flow */\n\nfunction _authSessionIsNativelySupported(): boolean {\n if (Platform.OS === 'android') {\n return false;\n } else if (Platform.OS === 'web') {\n return true;\n }\n\n const versionNumber = parseInt(String(Platform.Version), 10);\n return versionNumber >= 11;\n}\n\nlet _redirectSubscription: EmitterSubscription | null = null;\n\n/*\n * openBrowserAsync on Android doesn't wait until closed, so we need to polyfill\n * it with AppState\n */\n\n// Store the `resolve` function from a Promise to fire when the AppState\n// returns to active\nlet _onWebBrowserCloseAndroid: null | (() => void) = null;\n\n// If the initial AppState.currentState is null, we assume that the first call to\n// AppState#change event is not actually triggered by a real change,\n// is triggered instead by the bridge capturing the current state\n// (https://reactnative.dev/docs/appstate#basic-usage)\nlet _isAppStateAvailable: boolean = AppState.currentState !== null;\nfunction _onAppStateChangeAndroid(state: AppStateStatus) {\n if (!_isAppStateAvailable) {\n _isAppStateAvailable = true;\n return;\n }\n\n if (state === 'active' && _onWebBrowserCloseAndroid) {\n _onWebBrowserCloseAndroid();\n }\n}\n\nasync function _openBrowserAndWaitAndroidAsync(\n startUrl: string,\n browserParams: WebBrowserOpenOptions = {}\n): Promise<WebBrowserResult> {\n const appStateChangedToActive = new Promise<void>((resolve) => {\n _onWebBrowserCloseAndroid = resolve;\n });\n const stateChangeSubscription = AppState.addEventListener('change', _onAppStateChangeAndroid);\n\n let result: WebBrowserResult = { type: WebBrowserResultType.CANCEL };\n let type: string | null = null;\n\n try {\n ({ type } = await openBrowserAsync(startUrl, browserParams));\n } catch (e) {\n stateChangeSubscription.remove();\n _onWebBrowserCloseAndroid = null;\n throw e;\n }\n\n if (type === 'opened') {\n await appStateChangedToActive;\n result = { type: WebBrowserResultType.DISMISS };\n }\n\n stateChangeSubscription.remove();\n _onWebBrowserCloseAndroid = null;\n return result;\n}\n\nasync function _openAuthSessionPolyfillAsync(\n startUrl: string,\n returnUrl: string | null | undefined,\n browserParams: WebBrowserOpenOptions = {}\n): Promise<WebBrowserAuthSessionResult> {\n if (_redirectSubscription) {\n throw new Error(\n `The WebBrowser's auth session is in an invalid state with a redirect handler set when it should not be`\n );\n }\n\n if (_onWebBrowserCloseAndroid) {\n throw new Error(`WebBrowser is already open, only one can be open at a time`);\n }\n\n try {\n if (Platform.OS === 'android') {\n return await Promise.race([\n _openBrowserAndWaitAndroidAsync(startUrl, browserParams),\n _waitForRedirectAsync(returnUrl),\n ]);\n } else {\n return await Promise.race([\n openBrowserAsync(startUrl, browserParams),\n _waitForRedirectAsync(returnUrl),\n ]);\n }\n } finally {\n // We can't dismiss the browser on Android, only call this when it's available.\n // Users on Android need to manually press the 'x' button in Chrome Custom Tabs, sadly.\n if (ExponentWebBrowser.dismissBrowser) {\n ExponentWebBrowser.dismissBrowser();\n }\n\n _stopWaitingForRedirect();\n }\n}\n\nfunction _stopWaitingForRedirect() {\n if (!_redirectSubscription) {\n throw new Error(\n `The WebBrowser auth session is in an invalid state with no redirect handler when one should be set`\n );\n }\n\n _redirectSubscription.remove();\n _redirectSubscription = null;\n}\n\nfunction _waitForRedirectAsync(\n returnUrl: string | null | undefined\n): Promise<WebBrowserRedirectResult> {\n // Note that this Promise never resolves when `returnUrl` is nullish\n return new Promise((resolve) => {\n const redirectHandler = (event: RedirectEvent) => {\n if (returnUrl && event.url.startsWith(returnUrl)) {\n resolve({ url: event.url, type: 'success' });\n }\n };\n\n _redirectSubscription = Linking.addEventListener('url', redirectHandler);\n });\n}\n"]}
1
+ {"version":3,"file":"WebBrowser.js","sourceRoot":"","sources":["../src/WebBrowser.ts"],"names":[],"mappings":"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","sourcesContent":["import { UnavailabilityError } from 'expo-modules-core';\nimport {\n AppState,\n AppStateStatus,\n Linking,\n Platform,\n EmitterSubscription,\n processColor,\n} from 'react-native';\n\nimport ExponentWebBrowser from './ExpoWebBrowser';\nimport {\n RedirectEvent,\n WebBrowserAuthSessionResult,\n WebBrowserCompleteAuthSessionOptions,\n WebBrowserCompleteAuthSessionResult,\n WebBrowserCoolDownResult,\n WebBrowserCustomTabsResults,\n WebBrowserMayInitWithUrlResult,\n WebBrowserOpenOptions,\n WebBrowserRedirectResult,\n WebBrowserResult,\n WebBrowserResultType,\n WebBrowserWarmUpResult,\n WebBrowserWindowFeatures,\n WebBrowserPresentationStyle,\n AuthSessionOpenOptions,\n} from './WebBrowser.types';\n\nexport {\n WebBrowserAuthSessionResult,\n WebBrowserCompleteAuthSessionOptions,\n WebBrowserCompleteAuthSessionResult,\n WebBrowserCoolDownResult,\n WebBrowserCustomTabsResults,\n WebBrowserMayInitWithUrlResult,\n WebBrowserOpenOptions,\n WebBrowserRedirectResult,\n WebBrowserResult,\n WebBrowserResultType,\n WebBrowserWarmUpResult,\n WebBrowserWindowFeatures,\n WebBrowserPresentationStyle,\n AuthSessionOpenOptions,\n};\n\nconst emptyCustomTabsPackages: WebBrowserCustomTabsResults = {\n defaultBrowserPackage: undefined,\n preferredBrowserPackage: undefined,\n browserPackages: [],\n servicePackages: [],\n};\n\n// @needsAudit\n/**\n * Returns a list of applications package names supporting Custom Tabs, Custom Tabs\n * service, user chosen and preferred one. This may not be fully reliable, since it uses\n * `PackageManager.getResolvingActivities` under the hood. (For example, some browsers might not be\n * present in browserPackages list once another browser is set to default.)\n *\n * @return The promise which fulfils with [`WebBrowserCustomTabsResults`](#webbrowsercustomtabsresults) object.\n * @platform android\n */\nexport async function getCustomTabsSupportingBrowsersAsync(): Promise<WebBrowserCustomTabsResults> {\n if (!ExponentWebBrowser.getCustomTabsSupportingBrowsersAsync) {\n throw new UnavailabilityError('WebBrowser', 'getCustomTabsSupportingBrowsersAsync');\n }\n if (Platform.OS !== 'android') {\n return emptyCustomTabsPackages;\n } else {\n return await ExponentWebBrowser.getCustomTabsSupportingBrowsersAsync();\n }\n}\n\n// @needsAudit\n/**\n * This method calls `warmUp` method on [CustomTabsClient](https://developer.android.com/reference/android/support/customtabs/CustomTabsClient.html#warmup(long))\n * for specified package.\n *\n * @param browserPackage Package of browser to be warmed up. If not set, preferred browser will be warmed.\n *\n * @return A promise which fulfils with `WebBrowserWarmUpResult` object.\n * @platform android\n */\nexport async function warmUpAsync(browserPackage?: string): Promise<WebBrowserWarmUpResult> {\n if (!ExponentWebBrowser.warmUpAsync) {\n throw new UnavailabilityError('WebBrowser', 'warmUpAsync');\n }\n if (Platform.OS !== 'android') {\n return {};\n } else {\n return await ExponentWebBrowser.warmUpAsync(browserPackage);\n }\n}\n\n// @needsAudit\n/**\n * This method initiates (if needed) [CustomTabsSession](https://developer.android.com/reference/android/support/customtabs/CustomTabsSession.html#maylaunchurl)\n * and calls its `mayLaunchUrl` method for browser specified by the package.\n *\n * @param url The url of page that is likely to be loaded first when opening browser.\n * @param browserPackage Package of browser to be informed. If not set, preferred\n * browser will be used.\n *\n * @return A promise which fulfils with `WebBrowserMayInitWithUrlResult` object.\n * @platform android\n */\nexport async function mayInitWithUrlAsync(\n url: string,\n browserPackage?: string\n): Promise<WebBrowserMayInitWithUrlResult> {\n if (!ExponentWebBrowser.mayInitWithUrlAsync) {\n throw new UnavailabilityError('WebBrowser', 'mayInitWithUrlAsync');\n }\n if (Platform.OS !== 'android') {\n return {};\n } else {\n return await ExponentWebBrowser.mayInitWithUrlAsync(url, browserPackage);\n }\n}\n\n// @needsAudit\n/**\n * This methods removes all bindings to services created by [`warmUpAsync`](#webbrowserwarmupasyncbrowserpackage)\n * or [`mayInitWithUrlAsync`](#webbrowsermayinitwithurlasyncurl-browserpackage). You should call\n * this method once you don't need them to avoid potential memory leaks. However, those binding\n * would be cleared once your application is destroyed, which might be sufficient in most cases.\n *\n * @param browserPackage Package of browser to be cooled. If not set, preferred browser will be used.\n *\n * @return The promise which fulfils with ` WebBrowserCoolDownResult` when cooling is performed, or\n * an empty object when there was no connection to be dismissed.\n * @platform android\n */\nexport async function coolDownAsync(browserPackage?: string): Promise<WebBrowserCoolDownResult> {\n if (!ExponentWebBrowser.coolDownAsync) {\n throw new UnavailabilityError('WebBrowser', 'coolDownAsync');\n }\n if (Platform.OS !== 'android') {\n return {};\n } else {\n return await ExponentWebBrowser.coolDownAsync(browserPackage);\n }\n}\n\n// @needsAudit\n/**\n * Opens the url with Safari in a modal on iOS using [`SFSafariViewController`](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller),\n * and Chrome in a new [custom tab](https://developer.chrome.com/multidevice/android/customtabs)\n * on Android. On iOS, the modal Safari will not share cookies with the system Safari. If you need\n * this, use [`openAuthSessionAsync`](#webbrowseropenauthsessionasyncurl-redirecturl-options).\n *\n * @param url The url to open in the web browser.\n * @param browserParams A dictionary of key-value pairs.\n *\n * @return The promise behaves differently based on the platform.\n * On Android promise resolves with `{ type: 'opened' }` if we were able to open browser.\n * On iOS:\n * - If the user closed the web browser, the Promise resolves with `{ type: 'cancel' }`.\n * - If the browser is closed using [`dismissBrowser`](#webbrowserdismissbrowser), the Promise resolves with `{ type: 'dismiss' }`.\n */\nexport async function openBrowserAsync(\n url: string,\n browserParams: WebBrowserOpenOptions = {}\n): Promise<WebBrowserResult> {\n if (!ExponentWebBrowser.openBrowserAsync) {\n throw new UnavailabilityError('WebBrowser', 'openBrowserAsync');\n }\n\n let result: WebBrowserResult;\n try {\n result = await ExponentWebBrowser.openBrowserAsync(url, _processOptions(browserParams));\n } catch {\n return { type: WebBrowserResultType.LOCKED };\n }\n\n return result;\n}\n\n// @needsAudit\n/**\n * Dismisses the presented web browser.\n *\n * @return The `void` on successful attempt, or throws error, if dismiss functionality is not avaiable.\n * @platform ios\n */\nexport function dismissBrowser(): void {\n ExponentWebBrowser.dismissBrowser?.();\n}\n\n// @needsAudit\n/**\n * # On Android:\n * This will be done using a \"custom Chrome tabs\" browser, [AppState](https://reactnative.dev/docs/appstate),\n * and [Linking](./linking/) APIs.\n *\n * # On iOS:\n * Opens the url with Safari in a modal using `ASWebAuthenticationSession`. The user will be asked\n * whether to allow the app to authenticate using the given url.\n * To handle redirection back to the mobile application, the redirect URI set in the authentication server\n * has to use the protocol provided as the scheme in **app.json** [`expo.scheme`](./../config/app/#scheme).\n * For example, `demo://` not `https://` protocol.\n * Using `Linking.addEventListener` is not needed and can have side effects.\n *\n * # On web:\n * > This API can only be used in a secure environment (localhost/https).\n * to test this. Otherwise, an error with code [`ERR_WEB_BROWSER_CRYPTO`](#err_web_browser_crypto) will be thrown.\n * This will use the browser's [`window.open()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) API.\n * - _Desktop_: This will create a new web popup window in the browser that can be closed later using `WebBrowser.maybeCompleteAuthSession()`.\n * - _Mobile_: This will open a new tab in the browser which can be closed using `WebBrowser.maybeCompleteAuthSession()`.\n *\n * How this works on web:\n * - A crypto state will be created for verifying the redirect.\n * - This means you need to run with `npx expo start --https`\n * - The state will be added to the window's `localstorage`. This ensures that auth cannot complete\n * unless it's done from a page running with the same origin as it was started.\n * Ex: if `openAuthSessionAsync` is invoked on `https://localhost:19006`, then `maybeCompleteAuthSession`\n * must be invoked on a page hosted from the origin `https://localhost:19006`. Using a different\n * website, or even a different host like `https://128.0.0.*:19006` for example will not work.\n * - A timer will be started to check for every 1000 milliseconds (1 second) to detect if the window\n * has been closed by the user. If this happens then a promise will resolve with `{ type: 'dismiss' }`.\n *\n * > On mobile web, Chrome and Safari will block any call to [`window.open()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open)\n * which takes too long to fire after a user interaction. This method must be invoked immediately\n * after a user interaction. If the event is blocked, an error with code [`ERR_WEB_BROWSER_BLOCKED`](#err_web_browser_blocked) will be thrown.\n *\n * @param url The url to open in the web browser. This should be a login page.\n * @param redirectUrl _Optional_ - The url to deep link back into your app.\n * On web, this defaults to the output of [`Linking.createURL(\"\")`](./linking/#linkingcreateurlpath-namedparameters).\n * @param options _Optional_ - An object extending the [`WebBrowserOpenOptions`](#webbrowseropenoptions).\n * If there is no native AuthSession implementation available (which is the case on Android)\n * these params will be used in the browser polyfill. If there is a native AuthSession implementation,\n * these params will be ignored.\n *\n * @return\n * - If the user does not permit the application to authenticate with the given url, the Promise fulfills with `{ type: 'cancel' }` object.\n * - If the user closed the web browser, the Promise fulfills with `{ type: 'cancel' }` object.\n * - If the browser is closed using [`dismissBrowser`](#webbrowserdismissbrowser),\n * the Promise fulfills with `{ type: 'dismiss' }` object.\n */\nexport async function openAuthSessionAsync(\n url: string,\n redirectUrl?: string | null,\n options: AuthSessionOpenOptions = {}\n): Promise<WebBrowserAuthSessionResult> {\n if (_authSessionIsNativelySupported()) {\n if (!ExponentWebBrowser.openAuthSessionAsync) {\n throw new UnavailabilityError('WebBrowser', 'openAuthSessionAsync');\n }\n if (['ios', 'web'].includes(Platform.OS)) {\n return ExponentWebBrowser.openAuthSessionAsync(url, redirectUrl, _processOptions(options));\n }\n return ExponentWebBrowser.openAuthSessionAsync(url, redirectUrl);\n } else {\n return _openAuthSessionPolyfillAsync(url, redirectUrl, options);\n }\n}\n\n// @docsMissing\nexport function dismissAuthSession(): void {\n if (_authSessionIsNativelySupported()) {\n if (!ExponentWebBrowser.dismissAuthSession) {\n throw new UnavailabilityError('WebBrowser', 'dismissAuthSession');\n }\n ExponentWebBrowser.dismissAuthSession();\n } else {\n if (!ExponentWebBrowser.dismissBrowser) {\n throw new UnavailabilityError('WebBrowser', 'dismissAuthSession');\n }\n ExponentWebBrowser.dismissBrowser();\n }\n}\n\n// @needsAudit\n/**\n * Possibly completes an authentication session on web in a window popup. The method\n * should be invoked on the page that the window redirects to.\n *\n * @param options\n *\n * @return Returns an object with message about why the redirect failed or succeeded:\n *\n * If `type` is set to `failed`, the reason depends on the message:\n * - `Not supported on this platform`: If the platform doesn't support this method (iOS, Android).\n * - `Cannot use expo-web-browser in a non-browser environment`: If the code was executed in an SSR\n * or node environment.\n * - `No auth session is currently in progress`: (the cached state wasn't found in local storage).\n * This can happen if the window redirects to an origin (website) that is different to the initial\n * website origin. If this happens in development, it may be because the auth started on localhost\n * and finished on your computer port (Ex: `128.0.0.*`). This is controlled by the `redirectUrl`\n * and `returnUrl`.\n * - `Current URL \"<URL>\" and original redirect URL \"<URL>\" do not match`: This can occur when the\n * redirect URL doesn't match what was initial defined as the `returnUrl`. You can skip this test\n * in development by passing `{ skipRedirectCheck: true }` to the function.\n *\n * If `type` is set to `success`, the parent window will attempt to close the child window immediately.\n *\n * If the error `ERR_WEB_BROWSER_REDIRECT` was thrown, it may mean that the parent window was\n * reloaded before the auth was completed. In this case you'll need to close the child window manually.\n *\n * @platform web\n */\nexport function maybeCompleteAuthSession(\n options: WebBrowserCompleteAuthSessionOptions = {}\n): WebBrowserCompleteAuthSessionResult {\n if (ExponentWebBrowser.maybeCompleteAuthSession) {\n return ExponentWebBrowser.maybeCompleteAuthSession(options);\n }\n return { type: 'failed', message: 'Not supported on this platform' };\n}\n\nfunction _processOptions(options: WebBrowserOpenOptions) {\n return {\n ...options,\n controlsColor: processColor(options.controlsColor),\n toolbarColor: processColor(options.toolbarColor),\n secondaryToolbarColor: processColor(options.secondaryToolbarColor),\n };\n}\n\n/* iOS <= 10 and Android polyfill for SFAuthenticationSession flow */\n\nfunction _authSessionIsNativelySupported(): boolean {\n if (Platform.OS === 'android') {\n return false;\n } else if (Platform.OS === 'web') {\n return true;\n }\n\n const versionNumber = parseInt(String(Platform.Version), 10);\n return versionNumber >= 11;\n}\n\nlet _redirectSubscription: EmitterSubscription | null = null;\n\n/*\n * openBrowserAsync on Android doesn't wait until closed, so we need to polyfill\n * it with AppState\n */\n\n// Store the `resolve` function from a Promise to fire when the AppState\n// returns to active\nlet _onWebBrowserCloseAndroid: null | (() => void) = null;\n\n// If the initial AppState.currentState is null, we assume that the first call to\n// AppState#change event is not actually triggered by a real change,\n// is triggered instead by the bridge capturing the current state\n// (https://reactnative.dev/docs/appstate#basic-usage)\nlet _isAppStateAvailable: boolean = AppState.currentState !== null;\nfunction _onAppStateChangeAndroid(state: AppStateStatus) {\n if (!_isAppStateAvailable) {\n _isAppStateAvailable = true;\n return;\n }\n\n if (state === 'active' && _onWebBrowserCloseAndroid) {\n _onWebBrowserCloseAndroid();\n }\n}\n\nasync function _openBrowserAndWaitAndroidAsync(\n startUrl: string,\n browserParams: WebBrowserOpenOptions = {}\n): Promise<WebBrowserResult> {\n const appStateChangedToActive = new Promise<void>((resolve) => {\n _onWebBrowserCloseAndroid = resolve;\n });\n const stateChangeSubscription = AppState.addEventListener('change', _onAppStateChangeAndroid);\n\n let result: WebBrowserResult = { type: WebBrowserResultType.CANCEL };\n let type: string | null = null;\n\n try {\n ({ type } = await openBrowserAsync(startUrl, browserParams));\n } catch (e) {\n stateChangeSubscription.remove();\n _onWebBrowserCloseAndroid = null;\n throw e;\n }\n\n if (type === 'opened') {\n await appStateChangedToActive;\n result = { type: WebBrowserResultType.DISMISS };\n }\n\n stateChangeSubscription.remove();\n _onWebBrowserCloseAndroid = null;\n return result;\n}\n\nasync function _openAuthSessionPolyfillAsync(\n startUrl: string,\n returnUrl: string | null | undefined,\n browserParams: WebBrowserOpenOptions = {}\n): Promise<WebBrowserAuthSessionResult> {\n if (_redirectSubscription) {\n throw new Error(\n `The WebBrowser's auth session is in an invalid state with a redirect handler set when it should not be`\n );\n }\n\n if (_onWebBrowserCloseAndroid) {\n throw new Error(`WebBrowser is already open, only one can be open at a time`);\n }\n\n try {\n if (Platform.OS === 'android') {\n return await Promise.race([\n _openBrowserAndWaitAndroidAsync(startUrl, browserParams),\n _waitForRedirectAsync(returnUrl),\n ]);\n } else {\n return await Promise.race([\n openBrowserAsync(startUrl, browserParams),\n _waitForRedirectAsync(returnUrl),\n ]);\n }\n } finally {\n // We can't dismiss the browser on Android, only call this when it's available.\n // Users on Android need to manually press the 'x' button in Chrome Custom Tabs, sadly.\n if (ExponentWebBrowser.dismissBrowser) {\n ExponentWebBrowser.dismissBrowser();\n }\n\n _stopWaitingForRedirect();\n }\n}\n\nfunction _stopWaitingForRedirect() {\n if (!_redirectSubscription) {\n throw new Error(\n `The WebBrowser auth session is in an invalid state with no redirect handler when one should be set`\n );\n }\n\n _redirectSubscription.remove();\n _redirectSubscription = null;\n}\n\nfunction _waitForRedirectAsync(\n returnUrl: string | null | undefined\n): Promise<WebBrowserRedirectResult> {\n // Note that this Promise never resolves when `returnUrl` is nullish\n return new Promise((resolve) => {\n const redirectHandler = (event: RedirectEvent) => {\n if (returnUrl && event.url.startsWith(returnUrl)) {\n resolve({ url: event.url, type: 'success' });\n }\n };\n\n _redirectSubscription = Linking.addEventListener('url', redirectHandler);\n });\n}\n"]}
@@ -7,6 +7,7 @@ import AuthenticationServices
7
7
  final public class WebBrowserModule: Module {
8
8
  private var currentWebBrowserSession: WebBrowserSession?
9
9
  private var currentAuthSession: WebAuthSession?
10
+ private var vcDidPresent = false
10
11
 
11
12
  private func isValid(url: URL) -> Bool {
12
13
  return url.scheme == "http" || url.scheme == "https"
@@ -16,6 +17,11 @@ final public class WebBrowserModule: Module {
16
17
  Name("ExpoWebBrowser")
17
18
 
18
19
  AsyncFunction("openBrowserAsync") { (url: URL, options: WebBrowserOptions, promise: Promise) in
20
+ if vcDidPresent {
21
+ self.currentWebBrowserSession = nil
22
+ vcDidPresent = false
23
+ }
24
+
19
25
  guard self.currentWebBrowserSession == nil else {
20
26
  throw WebBrowserAlreadyOpenException()
21
27
  }
@@ -27,6 +33,8 @@ final public class WebBrowserModule: Module {
27
33
  self.currentWebBrowserSession = WebBrowserSession(url: url, options: options) { [promise] type in
28
34
  promise.resolve(["type": type])
29
35
  self.currentWebBrowserSession = nil
36
+ } didPresent: {
37
+ self.vcDidPresent = true
30
38
  }
31
39
 
32
40
  self.currentWebBrowserSession?.open()
@@ -18,7 +18,7 @@ struct WebBrowserOptions: Record {
18
18
 
19
19
  @Field
20
20
  var controlsColor: UIColor?
21
-
21
+
22
22
  // Defaults to .overFullScreen to keep backwards compatibility
23
23
  @Field
24
24
  var presentationStyle: PresentationStyle = .overFullScreen
@@ -6,9 +6,11 @@ import ExpoModulesCore
6
6
  internal class WebBrowserSession: NSObject, SFSafariViewControllerDelegate, UIAdaptivePresentationControllerDelegate {
7
7
  let viewController: SFSafariViewController
8
8
  let onDismiss: (String) -> Void
9
+ let didPresent: () -> Void
9
10
 
10
- init(url: URL, options: WebBrowserOptions, onDismiss: @escaping (String) -> Void) {
11
+ init(url: URL, options: WebBrowserOptions, onDismiss: @escaping (String) -> Void, didPresent: @escaping () -> Void) {
11
12
  self.onDismiss = onDismiss
13
+ self.didPresent = didPresent
12
14
 
13
15
  let configuration = SFSafariViewController.Configuration()
14
16
  configuration.barCollapsingEnabled = options.enableBarCollapsing
@@ -30,7 +32,9 @@ internal class WebBrowserSession: NSObject, SFSafariViewControllerDelegate, UIAd
30
32
  while currentViewController?.presentedViewController != nil {
31
33
  currentViewController = currentViewController?.presentedViewController
32
34
  }
33
- currentViewController?.present(viewController, animated: true, completion: nil)
35
+ currentViewController?.present(viewController, animated: true) {
36
+ self.didPresent()
37
+ }
34
38
  }
35
39
 
36
40
  func dismiss() {
@@ -44,7 +48,7 @@ internal class WebBrowserSession: NSObject, SFSafariViewControllerDelegate, UIAd
44
48
  func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
45
49
  finish(type: "cancel")
46
50
  }
47
-
51
+
48
52
  // MARK: - UIAdaptivePresentationControllerDelegate
49
53
 
50
54
  func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-web-browser",
3
- "version": "13.0.1",
3
+ "version": "13.0.3",
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",
@@ -41,5 +41,5 @@
41
41
  "peerDependencies": {
42
42
  "expo": "*"
43
43
  },
44
- "gitHead": "ee4f30ef3b5fa567ad1bf94794197f7683fdd481"
44
+ "gitHead": "4a7cf0d0baf6dfc595d93f604945d2142e705a36"
45
45
  }
package/src/WebBrowser.ts CHANGED
@@ -143,20 +143,18 @@ export async function coolDownAsync(browserPackage?: string): Promise<WebBrowser
143
143
  }
144
144
  }
145
145
 
146
- let browserLocked = false;
147
-
148
146
  // @needsAudit
149
147
  /**
150
148
  * Opens the url with Safari in a modal on iOS using [`SFSafariViewController`](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller),
151
149
  * and Chrome in a new [custom tab](https://developer.chrome.com/multidevice/android/customtabs)
152
150
  * on Android. On iOS, the modal Safari will not share cookies with the system Safari. If you need
153
- * this, use [`openAuthSessionAsync`](#webbrowseropenauthsessionasyncurl-redirecturl-browserparams).
151
+ * this, use [`openAuthSessionAsync`](#webbrowseropenauthsessionasyncurl-redirecturl-options).
154
152
  *
155
153
  * @param url The url to open in the web browser.
156
154
  * @param browserParams A dictionary of key-value pairs.
157
155
  *
158
156
  * @return The promise behaves differently based on the platform.
159
- * On Android promise resolves with `{type: 'opened'}` if we were able to open browser.
157
+ * On Android promise resolves with `{ type: 'opened' }` if we were able to open browser.
160
158
  * On iOS:
161
159
  * - If the user closed the web browser, the Promise resolves with `{ type: 'cancel' }`.
162
160
  * - If the browser is closed using [`dismissBrowser`](#webbrowserdismissbrowser), the Promise resolves with `{ type: 'dismiss' }`.
@@ -169,25 +167,11 @@ export async function openBrowserAsync(
169
167
  throw new UnavailabilityError('WebBrowser', 'openBrowserAsync');
170
168
  }
171
169
 
172
- if (browserLocked) {
173
- // Prevent multiple sessions from running at the same time, WebBrowser doesn't
174
- // support it this makes the behavior predictable.
175
- if (__DEV__) {
176
- console.warn(
177
- 'Attempted to call WebBrowser.openBrowserAsync multiple times while already active. Only one WebBrowser controller can be active at any given time.'
178
- );
179
- }
180
-
181
- return { type: WebBrowserResultType.LOCKED };
182
- }
183
- browserLocked = true;
184
-
185
170
  let result: WebBrowserResult;
186
171
  try {
187
172
  result = await ExponentWebBrowser.openBrowserAsync(url, _processOptions(browserParams));
188
- } finally {
189
- // WebBrowser session complete, unset lock
190
- browserLocked = false;
173
+ } catch {
174
+ return { type: WebBrowserResultType.LOCKED };
191
175
  }
192
176
 
193
177
  return result;
@@ -201,10 +185,7 @@ export async function openBrowserAsync(
201
185
  * @platform ios
202
186
  */
203
187
  export function dismissBrowser(): void {
204
- if (!ExponentWebBrowser.dismissBrowser) {
205
- throw new UnavailabilityError('WebBrowser', 'dismissBrowser');
206
- }
207
- ExponentWebBrowser.dismissBrowser();
188
+ ExponentWebBrowser.dismissBrowser?.();
208
189
  }
209
190
 
210
191
  // @needsAudit
@@ -223,7 +204,7 @@ export function dismissBrowser(): void {
223
204
  *
224
205
  * # On web:
225
206
  * > This API can only be used in a secure environment (localhost/https).
226
- * to test this. Otherwise, an error with code [`ERR_WEB_BROWSER_CRYPTO`](#errwebbrowsercrypto) will be thrown.
207
+ * to test this. Otherwise, an error with code [`ERR_WEB_BROWSER_CRYPTO`](#err_web_browser_crypto) will be thrown.
227
208
  * This will use the browser's [`window.open()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) API.
228
209
  * - _Desktop_: This will create a new web popup window in the browser that can be closed later using `WebBrowser.maybeCompleteAuthSession()`.
229
210
  * - _Mobile_: This will open a new tab in the browser which can be closed using `WebBrowser.maybeCompleteAuthSession()`.
@@ -241,7 +222,7 @@ export function dismissBrowser(): void {
241
222
  *
242
223
  * > On mobile web, Chrome and Safari will block any call to [`window.open()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open)
243
224
  * which takes too long to fire after a user interaction. This method must be invoked immediately
244
- * after a user interaction. If the event is blocked, an error with code [`ERR_WEB_BROWSER_BLOCKED`](#errwebbrowserblocked) will be thrown.
225
+ * after a user interaction. If the event is blocked, an error with code [`ERR_WEB_BROWSER_BLOCKED`](#err_web_browser_blocked) will be thrown.
245
226
  *
246
227
  * @param url The url to open in the web browser. This should be a login page.
247
228
  * @param redirectUrl _Optional_ - The url to deep link back into your app.