Application Android et IOS pour l'amicale des élèves
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.

WebSectionList.js 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. // @flow
  2. import * as React from 'react';
  3. import {ERROR_TYPE, readData} from "../../utils/WebData";
  4. import i18n from "i18n-js";
  5. import {Snackbar} from 'react-native-paper';
  6. import {RefreshControl, SectionList, View} from "react-native";
  7. import ErrorView from "../Custom/ErrorView";
  8. import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
  9. type Props = {
  10. navigation: Object,
  11. fetchUrl: string,
  12. autoRefreshTime: number,
  13. refreshOnFocus: boolean,
  14. renderItem: React.Node,
  15. renderSectionHeader: React.Node,
  16. stickyHeader: boolean,
  17. createDataset: Function,
  18. updateData: number,
  19. itemHeight: number | null,
  20. }
  21. type State = {
  22. refreshing: boolean,
  23. firstLoading: boolean,
  24. fetchedData: ?Object,
  25. snackbarVisible: boolean
  26. };
  27. const MIN_REFRESH_TIME = 5 * 1000;
  28. /**
  29. * Component used to render a SectionList with data fetched from the web
  30. *
  31. * This is a pure component, meaning it will only update if a shallow comparison of state and props is different.
  32. * To force the component to update, change the value of updateData.
  33. */
  34. export default class WebSectionList extends React.PureComponent<Props, State> {
  35. static defaultProps = {
  36. renderSectionHeader: null,
  37. stickyHeader: false,
  38. updateData: 0,
  39. itemHeight: null,
  40. };
  41. refreshInterval: IntervalID;
  42. lastRefresh: Date;
  43. state = {
  44. refreshing: false,
  45. firstLoading: true,
  46. fetchedData: undefined,
  47. snackbarVisible: false
  48. };
  49. onRefresh: Function;
  50. onFetchSuccess: Function;
  51. onFetchError: Function;
  52. getEmptySectionHeader: Function;
  53. constructor() {
  54. super();
  55. // creating references to functions used in render()
  56. this.onRefresh = this.onRefresh.bind(this);
  57. this.onFetchSuccess = this.onFetchSuccess.bind(this);
  58. this.onFetchError = this.onFetchError.bind(this);
  59. this.getEmptySectionHeader = this.getEmptySectionHeader.bind(this);
  60. }
  61. /**
  62. * Registers react navigation events on first screen load.
  63. * Allows to detect when the screen is focused
  64. */
  65. componentDidMount() {
  66. const onScreenFocus = this.onScreenFocus.bind(this);
  67. const onScreenBlur = this.onScreenBlur.bind(this);
  68. this.props.navigation.addListener('focus', onScreenFocus);
  69. this.props.navigation.addListener('blur', onScreenBlur);
  70. this.onRefresh();
  71. }
  72. /**
  73. * Refreshes data when focusing the screen and setup a refresh interval if asked to
  74. */
  75. onScreenFocus() {
  76. if (this.props.refreshOnFocus && this.lastRefresh !== undefined)
  77. this.onRefresh();
  78. if (this.props.autoRefreshTime > 0)
  79. this.refreshInterval = setInterval(this.onRefresh, this.props.autoRefreshTime)
  80. }
  81. /**
  82. * Removes any interval on un-focus
  83. */
  84. onScreenBlur() {
  85. clearInterval(this.refreshInterval);
  86. }
  87. /**
  88. * Callback used when fetch is successful.
  89. * It will update the displayed data and stop the refresh animation
  90. *
  91. * @param fetchedData The newly fetched data
  92. */
  93. onFetchSuccess(fetchedData: Object) {
  94. this.setState({
  95. fetchedData: fetchedData,
  96. refreshing: false,
  97. firstLoading: false
  98. });
  99. this.lastRefresh = new Date();
  100. }
  101. /**
  102. * Callback used when fetch encountered an error.
  103. * It will reset the displayed data and show an error.
  104. */
  105. onFetchError() {
  106. this.setState({
  107. fetchedData: undefined,
  108. refreshing: false,
  109. firstLoading: false
  110. });
  111. this.showSnackBar();
  112. }
  113. /**
  114. * Refreshes data and shows an animations while doing it
  115. */
  116. onRefresh() {
  117. let canRefresh;
  118. if (this.lastRefresh !== undefined)
  119. canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) > MIN_REFRESH_TIME;
  120. else
  121. canRefresh = true;
  122. if (canRefresh) {
  123. this.setState({refreshing: true});
  124. readData(this.props.fetchUrl)
  125. .then(this.onFetchSuccess)
  126. .catch(this.onFetchError);
  127. }
  128. }
  129. /**
  130. * Gets an empty section header
  131. *
  132. * @param section The current section
  133. * @return {*}
  134. */
  135. getEmptySectionHeader({section}: Object) {
  136. return <View/>;
  137. }
  138. /**
  139. * Shows the error popup
  140. */
  141. showSnackBar = () => this.setState({snackbarVisible: true});
  142. /**
  143. * Hides the error popup
  144. */
  145. hideSnackBar = () => this.setState({snackbarVisible: false});
  146. itemLayout = (data, index) => ({length: this.props.itemHeight, offset: this.props.itemHeight * index, index});
  147. render() {
  148. let dataset = [];
  149. if (this.state.fetchedData !== undefined)
  150. dataset = this.props.createDataset(this.state.fetchedData);
  151. const shouldRenderHeader = this.props.renderSectionHeader !== null;
  152. return (
  153. <View>
  154. {/*$FlowFixMe*/}
  155. <SectionList
  156. sections={dataset}
  157. extraData={this.props.updateData}
  158. refreshControl={
  159. <RefreshControl
  160. refreshing={this.state.refreshing}
  161. onRefresh={this.onRefresh}
  162. />
  163. }
  164. //$FlowFixMe
  165. renderSectionHeader={shouldRenderHeader ? this.props.renderSectionHeader : this.getEmptySectionHeader}
  166. //$FlowFixMe
  167. renderItem={this.props.renderItem}
  168. stickySectionHeadersEnabled={this.props.stickyHeader}
  169. contentContainerStyle={{minHeight: '100%'}}
  170. style={{minHeight: '100%'}}
  171. ListEmptyComponent={this.state.refreshing
  172. ? <BasicLoadingScreen/>
  173. : <ErrorView
  174. {...this.props}
  175. errorCode={ERROR_TYPE.CONNECTION_ERROR}
  176. onRefresh={this.onRefresh}/>
  177. }
  178. getItemLayout={this.props.itemHeight !== null ? this.itemLayout : undefined}
  179. />
  180. <Snackbar
  181. visible={this.state.snackbarVisible}
  182. onDismiss={this.hideSnackBar}
  183. action={{
  184. label: 'OK',
  185. onPress: () => {},
  186. }}
  187. duration={4000}
  188. >
  189. {i18n.t("homeScreen.listUpdateFail")}
  190. </Snackbar>
  191. </View>
  192. );
  193. }
  194. }