Нема описа

index.android.js 9.0KB

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