mirror of
https://github.com/status-im/react-native-webview.git
synced 2025-02-23 01:08:36 +00:00
Merge pull request #32 from empyrical/flow-check
Add WebView Flow types
This commit is contained in:
commit
3f2dd70b94
88
.flowconfig
Normal file
88
.flowconfig
Normal file
@ -0,0 +1,88 @@
|
||||
[ignore]
|
||||
; This flowconfig is forked by platform - the only difference between them is which suffix is ignored.
|
||||
.*/*[.]android.js
|
||||
;.*/*[.]ios.js
|
||||
|
||||
; Ignore templates for 'react-native init'
|
||||
.*/local-cli/templates/.*
|
||||
|
||||
; Ignore the Dangerfile
|
||||
node_modules/react-native/bots/dangerfile.js
|
||||
|
||||
; Ignore "BUCK" generated dirs
|
||||
node_modules/react-native/\.buckd/
|
||||
|
||||
; Ignore unexpected extra "@providesModule"
|
||||
.*/node_modules/.*/node_modules/fbjs/.*
|
||||
|
||||
; Ignore duplicate module providers
|
||||
; For RN Apps installed via npm, "Libraries" folder is inside
|
||||
; "node_modules/react-native" but in the source repo it is in the root
|
||||
.*/Libraries/react-native/React.js
|
||||
|
||||
; Ignore polyfills
|
||||
.*/Libraries/polyfills/.*
|
||||
|
||||
; Ignore metro
|
||||
.*/node_modules/metro/.*
|
||||
|
||||
; Ignore "config-chain"'s test folder - it has a corrupt JSON file that's tripping flow
|
||||
.*/node_modules/config-chain/test/*.
|
||||
|
||||
; These should not be required directly
|
||||
; require from fbjs/lib instead: require('fbjs/lib/invariant')
|
||||
.*/node_modules/invariant/.*
|
||||
.*/node_modules/warning/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
node_modules/react-native/Libraries/react-native/react-native-interface.js
|
||||
node_modules/react-native/flow/
|
||||
node_modules/react-native/flow-github/
|
||||
|
||||
[lints]
|
||||
|
||||
[options]
|
||||
emoji=true
|
||||
|
||||
esproposal.optional_chaining=enable
|
||||
esproposal.nullish_coalescing=enable
|
||||
|
||||
module.system=haste
|
||||
module.system.haste.use_name_reducers=true
|
||||
# keep the following in sync with server/haste/hasteImpl.js
|
||||
# get basename
|
||||
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
|
||||
# strip .js or .js.flow suffix
|
||||
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
|
||||
# strip platform suffix
|
||||
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
|
||||
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
|
||||
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
|
||||
module.system.haste.paths.blacklist=.*/__tests__/.*
|
||||
module.system.haste.paths.blacklist=.*/__mocks__/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/js/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/RNTester/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/IntegrationTests/.*
|
||||
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
|
||||
; Surpress error `Duplicate module provider` until the slimmening is done
|
||||
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WebView/*.
|
||||
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WKWebView/*.
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FlowFixMeProps
|
||||
suppress_type=$FlowFixMeState
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
|
||||
|
||||
[strict]
|
88
.flowconfig.android
Normal file
88
.flowconfig.android
Normal file
@ -0,0 +1,88 @@
|
||||
[ignore]
|
||||
; This flowconfig is forked by platform - the only difference between them is which suffix is ignored.
|
||||
;.*/*[.]android.js
|
||||
.*/*[.]ios.js
|
||||
|
||||
; Ignore templates for 'react-native init'
|
||||
.*/local-cli/templates/.*
|
||||
|
||||
; Ignore the Dangerfile
|
||||
node_modules/react-native/bots/dangerfile.js
|
||||
|
||||
; Ignore "BUCK" generated dirs
|
||||
node_modules/react-native/\.buckd/
|
||||
|
||||
; Ignore unexpected extra "@providesModule"
|
||||
.*/node_modules/.*/node_modules/fbjs/.*
|
||||
|
||||
; Ignore duplicate module providers
|
||||
; For RN Apps installed via npm, "Libraries" folder is inside
|
||||
; "node_modules/react-native" but in the source repo it is in the root
|
||||
.*/Libraries/react-native/React.js
|
||||
|
||||
; Ignore polyfills
|
||||
.*/Libraries/polyfills/.*
|
||||
|
||||
; Ignore metro
|
||||
.*/node_modules/metro/.*
|
||||
|
||||
; Ignore "config-chain"'s test folder - it has a corrupt JSON file that's tripping flow
|
||||
.*/node_modules/config-chain/test/*.
|
||||
|
||||
; These should not be required directly
|
||||
; require from fbjs/lib instead: require('fbjs/lib/invariant')
|
||||
.*/node_modules/invariant/.*
|
||||
.*/node_modules/warning/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
node_modules/react-native/Libraries/react-native/react-native-interface.js
|
||||
node_modules/react-native/flow/
|
||||
node_modules/react-native/flow-github/
|
||||
|
||||
[lints]
|
||||
|
||||
[options]
|
||||
emoji=true
|
||||
|
||||
esproposal.optional_chaining=enable
|
||||
esproposal.nullish_coalescing=enable
|
||||
|
||||
module.system=haste
|
||||
module.system.haste.use_name_reducers=true
|
||||
# keep the following in sync with server/haste/hasteImpl.js
|
||||
# get basename
|
||||
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
|
||||
# strip .js or .js.flow suffix
|
||||
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
|
||||
# strip platform suffix
|
||||
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
|
||||
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
|
||||
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
|
||||
module.system.haste.paths.blacklist=.*/__tests__/.*
|
||||
module.system.haste.paths.blacklist=.*/__mocks__/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/js/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/RNTester/.*
|
||||
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/IntegrationTests/.*
|
||||
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
|
||||
; Surpress error `Duplicate module provider` until the slimmening is done
|
||||
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WebView/*.
|
||||
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WKWebView/*.
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FlowFixMeProps
|
||||
suppress_type=$FlowFixMeState
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
|
||||
|
||||
[strict]
|
@ -51,8 +51,14 @@ Simply install React Native WebView and then use it in place of the core WebView
|
||||
|
||||
### Contributor Notes
|
||||
|
||||
* I've removed all PropTypes for now. Instead, we'll be moving toward Flow or TypeScript at a later date
|
||||
* I've removed all PropTypes for now. Instead, we'll be using Flow types. TypeScript types will be added at a later date.
|
||||
* UIWebView is not tested fully and you will encounter some yellow warning boxes. Since it is deprecated, we don't intend to put a lot of time into supporting it, but feel free to submit PRs if you have a special use case. Note that you will need to specify `useWebKit={false}` to use UIWebView
|
||||
* After pulling this repo and installing all dependencies, you can run flow on iOS and Android-specific files using the commands:
|
||||
* `yarn flow` or `npm run flow` for iOS
|
||||
* `yarn flow-android` or `npm run flow-android` for Android
|
||||
* If you want to add another React Native platform to this repository, you will need to create another `.flowconfig` for it. If your platform is `example`, copy the main flowconfig and rename it to `.flowconfig.example`. Then edit the config to ignore other platforms, and add `.*/*[.]example.js` to the ignore lists of the other platforms. Then add an entry to `package.json` like this:
|
||||
* ` "flow-example": "flow check --flowconfig-name .flowconfig.example"`
|
||||
* Currently you need to install React Native 0.57 to be able to test these types - `flow check` will not pass aganst 0.56.
|
||||
|
||||
## Maintainers
|
||||
|
||||
|
@ -13,16 +13,26 @@ import React from 'react';
|
||||
|
||||
import { requireNativeComponent } from 'react-native';
|
||||
|
||||
import type {DataDetectorTypes} from './WebViewTypes';
|
||||
|
||||
const RCTWKWebView = requireNativeComponent('RCTWKWebView');
|
||||
|
||||
class WKWebView extends React.Component {
|
||||
componentWillReceiveProps(nextProps) {
|
||||
type RCTWKWebViewProps = $ReadOnly<{|
|
||||
allowsInlineMediaPlayback?: ?boolean,
|
||||
mediaPlaybackRequiresUserAction?: ?boolean,
|
||||
dataDetectorTypes?:
|
||||
| ?DataDetectorTypes
|
||||
| $ReadOnlyArray<DataDetectorTypes>,
|
||||
|}>;
|
||||
|
||||
class WKWebView extends React.Component<RCTWKWebViewProps> {
|
||||
componentWillReceiveProps(nextProps: RCTWKWebViewProps) {
|
||||
this.showRedboxOnPropChanges(nextProps, 'allowsInlineMediaPlayback');
|
||||
this.showRedboxOnPropChanges(nextProps, 'mediaPlaybackRequiresUserAction');
|
||||
this.showRedboxOnPropChanges(nextProps, 'dataDetectorTypes');
|
||||
}
|
||||
|
||||
showRedboxOnPropChanges(nextProps, propName) {
|
||||
showRedboxOnPropChanges(nextProps: RCTWKWebViewProps, propName: string) {
|
||||
if (this.props[propName] !== nextProps[propName]) {
|
||||
console.error(`Changes to property ${propName} do nothing after the initial render.`);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@ -21,9 +22,20 @@ import {
|
||||
requireNativeComponent
|
||||
} from 'react-native';
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import keyMirror from 'fbjs/lib/keyMirror';
|
||||
|
||||
import WebViewShared from './WebViewShared';
|
||||
import type {
|
||||
WebViewEvent,
|
||||
WebViewError,
|
||||
WebViewErrorEvent,
|
||||
WebViewMessageEvent,
|
||||
WebViewNavigation,
|
||||
WebViewNavigationEvent,
|
||||
WebViewSharedProps,
|
||||
WebViewSource,
|
||||
} from './WebViewTypes';
|
||||
|
||||
const resolveAssetSource = Image.resolveAssetSource;
|
||||
|
||||
@ -41,10 +53,16 @@ const defaultRenderLoading = () => (
|
||||
</View>
|
||||
);
|
||||
|
||||
type State = {|
|
||||
viewState: WebViewState,
|
||||
lastErrorEvent: ?WebViewError,
|
||||
startInLoadingState: boolean,
|
||||
|};
|
||||
|
||||
/**
|
||||
* Renders a native WebView.
|
||||
*/
|
||||
class WebView extends React.Component {
|
||||
class WebView extends React.Component<WebViewSharedProps, State> {
|
||||
static defaultProps = {
|
||||
javaScriptEnabled: true,
|
||||
thirdPartyCookiesEnabled: true,
|
||||
@ -72,6 +90,7 @@ class WebView extends React.Component {
|
||||
otherView = (this.props.renderLoading || defaultRenderLoading)();
|
||||
} else if (this.state.viewState === WebViewState.ERROR) {
|
||||
const errorEvent = this.state.lastErrorEvent;
|
||||
invariant(errorEvent != null, 'lastErrorEvent expected to be non-null');
|
||||
otherView =
|
||||
this.props.renderError &&
|
||||
this.props.renderError(
|
||||
@ -81,7 +100,7 @@ class WebView extends React.Component {
|
||||
);
|
||||
} else if (this.state.viewState !== WebViewState.IDLE) {
|
||||
console.error(
|
||||
'RCTWebView invalid state encountered: ' + this.state.loading,
|
||||
'RCTWebView invalid state encountered: ' + this.state.viewState,
|
||||
);
|
||||
}
|
||||
|
||||
@ -94,11 +113,11 @@ class WebView extends React.Component {
|
||||
webViewStyles.push(styles.hidden);
|
||||
}
|
||||
|
||||
const source = this.props.source || {};
|
||||
if (this.props.html) {
|
||||
source.html = this.props.html;
|
||||
} else if (this.props.url) {
|
||||
source.uri = this.props.url;
|
||||
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) {
|
||||
@ -198,7 +217,7 @@ class WebView extends React.Component {
|
||||
);
|
||||
};
|
||||
|
||||
postMessage = data => {
|
||||
postMessage = (data: string) => {
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
this.getWebViewHandle(),
|
||||
UIManager.RCTWebView.Commands.postMessage,
|
||||
@ -212,7 +231,7 @@ class WebView extends React.Component {
|
||||
* on pages with a Content Security Policy that disallows eval(). If you need that
|
||||
* functionality, look into postMessage/onMessage.
|
||||
*/
|
||||
injectJavaScript = data => {
|
||||
injectJavaScript = (data: string) => {
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
this.getWebViewHandle(),
|
||||
UIManager.RCTWebView.Commands.injectJavaScript,
|
||||
@ -224,7 +243,7 @@ class WebView extends React.Component {
|
||||
* We return an event with a bunch of fields including:
|
||||
* url, title, loading, canGoBack, canGoForward
|
||||
*/
|
||||
updateNavigationState = event => {
|
||||
updateNavigationState = (event: WebViewNavigationEvent) => {
|
||||
if (this.props.onNavigationStateChange) {
|
||||
this.props.onNavigationStateChange(event.nativeEvent);
|
||||
}
|
||||
@ -234,13 +253,13 @@ class WebView extends React.Component {
|
||||
return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]);
|
||||
};
|
||||
|
||||
onLoadingStart = event => {
|
||||
onLoadingStart = (event: WebViewNavigationEvent) => {
|
||||
const onLoadStart = this.props.onLoadStart;
|
||||
onLoadStart && onLoadStart(event);
|
||||
this.updateNavigationState(event);
|
||||
};
|
||||
|
||||
onLoadingError = event => {
|
||||
onLoadingError = (event: WebViewErrorEvent) => {
|
||||
event.persist(); // persist this event because we need to store it
|
||||
const { onError, onLoadEnd } = this.props;
|
||||
onError && onError(event);
|
||||
@ -253,7 +272,7 @@ class WebView extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
onLoadingFinish = event => {
|
||||
onLoadingFinish = (event: WebViewNavigationEvent) => {
|
||||
const { onLoad, onLoadEnd } = this.props;
|
||||
onLoad && onLoad(event);
|
||||
onLoadEnd && onLoadEnd(event);
|
||||
@ -263,7 +282,7 @@ class WebView extends React.Component {
|
||||
this.updateNavigationState(event);
|
||||
};
|
||||
|
||||
onMessage = (event) => {
|
||||
onMessage = (event: WebViewMessageEvent) => {
|
||||
const { onMessage } = this.props;
|
||||
onMessage && onMessage(event);
|
||||
};
|
||||
|
@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @noflow
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@ -29,6 +29,15 @@ import invariant from 'fbjs/lib/invariant';
|
||||
import keyMirror from 'fbjs/lib/keyMirror';
|
||||
|
||||
import WebViewShared from './WebViewShared';
|
||||
import type {
|
||||
WebViewEvent,
|
||||
WebViewError,
|
||||
WebViewErrorEvent,
|
||||
WebViewMessageEvent,
|
||||
WebViewNavigationEvent,
|
||||
WebViewSharedProps,
|
||||
WebViewSource,
|
||||
} from './WebViewTypes';
|
||||
|
||||
const resolveAssetSource = Image.resolveAssetSource;
|
||||
|
||||
@ -65,13 +74,11 @@ const NavigationType = keyMirror({
|
||||
|
||||
const JSNavigationScheme = 'react-js-navigation';
|
||||
|
||||
// type ErrorEvent = {
|
||||
// domain: any,
|
||||
// code: any,
|
||||
// description: any,
|
||||
// };
|
||||
|
||||
// type Event = Object;
|
||||
type State = {|
|
||||
viewState: WebViewState,
|
||||
lastErrorEvent: ?WebViewError,
|
||||
startInLoadingState: boolean,
|
||||
|};
|
||||
|
||||
const DataDetectorTypes = [
|
||||
'phoneNumber',
|
||||
@ -121,7 +128,7 @@ const defaultRenderError = (errorDomain, errorCode, errorDesc) => (
|
||||
* 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 {
|
||||
class WebView extends React.Component<WebViewSharedProps, State> {
|
||||
static JSNavigationScheme = JSNavigationScheme;
|
||||
static NavigationType = NavigationType;
|
||||
|
||||
@ -174,7 +181,7 @@ class WebView extends React.Component {
|
||||
);
|
||||
} else if (this.state.viewState !== WebViewState.IDLE) {
|
||||
console.error(
|
||||
'RCTWebView invalid state encountered: ' + this.state.loading,
|
||||
'RCTWebView invalid state encountered: ' + this.state.viewState,
|
||||
);
|
||||
}
|
||||
|
||||
@ -217,6 +224,7 @@ class WebView extends React.Component {
|
||||
shouldStart &&
|
||||
this.props.onShouldStartLoadWithRequest(event.nativeEvent);
|
||||
}
|
||||
invariant(viewManager != null, 'viewManager expected to be non-null');
|
||||
viewManager.startLoadWithResult(
|
||||
!!shouldStart,
|
||||
event.nativeEvent.lockIdentifier,
|
||||
@ -227,11 +235,11 @@ class WebView extends React.Component {
|
||||
this.props.decelerationRate,
|
||||
);
|
||||
|
||||
const source = this.props.source || {};
|
||||
if (this.props.html) {
|
||||
source.html = this.props.html;
|
||||
} else if (this.props.url) {
|
||||
source.uri = this.props.url;
|
||||
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 };
|
||||
}
|
||||
|
||||
const messagingEnabled = typeof this.props.onMessage === 'function';
|
||||
@ -345,7 +353,7 @@ class WebView extends React.Component {
|
||||
* document.addEventListener('message', e => { document.title = e.data; });
|
||||
* ```
|
||||
*/
|
||||
postMessage = data => {
|
||||
postMessage = (data: string) => {
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
this.getWebViewHandle(),
|
||||
this._getCommands().postMessage,
|
||||
@ -359,7 +367,7 @@ class WebView extends React.Component {
|
||||
* on pages with a Content Security Policy that disallows eval(). If you need that
|
||||
* functionality, look into postMessage/onMessage.
|
||||
*/
|
||||
injectJavaScript = data => {
|
||||
injectJavaScript = (data: string) => {
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
this.getWebViewHandle(),
|
||||
this._getCommands().injectJavaScript,
|
||||
@ -371,7 +379,7 @@ class WebView extends React.Component {
|
||||
* We return an event with a bunch of fields including:
|
||||
* url, title, loading, canGoBack, canGoForward
|
||||
*/
|
||||
_updateNavigationState = (event) => {
|
||||
_updateNavigationState = (event: WebViewNavigationEvent) => {
|
||||
if (this.props.onNavigationStateChange) {
|
||||
this.props.onNavigationStateChange(event.nativeEvent);
|
||||
}
|
||||
@ -384,13 +392,13 @@ class WebView extends React.Component {
|
||||
return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]);
|
||||
};
|
||||
|
||||
_onLoadingStart = (event) => {
|
||||
_onLoadingStart = (event: WebViewNavigationEvent) => {
|
||||
const onLoadStart = this.props.onLoadStart;
|
||||
onLoadStart && onLoadStart(event);
|
||||
this._updateNavigationState(event);
|
||||
};
|
||||
|
||||
_onLoadingError = (event) => {
|
||||
_onLoadingError = (event: WebViewErrorEvent) => {
|
||||
event.persist(); // persist this event because we need to store it
|
||||
const { onError, onLoadEnd } = this.props;
|
||||
onError && onError(event);
|
||||
@ -403,7 +411,7 @@ class WebView extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
_onLoadingFinish = (event) => {
|
||||
_onLoadingFinish = (event: WebViewNavigationEvent) => {
|
||||
const { onLoad, onLoadEnd } = this.props;
|
||||
onLoad && onLoad(event);
|
||||
onLoadEnd && onLoadEnd(event);
|
||||
@ -413,12 +421,12 @@ class WebView extends React.Component {
|
||||
this._updateNavigationState(event);
|
||||
};
|
||||
|
||||
_onMessage = (event) => {
|
||||
_onMessage = (event: WebViewMessageEvent) => {
|
||||
const { onMessage } = this.props;
|
||||
onMessage && onMessage(event);
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
componentDidUpdate(prevProps: WebViewSharedProps) {
|
||||
if (!(prevProps.useWebKit && this.props.useWebKit)) {
|
||||
return;
|
||||
}
|
||||
@ -434,7 +442,7 @@ class WebView extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
_showRedboxOnPropChanges(prevProps, propName) {
|
||||
_showRedboxOnPropChanges(prevProps, propName: string) {
|
||||
if (this.props[propName] !== prevProps[propName]) {
|
||||
console.error(
|
||||
`Changes to property ${propName} do nothing after the initial render.`,
|
||||
@ -443,16 +451,8 @@ class WebView extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const RCTWebView = requireNativeComponent(
|
||||
'RCTWebView',
|
||||
WebView,
|
||||
WebView.extraNativeComponentConfig,
|
||||
);
|
||||
const RCTWKWebView = requireNativeComponent(
|
||||
'RCTWKWebView',
|
||||
WebView,
|
||||
WebView.extraNativeComponentConfig,
|
||||
);
|
||||
const RCTWebView = requireNativeComponent('RCTWebView');
|
||||
const RCTWKWebView = requireNativeComponent('RCTWKWebView');
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
|
@ -14,11 +14,11 @@ const escapeStringRegexp = require('escape-string-regexp');
|
||||
|
||||
const WebViewShared = {
|
||||
defaultOriginWhitelist: ['http://*', 'https://*'],
|
||||
extractOrigin: (url) => {
|
||||
extractOrigin: (url: string) => {
|
||||
const result = /^[A-Za-z0-9]+:(\/\/)?[^/]*/.exec(url);
|
||||
return result === null ? null : result[0];
|
||||
return result === null ? '' : result[0];
|
||||
},
|
||||
originWhitelistToRegex: (originWhitelist) => {
|
||||
originWhitelistToRegex: (originWhitelist: string) => {
|
||||
return escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*');
|
||||
},
|
||||
};
|
||||
|
406
js/WebViewTypes.js
Normal file
406
js/WebViewTypes.js
Normal file
@ -0,0 +1,406 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import type {Node, Element, ComponentType} from 'react';
|
||||
|
||||
import type {SyntheticEvent} from 'CoreEventTypes';
|
||||
import type {EdgeInsetsProp} from 'EdgeInsetsPropType';
|
||||
import type {ViewStyleProp} from 'StyleSheet';
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
|
||||
export type WebViewNativeEvent = $ReadOnly<{|
|
||||
url: string,
|
||||
loading: boolean,
|
||||
title: string,
|
||||
canGoBack: boolean,
|
||||
canGoForward: boolean,
|
||||
|}>;
|
||||
|
||||
export type WebViewNavigation = $ReadOnly<{|
|
||||
...WebViewNativeEvent,
|
||||
navigationType:
|
||||
| 'click'
|
||||
| 'formsubmit'
|
||||
| 'backforward'
|
||||
| 'reload'
|
||||
| 'formresubmit'
|
||||
| 'other',
|
||||
|}>;
|
||||
|
||||
export type WebViewMessage = $ReadOnly<{|
|
||||
...WebViewNativeEvent,
|
||||
data: string,
|
||||
|}>;
|
||||
|
||||
export type WebViewError = $ReadOnly<{|
|
||||
...WebViewNativeEvent,
|
||||
/**
|
||||
* `domain` is only used on iOS
|
||||
*/
|
||||
domain: ?string,
|
||||
code: number,
|
||||
description: string,
|
||||
|}>;
|
||||
|
||||
export type WebViewEvent = SyntheticEvent<WebViewNativeEvent>;
|
||||
|
||||
export type WebViewNavigationEvent = SyntheticEvent<WebViewNavigation>;
|
||||
|
||||
export type WebViewMessageEvent = SyntheticEvent<WebViewMessage>;
|
||||
|
||||
export type WebViewErrorEvent = SyntheticEvent<WebViewError>;
|
||||
|
||||
export type DataDetectorTypes =
|
||||
| 'phoneNumber'
|
||||
| 'link'
|
||||
| 'address'
|
||||
| 'calendarEvent'
|
||||
| 'trackingNumber'
|
||||
| 'flightNumber'
|
||||
| 'lookupSuggestion'
|
||||
| 'none'
|
||||
| 'all';
|
||||
|
||||
export type WebViewSourceUri = $ReadOnly<{|
|
||||
/**
|
||||
* 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 type WebViewSourceHtml = $ReadOnly<{|
|
||||
/**
|
||||
* 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 type WebViewNativeConfig = $ReadOnly<{|
|
||||
/*
|
||||
* The native component used to render the WebView.
|
||||
*/
|
||||
component?: ComponentType<WebViewSharedProps>,
|
||||
/*
|
||||
* 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?: ?Object,
|
||||
|}>;
|
||||
|
||||
export type IOSWebViewProps = $ReadOnly<{|
|
||||
/**
|
||||
* If true, use WKWebView instead of UIWebView.
|
||||
* @platform ios
|
||||
*/
|
||||
useWebKit?: ?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?: ?('fast' | 'normal' | number),
|
||||
|
||||
/**
|
||||
* Boolean value that determines whether scrolling is enabled in the
|
||||
* `WebView`. The default value is `true`.
|
||||
* @platform ios
|
||||
*/
|
||||
scrollEnabled?: ?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?: ?EdgeInsetsProp,
|
||||
|
||||
/**
|
||||
* 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
|
||||
| $ReadOnlyArray<DataDetectorTypes>,
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @platform ios
|
||||
*/
|
||||
onShouldStartLoadWithRequest?: (event: WebViewEvent) => mixed,
|
||||
|
||||
/**
|
||||
* 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,
|
||||
|}>;
|
||||
|
||||
export type AndroidWebViewProps = $ReadOnly<{|
|
||||
onNavigationStateChange?: (event: WebViewNavigation) => mixed,
|
||||
onContentSizeChange?: (event: WebViewEvent) => mixed,
|
||||
|
||||
/**
|
||||
* 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,
|
||||
|
||||
/**
|
||||
* 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?: $ReadOnlyArray<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 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 type WebViewSharedProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
...IOSWebViewProps,
|
||||
...AndroidWebViewProps,
|
||||
/**
|
||||
* Deprecated. Use `source` instead.
|
||||
*/
|
||||
url?: ?string,
|
||||
/**
|
||||
* Deprecated. Use `source` instead.
|
||||
*/
|
||||
html?: ?string,
|
||||
|
||||
/**
|
||||
* 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, errorCode: number, errorDesc: string) => Element<any>, // view to show if there's an error
|
||||
|
||||
/**
|
||||
* Function that returns a loading indicator.
|
||||
*/
|
||||
renderLoading: () => Element<any>,
|
||||
|
||||
/**
|
||||
* Function that is invoked when the `WebView` has finished loading.
|
||||
*/
|
||||
onLoad: (event: WebViewNavigationEvent) => mixed,
|
||||
|
||||
/**
|
||||
* Function that is invoked when the `WebView` load succeeds or fails.
|
||||
*/
|
||||
onLoadEnd: (event: WebViewNavigationEvent | WebViewErrorEvent) => mixed,
|
||||
|
||||
/**
|
||||
* Function that is invoked when the `WebView` starts loading.
|
||||
*/
|
||||
onLoadStart: (event: WebViewNavigationEvent) => mixed,
|
||||
|
||||
/**
|
||||
* Function that is invoked when the `WebView` load fails.
|
||||
*/
|
||||
onError: (event: WebViewErrorEvent) => mixed,
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
*/
|
||||
automaticallyAdjustContentInsets?: ?boolean,
|
||||
|
||||
/**
|
||||
* Function that is invoked when the `WebView` loading starts or ends.
|
||||
*/
|
||||
onNavigationStateChange?: (event: WebViewNavigation) => mixed,
|
||||
|
||||
/**
|
||||
* A function that is invoked when the webview calls `window.postMessage`.
|
||||
* Setting this property will inject a `postMessage` global into your
|
||||
* webview, but will still call pre-existing values of `postMessage`.
|
||||
*
|
||||
* `window.postMessage` accepts one argument, `data`, which will be
|
||||
* available on the event object, `event.nativeEvent.data`. `data`
|
||||
* must be a string.
|
||||
*/
|
||||
onMessage?: (event: WebViewMessageEvent) => mixed,
|
||||
|
||||
/**
|
||||
* 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 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?: $ReadOnlyArray<string>,
|
||||
|
||||
/**
|
||||
* 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,
|
||||
|
||||
style?: ViewStyleProp,
|
||||
children: Node,
|
||||
|}>;
|
@ -5,6 +5,10 @@
|
||||
"author": "Jamon Holmgren <jamon@infinite.red>",
|
||||
"version": "0.1.0",
|
||||
"homepage": "https://github.com/react-native-community/react-native-webview#readme",
|
||||
"scripts": {
|
||||
"flow": "flow check",
|
||||
"flow-android": "flow check --flowconfig-name .flowconfig.android"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.0",
|
||||
"react-native": "^0.56"
|
||||
@ -12,5 +16,9 @@
|
||||
"dependencies": {
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"fbjs": "^0.8.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"flow-bin": "^0.80.0",
|
||||
"react-native": "^0.56"
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user