Нема описа

WebView.windows.tsx 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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. /**
  96. * We return an event with a bunch of fields including:
  97. * url, title, loading, canGoBack, canGoForward
  98. */
  99. updateNavigationState = (event: WebViewNavigationEvent) => {
  100. if (this.props.onNavigationStateChange) {
  101. this.props.onNavigationStateChange(event.nativeEvent);
  102. }
  103. }
  104. getWebViewHandle = () => {
  105. // eslint-disable-next-line react/no-string-refs
  106. return findNodeHandle(this.webViewRef.current);
  107. }
  108. onLoadingStart = (event: WebViewNavigationEvent) => {
  109. const { onLoadStart } = this.props;
  110. if(onLoadStart) {
  111. onLoadStart(event);
  112. }
  113. this.updateNavigationState(event);
  114. }
  115. onLoadingProgress = (event: WebViewProgressEvent) => {
  116. const { onLoadProgress } = this.props;
  117. if (onLoadProgress) {
  118. onLoadProgress(event);
  119. }
  120. };
  121. onLoadingError = (event: WebViewErrorEvent) => {
  122. event.persist(); // persist this event because we need to store it
  123. const {onError, onLoadEnd} = this.props;
  124. if(onError) {
  125. onError(event);
  126. }
  127. if(onLoadEnd) {
  128. onLoadEnd(event);
  129. }
  130. console.error('Encountered an error loading page', event.nativeEvent);
  131. this.setState({
  132. lastErrorEvent: event.nativeEvent,
  133. viewState: 'ERROR',
  134. });
  135. }
  136. onLoadingFinish =(event: WebViewNavigationEvent) => {
  137. const {onLoad, onLoadEnd} = this.props;
  138. if(onLoad) {
  139. onLoad(event);
  140. }
  141. if(onLoadEnd) {
  142. onLoadEnd(event);
  143. }
  144. this.setState({
  145. viewState: 'IDLE',
  146. });
  147. this.updateNavigationState(event);
  148. }
  149. onMessage = (event: WebViewMessageEvent) => {
  150. const { onMessage } = this.props;
  151. if (onMessage) {
  152. onMessage(event);
  153. }
  154. }
  155. onHttpError = (event: WebViewHttpErrorEvent) => {
  156. const { onHttpError } = this.props;
  157. if (onHttpError) {
  158. onHttpError(event);
  159. }
  160. }
  161. render () {
  162. const {
  163. nativeConfig = {},
  164. onMessage,
  165. onShouldStartLoadWithRequest: onShouldStartLoadWithRequestProp,
  166. originWhitelist,
  167. renderError,
  168. renderLoading,
  169. style,
  170. containerStyle,
  171. ...otherProps
  172. } = this.props;
  173. let otherView = null;
  174. if (this.state.viewState === 'LOADING') {
  175. otherView = this.props.renderLoading && this.props.renderLoading();
  176. } else if (this.state.viewState === 'ERROR') {
  177. const errorEvent = this.state.lastErrorEvent;
  178. otherView = this.props.renderError
  179. && this.props.renderError(
  180. errorEvent.domain,
  181. errorEvent.code,
  182. errorEvent.description,
  183. );
  184. } else if (this.state.viewState !== 'IDLE') {
  185. console.error('RCTWebView invalid state encountered: ', this.state.viewState);
  186. }
  187. const webViewStyles = [styles.container, this.props.style];
  188. if (
  189. this.state.viewState === 'LOADING'
  190. || this.state.viewState === 'ERROR'
  191. ) {
  192. // if we're in either LOADING or ERROR states, don't show the webView
  193. webViewStyles.push(styles.hidden);
  194. }
  195. const onShouldStartLoadWithRequest = createOnShouldStartLoadWithRequest(
  196. ()=>{},
  197. // casting cause it's in the default props
  198. originWhitelist as readonly string[],
  199. onShouldStartLoadWithRequestProp,
  200. );
  201. const NativeWebView
  202. = (nativeConfig.component as typeof NativeWebViewWindows | undefined)
  203. || RCTWebView;
  204. const webView = (
  205. <NativeWebView
  206. ref={this.webViewRef}
  207. key="webViewKey"
  208. {...otherProps}
  209. messagingEnabled={typeof onMessage === 'function'}
  210. onLoadingError={this.onLoadingError}
  211. onLoadingFinish={this.onLoadingFinish}
  212. onLoadingProgress={this.onLoadingProgress}
  213. onLoadingStart={this.onLoadingStart}
  214. onHttpError={this.onHttpError}
  215. onMessage={this.onMessage}
  216. onScroll={this.props.onScroll}
  217. onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
  218. source={resolveAssetSource(this.props.source as ImageSourcePropType)}
  219. style={webViewStyles}
  220. {...nativeConfig.props}
  221. />
  222. );
  223. return (
  224. <View style={styles.container}>
  225. {webView}
  226. {otherView}
  227. </View>
  228. );
  229. }
  230. }