UNPKG

8.05 kBJavaScriptView Raw
1import { UnavailabilityError } from '@unimodules/core';
2import { AppState, Linking, Platform } from 'react-native';
3import ExponentWebBrowser from './ExpoWebBrowser';
4import { WebBrowserResultType, } from './WebBrowser.types';
5export { WebBrowserResultType, };
6const emptyCustomTabsPackages = {
7 defaultBrowserPackage: undefined,
8 preferredBrowserPackage: undefined,
9 browserPackages: [],
10 servicePackages: [],
11};
12export async function getCustomTabsSupportingBrowsersAsync() {
13 if (!ExponentWebBrowser.getCustomTabsSupportingBrowsersAsync) {
14 throw new UnavailabilityError('WebBrowser', 'getCustomTabsSupportingBrowsersAsync');
15 }
16 if (Platform.OS !== 'android') {
17 return emptyCustomTabsPackages;
18 }
19 else {
20 return await ExponentWebBrowser.getCustomTabsSupportingBrowsersAsync();
21 }
22}
23export async function warmUpAsync(browserPackage) {
24 if (!ExponentWebBrowser.warmUpAsync) {
25 throw new UnavailabilityError('WebBrowser', 'warmUpAsync');
26 }
27 if (Platform.OS !== 'android') {
28 return {};
29 }
30 else {
31 return await ExponentWebBrowser.warmUpAsync(browserPackage);
32 }
33}
34export async function mayInitWithUrlAsync(url, browserPackage) {
35 if (!ExponentWebBrowser.mayInitWithUrlAsync) {
36 throw new UnavailabilityError('WebBrowser', 'mayInitWithUrlAsync');
37 }
38 if (Platform.OS !== 'android') {
39 return {};
40 }
41 else {
42 return await ExponentWebBrowser.mayInitWithUrlAsync(url, browserPackage);
43 }
44}
45export async function coolDownAsync(browserPackage) {
46 if (!ExponentWebBrowser.coolDownAsync) {
47 throw new UnavailabilityError('WebBrowser', 'coolDownAsync');
48 }
49 if (Platform.OS !== 'android') {
50 return {};
51 }
52 else {
53 return await ExponentWebBrowser.coolDownAsync(browserPackage);
54 }
55}
56let browserLocked = false;
57export async function openBrowserAsync(url, browserParams = {}) {
58 if (!ExponentWebBrowser.openBrowserAsync) {
59 throw new UnavailabilityError('WebBrowser', 'openBrowserAsync');
60 }
61 if (browserLocked) {
62 // Prevent multiple sessions from running at the same time, WebBrowser doesn't
63 // support it this makes the behavior predictable.
64 if (__DEV__) {
65 console.warn('Attempted to call WebBrowser.openBrowserAsync multiple times while already active. Only one WebBrowser controller can be active at any given time.');
66 }
67 return { type: WebBrowserResultType.LOCKED };
68 }
69 browserLocked = true;
70 let result;
71 try {
72 result = await ExponentWebBrowser.openBrowserAsync(url, browserParams);
73 }
74 finally {
75 // WebBrowser session complete, unset lock
76 browserLocked = false;
77 }
78 return result;
79}
80export function dismissBrowser() {
81 if (!ExponentWebBrowser.dismissBrowser) {
82 throw new UnavailabilityError('WebBrowser', 'dismissBrowser');
83 }
84 ExponentWebBrowser.dismissBrowser();
85}
86export async function openAuthSessionAsync(url, redirectUrl, browserParams = {}) {
87 if (_authSessionIsNativelySupported()) {
88 if (!ExponentWebBrowser.openAuthSessionAsync) {
89 throw new UnavailabilityError('WebBrowser', 'openAuthSessionAsync');
90 }
91 if (Platform.OS === 'web') {
92 return ExponentWebBrowser.openAuthSessionAsync(url, redirectUrl, browserParams);
93 }
94 return ExponentWebBrowser.openAuthSessionAsync(url, redirectUrl);
95 }
96 else {
97 return _openAuthSessionPolyfillAsync(url, redirectUrl, browserParams);
98 }
99}
100export function dismissAuthSession() {
101 if (_authSessionIsNativelySupported()) {
102 if (!ExponentWebBrowser.dismissAuthSession) {
103 throw new UnavailabilityError('WebBrowser', 'dismissAuthSession');
104 }
105 ExponentWebBrowser.dismissAuthSession();
106 }
107 else {
108 if (!ExponentWebBrowser.dismissBrowser) {
109 throw new UnavailabilityError('WebBrowser', 'dismissAuthSession');
110 }
111 ExponentWebBrowser.dismissBrowser();
112 }
113}
114/**
115 * Attempts to complete an auth session in the browser.
116 *
117 * @param options
118 */
119export function maybeCompleteAuthSession(options = {}) {
120 if (ExponentWebBrowser.maybeCompleteAuthSession) {
121 return ExponentWebBrowser.maybeCompleteAuthSession(options);
122 }
123 return { type: 'failed', message: 'Not supported on this platform' };
124}
125/* iOS <= 10 and Android polyfill for SFAuthenticationSession flow */
126function _authSessionIsNativelySupported() {
127 if (Platform.OS === 'android') {
128 return false;
129 }
130 else if (Platform.OS === 'web') {
131 return true;
132 }
133 const versionNumber = parseInt(String(Platform.Version), 10);
134 return versionNumber >= 11;
135}
136let _redirectHandler = null;
137/*
138 * openBrowserAsync on Android doesn't wait until closed, so we need to polyfill
139 * it with AppState
140 */
141// Store the `resolve` function from a Promise to fire when the AppState
142// returns to active
143let _onWebBrowserCloseAndroid = null;
144// If the initial AppState.currentState is null, we assume that the first call to
145// AppState#change event is not actually triggered by a real change,
146// is triggered instead by the bridge capturing the current state
147// (https://reactnative.dev/docs/appstate#basic-usage)
148let _isAppStateAvailable = AppState.currentState !== null;
149function _onAppStateChangeAndroid(state) {
150 if (!_isAppStateAvailable) {
151 _isAppStateAvailable = true;
152 return;
153 }
154 if (state === 'active' && _onWebBrowserCloseAndroid) {
155 _onWebBrowserCloseAndroid();
156 }
157}
158async function _openBrowserAndWaitAndroidAsync(startUrl, browserParams = {}) {
159 const appStateChangedToActive = new Promise(resolve => {
160 _onWebBrowserCloseAndroid = resolve;
161 AppState.addEventListener('change', _onAppStateChangeAndroid);
162 });
163 let result = { type: WebBrowserResultType.CANCEL };
164 const { type } = await openBrowserAsync(startUrl, browserParams);
165 if (type === 'opened') {
166 await appStateChangedToActive;
167 result = { type: WebBrowserResultType.DISMISS };
168 }
169 AppState.removeEventListener('change', _onAppStateChangeAndroid);
170 _onWebBrowserCloseAndroid = null;
171 return result;
172}
173async function _openAuthSessionPolyfillAsync(startUrl, returnUrl, browserParams = {}) {
174 if (_redirectHandler) {
175 throw new Error(`The WebBrowser's auth session is in an invalid state with a redirect handler set when it should not be`);
176 }
177 if (_onWebBrowserCloseAndroid) {
178 throw new Error(`WebBrowser is already open, only one can be open at a time`);
179 }
180 try {
181 if (Platform.OS === 'android') {
182 return await Promise.race([
183 _openBrowserAndWaitAndroidAsync(startUrl, browserParams),
184 _waitForRedirectAsync(returnUrl),
185 ]);
186 }
187 else {
188 return await Promise.race([
189 openBrowserAsync(startUrl, browserParams),
190 _waitForRedirectAsync(returnUrl),
191 ]);
192 }
193 }
194 finally {
195 // We can't dismiss the browser on Android, only call this when it's available.
196 // Users on Android need to manually press the 'x' button in Chrome Custom Tabs, sadly.
197 if (ExponentWebBrowser.dismissBrowser) {
198 ExponentWebBrowser.dismissBrowser();
199 }
200 _stopWaitingForRedirect();
201 }
202}
203function _stopWaitingForRedirect() {
204 if (!_redirectHandler) {
205 throw new Error(`The WebBrowser auth session is in an invalid state with no redirect handler when one should be set`);
206 }
207 Linking.removeEventListener('url', _redirectHandler);
208 _redirectHandler = null;
209}
210function _waitForRedirectAsync(returnUrl) {
211 return new Promise(resolve => {
212 _redirectHandler = (event) => {
213 if (event.url.startsWith(returnUrl)) {
214 resolve({ url: event.url, type: 'success' });
215 }
216 };
217 Linking.addEventListener('url', _redirectHandler);
218 });
219}
220//# sourceMappingURL=WebBrowser.js.map
\No newline at end of file