Application Android et IOS pour l'amicale des élèves https://play.google.com/store/apps/details?id=fr.amicaleinsat.application
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

WebViewScreen.js 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. // @flow
  2. import * as React from 'react';
  3. import WebView from "react-native-webview";
  4. import BasicLoadingScreen from "./BasicLoadingScreen";
  5. import ErrorView from "./ErrorView";
  6. import {ERROR_TYPE} from "../../utils/WebData";
  7. import MaterialHeaderButtons, {Item} from '../Overrides/CustomHeaderButton';
  8. import {Divider, HiddenItem, OverflowMenu} from "react-navigation-header-buttons";
  9. import i18n from 'i18n-js';
  10. import {Animated, BackHandler, Linking} from "react-native";
  11. import {withCollapsible} from "../../utils/withCollapsible";
  12. import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
  13. import {withTheme} from "react-native-paper";
  14. import type {CustomTheme} from "../../managers/ThemeManager";
  15. import {StackNavigationProp} from "@react-navigation/stack";
  16. import {Collapsible} from "react-navigation-collapsible";
  17. type Props = {
  18. navigation: StackNavigationProp,
  19. theme: CustomTheme,
  20. url: string,
  21. customJS: string,
  22. customPaddingFunction: null | (padding: number) => string,
  23. collapsibleStack: Collapsible,
  24. onMessage: Function,
  25. onScroll: Function,
  26. showAdvancedControls: boolean,
  27. }
  28. const AnimatedWebView = Animated.createAnimatedComponent(WebView);
  29. /**
  30. * Class defining a webview screen.
  31. */
  32. class WebViewScreen extends React.PureComponent<Props> {
  33. static defaultProps = {
  34. customJS: '',
  35. showAdvancedControls: true,
  36. customPaddingFunction: null,
  37. };
  38. webviewRef: { current: null | WebView };
  39. canGoBack: boolean;
  40. constructor() {
  41. super();
  42. this.webviewRef = React.createRef();
  43. this.canGoBack = false;
  44. }
  45. /**
  46. * Creates header buttons and listens to events after mounting
  47. */
  48. componentDidMount() {
  49. this.props.navigation.setOptions({
  50. headerRight: this.props.showAdvancedControls
  51. ? this.getAdvancedButtons
  52. : this.getBasicButton,
  53. });
  54. this.props.navigation.addListener(
  55. 'focus',
  56. () =>
  57. BackHandler.addEventListener(
  58. 'hardwareBackPress',
  59. this.onBackButtonPressAndroid
  60. )
  61. );
  62. this.props.navigation.addListener(
  63. 'blur',
  64. () =>
  65. BackHandler.removeEventListener(
  66. 'hardwareBackPress',
  67. this.onBackButtonPressAndroid
  68. )
  69. );
  70. }
  71. /**
  72. * Goes back on the webview or on the navigation stack if we cannot go back anymore
  73. *
  74. * @returns {boolean}
  75. */
  76. onBackButtonPressAndroid = () => {
  77. if (this.canGoBack) {
  78. this.onGoBackClicked();
  79. return true;
  80. }
  81. return false;
  82. };
  83. /**
  84. * Gets header refresh and open in browser buttons
  85. *
  86. * @return {*}
  87. */
  88. getBasicButton = () => {
  89. return (
  90. <MaterialHeaderButtons>
  91. <Item
  92. title="refresh"
  93. iconName="refresh"
  94. onPress={this.onRefreshClicked}/>
  95. <Item
  96. title={i18n.t("general.openInBrowser")}
  97. iconName="open-in-new"
  98. onPress={this.onOpenClicked}/>
  99. </MaterialHeaderButtons>
  100. );
  101. };
  102. /**
  103. * Creates advanced header control buttons.
  104. * These buttons allows the user to refresh, go back, go forward and open in the browser.
  105. *
  106. * @returns {*}
  107. */
  108. getAdvancedButtons = () => {
  109. return (
  110. <MaterialHeaderButtons>
  111. <Item
  112. title="refresh"
  113. iconName="refresh"
  114. onPress={this.onRefreshClicked}
  115. />
  116. <OverflowMenu
  117. style={{marginHorizontal: 10}}
  118. OverflowIcon={
  119. <MaterialCommunityIcons
  120. name="dots-vertical"
  121. size={26}
  122. color={this.props.theme.colors.text}
  123. />}
  124. >
  125. <HiddenItem
  126. title={i18n.t("general.goBack")}
  127. onPress={this.onGoBackClicked}/>
  128. <HiddenItem
  129. title={i18n.t("general.goForward")}
  130. onPress={this.onGoForwardClicked}/>
  131. <Divider/>
  132. <HiddenItem
  133. title={i18n.t("general.openInBrowser")}
  134. onPress={this.onOpenClicked}/>
  135. </OverflowMenu>
  136. </MaterialHeaderButtons>
  137. );
  138. }
  139. /**
  140. * Callback to use when refresh button is clicked. Reloads the webview.
  141. */
  142. onRefreshClicked = () => {
  143. if (this.webviewRef.current != null)
  144. this.webviewRef.current.reload();
  145. }
  146. onGoBackClicked = () => {
  147. if (this.webviewRef.current != null)
  148. this.webviewRef.current.goBack();
  149. }
  150. onGoForwardClicked = () => {
  151. if (this.webviewRef.current != null)
  152. this.webviewRef.current.goForward();
  153. }
  154. onOpenClicked = () => Linking.openURL(this.props.url);
  155. /**
  156. * Injects the given javascript string into the web page
  157. *
  158. * @param script The script to inject
  159. */
  160. injectJavaScript = (script: string) => {
  161. if (this.webviewRef.current != null)
  162. this.webviewRef.current.injectJavaScript(script);
  163. }
  164. /**
  165. * Gets the loading indicator
  166. *
  167. * @return {*}
  168. */
  169. getRenderLoading = () => <BasicLoadingScreen isAbsolute={true}/>;
  170. /**
  171. * Gets the javascript needed to generate a padding on top of the page
  172. * This adds padding to the body and runs the custom padding function given in props
  173. *
  174. * @param padding The padding to add in pixels
  175. * @returns {string}
  176. */
  177. getJavascriptPadding(padding: number) {
  178. const customPadding = this.props.customPaddingFunction != null ? this.props.customPaddingFunction(padding) : "";
  179. return (
  180. "document.getElementsByTagName('body')[0].style.paddingTop = '" + padding + "px';" +
  181. customPadding +
  182. "true;"
  183. );
  184. }
  185. onScroll = (event: Object) => {
  186. if (this.props.onScroll)
  187. this.props.onScroll(event);
  188. }
  189. render() {
  190. const {containerPaddingTop, onScrollWithListener} = this.props.collapsibleStack;
  191. return (
  192. <AnimatedWebView
  193. ref={this.webviewRef}
  194. source={{uri: this.props.url}}
  195. startInLoadingState={true}
  196. injectedJavaScript={this.props.customJS}
  197. javaScriptEnabled={true}
  198. renderLoading={this.getRenderLoading}
  199. renderError={() => <ErrorView
  200. errorCode={ERROR_TYPE.CONNECTION_ERROR}
  201. onRefresh={this.onRefreshClicked}
  202. />}
  203. onNavigationStateChange={navState => {
  204. this.canGoBack = navState.canGoBack;
  205. }}
  206. onMessage={this.props.onMessage}
  207. onLoad={() => this.injectJavaScript(this.getJavascriptPadding(containerPaddingTop))}
  208. // Animations
  209. onScroll={onScrollWithListener(this.onScroll)}
  210. />
  211. );
  212. }
  213. }
  214. export default withCollapsible(withTheme(WebViewScreen));