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

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