diff --git a/docs/Reference.md b/docs/Reference.md index ab66728..4db6ddd 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -96,9 +96,9 @@ _Note that using static HTML requires the WebView property [originWhiteList](Ref Controls whether to adjust the content inset for web views that are placed behind a navigation bar, tab bar, or toolbar. The default value is `true`. -| Type | Required | -| ---- | -------- | -| bool | No | +| Type | Required | Platform | +| ---- | -------- | -------- | +| bool | No | iOS | --- @@ -154,28 +154,29 @@ Example: ```jsx { - const { nativeEvent } = syntheticEvent - console.warn('WebView error: ', nativeEvent) - }} - /> + source={{ uri: 'https://facebook.github.io/react-native' }} + onError={syntheticEvent => { + const { nativeEvent } = syntheticEvent; + console.warn('WebView error: ', nativeEvent); + }} +/> ``` Function passed to `onError` is called with a SyntheticEvent wrapping a nativeEvent with these properties: - ``` - canGoBack - canGoForward - code - description - didFailProvisionalNavigation - domain - loading - target - title - url ``` +canGoBack +canGoForward +code +description +didFailProvisionalNavigation +domain +loading +target +title +url +``` + > **_Note_** > Domain is only used on iOS @@ -193,8 +194,8 @@ Example: ```jsx { + source={{ uri: 'https://facebook.github.io/react-native' }} + onLoad={syntheticEvent => { const { nativeEvent } = syntheticEvent; this.url = nativeEvent.url; }} @@ -203,13 +204,13 @@ Example: Function passed to `onLoad` is called with a SyntheticEvent wrapping a nativeEvent with these properties: - ``` - canGoBack - canGoForward - loading - target - title - url +``` +canGoBack +canGoForward +loading +target +title +url ``` --- @@ -222,13 +223,12 @@ Function that is invoked when the `WebView` load succeeds or fails. | -------- | -------- | | function | No | - Example: ```jsx { + source={{ uri: 'https://facebook.github.io/react-native' }} + onLoadEnd={syntheticEvent => { // update component to be aware of loading status const { nativeEvent } = syntheticEvent; this.isLoading = nativeEvent.loading; @@ -238,13 +238,13 @@ Example: Function passed to `onLoadEnd` is called with a SyntheticEvent wrapping a nativeEvent with these properties: - ``` - canGoBack - canGoForward - loading - target - title - url +``` +canGoBack +canGoForward +loading +target +title +url ``` --- @@ -257,13 +257,12 @@ Function that is invoked when the `WebView` starts loading. | -------- | -------- | | function | No | - Example: ```jsx { + source={{ uri: 'https://facebook.github.io/react-native/=' }} + onLoadStart={syntheticEvent => { // update component to be aware of loading status const { nativeEvent } = syntheticEvent; this.isLoading = nativeEvent.loading; @@ -273,13 +272,13 @@ Example: Function passed to `onLoadStart` is called with a SyntheticEvent wrapping a nativeEvent with these properties: - ``` - canGoBack - canGoForward - loading - target - title - url +``` +canGoBack +canGoForward +loading +target +title +url ``` --- @@ -301,23 +300,23 @@ Example: ```jsx { - this.loadingProgress = nativeEvent.progress - }} - /> + source={{ uri: 'https://facebook.github.io/react-native' }} + onLoadProgress={({ nativeEvent }) => { + this.loadingProgress = nativeEvent.progress; + }} +/> ``` Function passed to `onLoadProgress` is called with a SyntheticEvent wrapping a nativeEvent with these properties: - ``` - canGoBack - canGoForward - loading - progress - target - title - url +``` +canGoBack +canGoForward +loading +progress +target +title +url ``` --- @@ -348,23 +347,24 @@ Example: ```jsx { + source={{ uri: 'https://facebook.github.io/react-native' }} + onNavigationStateChange={navState => { // Keep track of going back navigation within component this.canGoBack = navState.canGoBack; -}} /> + }} +/> ``` The `navState` object includes these properties: - ``` - canGoBack - canGoForward - loading - navigationType - target - title - url +``` +canGoBack +canGoForward +loading +navigationType +target +title +url ``` --- @@ -382,9 +382,9 @@ Example: ```jsx //only allow URIs that begin with https:// or git:// + source={{ uri: 'https://facebook.github.io/react-native' }} + originWhitelist={['https://*', 'git://*']} +/> ``` --- @@ -397,17 +397,16 @@ Function that returns a view to show if there's an error. | -------- | -------- | | function | No | - Example: ```jsx } - /> + source={{ uri: 'https://facebook.github.io/react-native' }} + renderError={errorName => } +/> ``` -The function passed to `renderError` will be called with the name of the error +The function passed to `renderError` will be called with the name of the error --- @@ -419,15 +418,14 @@ Function that returns a loading indicator. The startInLoadingState prop must be | -------- | -------- | | function | No | - Example: ```jsx } - /> + source={{ uri: 'https://facebook.github.io/react-native' }} + startInLoadingState={true} + renderLoading={() => } +/> ``` --- @@ -446,7 +444,7 @@ On iOS, when [`useWebKit=true`](Reference.md#usewebkit), this prop will not work ### `onShouldStartLoadWithRequest` -Function that allows custom handling of any web view requests. Return `true` from the function to continue loading the request and `false` to stop loading. +Function that allows custom handling of any web view requests. Return `true` from the function to continue loading the request and `false` to stop loading. On Android, is not called on the first load. @@ -458,10 +456,10 @@ Example: ```jsx { + source={{ uri: 'https://facebook.github.io/react-native' }} + onShouldStartLoadWithRequest={request => { // Only allow navigating within this website - return request.url.startsWith("https://facebook.github.io/react-native") + return request.url.startsWith('https://facebook.github.io/react-native'); }} /> ``` @@ -483,10 +481,10 @@ Example: ```jsx { + source={{ uri: 'https://facebook.github.io/react-native' }} + onShouldStartLoadWithRequest={request => { // Only allow navigating within this website - return request.url.startsWith("https://facebook.github.io/react-native") + return request.url.startsWith('https://facebook.github.io/react-native'); }} /> ``` @@ -528,9 +526,9 @@ Example: ```jsx + source={{ uri: 'https://facebook.github.io/react-native' }} + style={{ marginTop: 20 }} +/> ``` --- diff --git a/package.json b/package.json index 7663370..6d33980 100644 --- a/package.json +++ b/package.json @@ -25,21 +25,24 @@ "react-native": ">=0.57 <0.59" }, "dependencies": { - "escape-string-regexp": "^1.0.5", - "fbjs": "^0.8.17" + "escape-string-regexp": "1.0.5", + "invariant": "2.2.4" }, "devDependencies": { "@babel/core": "^7.2.2", "@semantic-release/git": "7.0.5", + "@types/escape-string-regexp": "^1.0.0", + "@types/invariant": "^2.2.29", "@types/react": "^16.6.3", - "@types/react-native": "^0.57.8", + "@types/react-native": "0.57.40", "babel-jest": "^24.0.0", "flow-bin": "^0.80.0", "jest": "^24.0.0", "metro-react-native-babel-preset": "^0.51.1", "react": "16.6.3", "react-native": "^0.57.8", - "semantic-release": "15.10.3" + "semantic-release": "15.10.3", + "typescript": "^3.3.3333" }, "repository": { "type": "git", diff --git a/js/WebView.android.js b/src/WebView.android.tsx similarity index 56% rename from js/WebView.android.js rename to src/WebView.android.tsx index 562829f..fa50630 100644 --- a/js/WebView.android.js +++ b/src/WebView.android.tsx @@ -1,49 +1,40 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - * @flow - */ - import React from 'react'; -import ReactNative, { +import { ActivityIndicator, Image, requireNativeComponent, - StyleSheet, - UIManager, + UIManager as NotTypedUIManager, View, NativeModules, + WebViewUriSource, + ImageSourcePropType, + findNodeHandle, } from 'react-native'; -import invariant from 'fbjs/lib/invariant'; -import keyMirror from 'fbjs/lib/keyMirror'; +import invariant from 'invariant'; import { defaultOriginWhitelist, createOnShouldStartLoadWithRequest, + getViewManagerConfig, } from './WebViewShared'; -import type { - WebViewError, +import { WebViewErrorEvent, WebViewMessageEvent, WebViewNavigationEvent, WebViewProgressEvent, - WebViewSharedProps, - WebViewSource, + AndroidWebViewProps, + NativeWebViewAndroid, + State, + CustomUIManager, } from './WebViewTypes'; -const resolveAssetSource = Image.resolveAssetSource; +import styles from './WebView.styles'; -const WebViewState = keyMirror({ - IDLE: null, - LOADING: null, - ERROR: null, -}); +const UIManager = NotTypedUIManager as CustomUIManager; + +const resolveAssetSource = Image.resolveAssetSource; const defaultRenderLoading = () => ( @@ -51,15 +42,10 @@ const defaultRenderLoading = () => ( ); -type State = {| - viewState: WebViewState, - lastErrorEvent: ?WebViewError, -|}; - /** * Renders a native WebView. */ -class WebView extends React.Component { +class WebView extends React.Component { static defaultProps = { overScrollMode: 'always', javaScriptEnabled: true, @@ -77,113 +63,88 @@ class WebView extends React.Component { return NativeModules.RNCWebView.isFileUploadSupported(); }; - state = { - viewState: this.props.startInLoadingState - ? WebViewState.LOADING - : WebViewState.IDLE, + state: State = { + viewState: this.props.startInLoadingState ? 'LOADING' : 'IDLE', lastErrorEvent: null, }; - webViewRef = React.createRef(); + webViewRef = React.createRef(); render() { + const { + onMessage, + onShouldStartLoadWithRequest: onShouldStartLoadWithRequestProp, + originWhitelist, + renderError, + renderLoading, + source, + style, + nativeConfig = {}, + ...otherProps + } = this.props; let otherView = null; - if (this.state.viewState === WebViewState.LOADING) { - otherView = (this.props.renderLoading || defaultRenderLoading)(); - } else if (this.state.viewState === WebViewState.ERROR) { + if (this.state.viewState === 'LOADING') { + otherView = (renderLoading || defaultRenderLoading)(); + } else if (this.state.viewState === 'ERROR') { const errorEvent = this.state.lastErrorEvent; invariant(errorEvent != null, 'lastErrorEvent expected to be non-null'); otherView = - this.props.renderError && - this.props.renderError( - errorEvent.domain, - errorEvent.code, - errorEvent.description, - ); - } else if (this.state.viewState !== WebViewState.IDLE) { + renderError && + renderError(errorEvent.domain, errorEvent.code, errorEvent.description); + } else if (this.state.viewState !== 'IDLE') { console.error( 'RNCWebView invalid state encountered: ' + this.state.viewState, ); } - const webViewStyles = [styles.container, this.props.style]; + const webViewStyles = [styles.container, style]; if ( - this.state.viewState === WebViewState.LOADING || - this.state.viewState === WebViewState.ERROR + this.state.viewState === 'LOADING' || + this.state.viewState === 'ERROR' ) { // if we're in either LOADING or ERROR states, don't show the webView webViewStyles.push(styles.hidden); } - let source: WebViewSource = this.props.source || {}; - if (!this.props.source && this.props.html) { - source = { html: this.props.html }; - } else if (!this.props.source && this.props.url) { - source = { uri: this.props.url }; - } - - if (source.method === 'POST' && source.headers) { + if ( + (source as WebViewUriSource).method === 'POST' && + (source as WebViewUriSource).headers + ) { console.warn( 'WebView: `source.headers` is not supported when using POST.', ); - } else if (source.method === 'GET' && source.body) { + } else if ( + (source as WebViewUriSource).method === 'GET' && + (source as WebViewUriSource).body + ) { console.warn('WebView: `source.body` is not supported when using GET.'); } - const nativeConfig = this.props.nativeConfig || {}; - - let NativeWebView = nativeConfig.component || RNCWebView; + let NativeWebView = + (nativeConfig.component as typeof NativeWebViewAndroid) || RNCWebView; const onShouldStartLoadWithRequest = createOnShouldStartLoadWithRequest( this.onShouldStartLoadWithRequestCallback, - this.props.originWhitelist, - this.props.onShouldStartLoadWithRequest, + originWhitelist, + onShouldStartLoadWithRequestProp, ); const webView = ( ); @@ -196,14 +157,7 @@ class WebView extends React.Component { ); } - getViewManagerConfig = (viewManagerName: string) => { - if (!UIManager.getViewManagerConfig) { - return UIManager[viewManagerName]; - } - return UIManager.getViewManagerConfig(viewManagerName); - }; - - getCommands = () => this.getViewManagerConfig('RNCWebView').Commands; + getCommands = () => getViewManagerConfig('RNCWebView').Commands; goForward = () => { UIManager.dispatchViewManagerCommand( @@ -223,7 +177,7 @@ class WebView extends React.Component { reload = () => { this.setState({ - viewState: WebViewState.LOADING, + viewState: 'LOADING', }); UIManager.dispatchViewManagerCommand( this.getWebViewHandle(), @@ -272,8 +226,13 @@ class WebView extends React.Component { } }; + /** + * Returns the native `WebView` node. + */ getWebViewHandle = () => { - return ReactNative.findNodeHandle(this.webViewRef.current); + const nodeHandle = findNodeHandle(this.webViewRef.current); + invariant(nodeHandle != null, 'nodeHandle expected to be non-null'); + return nodeHandle as number; }; onLoadingStart = (event: WebViewNavigationEvent) => { @@ -291,7 +250,7 @@ class WebView extends React.Component { this.setState({ lastErrorEvent: event.nativeEvent, - viewState: WebViewState.ERROR, + viewState: 'ERROR', }); }; @@ -300,7 +259,7 @@ class WebView extends React.Component { onLoad && onLoad(event); onLoadEnd && onLoadEnd(event); this.setState({ - viewState: WebViewState.IDLE, + viewState: 'IDLE', }); this.updateNavigationState(event); }; @@ -329,24 +288,8 @@ class WebView extends React.Component { }; } -const RNCWebView = requireNativeComponent('RNCWebView'); - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - hidden: { - height: 0, - flex: 0, // disable 'flex:1' when hiding a View - }, - loadingView: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - }, - loadingProgressBar: { - height: 20, - }, -}); +const RNCWebView = requireNativeComponent( + 'RNCWebView', +) as typeof NativeWebViewAndroid; module.exports = WebView; diff --git a/js/WebView.ios.js b/src/WebView.ios.tsx similarity index 59% rename from js/WebView.ios.js rename to src/WebView.ios.tsx index e8a4279..a67f5d0 100644 --- a/js/WebView.ios.js +++ b/src/WebView.ios.tsx @@ -4,99 +4,71 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format - * @flow */ import React from 'react'; import { ActivityIndicator, - Linking, - StyleSheet, Text, - UIManager, + UIManager as NotTypedUIManager, View, requireNativeComponent, NativeModules, Image, findNodeHandle, + ImageSourcePropType, } from 'react-native'; - -import invariant from 'fbjs/lib/invariant'; -import keyMirror from 'fbjs/lib/keyMirror'; +import invariant from 'invariant'; import { defaultOriginWhitelist, createOnShouldStartLoadWithRequest, + getViewManagerConfig, } from './WebViewShared'; -import type { - WebViewEvent, - WebViewError, +import { WebViewErrorEvent, WebViewMessageEvent, WebViewNavigationEvent, - WebViewSharedProps, - WebViewSource, WebViewProgressEvent, + IOSWebViewProps, + DecelerationRateConstant, + NativeWebViewIOS, + ViewManager, + State, + CustomUIManager, } from './WebViewTypes'; +import styles from './WebView.styles'; + +const UIManager = NotTypedUIManager as CustomUIManager; + const resolveAssetSource = Image.resolveAssetSource; let didWarnAboutUIWebViewUsage = false; // Imported from https://github.com/facebook/react-native/blob/master/Libraries/Components/ScrollView/processDecelerationRate.js -function processDecelerationRate(decelerationRate) { +const processDecelerationRate = ( + decelerationRate: DecelerationRateConstant | number | undefined, +) => { if (decelerationRate === 'normal') { decelerationRate = 0.998; } else if (decelerationRate === 'fast') { decelerationRate = 0.99; } return decelerationRate; -} +}; -const RNCUIWebViewManager = NativeModules.RNCUIWebViewManager; -const RNCWKWebViewManager = NativeModules.RNCWKWebViewManager; - -const BGWASH = 'rgba(255,255,255,0.8)'; - -const WebViewState = keyMirror({ - IDLE: null, - LOADING: null, - ERROR: null, -}); - -const NavigationType = keyMirror({ - click: true, - formsubmit: true, - backforward: true, - reload: true, - formresubmit: true, - other: true, -}); - -const JSNavigationScheme = 'react-js-navigation'; - -type State = {| - viewState: WebViewState, - lastErrorEvent: ?WebViewError, -|}; - -const DataDetectorTypes = [ - 'phoneNumber', - 'link', - 'address', - 'calendarEvent', - 'trackingNumber', - 'flightNumber', - 'lookupSuggestion', - 'none', - 'all', -]; +const RNCUIWebViewManager = NativeModules.RNCUIWebViewManager as ViewManager; +const RNCWKWebViewManager = NativeModules.RNCWKWebViewManager as ViewManager; const defaultRenderLoading = () => ( ); -const defaultRenderError = (errorDomain, errorCode, errorDesc) => ( +const defaultRenderError = ( + errorDomain: string | undefined, + errorCode: number, + errorDesc: string, +) => ( Error loading page {'Domain: ' + errorDomain} @@ -105,32 +77,7 @@ const defaultRenderError = (errorDomain, errorCode, errorDesc) => ( ); -/** - * `WebView` renders web content in a native view. - * - *``` - * import React, { Component } from 'react'; - * import { WebView } from 'react-native'; - * - * class MyWeb extends Component { - * render() { - * return ( - * - * ); - * } - * } - *``` - * - * You can use this component to navigate back and forth in the web view's - * history and configure various properties for the web content. - */ -class WebView extends React.Component { - static JSNavigationScheme = JSNavigationScheme; - static NavigationType = NavigationType; - +class WebView extends React.Component { static defaultProps = { useWebKit: true, cacheEnabled: true, @@ -143,14 +90,12 @@ class WebView extends React.Component { return true; }; - state = { - viewState: this.props.startInLoadingState - ? WebViewState.LOADING - : WebViewState.IDLE, + state: State = { + viewState: this.props.startInLoadingState ? 'LOADING' : 'IDLE', lastErrorEvent: null, }; - webViewRef = React.createRef(); + webViewRef = React.createRef(); UNSAFE_componentWillMount() { if (!this.props.useWebKit && !didWarnAboutUIWebViewUsage) { @@ -185,108 +130,80 @@ class WebView extends React.Component { } render() { + const { + decelerationRate: decelerationRateProp, + nativeConfig = {}, + onMessage, + onShouldStartLoadWithRequest: onShouldStartLoadWithRequestProp, + originWhitelist, + renderError, + renderLoading, + scalesPageToFit = this.props.useWebKit ? undefined : true, + style, + useWebKit, + ...otherProps + } = this.props; + let otherView = null; - let scalesPageToFit; - - if (this.props.useWebKit) { - ({ scalesPageToFit } = this.props); - } else { - ({ scalesPageToFit = true } = this.props); - } - - if (this.state.viewState === WebViewState.LOADING) { - otherView = (this.props.renderLoading || defaultRenderLoading)(); - } else if (this.state.viewState === WebViewState.ERROR) { + if (this.state.viewState === 'LOADING') { + otherView = (renderLoading || defaultRenderLoading)(); + } else if (this.state.viewState === 'ERROR') { const errorEvent = this.state.lastErrorEvent; invariant(errorEvent != null, 'lastErrorEvent expected to be non-null'); - otherView = (this.props.renderError || defaultRenderError)( + otherView = (renderError || defaultRenderError)( errorEvent.domain, errorEvent.code, errorEvent.description, ); - } else if (this.state.viewState !== WebViewState.IDLE) { + } else if (this.state.viewState !== 'IDLE') { console.error( 'RNCWebView invalid state encountered: ' + this.state.viewState, ); } - const webViewStyles = [styles.container, styles.webView, this.props.style]; + const webViewStyles = [styles.container, styles.webView, style]; if ( - this.state.viewState === WebViewState.LOADING || - this.state.viewState === WebViewState.ERROR + this.state.viewState === 'LOADING' || + this.state.viewState === 'ERROR' ) { // if we're in either LOADING or ERROR states, don't show the webView webViewStyles.push(styles.hidden); } - const nativeConfig = this.props.nativeConfig || {}; - const onShouldStartLoadWithRequest = createOnShouldStartLoadWithRequest( this.onShouldStartLoadWithRequestCallback, - this.props.originWhitelist, - this.props.onShouldStartLoadWithRequest, + originWhitelist, + onShouldStartLoadWithRequestProp, ); - const decelerationRate = processDecelerationRate( - this.props.decelerationRate, - ); + const decelerationRate = processDecelerationRate(decelerationRateProp); - let source: WebViewSource = this.props.source || {}; - if (!this.props.source && this.props.html) { - source = { html: this.props.html }; - } else if (!this.props.source && this.props.url) { - source = { uri: this.props.url }; - } + let NativeWebView = nativeConfig.component as typeof NativeWebViewIOS; - let NativeWebView = nativeConfig.component; - - if (this.props.useWebKit) { - NativeWebView = NativeWebView || RNCWKWebView; + if (useWebKit) { + NativeWebView = NativeWebViewIOS || RNCWKWebView; } else { - NativeWebView = NativeWebView || RNCUIWebView; + NativeWebView = NativeWebViewIOS || RNCUIWebView; } const webView = ( ); @@ -299,17 +216,10 @@ class WebView extends React.Component { ); } - _getViewManagerConfig = (viewManagerName: string) => { - if (!UIManager.getViewManagerConfig) { - return UIManager[viewManagerName]; - } - return UIManager.getViewManagerConfig(viewManagerName); - }; - _getCommands = () => !this.props.useWebKit - ? this._getViewManagerConfig('RNCUIWebView').Commands - : this._getViewManagerConfig('RNCWKWebView').Commands; + ? getViewManagerConfig('RNCUIWebView').Commands + : getViewManagerConfig('RNCWKWebView').Commands; /** * Go forward one page in the web view's history. @@ -337,7 +247,7 @@ class WebView extends React.Component { * Reloads the current page. */ reload = () => { - this.setState({ viewState: WebViewState.LOADING }); + this.setState({ viewState: 'LOADING' }); UIManager.dispatchViewManagerCommand( this.getWebViewHandle(), this._getCommands().reload, @@ -402,7 +312,9 @@ class WebView extends React.Component { * Returns the native `WebView` node. */ getWebViewHandle = () => { - return findNodeHandle(this.webViewRef.current); + const nodeHandle = findNodeHandle(this.webViewRef.current); + invariant(nodeHandle != null, 'nodeHandle expected to be non-null'); + return nodeHandle as number; }; _onLoadingStart = (event: WebViewNavigationEvent) => { @@ -420,7 +332,7 @@ class WebView extends React.Component { this.setState({ lastErrorEvent: event.nativeEvent, - viewState: WebViewState.ERROR, + viewState: 'ERROR', }); }; @@ -429,7 +341,7 @@ class WebView extends React.Component { onLoad && onLoad(event); onLoadEnd && onLoadEnd(event); this.setState({ - viewState: WebViewState.IDLE, + viewState: 'IDLE', }); this._updateNavigationState(event); }; @@ -460,7 +372,7 @@ class WebView extends React.Component { viewManager.startLoadWithResult(!!shouldStart, lockIdentifier); }; - componentDidUpdate(prevProps: WebViewSharedProps) { + componentDidUpdate(prevProps: IOSWebViewProps) { if (!(prevProps.useWebKit && this.props.useWebKit)) { return; } @@ -477,7 +389,10 @@ class WebView extends React.Component { } } - _showRedboxOnPropChanges(prevProps, propName: string) { + _showRedboxOnPropChanges( + prevProps: IOSWebViewProps, + propName: keyof IOSWebViewProps, + ) { if (this.props[propName] !== prevProps[propName]) { console.error( `Changes to property ${propName} do nothing after the initial render.`, @@ -486,43 +401,11 @@ class WebView extends React.Component { } } -const RNCUIWebView = requireNativeComponent('RNCUIWebView'); -const RNCWKWebView = requireNativeComponent('RNCWKWebView'); - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - errorContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: BGWASH, - }, - errorText: { - fontSize: 14, - textAlign: 'center', - marginBottom: 2, - }, - errorTextTitle: { - fontSize: 15, - fontWeight: '500', - marginBottom: 10, - }, - hidden: { - height: 0, - flex: 0, // disable 'flex:1' when hiding a View - }, - loadingView: { - backgroundColor: BGWASH, - flex: 1, - justifyContent: 'center', - alignItems: 'center', - height: 100, - }, - webView: { - backgroundColor: '#ffffff', - }, -}); +const RNCUIWebView: typeof NativeWebViewIOS = requireNativeComponent( + 'RNCUIWebView', +); +const RNCWKWebView: typeof NativeWebViewIOS = requireNativeComponent( + 'RNCWKWebView', +); module.exports = WebView; diff --git a/src/WebView.styles.ts b/src/WebView.styles.ts new file mode 100644 index 0000000..eaa9692 --- /dev/null +++ b/src/WebView.styles.ts @@ -0,0 +1,53 @@ +import { StyleSheet, ViewStyle, TextStyle } from 'react-native'; + +interface Styles { + container: ViewStyle; + errorContainer: ViewStyle; + errorText: TextStyle; + errorTextTitle: TextStyle; + hidden: ViewStyle; + loadingView: ViewStyle; + webView: ViewStyle; + loadingProgressBar: ViewStyle; +} + +const BGWASH = 'rgba(255,255,255,0.8)'; + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + errorContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: BGWASH, + }, + hidden: { + height: 0, + flex: 0, // disable 'flex:1' when hiding a View + }, + loadingView: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + }, + loadingProgressBar: { + height: 20, + }, + errorText: { + fontSize: 14, + textAlign: 'center', + marginBottom: 2, + }, + errorTextTitle: { + fontSize: 15, + fontWeight: '500', + marginBottom: 10, + }, + webView: { + backgroundColor: '#ffffff', + }, +}); + +export default styles; diff --git a/js/WebViewShared.js b/src/WebViewShared.ts similarity index 62% rename from js/WebViewShared.js rename to src/WebViewShared.ts index 323ec7c..792f450 100644 --- a/js/WebViewShared.js +++ b/src/WebViewShared.ts @@ -9,13 +9,15 @@ */ import escapeStringRegexp from 'escape-string-regexp'; -import { Linking } from 'react-native'; -import type { +import { Linking, UIManager as NotTypedUIManager } from 'react-native'; +import { WebViewNavigationEvent, - WebViewNavigation, OnShouldStartLoadWithRequest, + CustomUIManager, } from './WebViewTypes'; +const UIManager = NotTypedUIManager as CustomUIManager; + const defaultOriginWhitelist = ['http://*', 'https://*']; const extractOrigin = (url: string): string => { @@ -24,16 +26,14 @@ const extractOrigin = (url: string): string => { }; const originWhitelistToRegex = (originWhitelist: string): string => - `^${escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*')}`; + `^${escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*')}`; -const passesWhitelist = (compiledWhitelist: Array, url: string) => { +const passesWhitelist = (compiledWhitelist: string[], url: string) => { const origin = extractOrigin(url); return compiledWhitelist.some(x => new RegExp(x).test(origin)); }; -const compileWhitelist = ( - originWhitelist: ?$ReadOnlyArray, -): Array => +const compileWhitelist = (originWhitelist: string[]): string[] => ['about:blank', ...(originWhitelist || [])].map(originWhitelistToRegex); const createOnShouldStartLoadWithRequest = ( @@ -42,8 +42,8 @@ const createOnShouldStartLoadWithRequest = ( url: string, lockIdentifier: number, ) => void, - originWhitelist: ?$ReadOnlyArray, - onShouldStartLoadWithRequest: ?OnShouldStartLoadWithRequest, + originWhitelist: string[], + onShouldStartLoadWithRequest?: OnShouldStartLoadWithRequest, ) => { return ({ nativeEvent }: WebViewNavigationEvent) => { let shouldStart = true; @@ -51,7 +51,7 @@ const createOnShouldStartLoadWithRequest = ( if (!passesWhitelist(compileWhitelist(originWhitelist), url)) { Linking.openURL(url); - shouldStart = false + shouldStart = false; } if (onShouldStartLoadWithRequest) { @@ -62,4 +62,17 @@ const createOnShouldStartLoadWithRequest = ( }; }; -export { defaultOriginWhitelist, createOnShouldStartLoadWithRequest }; +const getViewManagerConfig = ( + viewManagerName: 'RNCUIWebView' | 'RNCWKWebView' | 'RNCWebView', +) => { + if (!UIManager.getViewManagerConfig) { + return UIManager[viewManagerName]; + } + return UIManager.getViewManagerConfig(viewManagerName); +}; + +export { + defaultOriginWhitelist, + createOnShouldStartLoadWithRequest, + getViewManagerConfig, +}; diff --git a/js/WebViewTypes.js b/src/WebViewTypes.js similarity index 100% rename from js/WebViewTypes.js rename to src/WebViewTypes.js diff --git a/src/WebViewTypes.ts b/src/WebViewTypes.ts new file mode 100644 index 0000000..7766ac3 --- /dev/null +++ b/src/WebViewTypes.ts @@ -0,0 +1,628 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import { ReactNode, ReactElement, Component } from 'react'; +import { + NativeSyntheticEvent, + ViewStyle, + ViewProps, + StyleProp, + NativeMethodsMixin, + Constructor, + UIManagerStatic, +} from 'react-native'; + +interface WebViewCommands { + goForward: Function; + goBack: Function; + reload: Function; + stopLoading: Function; + postMessage: Function; + injectJavaScript: Function; + loadUrl: Function; +} + +export interface CustomUIManager extends UIManagerStatic { + getViewManagerConfig?: ( + name: string, + ) => { + Commands: WebViewCommands; + }; + dispatchViewManagerCommand: ( + viewHandle: number, + command: any, + params: any, + ) => void; + RNCUIWebView: { + Commands: WebViewCommands; + }; + RNCWKWebView: { + Commands: WebViewCommands; + }; + RNCWebView: { + Commands: WebViewCommands; + }; +} + +type WebViewState = 'IDLE' | 'LOADING' | 'ERROR'; + +interface BaseState { + viewState: WebViewState; +} + +interface NormalState extends BaseState { + viewState: 'IDLE' | 'LOADING'; + lastErrorEvent: WebViewError | null; +} + +interface ErrorState extends BaseState { + viewState: 'ERROR'; + lastErrorEvent: WebViewError; +} + +export type State = NormalState | ErrorState; + +declare class NativeWebViewIOSComponent extends Component< + IOSNativeWebViewProps +> {} +declare const NativeWebViewIOSBase: Constructor & + typeof NativeWebViewIOSComponent; +export class NativeWebViewIOS extends NativeWebViewIOSBase {} + +declare class NativeWebViewAndroidComponent extends Component< + AndroidNativeWebViewProps +> {} +declare const NativeWebViewAndroidBase: Constructor & + typeof NativeWebViewAndroidComponent; +export class NativeWebViewAndroid extends NativeWebViewAndroidBase {} + +export interface ContentInsetProp { + top?: number; + left?: number; + bottom?: number; + right?: number; +} + +export interface WebViewNativeEvent { + url: string; + loading: boolean; + title: string; + canGoBack: boolean; + canGoForward: boolean; + lockIdentifier: number; +} + +export interface WebViewProgressEvent extends WebViewNativeEvent { + progress: number; +} + +export interface WebViewNavigation extends WebViewNativeEvent { + navigationType: + | 'click' + | 'formsubmit' + | 'backforward' + | 'reload' + | 'formresubmit' + | 'other'; +} + +export type DecelerationRateConstant = 'normal' | 'fast'; + +export interface WebViewMessage extends WebViewNativeEvent { + data: string; +} + +export interface WebViewError extends WebViewNativeEvent { + /** + * `domain` is only used on iOS + */ + domain?: string; + code: number; + description: string; +} + +export type WebViewEvent = NativeSyntheticEvent; + +export type WebViewNavigationEvent = NativeSyntheticEvent; + +export type WebViewMessageEvent = NativeSyntheticEvent; + +export type WebViewErrorEvent = NativeSyntheticEvent; + +export type DataDetectorTypes = + | 'phoneNumber' + | 'link' + | 'address' + | 'calendarEvent' + | 'trackingNumber' + | 'flightNumber' + | 'lookupSuggestion' + | 'none' + | 'all'; + +export type OverScrollModeType = 'always' | 'content' | 'never'; + +export interface WebViewSourceUri { + /** + * The URI to load in the `WebView`. Can be a local or remote file. + */ + uri: string; + + /** + * The HTTP Method to use. Defaults to GET if not specified. + * NOTE: On Android, only GET and POST are supported. + */ + method?: string; + + /** + * Additional HTTP headers to send with the request. + * NOTE: On Android, this can only be used with GET requests. + */ + headers?: Object; + + /** + * The HTTP body to send with the request. This must be a valid + * UTF-8 string, and will be sent exactly as specified, with no + * additional encoding (e.g. URL-escaping or base64) applied. + * NOTE: On Android, this can only be used with POST requests. + */ + body?: string; +} + +export interface WebViewSourceHtml { + /** + * A static HTML page to display in the WebView. + */ + html: string; + /** + * The base URL to be used for any relative links in the HTML. + */ + baseUrl: string; +} + +export type WebViewSource = WebViewSourceUri | WebViewSourceHtml; + +export interface ViewManager { + startLoadWithResult: Function; +} + +export type WebViewNativeConfig = { + /** + * The native component used to render the WebView. + */ + component?: typeof NativeWebViewIOS | typeof NativeWebViewAndroid; + /** + * Set props directly on the native component WebView. Enables custom props which the + * original WebView doesn't pass through. + */ + props?: Object; + /** + * Set the ViewManager to use for communication with the native side. + * @platform ios + */ + viewManager?: ViewManager; +}; + +export type OnShouldStartLoadWithRequest = ( + event: WebViewNavigation, +) => boolean; + +export interface CommonNativeWebViewProps extends ViewProps { + cacheEnabled?: boolean; + injectedJavaScript?: string; + mediaPlaybackRequiresUserAction?: boolean; + messagingEnabled: boolean; + onLoadingError: (event: WebViewErrorEvent) => void; + onLoadingFinish: (event: WebViewNavigationEvent) => void; + onLoadingProgress: (event: WebViewProgressEvent) => void; + onLoadingStart: (event: WebViewNavigationEvent) => void; + onMessage: (event: WebViewMessageEvent) => void; + onShouldStartLoadWithRequest: (event: WebViewNavigationEvent) => void; + scalesPageToFit?: boolean; + showsHorizontalScrollIndicator?: boolean; + showsVerticalScrollIndicator?: boolean; + // TODO: find a better way to type this. + source: any; + userAgent?: string; +} + +export interface AndroidNativeWebViewProps extends CommonNativeWebViewProps { + allowFileAccess?: boolean; + allowUniversalAccessFromFileURLs?: boolean; + androidHardwareAccelerationDisabled?: boolean; + domStorageEnabled?: boolean; + geolocationEnabled?: boolean; + javaScriptEnabled?: boolean; + mixedContentMode?: 'never' | 'always' | 'compatibility'; + onContentSizeChange?: (event: WebViewEvent) => void; + overScrollMode?: OverScrollModeType; + saveFormDataDisabled?: boolean; + thirdPartyCookiesEnabled?: boolean; + urlPrefixesForDefaultIntent?: string[]; +} + +export interface IOSNativeWebViewProps extends CommonNativeWebViewProps { + allowsBackForwardNavigationGestures?: boolean; + allowsInlineMediaPlayback?: boolean; + allowsLinkPreview?: boolean; + automaticallyAdjustContentInsets?: boolean; + bounces?: boolean; + contentInset?: ContentInsetProp; + dataDetectorTypes?: DataDetectorTypes | DataDetectorTypes[]; + decelerationRate?: number; + directionalLockEnabled?: boolean; + hideKeyboardAccessoryView?: boolean; + incognito?: boolean; + pagingEnabled?: boolean; + scrollEnabled?: boolean; + useSharedProcessPool?: boolean; +} + +export interface IOSWebViewProps extends WebViewSharedProps { + /** + * If true, use WKWebView instead of UIWebView. + * @platform ios + */ + useWebKit?: boolean; + + /** + * Does not store any data within the lifetime of the WebView. + */ + incognito?: boolean; + + /** + * Boolean value that determines whether the web view bounces + * when it reaches the edge of the content. The default value is `true`. + * @platform ios + */ + bounces?: boolean; + + /** + * A floating-point number that determines how quickly the scroll view + * decelerates after the user lifts their finger. You may also use the + * string shortcuts `"normal"` and `"fast"` which match the underlying iOS + * settings for `UIScrollViewDecelerationRateNormal` and + * `UIScrollViewDecelerationRateFast` respectively: + * + * - normal: 0.998 + * - fast: 0.99 (the default for iOS web view) + * @platform ios + */ + decelerationRate?: DecelerationRateConstant | number; + + /** + * Boolean value that determines whether scrolling is enabled in the + * `WebView`. The default value is `true`. + * @platform ios + */ + scrollEnabled?: boolean; + + /** + * If the value of this property is true, the scroll view stops on multiples + * of the scroll view’s bounds when the user scrolls. + * The default value is false. + * @platform ios + */ + pagingEnabled?: boolean; + + /** + * Controls whether to adjust the content inset for web views that are + * placed behind a navigation bar, tab bar, or toolbar. The default value + * is `true`. + * @platform ios + */ + automaticallyAdjustContentInsets?: boolean; + + /** + * The amount by which the web view content is inset from the edges of + * the scroll view. Defaults to {top: 0, left: 0, bottom: 0, right: 0}. + * @platform ios + */ + contentInset?: ContentInsetProp; + + /** + * Determines the types of data converted to clickable URLs in the web view's content. + * By default only phone numbers are detected. + * + * You can provide one type or an array of many types. + * + * Possible values for `dataDetectorTypes` are: + * + * - `'phoneNumber'` + * - `'link'` + * - `'address'` + * - `'calendarEvent'` + * - `'none'` + * - `'all'` + * + * With the new WebKit implementation, we have three new values: + * - `'trackingNumber'`, + * - `'flightNumber'`, + * - `'lookupSuggestion'`, + * + * @platform ios + */ + dataDetectorTypes?: DataDetectorTypes | DataDetectorTypes[]; + + /** + * Boolean that determines whether HTML5 videos play inline or use the + * native full-screen controller. The default value is `false`. + * + * **NOTE** : In order for video to play inline, not only does this + * property need to be set to `true`, but the video element in the HTML + * document must also include the `webkit-playsinline` attribute. + * @platform ios + */ + allowsInlineMediaPlayback?: boolean; + /** + * Hide the accessory view when the keyboard is open. Default is false to be + * backward compatible. + */ + hideKeyboardAccessoryView?: boolean; + /** + * A Boolean value indicating whether horizontal swipe gestures will trigger + * back-forward list navigations. + */ + allowsBackForwardNavigationGestures?: boolean; + /** + * A Boolean value indicating whether WebKit WebView should be created using a shared + * process pool, enabling WebViews to share cookies and localStorage between each other. + * Default is true but can be set to false for backwards compatibility. + * @platform ios + */ + useSharedProcessPool?: boolean; + /** + * The custom user agent string. + */ + userAgent?: string; + + /** + * A Boolean value that determines whether pressing on a link + * displays a preview of the destination for the link. + * + * This property is available on devices that support 3D Touch. + * In iOS 10 and later, the default value is `true`; before that, the default value is `false`. + * @platform ios + */ + allowsLinkPreview?: boolean; + + /** + * A Boolean value that determines whether scrolling is disabled in a particular direction. + * The default value is `true`. + * @platform ios + */ + directionalLockEnabled?: boolean; +} + +export interface AndroidWebViewProps extends WebViewSharedProps { + onNavigationStateChange?: (event: WebViewNavigation) => void; + onContentSizeChange?: (event: WebViewEvent) => void; + + /** + * https://developer.android.com/reference/android/view/View#OVER_SCROLL_NEVER + * Sets the overScrollMode. Possible values are: + * + * - `'always'` (default) + * - `'content'` + * - `'never'` + * + * @platform android + */ + overScrollMode?: OverScrollModeType; + + /** + * Sets whether Geolocation is enabled. The default is false. + * @platform android + */ + geolocationEnabled?: boolean; + + /** + * Boolean that sets whether JavaScript running in the context of a file + * scheme URL should be allowed to access content from any origin. + * Including accessing content from other file scheme URLs + * @platform android + */ + allowUniversalAccessFromFileURLs?: boolean; + + /** + * Sets whether the webview allow access to file system. + * @platform android + */ + allowFileAccess?: boolean; + + /** + * Used on Android only, controls whether form autocomplete data should be saved + * @platform android + */ + saveFormDataDisabled?: boolean; + + /** + * Used on Android only, controls whether the given list of URL prefixes should + * make {@link com.facebook.react.views.webview.ReactWebViewClient} to launch a + * default activity intent for those URL instead of loading it within the webview. + * Use this to list URLs that WebView cannot handle, e.g. a PDF url. + * @platform android + */ + urlPrefixesForDefaultIntent?: string[]; + + /** + * Boolean value to enable JavaScript in the `WebView`. Used on Android only + * as JavaScript is enabled by default on iOS. The default value is `true`. + * @platform android + */ + javaScriptEnabled?: boolean; + + /** + * Boolean value to disable Hardware Acceleration in the `WebView`. Used on Android only + * as Hardware Acceleration is a feature only for Android. The default value is `false`. + * @platform android + */ + androidHardwareAccelerationDisabled?: boolean; + + /** + * Boolean value to enable third party cookies in the `WebView`. Used on + * Android Lollipop and above only as third party cookies are enabled by + * default on Android Kitkat and below and on iOS. The default value is `true`. + * @platform android + */ + thirdPartyCookiesEnabled?: boolean; + + /** + * Boolean value to control whether DOM Storage is enabled. Used only in + * Android. + * @platform android + */ + domStorageEnabled?: boolean; + + /** + * Sets the user-agent for the `WebView`. + * @platform android + */ + userAgent?: string; + + /** + * Specifies the mixed content mode. i.e WebView will allow a secure origin to load content from any other origin. + * + * Possible values for `mixedContentMode` are: + * + * - `'never'` (default) - WebView will not allow a secure origin to load content from an insecure origin. + * - `'always'` - WebView will allow a secure origin to load content from any other origin, even if that origin is insecure. + * - `'compatibility'` - WebView will attempt to be compatible with the approach of a modern web browser with regard to mixed content. + * @platform android + */ + mixedContentMode?: 'never' | 'always' | 'compatibility'; +} + +export interface WebViewSharedProps extends ViewProps { + /** + * Loads static html or a uri (with optional headers) in the WebView. + */ + source?: WebViewSource; + + /** + * Function that returns a view to show if there's an error. + */ + renderError: ( + errorDomain: string | undefined, + errorCode: number, + errorDesc: string, + ) => ReactElement; // view to show if there's an error + + /** + * Function that returns a loading indicator. + */ + renderLoading: () => ReactElement; + + /** + * Function that is invoked when the `WebView` has finished loading. + */ + onLoad: (event: WebViewNavigationEvent) => void; + + /** + * Function that is invoked when the `WebView` load succeeds or fails. + */ + onLoadEnd: (event: WebViewNavigationEvent | WebViewErrorEvent) => void; + + /** + * Function that is invoked when the `WebView` starts loading. + */ + onLoadStart: (event: WebViewNavigationEvent) => void; + + /** + * Function that is invoked when the `WebView` load fails. + */ + onError: (event: WebViewErrorEvent) => void; + + /** + * Function that is invoked when the `WebView` loading starts or ends. + */ + onNavigationStateChange?: (event: WebViewNavigation) => void; + + /** + * Function that is invoked when the webview calls `window.ReactNativeWebView.postMessage`. + * Setting this property will inject this global into your webview. + * + * `window.ReactNativeWebView.postMessage` accepts one argument, `data`, which will be + * available on the event object, `event.nativeEvent.data`. `data` must be a string. + */ + onMessage?: (event: WebViewMessageEvent) => void; + + /** + * Function that is invoked when the `WebView` is loading. + */ + onLoadProgress?: (event: WebViewProgressEvent) => void; + + /** + * Boolean value that forces the `WebView` to show the loading view + * on the first load. + */ + startInLoadingState?: boolean; + + /** + * Set this to provide JavaScript that will be injected into the web page + * when the view loads. + */ + injectedJavaScript?: string; + + /** + * Boolean value that determines whether a horizontal scroll indicator is + * shown in the `WebView`. The default value is `true`. + */ + showsHorizontalScrollIndicator?: boolean; + + /** + * Boolean value that determines whether a vertical scroll indicator is + * shown in the `WebView`. The default value is `true`. + */ + showsVerticalScrollIndicator?: boolean; + + /** + * Boolean that controls whether the web content is scaled to fit + * the view and enables the user to change the scale. The default value + * is `true`. + * + * On iOS, when `useWebKit=true`, this prop will not work. + */ + scalesPageToFit?: boolean; + + /** + * Boolean that determines whether HTML5 audio and video requires the user + * to tap them before they start playing. The default value is `true`. + */ + mediaPlaybackRequiresUserAction?: boolean; + + /** + * List of origin strings to allow being navigated to. The strings allow + * wildcards and get matched against *just* the origin (not the full URL). + * If the user taps to navigate to a new page but the new page is not in + * this whitelist, we will open the URL in Safari. + * The default whitelisted origins are "http://*" and "https://*". + */ + originWhitelist: string[]; + + /** + * Function that allows custom handling of any web view requests. Return + * `true` from the function to continue loading the request and `false` + * to stop loading. The `navigationType` is always `other` on android. + */ + onShouldStartLoadWithRequest?: OnShouldStartLoadWithRequest; + + /** + * Override the native component used to render the WebView. Enables a custom native + * WebView which uses the same JavaScript as the original WebView. + */ + nativeConfig?: WebViewNativeConfig; + + /** + * Should caching be enabled. Default is true. + */ + cacheEnabled?: boolean; + + style?: StyleProp; + children: ReactNode; +} diff --git a/js/__tests__/WebViewShared-test.js b/src/__tests__/WebViewShared-test.js similarity index 100% rename from js/__tests__/WebViewShared-test.js rename to src/__tests__/WebViewShared-test.js diff --git a/js/__tests__/__snapshots__/WebViewShared-test.js.snap b/src/__tests__/__snapshots__/WebViewShared-test.js.snap similarity index 100% rename from js/__tests__/__snapshots__/WebViewShared-test.js.snap rename to src/__tests__/__snapshots__/WebViewShared-test.js.snap diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b963937 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "baseUrl": "./src/", + "jsx": "react-native", + "module": "esnext", + "moduleResolution": "node", + "lib": ["es2017"], + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "pretty": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true + } +} diff --git a/yarn.lock b/yarn.lock index ba3fb17..7649ed8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -786,6 +786,11 @@ into-stream "^4.0.0" lodash "^4.17.4" +"@types/escape-string-regexp@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/escape-string-regexp/-/escape-string-regexp-1.0.0.tgz#052d16d87db583b72daceae4eaabddb66954424c" + integrity sha512-KAruqgk9/340M4MYYasdBET+lyYN8KMXUuRKWO72f4SbmIMMFp9nnJiXUkJS0HC2SFe4x0R/fLozXIzqoUfSjA== + "@types/events@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" @@ -800,6 +805,11 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/invariant@^2.2.29": + version "2.2.29" + resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.29.tgz#aa845204cd0a289f65d47e0de63a6a815e30cc66" + integrity sha512-lRVw09gOvgviOfeUrKc/pmTiRZ7g7oDOU6OAutyuSHpm1/o2RaBQvRhgK8QEdu+FFuw/wnWb29A/iuxv9i8OpQ== + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -815,10 +825,10 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.0.tgz#4c48fed958d6dcf9487195a0ef6456d5f6e0163a" integrity sha512-eItQyV43bj4rR3JPV0Skpl1SncRCdziTEK9/v8VwXmV6d/qOUO8/EuWeHBbCZcsfSHfzI5UyMJLCSXtxxznyZg== -"@types/react-native@^0.57.8": - version "0.57.38" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.57.38.tgz#2ff6ed1a7cc207afbfd87bf496513d96931d1446" - integrity sha512-bmY2ad/vQgP0HMT7Q7EQzirDBt5ibp+kBHclTnY7/i5MrdqE1oY+3b9NkDg3ohXlumr7p5stAG6I55nhfeUV6Q== +"@types/react-native@0.57.40": + version "0.57.40" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.57.40.tgz#a227acb6b2c7372cb678202df4cfe716ace9ce94" + integrity sha512-nCcF9gNYp/VeV5QBtHbn/VnXZG+T8EsMxZFx8qqZfBe1gxJUsldXgOXYd1kcrrYzUX0LqG/KipDBX++fB2qQ0g== dependencies: "@types/prop-types" "*" "@types/react" "*" @@ -2468,7 +2478,7 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= @@ -2741,7 +2751,7 @@ fbjs-scripts@^1.0.0: semver "^5.1.0" through2 "^2.0.0" -fbjs@^0.8.17, fbjs@^0.8.9: +fbjs@^0.8.9: version "0.8.17" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90= @@ -3511,7 +3521,7 @@ into-stream@^4.0.0: from2 "^2.1.1" p-is-promise "^2.0.0" -invariant@^2.2.4: +invariant@2.2.4, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -7999,6 +8009,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typescript@^3.3.3333: + version "3.3.3333" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.3333.tgz#171b2c5af66c59e9431199117a3bcadc66fdcfd6" + integrity sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw== + ua-parser-js@^0.7.18: version "0.7.19" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b"