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.

ProximoListScreen.js 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. // @flow
  2. import * as React from 'react';
  3. import {Animated, Image, Platform, ScrollView, StatusBar, View} from "react-native";
  4. import i18n from "i18n-js";
  5. import CustomModal from "../../components/Custom/CustomModal";
  6. import {RadioButton, Searchbar, Subheading, Text, Title, withTheme} from "react-native-paper";
  7. import {stringMatchQuery} from "../../utils/Search";
  8. import ProximoListItem from "../../components/Lists/ProximoListItem";
  9. import MaterialHeaderButtons, {Item} from "../../components/Custom/HeaderButton";
  10. import {withCollapsible} from "../../utils/withCollapsible";
  11. function sortPrice(a, b) {
  12. return a.price - b.price;
  13. }
  14. function sortPriceReverse(a, b) {
  15. return b.price - a.price;
  16. }
  17. function sortName(a, b) {
  18. if (a.name.toLowerCase() < b.name.toLowerCase())
  19. return -1;
  20. if (a.name.toLowerCase() > b.name.toLowerCase())
  21. return 1;
  22. return 0;
  23. }
  24. function sortNameReverse(a, b) {
  25. if (a.name.toLowerCase() < b.name.toLowerCase())
  26. return 1;
  27. if (a.name.toLowerCase() > b.name.toLowerCase())
  28. return -1;
  29. return 0;
  30. }
  31. const LIST_ITEM_HEIGHT = 84;
  32. type Props = {
  33. navigation: Object,
  34. route: Object,
  35. theme: Object,
  36. collapsibleStack: Object,
  37. }
  38. type State = {
  39. currentSortMode: number,
  40. modalCurrentDisplayItem: React.Node,
  41. currentSearchString: string,
  42. };
  43. /**
  44. * Class defining proximo's article list of a certain category.
  45. */
  46. class ProximoListScreen extends React.Component<Props, State> {
  47. modalRef: Object;
  48. listData: Array<Object>;
  49. shouldFocusSearchBar: boolean;
  50. constructor(props) {
  51. super(props);
  52. this.listData = this.props.route.params['data']['data'];
  53. this.shouldFocusSearchBar = this.props.route.params['shouldFocusSearchBar'];
  54. this.state = {
  55. currentSearchString: '',
  56. currentSortMode: 3,
  57. modalCurrentDisplayItem: null,
  58. };
  59. }
  60. /**
  61. * Creates the header content
  62. */
  63. componentDidMount() {
  64. this.props.navigation.setOptions({
  65. headerRight: this.getSortMenuButton,
  66. headerTitle: this.getSearchBar,
  67. headerBackTitleVisible: false,
  68. headerTitleContainerStyle: Platform.OS === 'ios' ?
  69. {marginHorizontal: 0, width: '70%'} :
  70. {marginHorizontal: 0, right: 50, left: 50},
  71. });
  72. }
  73. /**
  74. * Gets the header search bar
  75. *
  76. * @return {*}
  77. */
  78. getSearchBar = () => {
  79. return (
  80. <Searchbar
  81. placeholder={i18n.t('proximoScreen.search')}
  82. onChangeText={this.onSearchStringChange}
  83. />
  84. );
  85. };
  86. /**
  87. * Gets the sort menu header button
  88. *
  89. * @return {*}
  90. */
  91. getSortMenuButton = () => {
  92. return <MaterialHeaderButtons>
  93. <Item title="main" iconName="sort" onPress={this.onSortMenuPress}/>
  94. </MaterialHeaderButtons>;
  95. };
  96. /**
  97. * Callback used when clicking on the sort menu button.
  98. * It will open the modal to show a sort selection
  99. */
  100. onSortMenuPress = () => {
  101. this.setState({
  102. modalCurrentDisplayItem: this.getModalSortMenu()
  103. });
  104. if (this.modalRef) {
  105. this.modalRef.open();
  106. }
  107. };
  108. /**
  109. * Sets the current sort mode.
  110. *
  111. * @param mode The number representing the mode
  112. */
  113. setSortMode(mode: number) {
  114. this.setState({
  115. currentSortMode: mode,
  116. });
  117. switch (mode) {
  118. case 1:
  119. this.listData.sort(sortPrice);
  120. break;
  121. case 2:
  122. this.listData.sort(sortPriceReverse);
  123. break;
  124. case 3:
  125. this.listData.sort(sortName);
  126. break;
  127. case 4:
  128. this.listData.sort(sortNameReverse);
  129. break;
  130. }
  131. if (this.modalRef && mode !== this.state.currentSortMode) {
  132. this.modalRef.close();
  133. }
  134. }
  135. /**
  136. * Gets a color depending on the quantity available
  137. *
  138. * @param availableStock The quantity available
  139. * @return
  140. */
  141. getStockColor(availableStock: number) {
  142. let color: string;
  143. if (availableStock > 3)
  144. color = this.props.theme.colors.success;
  145. else if (availableStock > 0)
  146. color = this.props.theme.colors.warning;
  147. else
  148. color = this.props.theme.colors.danger;
  149. return color;
  150. }
  151. /**
  152. * Callback used when the search changes
  153. *
  154. * @param str The new search string
  155. */
  156. onSearchStringChange = (str: string) => {
  157. this.setState({currentSearchString: str})
  158. };
  159. /**
  160. * Gets the modal content depending on the given article
  161. *
  162. * @param item The article to display
  163. * @return {*}
  164. */
  165. getModalItemContent(item: Object) {
  166. return (
  167. <View style={{
  168. flex: 1,
  169. padding: 20
  170. }}>
  171. <Title>{item.name}</Title>
  172. <View style={{
  173. flexDirection: 'row',
  174. width: '100%',
  175. marginTop: 10,
  176. }}>
  177. <Subheading style={{
  178. color: this.getStockColor(parseInt(item.quantity)),
  179. }}>
  180. {item.quantity + ' ' + i18n.t('proximoScreen.inStock')}
  181. </Subheading>
  182. <Subheading style={{marginLeft: 'auto'}}>{item.price}€</Subheading>
  183. </View>
  184. <ScrollView>
  185. <View style={{width: '100%', height: 150, marginTop: 20, marginBottom: 20}}>
  186. <Image style={{flex: 1, resizeMode: "contain"}}
  187. source={{uri: item.image}}/>
  188. </View>
  189. <Text>{item.description}</Text>
  190. </ScrollView>
  191. </View>
  192. );
  193. }
  194. /**
  195. * Gets the modal content to display a sort menu
  196. *
  197. * @return {*}
  198. */
  199. getModalSortMenu() {
  200. return (
  201. <View style={{
  202. flex: 1,
  203. padding: 20
  204. }}>
  205. <Title style={{marginBottom: 10}}>{i18n.t('proximoScreen.sortOrder')}</Title>
  206. <RadioButton.Group
  207. onValueChange={value => this.setSortMode(value)}
  208. value={this.state.currentSortMode}
  209. >
  210. <RadioButton.Item label={i18n.t('proximoScreen.sortPrice')} value={1}/>
  211. <RadioButton.Item label={i18n.t('proximoScreen.sortPriceReverse')} value={2}/>
  212. <RadioButton.Item label={i18n.t('proximoScreen.sortName')} value={3}/>
  213. <RadioButton.Item label={i18n.t('proximoScreen.sortNameReverse')} value={4}/>
  214. </RadioButton.Group>
  215. </View>
  216. );
  217. }
  218. /**
  219. * Callback used when clicking an article in the list.
  220. * It opens the modal to show detailed information about the article
  221. *
  222. * @param item The article pressed
  223. */
  224. onListItemPress(item: Object) {
  225. this.setState({
  226. modalCurrentDisplayItem: this.getModalItemContent(item)
  227. });
  228. if (this.modalRef) {
  229. this.modalRef.open();
  230. }
  231. }
  232. /**
  233. * Gets a render item for the given article
  234. *
  235. * @param item The article to render
  236. * @return {*}
  237. */
  238. renderItem = ({item}: Object) => {
  239. if (stringMatchQuery(item.name, this.state.currentSearchString)) {
  240. const onPress = this.onListItemPress.bind(this, item);
  241. const color = this.getStockColor(parseInt(item.quantity));
  242. return (
  243. <ProximoListItem
  244. item={item}
  245. onPress={onPress}
  246. color={color}
  247. height={LIST_ITEM_HEIGHT}
  248. />
  249. );
  250. } else
  251. return null;
  252. };
  253. /**
  254. * Extracts a key for the given article
  255. *
  256. * @param item The article to extract the key from
  257. * @return {*} The extracted key
  258. */
  259. keyExtractor(item: Object) {
  260. return item.name + item.code;
  261. }
  262. /**
  263. * Callback used when receiving the modal ref
  264. *
  265. * @param ref
  266. */
  267. onModalRef = (ref: Object) => {
  268. this.modalRef = ref;
  269. };
  270. itemLayout = (data, index) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index});
  271. render() {
  272. const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
  273. const padding = Platform.OS === 'android' // Fix for android non translucent bar on expo
  274. ? containerPaddingTop - StatusBar.currentHeight
  275. : containerPaddingTop;
  276. const inset = Platform.OS === 'android'
  277. ? scrollIndicatorInsetTop - StatusBar.currentHeight
  278. : scrollIndicatorInsetTop;
  279. return (
  280. <View style={{
  281. height: '100%'
  282. }}>
  283. <CustomModal onRef={this.onModalRef}>
  284. {this.state.modalCurrentDisplayItem}
  285. </CustomModal>
  286. {/*$FlowFixMe*/}
  287. <Animated.FlatList
  288. data={this.listData}
  289. extraData={this.state.currentSearchString + this.state.currentSortMode}
  290. keyExtractor={this.keyExtractor}
  291. renderItem={this.renderItem}
  292. // Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
  293. removeClippedSubviews={true}
  294. getItemLayout={this.itemLayout}
  295. initialNumToRender={10}
  296. // Animations
  297. onScroll={onScroll}
  298. contentContainerStyle={{paddingTop: padding}}
  299. scrollIndicatorInsets={{top: inset}}
  300. />
  301. </View>
  302. );
  303. }
  304. }
  305. export default withCollapsible(withTheme(ProximoListScreen));