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.

WebSectionList.tsx 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /*
  2. * Copyright (c) 2019 - 2020 Arnaud Vergnet.
  3. *
  4. * This file is part of Campus INSAT.
  5. *
  6. * Campus INSAT is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Campus INSAT is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. import React, { useState } from 'react';
  20. import i18n from 'i18n-js';
  21. import { Snackbar } from 'react-native-paper';
  22. import {
  23. RefreshControl,
  24. SectionListData,
  25. SectionListProps,
  26. StyleSheet,
  27. View,
  28. } from 'react-native';
  29. import ErrorView from './ErrorView';
  30. import { TAB_BAR_HEIGHT } from '../Tabbar/CustomTabBar';
  31. import CollapsibleSectionList from '../Collapsible/CollapsibleSectionList';
  32. import GENERAL_STYLES from '../../constants/Styles';
  33. import RequestScreen, { RequestScreenProps } from './RequestScreen';
  34. import { CollapsibleComponentPropsType } from '../Collapsible/CollapsibleComponent';
  35. import { REQUEST_CODES, REQUEST_STATUS } from '../../utils/Requests';
  36. export type SectionListDataType<ItemT> = Array<{
  37. title: string;
  38. icon?: string;
  39. data: Array<ItemT>;
  40. keyExtractor?: (data: ItemT) => string;
  41. }>;
  42. type Props<ItemT, RawData> = Omit<
  43. CollapsibleComponentPropsType,
  44. 'children' | 'paddedProps'
  45. > &
  46. Omit<
  47. RequestScreenProps<RawData>,
  48. | 'render'
  49. | 'showLoading'
  50. | 'showError'
  51. | 'refresh'
  52. | 'onFinish'
  53. | 'onMajorError'
  54. > &
  55. Omit<
  56. SectionListProps<ItemT>,
  57. 'sections' | 'getItemLayout' | 'ListHeaderComponent' | 'ListEmptyComponent'
  58. > & {
  59. createDataset: (
  60. data: RawData | undefined,
  61. loading: boolean,
  62. lastRefreshDate: Date | undefined,
  63. refreshData: (newRequest?: () => Promise<RawData>) => void,
  64. status: REQUEST_STATUS,
  65. code?: REQUEST_CODES
  66. ) => SectionListDataType<ItemT>;
  67. renderListHeaderComponent?: (
  68. data: RawData | undefined,
  69. loading: boolean,
  70. lastRefreshDate: Date | undefined,
  71. refreshData: (newRequest?: () => Promise<RawData>) => void,
  72. status: REQUEST_STATUS,
  73. code?: REQUEST_CODES
  74. ) => React.ComponentType<any> | React.ReactElement | null;
  75. itemHeight?: number | null;
  76. };
  77. const styles = StyleSheet.create({
  78. container: {
  79. minHeight: '100%',
  80. },
  81. });
  82. /**
  83. * Component used to render a SectionList with data fetched from the web
  84. * To force the component to update, change the value of updateData.
  85. */
  86. function WebSectionList<ItemT, RawData>(props: Props<ItemT, RawData>) {
  87. const [snackbarVisible, setSnackbarVisible] = useState(false);
  88. const showSnackBar = () => setSnackbarVisible(true);
  89. const hideSnackBar = () => setSnackbarVisible(false);
  90. const getItemLayout = (
  91. height: number,
  92. _data: Array<SectionListData<ItemT>> | null,
  93. index: number
  94. ): { length: number; offset: number; index: number } => {
  95. return {
  96. length: height,
  97. offset: height * index,
  98. index,
  99. };
  100. };
  101. const render = (
  102. data: RawData | undefined,
  103. loading: boolean,
  104. lastRefreshDate: Date | undefined,
  105. refreshData: (newRequest?: () => Promise<RawData>) => void,
  106. status: REQUEST_STATUS,
  107. code?: REQUEST_CODES
  108. ) => {
  109. const { itemHeight } = props;
  110. const dataset = props.createDataset(
  111. data,
  112. loading,
  113. lastRefreshDate,
  114. refreshData,
  115. status,
  116. code
  117. );
  118. if (!data && !loading) {
  119. showSnackBar();
  120. }
  121. return (
  122. <CollapsibleSectionList
  123. {...props}
  124. sections={dataset}
  125. paddedProps={(paddingTop) => ({
  126. refreshControl: (
  127. <RefreshControl
  128. progressViewOffset={paddingTop}
  129. refreshing={loading}
  130. onRefresh={refreshData}
  131. />
  132. ),
  133. })}
  134. renderItem={props.renderItem}
  135. style={styles.container}
  136. ListHeaderComponent={
  137. props.renderListHeaderComponent != null
  138. ? props.renderListHeaderComponent(
  139. data,
  140. loading,
  141. lastRefreshDate,
  142. refreshData,
  143. status,
  144. code
  145. )
  146. : null
  147. }
  148. ListEmptyComponent={
  149. loading ? undefined : (
  150. <ErrorView
  151. status={status}
  152. code={code}
  153. button={{
  154. icon: 'refresh',
  155. text: i18n.t('general.retry'),
  156. onPress: refreshData,
  157. }}
  158. />
  159. )
  160. }
  161. getItemLayout={
  162. itemHeight ? (d, i) => getItemLayout(itemHeight, d, i) : undefined
  163. }
  164. />
  165. );
  166. };
  167. return (
  168. <View style={GENERAL_STYLES.flex}>
  169. <RequestScreen<RawData>
  170. request={props.request}
  171. render={render}
  172. showError={false}
  173. showLoading={false}
  174. autoRefreshTime={props.autoRefreshTime}
  175. refreshOnFocus={props.refreshOnFocus}
  176. cache={props.cache}
  177. onCacheUpdate={props.onCacheUpdate}
  178. />
  179. <Snackbar
  180. visible={snackbarVisible}
  181. onDismiss={hideSnackBar}
  182. action={{
  183. label: 'OK',
  184. onPress: hideSnackBar,
  185. }}
  186. duration={4000}
  187. style={{
  188. bottom: TAB_BAR_HEIGHT,
  189. }}
  190. >
  191. {i18n.t('general.listUpdateFail')}
  192. </Snackbar>
  193. </View>
  194. );
  195. }
  196. export default WebSectionList;