Unforked RKWebView
This commit is contained in:
Родитель
3cc81c6561
Коммит
66bb821a31
|
@ -40,6 +40,7 @@ var EXAMPLES = [
|
||||||
require('./AsyncStorageExample'),
|
require('./AsyncStorageExample'),
|
||||||
require('./CameraRollExample.ios'),
|
require('./CameraRollExample.ios'),
|
||||||
require('./MapViewExample'),
|
require('./MapViewExample'),
|
||||||
|
require('./WebViewExample'),
|
||||||
require('./AppStateIOSExample'),
|
require('./AppStateIOSExample'),
|
||||||
require('./AlertIOSExample'),
|
require('./AlertIOSExample'),
|
||||||
require('./AdSupportIOSExample'),
|
require('./AdSupportIOSExample'),
|
||||||
|
|
|
@ -0,0 +1,264 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react-native');
|
||||||
|
var StyleSheet = require('StyleSheet');
|
||||||
|
var {
|
||||||
|
ActivityIndicatorIOS,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
TouchableOpacity,
|
||||||
|
View,
|
||||||
|
WebView
|
||||||
|
} = React;
|
||||||
|
|
||||||
|
var HEADER = '#3b5998';
|
||||||
|
var BGWASH = 'rgba(255,255,255,0.8)';
|
||||||
|
var DISABLED_WASH = 'rgba(255,255,255,0.25)';
|
||||||
|
|
||||||
|
var TEXT_INPUT_REF = 'urlInput';
|
||||||
|
var WEBVIEW_REF = 'webview';
|
||||||
|
var DEFAULT_URL = 'https://m.facebook.com';
|
||||||
|
|
||||||
|
var WebViewExample = React.createClass({
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
url: DEFAULT_URL,
|
||||||
|
status: 'No Page Loaded',
|
||||||
|
backButtonEnabled: false,
|
||||||
|
forwardButtonEnabled: false,
|
||||||
|
loading: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
handleTextInputChange: function(event) {
|
||||||
|
this.inputText = event.nativeEvent.text;
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
this.inputText = this.state.url;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[styles.container]}>
|
||||||
|
<View style={[styles.addressBarRow]}>
|
||||||
|
<TouchableOpacity onPress={this.goBack}>
|
||||||
|
<View style={this.state.backButtonEnabled ? styles.navButton : styles.disabledButton}>
|
||||||
|
<Text>
|
||||||
|
{'<'}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity onPress={this.goForward}>
|
||||||
|
<View style={this.state.forwardButtonEnabled ? styles.navButton : styles.disabledButton}>
|
||||||
|
<Text>
|
||||||
|
{'>'}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TextInput
|
||||||
|
ref={TEXT_INPUT_REF}
|
||||||
|
autoCapitalize="none"
|
||||||
|
value={this.state.url}
|
||||||
|
onSubmitEditing={this.onSubmitEditing}
|
||||||
|
onChange={this.handleTextInputChange}
|
||||||
|
clearButtonMode="while-editing"
|
||||||
|
style={styles.addressBarTextInput}
|
||||||
|
/>
|
||||||
|
<TouchableOpacity onPress={this.pressGoButton}>
|
||||||
|
<View style={styles.goButton}>
|
||||||
|
<Text>
|
||||||
|
Go!
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
<WebView
|
||||||
|
ref={WEBVIEW_REF}
|
||||||
|
automaticallyAdjustContentInsets={false}
|
||||||
|
style={styles.webView}
|
||||||
|
url={this.state.url}
|
||||||
|
renderErrorView={this.renderErrorView}
|
||||||
|
renderLoadingView={this.renderLoadingView}
|
||||||
|
onNavigationStateChange={this.onNavigationStateChange}
|
||||||
|
startInLoadingState={true}
|
||||||
|
/>
|
||||||
|
<View style={styles.statusBar}>
|
||||||
|
<Text style={styles.statusBarText}>{this.state.status}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
goBack: function() {
|
||||||
|
this.refs[WEBVIEW_REF].goBack();
|
||||||
|
},
|
||||||
|
|
||||||
|
goForward: function() {
|
||||||
|
this.refs[WEBVIEW_REF].goForward();
|
||||||
|
},
|
||||||
|
|
||||||
|
reload: function() {
|
||||||
|
this.refs[WEBVIEW_REF].reload();
|
||||||
|
},
|
||||||
|
|
||||||
|
onNavigationStateChange: function(navState) {
|
||||||
|
this.setState({
|
||||||
|
backButtonEnabled: navState.canGoBack,
|
||||||
|
forwardButtonEnabled: navState.canGoForward,
|
||||||
|
url: navState.url,
|
||||||
|
status: navState.title,
|
||||||
|
loading: navState.loading,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
renderErrorView: function(errorDomain, errorCode, errorDesc) {
|
||||||
|
return (
|
||||||
|
<View style={styles.errorContainer}>
|
||||||
|
<Text style={styles.errorTextTitle}>
|
||||||
|
Error loading page
|
||||||
|
</Text>
|
||||||
|
<Text style={styles.errorText}>
|
||||||
|
{'Domain: ' + errorDomain}
|
||||||
|
</Text>
|
||||||
|
<Text style={styles.errorText}>
|
||||||
|
{'Error Code: ' + errorCode}
|
||||||
|
</Text>
|
||||||
|
<Text style={styles.errorText}>
|
||||||
|
{'Description: ' + errorDesc}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
renderLoadingView: function() {
|
||||||
|
return (
|
||||||
|
<View style={styles.loadingView}>
|
||||||
|
<ActivityIndicatorIOS />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
onSubmitEditing: function(event) {
|
||||||
|
this.pressGoButton();
|
||||||
|
},
|
||||||
|
|
||||||
|
pressGoButton: function() {
|
||||||
|
var url = this.inputText.toLowerCase();
|
||||||
|
if (url === this.state.url) {
|
||||||
|
this.reload();
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
url: url,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// dismiss keyoard
|
||||||
|
this.refs[TEXT_INPUT_REF].blur();
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
var styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: HEADER,
|
||||||
|
},
|
||||||
|
addressBarRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
padding: 8,
|
||||||
|
},
|
||||||
|
webView: {
|
||||||
|
backgroundColor: BGWASH,
|
||||||
|
height: 350,
|
||||||
|
},
|
||||||
|
addressBarTextInput: {
|
||||||
|
backgroundColor: BGWASH,
|
||||||
|
borderColor: 'transparent',
|
||||||
|
borderRadius: 3,
|
||||||
|
borderWidth: 1,
|
||||||
|
height: 24,
|
||||||
|
paddingLeft: 10,
|
||||||
|
paddingTop: 3,
|
||||||
|
paddingBottom: 3,
|
||||||
|
flex: 1,
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
navButton: {
|
||||||
|
width: 20,
|
||||||
|
padding: 3,
|
||||||
|
marginRight: 3,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
backgroundColor: BGWASH,
|
||||||
|
borderColor: 'transparent',
|
||||||
|
borderRadius: 3,
|
||||||
|
},
|
||||||
|
disabledButton: {
|
||||||
|
width: 20,
|
||||||
|
padding: 3,
|
||||||
|
marginRight: 3,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
backgroundColor: DISABLED_WASH,
|
||||||
|
borderColor: 'transparent',
|
||||||
|
borderRadius: 3,
|
||||||
|
},
|
||||||
|
goButton: {
|
||||||
|
height: 24,
|
||||||
|
padding: 3,
|
||||||
|
marginLeft: 8,
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: BGWASH,
|
||||||
|
borderColor: 'transparent',
|
||||||
|
borderRadius: 3,
|
||||||
|
alignSelf: 'stretch',
|
||||||
|
},
|
||||||
|
loadingView: {
|
||||||
|
backgroundColor: BGWASH,
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
errorContainer: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: BGWASH,
|
||||||
|
},
|
||||||
|
errorTextTitle: {
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
errorText: {
|
||||||
|
fontSize: 14,
|
||||||
|
textAlign: 'center',
|
||||||
|
marginBottom: 2,
|
||||||
|
},
|
||||||
|
statusBar: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingLeft: 5,
|
||||||
|
height: 22,
|
||||||
|
},
|
||||||
|
statusBarText: {
|
||||||
|
color: 'white',
|
||||||
|
fontSize: 13,
|
||||||
|
},
|
||||||
|
spinner: {
|
||||||
|
width: 20,
|
||||||
|
marginRight: 6,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.title = '<WebView>';
|
||||||
|
exports.description = 'Base component to display web content';
|
||||||
|
exports.examples = [
|
||||||
|
{
|
||||||
|
title: 'WebView',
|
||||||
|
render() { return <WebViewExample />; }
|
||||||
|
}
|
||||||
|
];
|
|
@ -0,0 +1,169 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* @providesModule WebView
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var EdgeInsetsPropType = require('EdgeInsetsPropType');
|
||||||
|
var React = require('React');
|
||||||
|
var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
|
||||||
|
var StyleSheet = require('StyleSheet');
|
||||||
|
var View = require('View');
|
||||||
|
|
||||||
|
var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass');
|
||||||
|
var keyMirror = require('keyMirror');
|
||||||
|
var merge = require('merge');
|
||||||
|
|
||||||
|
var PropTypes = React.PropTypes;
|
||||||
|
var RKUIManager = require('NativeModules').RKUIManager;
|
||||||
|
|
||||||
|
var RK_WEBVIEW_REF = 'webview';
|
||||||
|
|
||||||
|
var WebViewState = keyMirror({
|
||||||
|
IDLE: null,
|
||||||
|
LOADING: null,
|
||||||
|
ERROR: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
var WebView = React.createClass({
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
renderErrorView: PropTypes.func.isRequired, // view to show if there's an error
|
||||||
|
renderLoadingView: PropTypes.func.isRequired, // loading indicator to show
|
||||||
|
url: PropTypes.string.isRequired,
|
||||||
|
automaticallyAdjustContentInsets: PropTypes.bool,
|
||||||
|
contentInset: EdgeInsetsPropType,
|
||||||
|
onNavigationStateChange: PropTypes.func,
|
||||||
|
startInLoadingState: PropTypes.bool, // force WebView to show loadingView on first load
|
||||||
|
style: View.propTypes.style,
|
||||||
|
/**
|
||||||
|
* Used to locate this view in end-to-end tests.
|
||||||
|
*/
|
||||||
|
testID: PropTypes.string,
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
viewState: WebViewState.IDLE,
|
||||||
|
lastErrorEvent: null,
|
||||||
|
startInLoadingState: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillMount: function() {
|
||||||
|
if (this.props.startInLoadingState) {
|
||||||
|
this.setState({viewState: WebViewState.LOADING});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var otherView = null;
|
||||||
|
|
||||||
|
if (this.state.viewState === WebViewState.LOADING) {
|
||||||
|
otherView = this.props.renderLoadingView();
|
||||||
|
} else if (this.state.viewState === WebViewState.ERROR) {
|
||||||
|
var errorEvent = this.state.lastErrorEvent;
|
||||||
|
otherView = this.props.renderErrorView(
|
||||||
|
errorEvent.domain,
|
||||||
|
errorEvent.code,
|
||||||
|
errorEvent.description);
|
||||||
|
} else if (this.state.viewState !== WebViewState.IDLE) {
|
||||||
|
console.error("RCTWebView invalid state encountered: " + this.state.loading);
|
||||||
|
}
|
||||||
|
|
||||||
|
var webViewStyles = [styles.container, this.props.style];
|
||||||
|
if (this.state.viewState === WebViewState.LOADING ||
|
||||||
|
this.state.viewState === WebViewState.ERROR) {
|
||||||
|
// if we're in either LOADING or ERROR states, don't show the webView
|
||||||
|
webViewStyles.push(styles.hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
var webView =
|
||||||
|
<RCTWebView
|
||||||
|
ref={RK_WEBVIEW_REF}
|
||||||
|
key="webViewKey"
|
||||||
|
style={webViewStyles}
|
||||||
|
url={this.props.url}
|
||||||
|
contentInset={this.props.contentInset}
|
||||||
|
automaticallyAdjustContentInsets={this.props.automaticallyAdjustContentInsets}
|
||||||
|
onLoadingStart={this.onLoadingStart}
|
||||||
|
onLoadingFinish={this.onLoadingFinish}
|
||||||
|
onLoadingError={this.onLoadingError}
|
||||||
|
testID={this.props.testID}
|
||||||
|
/>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{webView}
|
||||||
|
{otherView}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
goForward: function() {
|
||||||
|
RKUIManager.webViewGoForward(this.getWebWiewHandle());
|
||||||
|
},
|
||||||
|
|
||||||
|
goBack: function() {
|
||||||
|
RKUIManager.webViewGoBack(this.getWebWiewHandle());
|
||||||
|
},
|
||||||
|
|
||||||
|
reload: function() {
|
||||||
|
RKUIManager.webViewReload(this.getWebWiewHandle());
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We return an event with a bunch of fields including:
|
||||||
|
* url, title, loading, canGoBack, canGoForward
|
||||||
|
*/
|
||||||
|
updateNavigationState: function(event) {
|
||||||
|
if (this.props.onNavigationStateChange) {
|
||||||
|
this.props.onNavigationStateChange(event.nativeEvent);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getWebWiewHandle: function() {
|
||||||
|
return this.refs[RK_WEBVIEW_REF].getNodeHandle();
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadingStart: function(event) {
|
||||||
|
this.updateNavigationState(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadingError: function(event) {
|
||||||
|
event.persist(); // persist this event because we need to store it
|
||||||
|
console.error("encountered an error loading page", event.nativeEvent);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
lastErrorEvent: event.nativeEvent,
|
||||||
|
viewState: WebViewState.ERROR
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadingFinish: function(event) {
|
||||||
|
this.setState({
|
||||||
|
viewState: WebViewState.IDLE,
|
||||||
|
});
|
||||||
|
this.updateNavigationState(event);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var RCTWebView = createReactIOSNativeComponentClass({
|
||||||
|
validAttributes: merge(ReactIOSViewAttributes.UIView, {
|
||||||
|
url: true,
|
||||||
|
}),
|
||||||
|
uiViewClassName: 'RCTWebView',
|
||||||
|
});
|
||||||
|
|
||||||
|
var styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
hidden: {
|
||||||
|
height: 0,
|
||||||
|
flex: 0, // disable 'flex:1' when hiding a View
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = WebView;
|
|
@ -0,0 +1,182 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* @providesModule WebView
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var EdgeInsetsPropType = require('EdgeInsetsPropType');
|
||||||
|
var React = require('React');
|
||||||
|
var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
|
||||||
|
var StyleSheet = require('StyleSheet');
|
||||||
|
var View = require('View');
|
||||||
|
|
||||||
|
var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass');
|
||||||
|
var keyMirror = require('keyMirror');
|
||||||
|
var insetsDiffer = require('insetsDiffer');
|
||||||
|
var merge = require('merge');
|
||||||
|
|
||||||
|
var PropTypes = React.PropTypes;
|
||||||
|
var { RKWebViewManager } = require('NativeModules');
|
||||||
|
|
||||||
|
var RK_WEBVIEW_REF = 'webview';
|
||||||
|
|
||||||
|
var WebViewState = keyMirror({
|
||||||
|
IDLE: null,
|
||||||
|
LOADING: null,
|
||||||
|
ERROR: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
var NavigationType = {
|
||||||
|
click: RKWebViewManager.NavigationType.LinkClicked,
|
||||||
|
formsubmit: RKWebViewManager.NavigationType.FormSubmitted,
|
||||||
|
backforward: RKWebViewManager.NavigationType.BackForward,
|
||||||
|
reload: RKWebViewManager.NavigationType.Reload,
|
||||||
|
formresubmit: RKWebViewManager.NavigationType.FormResubmitted,
|
||||||
|
other: RKWebViewManager.NavigationType.Other,
|
||||||
|
};
|
||||||
|
|
||||||
|
var WebView = React.createClass({
|
||||||
|
statics: {
|
||||||
|
NavigationType: NavigationType,
|
||||||
|
},
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
renderErrorView: PropTypes.func.isRequired, // view to show if there's an error
|
||||||
|
renderLoadingView: PropTypes.func.isRequired, // loading indicator to show
|
||||||
|
url: PropTypes.string.isRequired,
|
||||||
|
automaticallyAdjustContentInsets: PropTypes.bool,
|
||||||
|
shouldInjectAJAXHandler: PropTypes.bool,
|
||||||
|
contentInset: EdgeInsetsPropType,
|
||||||
|
onNavigationStateChange: PropTypes.func,
|
||||||
|
startInLoadingState: PropTypes.bool, // force WebView to show loadingView on first load
|
||||||
|
style: View.propTypes.style,
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
viewState: WebViewState.IDLE,
|
||||||
|
lastErrorEvent: null,
|
||||||
|
startInLoadingState: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillMount: function() {
|
||||||
|
if (this.props.startInLoadingState) {
|
||||||
|
this.setState({viewState: WebViewState.LOADING});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var otherView = null;
|
||||||
|
|
||||||
|
if (this.state.viewState === WebViewState.LOADING) {
|
||||||
|
otherView = this.props.renderLoadingView();
|
||||||
|
} else if (this.state.viewState === WebViewState.ERROR) {
|
||||||
|
var errorEvent = this.state.lastErrorEvent;
|
||||||
|
otherView = this.props.renderErrorView(
|
||||||
|
errorEvent.domain,
|
||||||
|
errorEvent.code,
|
||||||
|
errorEvent.description);
|
||||||
|
} else if (this.state.viewState !== WebViewState.IDLE) {
|
||||||
|
console.error("RKWebView invalid state encountered: " + this.state.loading);
|
||||||
|
}
|
||||||
|
|
||||||
|
var webViewStyles = [styles.container, this.props.style];
|
||||||
|
if (this.state.viewState === WebViewState.LOADING ||
|
||||||
|
this.state.viewState === WebViewState.ERROR) {
|
||||||
|
// if we're in either LOADING or ERROR states, don't show the webView
|
||||||
|
webViewStyles.push(styles.hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
var webView =
|
||||||
|
<RCTWebView
|
||||||
|
ref={RK_WEBVIEW_REF}
|
||||||
|
key="webViewKey"
|
||||||
|
style={webViewStyles}
|
||||||
|
url={this.props.url}
|
||||||
|
shouldInjectAJAXHandler={this.props.shouldInjectAJAXHandler}
|
||||||
|
contentInset={this.props.contentInset}
|
||||||
|
automaticallyAdjustContentInsets={this.props.automaticallyAdjustContentInsets}
|
||||||
|
onLoadingStart={this.onLoadingStart}
|
||||||
|
onLoadingFinish={this.onLoadingFinish}
|
||||||
|
onLoadingError={this.onLoadingError}
|
||||||
|
/>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{webView}
|
||||||
|
{otherView}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
goForward: function() {
|
||||||
|
RKWebViewManager.goForward(this.getWebWiewHandle());
|
||||||
|
},
|
||||||
|
|
||||||
|
goBack: function() {
|
||||||
|
RKWebViewManager.goBack(this.getWebWiewHandle());
|
||||||
|
},
|
||||||
|
|
||||||
|
reload: function() {
|
||||||
|
RKWebViewManager.reload(this.getWebWiewHandle());
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We return an event with a bunch of fields including:
|
||||||
|
* url, title, loading, canGoBack, canGoForward
|
||||||
|
*/
|
||||||
|
updateNavigationState: function(event) {
|
||||||
|
if (this.props.onNavigationStateChange) {
|
||||||
|
this.props.onNavigationStateChange(event.nativeEvent);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getWebWiewHandle: function() {
|
||||||
|
return this.refs[RK_WEBVIEW_REF].getNodeHandle();
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadingStart: function(event) {
|
||||||
|
this.updateNavigationState(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadingError: function(event) {
|
||||||
|
event.persist(); // persist this event because we need to store it
|
||||||
|
console.error("encountered an error loading page", event.nativeEvent);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
lastErrorEvent: event.nativeEvent,
|
||||||
|
viewState: WebViewState.ERROR
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadingFinish: function(event) {
|
||||||
|
this.setState({
|
||||||
|
viewState: WebViewState.IDLE,
|
||||||
|
});
|
||||||
|
this.updateNavigationState(event);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var RCTWebView = createReactIOSNativeComponentClass({
|
||||||
|
validAttributes: merge(ReactIOSViewAttributes.UIView, {
|
||||||
|
url: true,
|
||||||
|
contentInset: {diff: insetsDiffer},
|
||||||
|
automaticallyAdjustContentInsets: true,
|
||||||
|
shouldInjectAJAXHandler: true
|
||||||
|
}),
|
||||||
|
uiViewClassName: 'RCTWebView',
|
||||||
|
});
|
||||||
|
|
||||||
|
var styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
hidden: {
|
||||||
|
height: 0,
|
||||||
|
flex: 0, // disable 'flex:1' when hiding a View
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = WebView;
|
|
@ -37,6 +37,7 @@ var ReactNative = {
|
||||||
TouchableOpacity: require('TouchableOpacity'),
|
TouchableOpacity: require('TouchableOpacity'),
|
||||||
TouchableWithoutFeedback: require('TouchableWithoutFeedback'),
|
TouchableWithoutFeedback: require('TouchableWithoutFeedback'),
|
||||||
View: require('View'),
|
View: require('View'),
|
||||||
|
WebView: require('WebView'),
|
||||||
invariant: require('invariant'),
|
invariant: require('invariant'),
|
||||||
ix: require('ix'),
|
ix: require('ix'),
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
13B0801F1A69489C00A75B9A /* RCTTextFieldManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080171A69489C00A75B9A /* RCTTextFieldManager.m */; };
|
13B0801F1A69489C00A75B9A /* RCTTextFieldManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080171A69489C00A75B9A /* RCTTextFieldManager.m */; };
|
||||||
13B080201A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080191A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m */; };
|
13B080201A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080191A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m */; };
|
||||||
13B080261A694A8400A75B9A /* RCTWrapperViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080241A694A8400A75B9A /* RCTWrapperViewController.m */; };
|
13B080261A694A8400A75B9A /* RCTWrapperViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080241A694A8400A75B9A /* RCTWrapperViewController.m */; };
|
||||||
|
13C156051AB1A2840079392D /* RCTWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13C156021AB1A2840079392D /* RCTWebView.m */; };
|
||||||
|
13C156061AB1A2840079392D /* RCTWebViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13C156041AB1A2840079392D /* RCTWebViewManager.m */; };
|
||||||
13E0674A1A70F434002CDEE1 /* RCTUIManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067491A70F434002CDEE1 /* RCTUIManager.m */; };
|
13E0674A1A70F434002CDEE1 /* RCTUIManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067491A70F434002CDEE1 /* RCTUIManager.m */; };
|
||||||
13E067551A70F44B002CDEE1 /* RCTShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E0674C1A70F44B002CDEE1 /* RCTShadowView.m */; };
|
13E067551A70F44B002CDEE1 /* RCTShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E0674C1A70F44B002CDEE1 /* RCTShadowView.m */; };
|
||||||
13E067561A70F44B002CDEE1 /* RCTViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E0674E1A70F44B002CDEE1 /* RCTViewManager.m */; };
|
13E067561A70F44B002CDEE1 /* RCTViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E0674E1A70F44B002CDEE1 /* RCTViewManager.m */; };
|
||||||
|
@ -124,6 +126,10 @@
|
||||||
13B080191A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIActivityIndicatorViewManager.m; sourceTree = "<group>"; };
|
13B080191A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIActivityIndicatorViewManager.m; sourceTree = "<group>"; };
|
||||||
13B080231A694A8400A75B9A /* RCTWrapperViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWrapperViewController.h; sourceTree = "<group>"; };
|
13B080231A694A8400A75B9A /* RCTWrapperViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWrapperViewController.h; sourceTree = "<group>"; };
|
||||||
13B080241A694A8400A75B9A /* RCTWrapperViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWrapperViewController.m; sourceTree = "<group>"; };
|
13B080241A694A8400A75B9A /* RCTWrapperViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWrapperViewController.m; sourceTree = "<group>"; };
|
||||||
|
13C156011AB1A2840079392D /* RCTWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebView.h; sourceTree = "<group>"; };
|
||||||
|
13C156021AB1A2840079392D /* RCTWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebView.m; sourceTree = "<group>"; };
|
||||||
|
13C156031AB1A2840079392D /* RCTWebViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebViewManager.h; sourceTree = "<group>"; };
|
||||||
|
13C156041AB1A2840079392D /* RCTWebViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebViewManager.m; sourceTree = "<group>"; };
|
||||||
13C325261AA63B6A0048765F /* RCTAutoInsetsProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAutoInsetsProtocol.h; sourceTree = "<group>"; };
|
13C325261AA63B6A0048765F /* RCTAutoInsetsProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAutoInsetsProtocol.h; sourceTree = "<group>"; };
|
||||||
13C325271AA63B6A0048765F /* RCTScrollableProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTScrollableProtocol.h; sourceTree = "<group>"; };
|
13C325271AA63B6A0048765F /* RCTScrollableProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTScrollableProtocol.h; sourceTree = "<group>"; };
|
||||||
13C325281AA63B6A0048765F /* RCTViewNodeProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewNodeProtocol.h; sourceTree = "<group>"; };
|
13C325281AA63B6A0048765F /* RCTViewNodeProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewNodeProtocol.h; sourceTree = "<group>"; };
|
||||||
|
@ -299,6 +305,10 @@
|
||||||
13E0674D1A70F44B002CDEE1 /* RCTViewManager.h */,
|
13E0674D1A70F44B002CDEE1 /* RCTViewManager.h */,
|
||||||
13E0674E1A70F44B002CDEE1 /* RCTViewManager.m */,
|
13E0674E1A70F44B002CDEE1 /* RCTViewManager.m */,
|
||||||
13C325281AA63B6A0048765F /* RCTViewNodeProtocol.h */,
|
13C325281AA63B6A0048765F /* RCTViewNodeProtocol.h */,
|
||||||
|
13C156011AB1A2840079392D /* RCTWebView.h */,
|
||||||
|
13C156021AB1A2840079392D /* RCTWebView.m */,
|
||||||
|
13C156031AB1A2840079392D /* RCTWebViewManager.h */,
|
||||||
|
13C156041AB1A2840079392D /* RCTWebViewManager.m */,
|
||||||
13B080231A694A8400A75B9A /* RCTWrapperViewController.h */,
|
13B080231A694A8400A75B9A /* RCTWrapperViewController.h */,
|
||||||
13B080241A694A8400A75B9A /* RCTWrapperViewController.m */,
|
13B080241A694A8400A75B9A /* RCTWrapperViewController.m */,
|
||||||
13E067531A70F44B002CDEE1 /* UIView+ReactKit.h */,
|
13E067531A70F44B002CDEE1 /* UIView+ReactKit.h */,
|
||||||
|
@ -480,7 +490,9 @@
|
||||||
83CBBA531A601E3B00E9B192 /* RCTUtils.m in Sources */,
|
83CBBA531A601E3B00E9B192 /* RCTUtils.m in Sources */,
|
||||||
14435CE61AAC4AE100FC20F4 /* RCTMapManager.m in Sources */,
|
14435CE61AAC4AE100FC20F4 /* RCTMapManager.m in Sources */,
|
||||||
83C911101AAE6521001323A3 /* RCTAnimationManager.m in Sources */,
|
83C911101AAE6521001323A3 /* RCTAnimationManager.m in Sources */,
|
||||||
|
13C156051AB1A2840079392D /* RCTWebView.m in Sources */,
|
||||||
83CBBA601A601EAA00E9B192 /* RCTBridge.m in Sources */,
|
83CBBA601A601EAA00E9B192 /* RCTBridge.m in Sources */,
|
||||||
|
13C156061AB1A2840079392D /* RCTWebViewManager.m in Sources */,
|
||||||
58114A161AAE854800E7D092 /* RCTPicker.m in Sources */,
|
58114A161AAE854800E7D092 /* RCTPicker.m in Sources */,
|
||||||
137327E81AA5CF210034F82E /* RCTTabBarItem.m in Sources */,
|
137327E81AA5CF210034F82E /* RCTTabBarItem.m in Sources */,
|
||||||
13E067551A70F44B002CDEE1 /* RCTShadowView.m in Sources */,
|
13E067551A70F44B002CDEE1 /* RCTShadowView.m in Sources */,
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
#import "RCTView.h"
|
||||||
|
|
||||||
|
@class RCTEventDispatcher;
|
||||||
|
|
||||||
|
@interface RCTWebView : RCTView
|
||||||
|
|
||||||
|
@property (nonatomic, strong) NSURL *URL;
|
||||||
|
@property (nonatomic, assign) UIEdgeInsets contentInset;
|
||||||
|
@property (nonatomic, assign) BOOL shouldInjectAJAXHandler;
|
||||||
|
@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
|
||||||
|
|
||||||
|
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
- (void)goForward;
|
||||||
|
- (void)goBack;
|
||||||
|
- (void)reload;
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,180 @@
|
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
#import "RCTWebView.h"
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
#import "RCTAutoInsetsProtocol.h"
|
||||||
|
#import "RCTEventDispatcher.h"
|
||||||
|
#import "RCTLog.h"
|
||||||
|
#import "RCTUtils.h"
|
||||||
|
#import "RCTView.h"
|
||||||
|
#import "UIView+ReactKit.h"
|
||||||
|
|
||||||
|
@interface RCTWebView () <UIWebViewDelegate, RCTAutoInsetsProtocol>
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTWebView
|
||||||
|
{
|
||||||
|
RCTEventDispatcher *_eventDispatcher;
|
||||||
|
UIWebView *_webView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||||
|
{
|
||||||
|
if ((self = [super initWithFrame:CGRectZero])) {
|
||||||
|
_automaticallyAdjustContentInsets = YES;
|
||||||
|
_contentInset = UIEdgeInsetsZero;
|
||||||
|
_eventDispatcher = eventDispatcher;
|
||||||
|
_webView = [[UIWebView alloc] initWithFrame:self.bounds];
|
||||||
|
_webView.delegate = self;
|
||||||
|
[self addSubview:_webView];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)goForward
|
||||||
|
{
|
||||||
|
[_webView goForward];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)goBack
|
||||||
|
{
|
||||||
|
[_webView goBack];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)reload
|
||||||
|
{
|
||||||
|
[_webView reload];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setURL:(NSURL *)URL
|
||||||
|
{
|
||||||
|
// Because of the way React works, as pages redirect, we actually end up
|
||||||
|
// passing the redirect urls back here, so we ignore them if trying to load
|
||||||
|
// the same url. We'll expose a call to 'reload' to allow a user to load
|
||||||
|
// the existing page.
|
||||||
|
if ([URL isEqual:_webView.request.URL]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!URL) {
|
||||||
|
// Clear the webview
|
||||||
|
[_webView loadHTMLString:nil baseURL:nil];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[_webView loadRequest:[NSURLRequest requestWithURL:URL]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)layoutSubviews
|
||||||
|
{
|
||||||
|
[super layoutSubviews];
|
||||||
|
_webView.frame = self.bounds;
|
||||||
|
[RCTView autoAdjustInsetsForView:self
|
||||||
|
withScrollView:_webView.scrollView
|
||||||
|
updateOffset:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setContentInset:(UIEdgeInsets)contentInset
|
||||||
|
{
|
||||||
|
_contentInset = contentInset;
|
||||||
|
[RCTView autoAdjustInsetsForView:self
|
||||||
|
withScrollView:_webView.scrollView
|
||||||
|
updateOffset:NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSMutableDictionary *)baseEvent
|
||||||
|
{
|
||||||
|
NSURL *url = _webView.request.URL;
|
||||||
|
NSString *title = [_webView stringByEvaluatingJavaScriptFromString:@"document.title"];
|
||||||
|
NSMutableDictionary *event = [[NSMutableDictionary alloc] initWithDictionary: @{
|
||||||
|
@"target": self.reactTag,
|
||||||
|
@"url": url ? [url absoluteString] : @"",
|
||||||
|
@"loading" : @(_webView.loading),
|
||||||
|
@"title": title,
|
||||||
|
@"canGoBack": @([_webView canGoBack]),
|
||||||
|
@"canGoForward" : @([_webView canGoForward]),
|
||||||
|
}];
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - UIWebViewDelegate methods
|
||||||
|
|
||||||
|
static NSString *const RCTJSAJAXScheme = @"react-ajax";
|
||||||
|
|
||||||
|
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
|
||||||
|
navigationType:(UIWebViewNavigationType)navigationType
|
||||||
|
{
|
||||||
|
// We have this check to filter out iframe requests and whatnot
|
||||||
|
BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL];
|
||||||
|
if (isTopFrame) {
|
||||||
|
NSMutableDictionary *event = [self baseEvent];
|
||||||
|
[event addEntriesFromDictionary: @{
|
||||||
|
@"url": [request.URL absoluteString],
|
||||||
|
@"navigationType": @(navigationType)
|
||||||
|
}];
|
||||||
|
[_eventDispatcher sendInputEventWithName:@"topLoadingStart" body:event];
|
||||||
|
}
|
||||||
|
|
||||||
|
// AJAX handler
|
||||||
|
return ![request.URL.scheme isEqualToString:RCTJSAJAXScheme];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
|
||||||
|
{
|
||||||
|
if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) {
|
||||||
|
// NSURLErrorCancelled is reported when a page has a redirect OR if you load
|
||||||
|
// a new URL in the WebView before the previous one came back. We can just
|
||||||
|
// ignore these since they aren't real errors.
|
||||||
|
// http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSMutableDictionary *event = [self baseEvent];
|
||||||
|
[event addEntriesFromDictionary: @{
|
||||||
|
@"domain": error.domain,
|
||||||
|
@"code": @(error.code),
|
||||||
|
@"description": [error localizedDescription],
|
||||||
|
}];
|
||||||
|
[_eventDispatcher sendInputEventWithName:@"topLoadingError" body:event];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)webViewDidFinishLoad:(UIWebView *)webView
|
||||||
|
{
|
||||||
|
if (_shouldInjectAJAXHandler) {
|
||||||
|
|
||||||
|
// From http://stackoverflow.com/questions/5353278/uiwebviewdelegate-not-monitoring-xmlhttprequest
|
||||||
|
|
||||||
|
[webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"\
|
||||||
|
var s_ajaxListener = new Object(); \n\
|
||||||
|
s_ajaxListener.tempOpen = XMLHttpRequest.prototype.open; \n\
|
||||||
|
s_ajaxListener.tempSend = XMLHttpRequest.prototype.send; \n\
|
||||||
|
s_ajaxListener.callback = function() { \n\
|
||||||
|
window.location.href = '%@://' + this.url; \n\
|
||||||
|
} \n\
|
||||||
|
XMLHttpRequest.prototype.open = function(a,b) { \n\
|
||||||
|
s_ajaxListener.tempOpen.apply(this, arguments); \n\
|
||||||
|
s_ajaxListener.method = a; \n\
|
||||||
|
s_ajaxListener.url = b; \n\
|
||||||
|
if (a.toLowerCase() === 'get') { \n\
|
||||||
|
s_ajaxListener.data = (b.split('?'))[1]; \n\
|
||||||
|
} \n\
|
||||||
|
} \n\
|
||||||
|
XMLHttpRequest.prototype.send = function(a,b) { \n\
|
||||||
|
s_ajaxListener.tempSend.apply(this, arguments); \n\
|
||||||
|
if (s_ajaxListener.method.toLowerCase() === 'post') { \n\
|
||||||
|
s_ajaxListener.data = a; \n\
|
||||||
|
} \n\
|
||||||
|
s_ajaxListener.callback(); \n\
|
||||||
|
} \n\
|
||||||
|
", RCTJSAJAXScheme]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// we only need the final 'finishLoad' call so only fire the event when we're actually done loading.
|
||||||
|
if (!webView.loading && ![webView.request.URL.absoluteString isEqualToString:@"about:blank"]) {
|
||||||
|
[_eventDispatcher sendInputEventWithName:@"topLoadingFinish" body:[self baseEvent]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,7 @@
|
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
#import "RCTViewManager.h"
|
||||||
|
|
||||||
|
@interface RCTWebViewManager : RCTViewManager
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,76 @@
|
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
#import "RCTWebViewManager.h"
|
||||||
|
|
||||||
|
#import "RCTBridge.h"
|
||||||
|
#import "RCTSparseArray.h"
|
||||||
|
#import "RCTUIManager.h"
|
||||||
|
#import "RCTWebView.h"
|
||||||
|
|
||||||
|
@implementation RCTWebViewManager
|
||||||
|
|
||||||
|
- (UIView *)view
|
||||||
|
{
|
||||||
|
return [[RCTWebView alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_REMAP_VIEW_PROPERTY(url, URL);
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(contentInset);
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets);
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(shouldInjectAJAXHandler);
|
||||||
|
|
||||||
|
- (NSDictionary *)constantsToExport
|
||||||
|
{
|
||||||
|
return @{
|
||||||
|
@"NavigationType": @{
|
||||||
|
@"LinkClicked": @(UIWebViewNavigationTypeLinkClicked),
|
||||||
|
@"FormSubmitted": @(UIWebViewNavigationTypeFormSubmitted),
|
||||||
|
@"BackForward": @(UIWebViewNavigationTypeBackForward),
|
||||||
|
@"Reload": @(UIWebViewNavigationTypeReload),
|
||||||
|
@"FormResubmitted": @(UIWebViewNavigationTypeFormResubmitted),
|
||||||
|
@"Other": @(UIWebViewNavigationTypeOther)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)goBack:(NSNumber *)reactTag
|
||||||
|
{
|
||||||
|
RCT_EXPORT();
|
||||||
|
|
||||||
|
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
|
RCTWebView *view = viewRegistry[reactTag];
|
||||||
|
if (![view isKindOfClass:[RCTWebView class]]) {
|
||||||
|
RCTLogError(@"Invalid view returned from registry, expecting RKWebView, got: %@", view);
|
||||||
|
}
|
||||||
|
[view goBack];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)goForward:(NSNumber *)reactTag
|
||||||
|
{
|
||||||
|
RCT_EXPORT();
|
||||||
|
|
||||||
|
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
|
id view = viewRegistry[reactTag];
|
||||||
|
if (![view isKindOfClass:[RCTWebView class]]) {
|
||||||
|
RCTLogError(@"Invalid view returned from registry, expecting RKWebView, got: %@", view);
|
||||||
|
}
|
||||||
|
[view goForward];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (void)reload:(NSNumber *)reactTag
|
||||||
|
{
|
||||||
|
RCT_EXPORT();
|
||||||
|
|
||||||
|
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
|
RCTWebView *view = viewRegistry[reactTag];
|
||||||
|
if (![view isKindOfClass:[RCTWebView class]]) {
|
||||||
|
RCTLogMustFix(@"Invalid view returned from registry, expecting RKWebView, got: %@", view);
|
||||||
|
}
|
||||||
|
[view reload];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
Загрузка…
Ссылка в новой задаче