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.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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. refreshData: (newRequest?: () => Promise<RawData>) => void,
  63. status: REQUEST_STATUS,
  64. code?: REQUEST_CODES
  65. ) => SectionListDataType<ItemT>;
  66. renderListHeaderComponent?: (
  67. data: RawData | undefined,
  68. loading: boolean,
  69. refreshData: (newRequest?: () => Promise<RawData>) => void,
  70. status: REQUEST_STATUS,
  71. code?: REQUEST_CODES
  72. ) => React.ComponentType<any> | React.ReactElement | null;
  73. itemHeight?: number | null;
  74. };
  75. const styles = StyleSheet.create({
  76. container: {
  77. minHeight: '100%',
  78. },
  79. });
  80. /**
  81. * Component used to render a SectionList with data fetched from the web
  82. * To force the component to update, change the value of updateData.
  83. */
  84. function WebSectionList<ItemT, RawData>(props: Props<ItemT, RawData>) {
  85. const [snackbarVisible, setSnackbarVisible] = useState(false);
  86. const showSnackBar = () => setSnackbarVisible(true);
  87. const hideSnackBar = () => setSnackbarVisible(false);
  88. const getItemLayout = (
  89. height: number,
  90. _data: Array<SectionListData<ItemT>> | null,
  91. index: number
  92. ): { length: number; offset: number; index: number } => {
  93. return {
  94. length: height,
  95. offset: height * index,
  96. index,
  97. };
  98. };
  99. const render = (
  100. data: RawData | undefined,
  101. loading: boolean,
  102. refreshData: (newRequest?: () => Promise<RawData>) => void,
  103. status: REQUEST_STATUS,
  104. code?: REQUEST_CODES
  105. ) => {
  106. const { itemHeight } = props;
  107. const dataset = props.createDataset(
  108. data,
  109. loading,
  110. refreshData,
  111. status,
  112. code
  113. );
  114. if (!data && !loading) {
  115. showSnackBar();
  116. }
  117. return (
  118. <CollapsibleSectionList
  119. {...props}
  120. sections={dataset}
  121. paddedProps={(paddingTop) => ({
  122. refreshControl: (
  123. <RefreshControl
  124. progressViewOffset={paddingTop}
  125. refreshing={loading}
  126. onRefresh={refreshData}
  127. />
  128. ),
  129. })}
  130. renderItem={props.renderItem}
  131. style={styles.container}
  132. ListHeaderComponent={
  133. props.renderListHeaderComponent != null
  134. ? props.renderListHeaderComponent(
  135. data,
  136. loading,
  137. refreshData,
  138. status,
  139. code
  140. )
  141. : null
  142. }
  143. ListEmptyComponent={
  144. loading ? undefined : (
  145. <ErrorView
  146. status={status}
  147. code={code}
  148. button={{
  149. icon: 'refresh',
  150. text: i18n.t('general.retry'),
  151. onPress: refreshData,
  152. }}
  153. />
  154. )
  155. }
  156. getItemLayout={
  157. itemHeight ? (d, i) => getItemLayout(itemHeight, d, i) : undefined
  158. }
  159. />
  160. );
  161. };
  162. return (
  163. <View style={GENERAL_STYLES.flex}>
  164. <RequestScreen<RawData>
  165. request={props.request}
  166. render={render}
  167. showError={false}
  168. showLoading={false}
  169. autoRefreshTime={props.autoRefreshTime}
  170. refreshOnFocus={props.refreshOnFocus}
  171. cache={props.cache}
  172. onCacheUpdate={props.onCacheUpdate}
  173. />
  174. <Snackbar
  175. visible={snackbarVisible}
  176. onDismiss={hideSnackBar}
  177. action={{
  178. label: 'OK',
  179. onPress: hideSnackBar,
  180. }}
  181. duration={4000}
  182. style={{
  183. bottom: TAB_BAR_HEIGHT,
  184. }}
  185. >
  186. {i18n.t('general.listUpdateFail')}
  187. </Snackbar>
  188. </View>
  189. );
  190. }
  191. export default WebSectionList;