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 10KB

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