Geen omschrijving

WebView.windows.tsx 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * Portions copyright for react-native-windows:
  8. *
  9. * Copyright (c) Microsoft Corporation. All rights reserved.
  10. * Licensed under the MIT License.
  11. */
  12. import React from 'react';
  13. import {
  14. UIManager as NotTypedUIManager,
  15. View,
  16. requireNativeComponent,
  17. StyleSheet,
  18. Image,
  19. ImageSourcePropType,
  20. findNodeHandle,
  21. } from 'react-native';
  22. import {
  23. createOnShouldStartLoadWithRequest,
  24. } from './WebViewShared';
  25. import {
  26. NativeWebViewWindows,
  27. WebViewSharedProps,
  28. WebViewProgressEvent,
  29. WebViewNavigationEvent,
  30. WebViewErrorEvent,
  31. WebViewHttpErrorEvent,
  32. WebViewMessageEvent,
  33. RNCWebViewUIManagerWindows,
  34. State,
  35. } from './WebViewTypes';
  36. const UIManager = NotTypedUIManager as RNCWebViewUIManagerWindows;
  37. const { resolveAssetSource } = Image;
  38. const RCTWebView: typeof NativeWebViewWindows = requireNativeComponent(
  39. 'RCTWebView',
  40. );
  41. const styles = StyleSheet.create({
  42. container: {
  43. flex: 1,
  44. },
  45. hidden: {
  46. height: 0,
  47. flex: 0, // disable 'flex:1' when hiding a View
  48. },
  49. loadingView: {
  50. flex: 1,
  51. justifyContent: 'center',
  52. alignItems: 'center',
  53. },
  54. loadingProgressBar: {
  55. height: 20,
  56. },
  57. });
  58. export default class WebView extends React.Component<WebViewSharedProps, State> {
  59. static defaultProps = {
  60. javaScriptEnabled: true,
  61. };
  62. state: State = {
  63. viewState: this.props.startInLoadingState ? 'LOADING' : 'IDLE',
  64. lastErrorEvent: null,
  65. }
  66. webViewRef = React.createRef<NativeWebViewWindows>();
  67. goForward = () => {
  68. UIManager.dispatchViewManagerCommand(
  69. this.getWebViewHandle(),
  70. UIManager.getViewManagerConfig('RCTWebView').Commands.goForward,
  71. undefined,
  72. );
  73. }
  74. goBack = () => {
  75. UIManager.dispatchViewManagerCommand(
  76. this.getWebViewHandle(),
  77. UIManager.getViewManagerConfig('RCTWebView').Commands.goBack,
  78. undefined,
  79. );
  80. }
  81. reload = () => {
  82. UIManager.dispatchViewManagerCommand(
  83. this.getWebViewHandle(),
  84. UIManager.getViewManagerConfig('RCTWebView').Commands.reload,
  85. undefined,
  86. );
  87. }
  88. injectJavaScript = (data: string) => {
  89. UIManager.dispatchViewManagerCommand(
  90. this.getWebViewHandle(),
  91. UIManager.getViewManagerConfig('RCTWebView').Commands.injectJavaScript,
  92. [data],
  93. );
  94. }
  95. postMessage = (data: string) => {
  96. UIManager.dispatchViewManagerCommand(
  97. this.getWebViewHandle(),
  98. UIManager.getViewManagerConfig('RCTWebView').Commands.postMessage,
  99. [String(data)],
  100. );
  101. };
  102. /**
  103. * We return an event with a bunch of fields including:
  104. * url, title, loading, canGoBack, canGoForward
  105. */
  106. updateNavigationState = (event: WebViewNavigationEvent) => {
  107. if (this.props.onNavigationStateChange) {
  108. this.props.onNavigationStateChange(event.nativeEvent);
  109. }
  110. }
  111. getWebViewHandle = () => {
  112. // eslint-disable-next-line react/no-string-refs
  113. return findNodeHandle(this.webViewRef.current);
  114. }
  115. onLoadingStart = (event: WebViewNavigationEvent) => {
  116. const { onLoadStart } = this.props;
  117. if(onLoadStart) {
  118. onLoadStart(event);
  119. }
  120. this.updateNavigationState(event);
  121. }
  122. onLoadingProgress = (event: WebViewProgressEvent) => {
  123. const { onLoadProgress } = this.props;
  124. if (onLoadProgress) {
  125. onLoadProgress(event);
  126. }
  127. };
  128. onLoadingError = (event: WebViewErrorEvent) => {
  129. event.persist(); // persist this event because we need to store it
  130. const {onError, onLoadEnd} = this.props;
  131. if(onError) {
  132. onError(event);
  133. }
  134. if(onLoadEnd) {
  135. onLoadEnd(event);
  136. }
  137. console.error('Encountered an error loading page', event.nativeEvent);
  138. this.setState({
  139. lastErrorEvent: event.nativeEvent,
  140. viewState: 'ERROR',
  141. });
  142. }
  143. onLoadingFinish =(event: WebViewNavigationEvent) => {
  144. const {onLoad, onLoadEnd} = this.props;
  145. if(onLoad) {
  146. onLoad(event);
  147. }
  148. if(onLoadEnd) {
  149. onLoadEnd(event);
  150. }
  151. this.setState({
  152. viewState: 'IDLE',
  153. });
  154. this.updateNavigationState(event);
  155. }
  156. onMessage = (event: WebViewMessageEvent) => {
  157. const { onMessage } = this.props;
  158. if (onMessage) {
  159. onMessage(event);
  160. }
  161. }
  162. onHttpError = (event: WebViewHttpErrorEvent) => {
  163. const { onHttpError } = this.props;
  164. if (onHttpError) {
  165. onHttpError(event);
  166. }
  167. }
  168. render () {
  169. const {
  170. nativeConfig = {},
  171. onMessage,
  172. onShouldStartLoadWithRequest: onShouldStartLoadWithRequestProp,
  173. originWhitelist,
  174. renderError,
  175. renderLoading,
  176. style,
  177. containerStyle,
  178. ...otherProps
  179. } = this.props;
  180. let otherView = null;
  181. if (this.state.viewState === 'LOADING') {
  182. otherView = this.props.renderLoading && this.props.renderLoading();
  183. } else if (this.state.viewState === 'ERROR') {
  184. const errorEvent = this.state.lastErrorEvent;
  185. otherView = this.props.renderError
  186. && this.props.renderError(
  187. errorEvent.domain,
  188. errorEvent.code,
  189. errorEvent.description,
  190. );
  191. } else if (this.state.viewState !== 'IDLE') {
  192. console.error('RCTWebView invalid state encountered: ', this.state.viewState);
  193. }
  194. const webViewStyles = [styles.container, this.props.style];
  195. if (
  196. this.state.viewState === 'LOADING'
  197. || this.state.viewState === 'ERROR'
  198. ) {
  199. // if we're in either LOADING or ERROR states, don't show the webView
  200. webViewStyles.push(styles.hidden);
  201. }
  202. const onShouldStartLoadWithRequest = createOnShouldStartLoadWithRequest(
  203. ()=>{},
  204. // casting cause it's in the default props
  205. originWhitelist as readonly string[],
  206. onShouldStartLoadWithRequestProp,
  207. );
  208. const NativeWebView
  209. = (nativeConfig.component as typeof NativeWebViewWindows | undefined)
  210. || RCTWebView;
  211. const webView = (
  212. <NativeWebView
  213. ref={this.webViewRef}
  214. key="webViewKey"
  215. {...otherProps}
  216. messagingEnabled={typeof onMessage === 'function'}
  217. onLoadingError={this.onLoadingError}
  218. onLoadingFinish={this.onLoadingFinish}
  219. onLoadingProgress={this.onLoadingProgress}
  220. onLoadingStart={this.onLoadingStart}
  221. onHttpError={this.onHttpError}
  222. onMessage={this.onMessage}
  223. onScroll={this.props.onScroll}
  224. onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
  225. source={resolveAssetSource(this.props.source as ImageSourcePropType)}
  226. style={webViewStyles}
  227. {...nativeConfig.props}
  228. />
  229. );
  230. return (
  231. <View style={styles.container}>
  232. {webView}
  233. {otherView}
  234. </View>
  235. );
  236. }
  237. }