|
@@ -1,257 +1,307 @@
|
1
|
|
-'use strict'
|
|
1
|
+"use strict";
|
2
|
2
|
|
3
|
|
-import React, { PureComponent } from 'react';
|
|
3
|
+import React, { PureComponent } from "react";
|
4
|
4
|
|
5
|
5
|
import {
|
6
|
|
- findNodeHandle,
|
7
|
|
- requireNativeComponent,
|
8
|
|
- Animated,
|
9
|
|
- DeviceEventEmitter,
|
10
|
|
- Dimensions,
|
11
|
|
- StyleSheet,
|
12
|
|
- Platform,
|
13
|
|
- UIManager,
|
14
|
|
- View,
|
15
|
|
- ViewPropTypes,
|
16
|
|
- WebView
|
17
|
|
-} from 'react-native';
|
|
6
|
+ findNodeHandle,
|
|
7
|
+ requireNativeComponent,
|
|
8
|
+ Animated,
|
|
9
|
+ DeviceEventEmitter,
|
|
10
|
+ Dimensions,
|
|
11
|
+ StyleSheet,
|
|
12
|
+ Platform,
|
|
13
|
+ UIManager,
|
|
14
|
+ View,
|
|
15
|
+ ViewPropTypes,
|
|
16
|
+ WebView
|
|
17
|
+} from "react-native";
|
18
|
18
|
|
19
|
|
-import PropTypes from 'prop-types';
|
|
19
|
+import PropTypes from "prop-types";
|
20
|
20
|
|
21
|
|
-import Immutable from 'immutable';
|
|
21
|
+import Immutable from "immutable";
|
22
|
22
|
|
23
|
|
-const RCTAutoHeightWebView = requireNativeComponent('RCTAutoHeightWebView', AutoHeightWebView, { nativeOnly: { messagingEnabled: PropTypes.bool } });
|
|
23
|
+const RCTAutoHeightWebView = requireNativeComponent(
|
|
24
|
+ "RCTAutoHeightWebView",
|
|
25
|
+ AutoHeightWebView,
|
|
26
|
+ { nativeOnly: { messagingEnabled: PropTypes.bool } }
|
|
27
|
+);
|
24
|
28
|
|
25
|
29
|
export default class AutoHeightWebView extends PureComponent {
|
26
|
|
- static propTypes = {
|
27
|
|
- source: WebView.propTypes.source,
|
28
|
|
- onHeightUpdated: PropTypes.func,
|
29
|
|
- customScript: PropTypes.string,
|
30
|
|
- enableAnimation: PropTypes.bool,
|
31
|
|
- // if set to true may cause some layout issues (smaller font size)
|
32
|
|
- scalesPageToFit: PropTypes.bool,
|
33
|
|
- // only works on enable animation
|
34
|
|
- animationDuration: PropTypes.number,
|
35
|
|
- // offset of rn webview margin
|
36
|
|
- heightOffset: PropTypes.number,
|
37
|
|
- // baseUrl not work in android 4.3 or below version
|
38
|
|
- enableBaseUrl: PropTypes.bool,
|
39
|
|
- style: ViewPropTypes.style,
|
40
|
|
- // works if set enableBaseUrl to true; add web/files... to android/app/src/assets/
|
41
|
|
- files: PropTypes.arrayOf(PropTypes.shape({
|
42
|
|
- href: PropTypes.string,
|
43
|
|
- type: PropTypes.string,
|
44
|
|
- rel: PropTypes.string
|
45
|
|
- }))
|
46
|
|
- }
|
|
30
|
+ static propTypes = {
|
|
31
|
+ source: WebView.propTypes.source,
|
|
32
|
+ onHeightUpdated: PropTypes.func,
|
|
33
|
+ customScript: PropTypes.string,
|
|
34
|
+ enableAnimation: PropTypes.bool,
|
|
35
|
+ // if set to false may cause some layout issues (width of container will be than width of screen)
|
|
36
|
+ scalesPageToFit: PropTypes.bool,
|
|
37
|
+ // only works on enable animation
|
|
38
|
+ animationDuration: PropTypes.number,
|
|
39
|
+ // offset of rn webview margin
|
|
40
|
+ heightOffset: PropTypes.number,
|
|
41
|
+ // baseUrl not work in android 4.3 or below version
|
|
42
|
+ enableBaseUrl: PropTypes.bool,
|
|
43
|
+ style: ViewPropTypes.style,
|
|
44
|
+ // works if set enableBaseUrl to true; add web/files... to android/app/src/assets/
|
|
45
|
+ files: PropTypes.arrayOf(
|
|
46
|
+ PropTypes.shape({
|
|
47
|
+ href: PropTypes.string,
|
|
48
|
+ type: PropTypes.string,
|
|
49
|
+ rel: PropTypes.string
|
|
50
|
+ })
|
|
51
|
+ )
|
|
52
|
+ };
|
47
|
53
|
|
48
|
|
- static defaultProps = {
|
49
|
|
- scalesPageToFit: false,
|
50
|
|
- enableBaseUrl: false,
|
51
|
|
- enableAnimation: true,
|
52
|
|
- animationDuration: 555,
|
53
|
|
- heightOffset: 20
|
54
|
|
- }
|
|
54
|
+ static defaultProps = {
|
|
55
|
+ scalesPageToFit: true,
|
|
56
|
+ enableBaseUrl: false,
|
|
57
|
+ enableAnimation: true,
|
|
58
|
+ animationDuration: 555,
|
|
59
|
+ heightOffset: 20
|
|
60
|
+ };
|
55
|
61
|
|
56
|
|
- constructor(props) {
|
57
|
|
- super(props);
|
58
|
|
- this.onMessage = this.onMessage.bind(this);
|
59
|
|
- if (this.props.enableAnimation) {
|
60
|
|
- this.opacityAnimatedValue = new Animated.Value(0);
|
61
|
|
- }
|
62
|
|
- if (IsBelowKitKat) {
|
63
|
|
- this.listenWebViewBridgeMessage = this.listenWebViewBridgeMessage.bind(this);
|
64
|
|
- }
|
65
|
|
- const initialScript = props.files ? this.appendFilesToHead(props.files, BaseScript) : BaseScript;
|
66
|
|
- this.state = {
|
67
|
|
- isChangingSource: false,
|
68
|
|
- height: 0,
|
69
|
|
- heightOffset: 0,
|
70
|
|
- script: initialScript
|
71
|
|
- };
|
|
62
|
+ constructor(props) {
|
|
63
|
+ super(props);
|
|
64
|
+ this.onMessage = this.onMessage.bind(this);
|
|
65
|
+ if (this.props.enableAnimation) {
|
|
66
|
+ this.opacityAnimatedValue = new Animated.Value(0);
|
72
|
67
|
}
|
73
|
|
-
|
74
|
|
- componentWillMount() {
|
75
|
|
- if (IsBelowKitKat) {
|
76
|
|
- DeviceEventEmitter.addListener("webViewBridgeMessage", this.listenWebViewBridgeMessage);
|
77
|
|
- }
|
|
68
|
+ if (IsBelowKitKat) {
|
|
69
|
+ this.listenWebViewBridgeMessage = this.listenWebViewBridgeMessage.bind(
|
|
70
|
+ this
|
|
71
|
+ );
|
78
|
72
|
}
|
|
73
|
+ const initialScript = props.files
|
|
74
|
+ ? this.appendFilesToHead(props.files, BaseScript)
|
|
75
|
+ : BaseScript;
|
|
76
|
+ this.state = {
|
|
77
|
+ isChangingSource: false,
|
|
78
|
+ height: 0,
|
|
79
|
+ heightOffset: 0,
|
|
80
|
+ script: initialScript
|
|
81
|
+ };
|
|
82
|
+ }
|
79
|
83
|
|
80
|
|
- componentDidMount() {
|
81
|
|
- this.startInterval();
|
|
84
|
+ componentWillMount() {
|
|
85
|
+ if (IsBelowKitKat) {
|
|
86
|
+ DeviceEventEmitter.addListener(
|
|
87
|
+ "webViewBridgeMessage",
|
|
88
|
+ this.listenWebViewBridgeMessage
|
|
89
|
+ );
|
82
|
90
|
}
|
|
91
|
+ }
|
83
|
92
|
|
84
|
|
- componentWillReceiveProps(nextProps) {
|
85
|
|
- // injectedJavaScript only works when webview reload (source changed)
|
86
|
|
- if (Immutable.is(Immutable.fromJS(this.props.source), Immutable.fromJS(nextProps.source))) {
|
87
|
|
- return;
|
88
|
|
- }
|
89
|
|
- else {
|
90
|
|
- this.setState({
|
91
|
|
- isChangingSource: true,
|
92
|
|
- height: 0,
|
93
|
|
- heightOffset: 0
|
94
|
|
- }, () => {
|
95
|
|
- this.startInterval();
|
96
|
|
- this.setState({ isChangingSource: false });
|
97
|
|
- });
|
98
|
|
- }
|
99
|
|
- let currentScript = BaseScript;
|
100
|
|
- if (nextProps.files) {
|
101
|
|
- currentScript = this.appendFilesToHead(nextProps.files, BaseScript);
|
102
|
|
- }
|
103
|
|
- this.setState({ script: currentScript });
|
104
|
|
- }
|
|
93
|
+ componentDidMount() {
|
|
94
|
+ this.startInterval();
|
|
95
|
+ }
|
105
|
96
|
|
106
|
|
- componentWillUnmount() {
|
107
|
|
- this.stopInterval();
|
108
|
|
- if (IsBelowKitKat) {
|
109
|
|
- DeviceEventEmitter.removeListener("webViewBridgeMessage", this.listenWebViewBridgeMessage);
|
|
97
|
+ componentWillReceiveProps(nextProps) {
|
|
98
|
+ // injectedJavaScript only works when webview reload (source changed)
|
|
99
|
+ if (
|
|
100
|
+ Immutable.is(
|
|
101
|
+ Immutable.fromJS(this.props.source),
|
|
102
|
+ Immutable.fromJS(nextProps.source)
|
|
103
|
+ )
|
|
104
|
+ ) {
|
|
105
|
+ return;
|
|
106
|
+ } else {
|
|
107
|
+ this.setState(
|
|
108
|
+ {
|
|
109
|
+ isChangingSource: true,
|
|
110
|
+ height: 0,
|
|
111
|
+ heightOffset: 0
|
|
112
|
+ },
|
|
113
|
+ () => {
|
|
114
|
+ this.startInterval();
|
|
115
|
+ this.setState({ isChangingSource: false });
|
110
|
116
|
}
|
|
117
|
+ );
|
111
|
118
|
}
|
112
|
|
-
|
113
|
|
- // below kitkat
|
114
|
|
- listenWebViewBridgeMessage(body) {
|
115
|
|
- this.onMessage(body.message);
|
|
119
|
+ let currentScript = BaseScript;
|
|
120
|
+ if (nextProps.files) {
|
|
121
|
+ currentScript = this.appendFilesToHead(nextProps.files, BaseScript);
|
116
|
122
|
}
|
|
123
|
+ this.setState({ script: currentScript });
|
|
124
|
+ }
|
117
|
125
|
|
118
|
|
- // below kitkat
|
119
|
|
- sendToWebView(message) {
|
120
|
|
- UIManager.dispatchViewManagerCommand(
|
121
|
|
- findNodeHandle(this.webview),
|
122
|
|
- UIManager.RCTAutoHeightWebView.Commands.sendToWebView,
|
123
|
|
- [String(message)]
|
124
|
|
- );
|
|
126
|
+ componentWillUnmount() {
|
|
127
|
+ this.stopInterval();
|
|
128
|
+ if (IsBelowKitKat) {
|
|
129
|
+ DeviceEventEmitter.removeListener(
|
|
130
|
+ "webViewBridgeMessage",
|
|
131
|
+ this.listenWebViewBridgeMessage
|
|
132
|
+ );
|
125
|
133
|
}
|
|
134
|
+ }
|
126
|
135
|
|
127
|
|
- postMessage(data) {
|
128
|
|
- UIManager.dispatchViewManagerCommand(
|
129
|
|
- findNodeHandle(this.webview),
|
130
|
|
- UIManager.RCTAutoHeightWebView.Commands.postMessage,
|
131
|
|
- [String(data)]
|
132
|
|
- );
|
133
|
|
- };
|
|
136
|
+ // below kitkat
|
|
137
|
+ listenWebViewBridgeMessage(body) {
|
|
138
|
+ this.onMessage(body.message);
|
|
139
|
+ }
|
134
|
140
|
|
135
|
|
- startInterval() {
|
136
|
|
- this.finishInterval = false;
|
137
|
|
- this.interval = setInterval(() => {
|
138
|
|
- if (!this.finishInterval) {
|
139
|
|
- IsBelowKitKat ? this.sendToWebView('getBodyHeight') : this.postMessage('getBodyHeight');
|
140
|
|
- }
|
141
|
|
- }, 205);
|
142
|
|
- }
|
|
141
|
+ // below kitkat
|
|
142
|
+ sendToWebView(message) {
|
|
143
|
+ UIManager.dispatchViewManagerCommand(
|
|
144
|
+ findNodeHandle(this.webview),
|
|
145
|
+ UIManager.RCTAutoHeightWebView.Commands.sendToWebView,
|
|
146
|
+ [String(message)]
|
|
147
|
+ );
|
|
148
|
+ }
|
143
|
149
|
|
144
|
|
- stopInterval() {
|
145
|
|
- this.finishInterval = true;
|
146
|
|
- clearInterval(this.interval);
|
147
|
|
- }
|
|
150
|
+ postMessage(data) {
|
|
151
|
+ UIManager.dispatchViewManagerCommand(
|
|
152
|
+ findNodeHandle(this.webview),
|
|
153
|
+ UIManager.RCTAutoHeightWebView.Commands.postMessage,
|
|
154
|
+ [String(data)]
|
|
155
|
+ );
|
|
156
|
+ }
|
148
|
157
|
|
149
|
|
- onHeightUpdated(height) {
|
150
|
|
- if (this.props.onHeightUpdated) {
|
151
|
|
- this.props.onHeightUpdated(height);
|
152
|
|
- }
|
|
158
|
+ startInterval() {
|
|
159
|
+ this.finishInterval = false;
|
|
160
|
+ this.interval = setInterval(() => {
|
|
161
|
+ if (!this.finishInterval) {
|
|
162
|
+ IsBelowKitKat
|
|
163
|
+ ? this.sendToWebView("getBodyHeight")
|
|
164
|
+ : this.postMessage("getBodyHeight");
|
|
165
|
+ }
|
|
166
|
+ }, 205);
|
|
167
|
+ }
|
|
168
|
+
|
|
169
|
+ stopInterval() {
|
|
170
|
+ this.finishInterval = true;
|
|
171
|
+ clearInterval(this.interval);
|
|
172
|
+ }
|
|
173
|
+
|
|
174
|
+ onHeightUpdated(height) {
|
|
175
|
+ if (this.props.onHeightUpdated) {
|
|
176
|
+ this.props.onHeightUpdated(height);
|
153
|
177
|
}
|
|
178
|
+ }
|
154
|
179
|
|
155
|
|
- onMessage(e) {
|
156
|
|
- const height = parseInt(IsBelowKitKat ? e.nativeEvent.message : e.nativeEvent.data);
|
157
|
|
- if (height) {
|
158
|
|
- if (this.props.enableAnimation) {
|
159
|
|
- this.opacityAnimatedValue.setValue(0);
|
160
|
|
- }
|
161
|
|
- this.stopInterval();
|
162
|
|
- this.setState({
|
163
|
|
- heightOffset: this.props.heightOffset,
|
164
|
|
- height
|
165
|
|
- }, () => {
|
166
|
|
- if (this.props.enableAnimation) {
|
167
|
|
- Animated.timing(this.opacityAnimatedValue, {
|
168
|
|
- toValue: 1,
|
169
|
|
- duration: this.props.animationDuration
|
170
|
|
- }).start(() => this.onHeightUpdated(height));
|
171
|
|
- }
|
172
|
|
- else {
|
173
|
|
- this.onHeightUpdated(height);
|
174
|
|
- }
|
175
|
|
- });
|
|
180
|
+ onMessage(e) {
|
|
181
|
+ const height = parseInt(
|
|
182
|
+ IsBelowKitKat ? e.nativeEvent.message : e.nativeEvent.data
|
|
183
|
+ );
|
|
184
|
+ if (height) {
|
|
185
|
+ if (this.props.enableAnimation) {
|
|
186
|
+ this.opacityAnimatedValue.setValue(0);
|
|
187
|
+ }
|
|
188
|
+ this.stopInterval();
|
|
189
|
+ this.setState(
|
|
190
|
+ {
|
|
191
|
+ heightOffset: this.props.heightOffset,
|
|
192
|
+ height
|
|
193
|
+ },
|
|
194
|
+ () => {
|
|
195
|
+ if (this.props.enableAnimation) {
|
|
196
|
+ Animated.timing(this.opacityAnimatedValue, {
|
|
197
|
+ toValue: 1,
|
|
198
|
+ duration: this.props.animationDuration
|
|
199
|
+ }).start(() => this.onHeightUpdated(height));
|
|
200
|
+ } else {
|
|
201
|
+ this.onHeightUpdated(height);
|
|
202
|
+ }
|
176
|
203
|
}
|
|
204
|
+ );
|
177
|
205
|
}
|
|
206
|
+ }
|
178
|
207
|
|
179
|
|
- appendFilesToHead(files, script) {
|
180
|
|
- if (!files) {
|
181
|
|
- return script;
|
182
|
|
- }
|
183
|
|
- for (let file of files) {
|
184
|
|
- script =
|
185
|
|
- `
|
|
208
|
+ appendFilesToHead(files, script) {
|
|
209
|
+ if (!files) {
|
|
210
|
+ return script;
|
|
211
|
+ }
|
|
212
|
+ for (let file of files) {
|
|
213
|
+ script =
|
|
214
|
+ `
|
186
|
215
|
var link = document.createElement('link');
|
187
|
|
- link.rel = '` + file.rel + `';
|
188
|
|
- link.type = '` + file.type + `';
|
189
|
|
- link.href = '` + file.href + `';
|
|
216
|
+ link.rel = '` +
|
|
217
|
+ file.rel +
|
|
218
|
+ `';
|
|
219
|
+ link.type = '` +
|
|
220
|
+ file.type +
|
|
221
|
+ `';
|
|
222
|
+ link.href = '` +
|
|
223
|
+ file.href +
|
|
224
|
+ `';
|
190
|
225
|
document.head.appendChild(link);
|
191
|
|
- `+ script;
|
192
|
|
- }
|
193
|
|
- return script;
|
|
226
|
+ ` +
|
|
227
|
+ script;
|
194
|
228
|
}
|
|
229
|
+ return script;
|
|
230
|
+ }
|
195
|
231
|
|
196
|
|
- render() {
|
197
|
|
- const { height, script, isChangingSource, heightOffset } = this.state;
|
198
|
|
- const { scalesPageToFit, enableAnimation, source, customScript, style, enableBaseUrl } = this.props;
|
199
|
|
- let webViewSource = source;
|
200
|
|
- if (enableBaseUrl) {
|
201
|
|
- webViewSource = Object.assign({}, source, { baseUrl: 'file:///android_asset/web/' });
|
202
|
|
- }
|
203
|
|
- return (
|
204
|
|
- <Animated.View style={[Styles.container, {
|
205
|
|
- opacity: enableAnimation ? this.opacityAnimatedValue : 1,
|
206
|
|
- height: height + heightOffset,
|
207
|
|
- }, style]}>
|
208
|
|
- {
|
209
|
|
- isChangingSource ? null :
|
210
|
|
- <RCTAutoHeightWebView
|
211
|
|
- ref={webview => this.webview = webview}
|
212
|
|
- style={Styles.webView}
|
213
|
|
- javaScriptEnabled={true}
|
214
|
|
- injectedJavaScript={script + customScript}
|
215
|
|
- scalesPageToFit={scalesPageToFit}
|
216
|
|
- source={webViewSource}
|
217
|
|
- onMessage={this.onMessage}
|
218
|
|
- messagingEnabled={true}
|
219
|
|
- // below kitkat
|
220
|
|
- onChange={this.onMessage} />
|
221
|
|
- }
|
222
|
|
- </Animated.View>
|
223
|
|
- );
|
|
232
|
+ render() {
|
|
233
|
+ const { height, script, isChangingSource, heightOffset } = this.state;
|
|
234
|
+ const {
|
|
235
|
+ scalesPageToFit,
|
|
236
|
+ enableAnimation,
|
|
237
|
+ source,
|
|
238
|
+ customScript,
|
|
239
|
+ style,
|
|
240
|
+ enableBaseUrl
|
|
241
|
+ } = this.props;
|
|
242
|
+ let webViewSource = source;
|
|
243
|
+ if (enableBaseUrl) {
|
|
244
|
+ webViewSource = Object.assign({}, source, {
|
|
245
|
+ baseUrl: "file:///android_asset/web/"
|
|
246
|
+ });
|
224
|
247
|
}
|
|
248
|
+ return (
|
|
249
|
+ <Animated.View
|
|
250
|
+ style={[
|
|
251
|
+ Styles.container,
|
|
252
|
+ {
|
|
253
|
+ opacity: enableAnimation ? this.opacityAnimatedValue : 1,
|
|
254
|
+ height: height + heightOffset
|
|
255
|
+ },
|
|
256
|
+ style
|
|
257
|
+ ]}
|
|
258
|
+ >
|
|
259
|
+ {isChangingSource ? null : (
|
|
260
|
+ <RCTAutoHeightWebView
|
|
261
|
+ ref={webview => (this.webview = webview)}
|
|
262
|
+ style={Styles.webView}
|
|
263
|
+ javaScriptEnabled={true}
|
|
264
|
+ injectedJavaScript={script + customScript}
|
|
265
|
+ scalesPageToFit={scalesPageToFit}
|
|
266
|
+ source={webViewSource}
|
|
267
|
+ onMessage={this.onMessage}
|
|
268
|
+ messagingEnabled={true}
|
|
269
|
+ // below kitkat
|
|
270
|
+ onChange={this.onMessage}
|
|
271
|
+ />
|
|
272
|
+ )}
|
|
273
|
+ </Animated.View>
|
|
274
|
+ );
|
|
275
|
+ }
|
225
|
276
|
}
|
226
|
277
|
|
227
|
|
-const ScreenWidth = Dimensions.get('window').width;
|
|
278
|
+const ScreenWidth = Dimensions.get("window").width;
|
228
|
279
|
|
229
|
280
|
const IsBelowKitKat = Platform.Version < 19;
|
230
|
281
|
|
231
|
282
|
const Styles = StyleSheet.create({
|
232
|
|
- container: {
|
233
|
|
- width: ScreenWidth,
|
234
|
|
- backgroundColor: 'transparent'
|
235
|
|
- },
|
236
|
|
- webView: {
|
237
|
|
- flex: 1,
|
238
|
|
- backgroundColor: 'transparent'
|
239
|
|
- }
|
|
283
|
+ container: {
|
|
284
|
+ width: ScreenWidth,
|
|
285
|
+ backgroundColor: "transparent"
|
|
286
|
+ },
|
|
287
|
+ webView: {
|
|
288
|
+ flex: 1,
|
|
289
|
+ backgroundColor: "transparent"
|
|
290
|
+ }
|
240
|
291
|
});
|
241
|
292
|
|
242
|
|
-const BaseScript =
|
243
|
|
- IsBelowKitKat ?
|
244
|
|
- `
|
|
293
|
+const BaseScript = IsBelowKitKat
|
|
294
|
+ ? `
|
245
|
295
|
; (function () {
|
246
|
296
|
AutoHeightWebView.onMessage = function (message) {
|
247
|
297
|
AutoHeightWebView.send(String(document.body.offsetHeight));
|
248
|
298
|
};
|
249
|
299
|
} ());
|
250
|
|
- ` :
|
251
|
|
- `
|
|
300
|
+ `
|
|
301
|
+ : `
|
252
|
302
|
; (function () {
|
253
|
303
|
document.addEventListener('message', function (e) {
|
254
|
304
|
window.postMessage(String(document.body.offsetHeight));
|
255
|
305
|
});
|
256
|
306
|
} ());
|
257
|
|
- `;
|
|
307
|
+ `;
|