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

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