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.

ProximoListScreen.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. // @flow
  2. import * as React from 'react';
  3. import {Body, Container, Content, Left, ListItem, Right, Text, Thumbnail, H1, H3} from 'native-base';
  4. import CustomHeader from "../../components/CustomHeader";
  5. import {FlatList, Platform, View, Image} from "react-native";
  6. import Touchable from 'react-native-platform-touchable';
  7. import Menu, {MenuItem} from 'react-native-material-menu';
  8. import i18n from "i18n-js";
  9. import CustomMaterialIcon from "../../components/CustomMaterialIcon";
  10. import ThemeManager from "../../utils/ThemeManager";
  11. import {Modalize} from 'react-native-modalize';
  12. const sortMode = {
  13. price: "0",
  14. name: '1',
  15. };
  16. function sortPrice(a, b) {
  17. return a.price - b.price;
  18. }
  19. function sortPriceReverse(a, b) {
  20. return b.price - a.price;
  21. }
  22. function sortName(a, b) {
  23. if (a.name < b.name)
  24. return -1;
  25. if (a.name > b.name)
  26. return 1;
  27. return 0;
  28. }
  29. function sortNameReverse(a, b) {
  30. if (a.name < b.name)
  31. return 1;
  32. if (a.name > b.name)
  33. return -1;
  34. return 0;
  35. }
  36. type Props = {
  37. navigation: Object
  38. }
  39. type State = {
  40. currentSortMode: string,
  41. isSortReversed: boolean,
  42. sortPriceIcon: React.Node,
  43. sortNameIcon: React.Node,
  44. modalCurrentDisplayItem: Object,
  45. currentlyDisplayedData: Array<Object>,
  46. };
  47. /**
  48. * Class defining proximo's article list of a certain category.
  49. */
  50. export default class ProximoListScreen extends React.Component<Props, State> {
  51. modalRef: { current: null | Modalize };
  52. originalData: Array<Object>;
  53. navData = this.props.navigation.getParam('data', []);
  54. shouldFocusSearchBar = this.props.navigation.getParam('shouldFocusSearchBar', false);
  55. constructor(props: any) {
  56. super(props);
  57. this.modalRef = React.createRef();
  58. this.originalData = this.navData['data'];
  59. }
  60. state = {
  61. currentlyDisplayedData: this.navData['data'].sort(sortPrice),
  62. currentSortMode: sortMode.price,
  63. isSortReversed: false,
  64. sortPriceIcon: '',
  65. sortNameIcon: '',
  66. modalCurrentDisplayItem: {},
  67. };
  68. _menu: Menu;
  69. /**
  70. * Saves the reference to the sort menu for later use
  71. *
  72. * @param ref The menu reference
  73. */
  74. setMenuRef = (ref: Menu) => {
  75. this._menu = ref;
  76. };
  77. /**
  78. * Sets the sort mode based on the one selected.
  79. * If the selected mode is the current one, reverse it.
  80. *
  81. * @param mode The string representing the mode
  82. */
  83. sortModeSelected(mode: string) {
  84. let isReverse = this.state.isSortReversed;
  85. if (mode === this.state.currentSortMode) // reverse mode
  86. isReverse = !isReverse; // this.state not updating on this function cycle
  87. else
  88. isReverse = false;
  89. this.setSortMode(mode, isReverse);
  90. }
  91. /**
  92. * Set the current sort mode.
  93. *
  94. * @param mode The string representing the mode
  95. * @param isReverse Whether to use a reverse sort
  96. */
  97. setSortMode(mode: string, isReverse: boolean) {
  98. this.setState({
  99. currentSortMode: mode,
  100. isSortReversed: isReverse
  101. });
  102. let data = this.state.currentlyDisplayedData;
  103. switch (mode) {
  104. case sortMode.price:
  105. if (isReverse) {
  106. data.sort(sortPriceReverse);
  107. } else {
  108. data.sort(sortPrice);
  109. }
  110. break;
  111. case sortMode.name:
  112. if (isReverse) {
  113. data.sort(sortNameReverse);
  114. } else {
  115. data.sort(sortName);
  116. }
  117. break;
  118. }
  119. this.setState({
  120. navData: data,
  121. });
  122. this.setupSortIcons(mode, isReverse);
  123. this._menu.hide();
  124. }
  125. /**
  126. * Set the sort mode from state when components are ready
  127. */
  128. componentDidMount() {
  129. this.setSortMode(this.state.currentSortMode, this.state.isSortReversed);
  130. }
  131. /**
  132. * get color depending on quantity available
  133. *
  134. * @param availableStock
  135. * @return
  136. */
  137. getStockColor(availableStock: number) {
  138. let color: string;
  139. if (availableStock > 3)
  140. color = ThemeManager.getCurrentThemeVariables().brandSuccess;
  141. else if (availableStock > 0)
  142. color = ThemeManager.getCurrentThemeVariables().brandWarning;
  143. else
  144. color = ThemeManager.getCurrentThemeVariables().brandDanger;
  145. return color;
  146. }
  147. /**
  148. * Set the sort menu icon based on the given mode.
  149. *
  150. * @param mode The string representing the mode
  151. * @param isReverse Whether to use a reversed icon
  152. */
  153. setupSortIcons(mode: string, isReverse: boolean) {
  154. const downSortIcon =
  155. <CustomMaterialIcon
  156. icon={'sort-descending'}/>;
  157. const upSortIcon =
  158. <CustomMaterialIcon
  159. icon={'sort-ascending'}/>;
  160. switch (mode) {
  161. case sortMode.price:
  162. this.setState({sortNameIcon: ''});
  163. if (isReverse) {
  164. this.setState({sortPriceIcon: upSortIcon});
  165. } else {
  166. this.setState({sortPriceIcon: downSortIcon});
  167. }
  168. break;
  169. case sortMode.name:
  170. this.setState({sortPriceIcon: ''});
  171. if (isReverse) {
  172. this.setState({sortNameIcon: upSortIcon});
  173. } else {
  174. this.setState({sortNameIcon: downSortIcon});
  175. }
  176. break;
  177. }
  178. }
  179. sanitizeString(str: string) {
  180. return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
  181. }
  182. /**
  183. * Returns only the articles whose name contains str. Case and accents insensitive.
  184. * @param str
  185. * @returns {[]}
  186. */
  187. filterData(str: string) {
  188. let filteredData = [];
  189. const testStr = this.sanitizeString(str);
  190. const articles = this.originalData;
  191. for (const article of articles) {
  192. const name = this.sanitizeString(article.name);
  193. if (name.includes(testStr)) {
  194. filteredData.push(article)
  195. }
  196. }
  197. return filteredData;
  198. }
  199. search(str: string) {
  200. this.setState({
  201. currentlyDisplayedData: this.filterData(str)
  202. })
  203. }
  204. getModalContent() {
  205. return (
  206. <View style={{
  207. flex: 1,
  208. padding: 20
  209. }}>
  210. <H1>{this.state.modalCurrentDisplayItem.name}</H1>
  211. <View style={{
  212. flexDirection: 'row',
  213. width: '100%',
  214. marginTop: 10,
  215. }}>
  216. <H3 style={{
  217. color: this.getStockColor(parseInt(this.state.modalCurrentDisplayItem.quantity)),
  218. }}>
  219. {this.state.modalCurrentDisplayItem.quantity + ' ' + i18n.t('proximoScreen.inStock')}
  220. </H3>
  221. <H3 style={{marginLeft: 'auto'}}>{this.state.modalCurrentDisplayItem.price}€</H3>
  222. </View>
  223. <Content>
  224. <View style={{width: '100%', height: 150, marginTop: 20, marginBottom: 20}}>
  225. <Image style={{flex: 1, resizeMode: "contain"}}
  226. source={{uri: this.state.modalCurrentDisplayItem.image}}/>
  227. </View>
  228. <Text>{this.state.modalCurrentDisplayItem.description}</Text>
  229. </Content>
  230. </View>
  231. );
  232. }
  233. showItemDetails(item: Object) {
  234. this.setState({
  235. modalCurrentDisplayItem: item
  236. });
  237. if (this.modalRef.current) {
  238. this.modalRef.current.open();
  239. }
  240. }
  241. getSortMenu() {
  242. return (
  243. <Menu
  244. ref={this.setMenuRef}
  245. button={
  246. <Touchable
  247. style={{padding: 6}}
  248. onPress={() =>
  249. this._menu.show()
  250. }>
  251. <CustomMaterialIcon
  252. color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"}
  253. icon={'sort'}/>
  254. </Touchable>
  255. }
  256. >
  257. <MenuItem
  258. onPress={() => this.sortModeSelected(sortMode.name)}>
  259. {this.state.sortNameIcon}
  260. {i18n.t('proximoScreen.sortName')}
  261. </MenuItem>
  262. <MenuItem
  263. onPress={() => this.sortModeSelected(sortMode.price)}>
  264. {this.state.sortPriceIcon}
  265. {i18n.t('proximoScreen.sortPrice')}
  266. </MenuItem>
  267. </Menu>
  268. );
  269. }
  270. render() {
  271. const nav = this.props.navigation;
  272. const navType = nav.getParam('type', '{name: "Error"}');
  273. return (
  274. <Container>
  275. <Modalize ref={this.modalRef}
  276. adjustToContentHeight
  277. modalStyle={{backgroundColor: ThemeManager.getCurrentThemeVariables().containerBgColor}}>
  278. {this.getModalContent()}
  279. </Modalize>
  280. <CustomHeader
  281. hasBackButton={true}
  282. navigation={nav}
  283. hasSearchField={true}
  284. searchCallback={(text) => this.search(text)}
  285. shouldFocusSearchBar={this.shouldFocusSearchBar}
  286. rightButton={this.getSortMenu()}
  287. />
  288. <FlatList
  289. data={this.state.currentlyDisplayedData}
  290. extraData={this.state.currentlyDisplayedData}
  291. keyExtractor={(item) => item.name + item.code}
  292. style={{minHeight: 300, width: '100%'}}
  293. renderItem={({item}) =>
  294. <ListItem
  295. thumbnail
  296. onPress={() => {
  297. this.showItemDetails(item);
  298. }}
  299. >
  300. <Left>
  301. <Thumbnail square source={{uri: item.image}}/>
  302. </Left>
  303. <Body>
  304. <Text style={{marginLeft: 20}}>
  305. {item.name}
  306. </Text>
  307. <Text note style={{
  308. marginLeft: 20,
  309. color: this.getStockColor(parseInt(item.quantity))
  310. }}>
  311. {item.quantity + ' ' + i18n.t('proximoScreen.inStock')}
  312. </Text>
  313. </Body>
  314. <Right>
  315. <Text style={{fontWeight: "bold"}}>
  316. {item.price}€
  317. </Text>
  318. </Right>
  319. </ListItem>}
  320. />
  321. </Container>
  322. );
  323. }
  324. }