forked from vergnet/application-amicale
Improved proximo search and display performance
This commit is contained in:
parent
7829b893c5
commit
ba893495e1
2 changed files with 83 additions and 88 deletions
40
components/Lists/ProximoListItem.js
Normal file
40
components/Lists/ProximoListItem.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import {Avatar, List, Text, withTheme} from 'react-native-paper';
|
||||||
|
import i18n from "i18n-js";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onPress: Function,
|
||||||
|
color: string,
|
||||||
|
item: Object,
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProximoListItem extends React.PureComponent<Props> {
|
||||||
|
|
||||||
|
colors: Object;
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.colors = props.theme.colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<List.Item
|
||||||
|
title={this.props.item.name}
|
||||||
|
description={this.props.item.quantity + ' ' + i18n.t('proximoScreen.inStock')}
|
||||||
|
descriptionStyle={{color: this.props.color}}
|
||||||
|
onPress={this.props.onPress}
|
||||||
|
left={() => <Avatar.Image style={{backgroundColor: 'transparent'}} size={64}
|
||||||
|
source={{uri: this.props.item.image}}/>}
|
||||||
|
right={() =>
|
||||||
|
<Text style={{fontWeight: "bold"}}>
|
||||||
|
{this.props.item.price}€
|
||||||
|
</Text>}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withTheme(ProximoListItem);
|
|
@ -1,11 +1,12 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Image, Platform, ScrollView, View} from "react-native";
|
import {FlatList, Image, Platform, ScrollView, View} from "react-native";
|
||||||
import i18n from "i18n-js";
|
import i18n from "i18n-js";
|
||||||
import CustomModal from "../../components/Custom/CustomModal";
|
import CustomModal from "../../components/Custom/CustomModal";
|
||||||
import {Avatar, IconButton, List, RadioButton, Searchbar, Subheading, Text, Title, withTheme} from "react-native-paper";
|
import {IconButton, RadioButton, Searchbar, Subheading, Text, Title, withTheme} from "react-native-paper";
|
||||||
import PureFlatList from "../../components/Lists/PureFlatList";
|
import {stringMatchQuery} from "../../utils/Search";
|
||||||
|
import ProximoListItem from "../../components/Lists/ProximoListItem";
|
||||||
|
|
||||||
function sortPrice(a, b) {
|
function sortPrice(a, b) {
|
||||||
return a.price - b.price;
|
return a.price - b.price;
|
||||||
|
@ -39,7 +40,7 @@ type Props = {
|
||||||
type State = {
|
type State = {
|
||||||
currentSortMode: number,
|
currentSortMode: number,
|
||||||
modalCurrentDisplayItem: React.Node,
|
modalCurrentDisplayItem: React.Node,
|
||||||
currentlyDisplayedData: Array<Object>,
|
currentSearchString: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,30 +49,21 @@ type State = {
|
||||||
class ProximoListScreen extends React.Component<Props, State> {
|
class ProximoListScreen extends React.Component<Props, State> {
|
||||||
|
|
||||||
modalRef: Object;
|
modalRef: Object;
|
||||||
originalData: Array<Object>;
|
listData: Array<Object>;
|
||||||
shouldFocusSearchBar: boolean;
|
shouldFocusSearchBar: boolean;
|
||||||
|
|
||||||
onSearchStringChange: Function;
|
|
||||||
onSortMenuPress: Function;
|
|
||||||
renderItem: Function;
|
|
||||||
onModalRef: Function;
|
|
||||||
|
|
||||||
colors: Object;
|
colors: Object;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.originalData = this.props.route.params['data']['data'];
|
this.listData = this.props.route.params['data']['data'];
|
||||||
this.shouldFocusSearchBar = this.props.route.params['shouldFocusSearchBar'];
|
this.shouldFocusSearchBar = this.props.route.params['shouldFocusSearchBar'];
|
||||||
this.state = {
|
this.state = {
|
||||||
currentlyDisplayedData: this.originalData.sort(sortName),
|
currentSearchString: '',
|
||||||
currentSortMode: 3,
|
currentSortMode: 3,
|
||||||
modalCurrentDisplayItem: null,
|
modalCurrentDisplayItem: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.onSearchStringChange = this.onSearchStringChange.bind(this);
|
|
||||||
this.onSortMenuPress = this.onSortMenuPress.bind(this);
|
|
||||||
this.renderItem = this.renderItem.bind(this);
|
|
||||||
this.onModalRef = this.onModalRef.bind(this);
|
|
||||||
this.colors = props.theme.colors;
|
this.colors = props.theme.colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,11 +72,9 @@ class ProximoListScreen extends React.Component<Props, State> {
|
||||||
* Creates the header content
|
* Creates the header content
|
||||||
*/
|
*/
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const button = this.getSortMenuButton.bind(this);
|
|
||||||
const title = this.getSearchBar.bind(this);
|
|
||||||
this.props.navigation.setOptions({
|
this.props.navigation.setOptions({
|
||||||
headerRight: button,
|
headerRight: this.getSortMenuButton,
|
||||||
headerTitle: title,
|
headerTitle: this.getSearchBar,
|
||||||
headerBackTitleVisible: false,
|
headerBackTitleVisible: false,
|
||||||
headerTitleContainerStyle: Platform.OS === 'ios' ?
|
headerTitleContainerStyle: Platform.OS === 'ios' ?
|
||||||
{marginHorizontal: 0, width: '70%'} :
|
{marginHorizontal: 0, width: '70%'} :
|
||||||
|
@ -97,21 +87,21 @@ class ProximoListScreen extends React.Component<Props, State> {
|
||||||
*
|
*
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getSearchBar() {
|
getSearchBar = () => {
|
||||||
return (
|
return (
|
||||||
<Searchbar
|
<Searchbar
|
||||||
placeholder={i18n.t('proximoScreen.search')}
|
placeholder={i18n.t('proximoScreen.search')}
|
||||||
onChangeText={this.onSearchStringChange}
|
onChangeText={this.onSearchStringChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the sort menu header button
|
* Gets the sort menu header button
|
||||||
*
|
*
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getSortMenuButton() {
|
getSortMenuButton = () => {
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<IconButton
|
||||||
icon="sort"
|
icon="sort"
|
||||||
|
@ -120,20 +110,20 @@ class ProximoListScreen extends React.Component<Props, State> {
|
||||||
onPress={this.onSortMenuPress}
|
onPress={this.onSortMenuPress}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback used when clicking on the sort menu button.
|
* Callback used when clicking on the sort menu button.
|
||||||
* It will open the modal to show a sort selection
|
* It will open the modal to show a sort selection
|
||||||
*/
|
*/
|
||||||
onSortMenuPress() {
|
onSortMenuPress = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
modalCurrentDisplayItem: this.getModalSortMenu()
|
modalCurrentDisplayItem: this.getModalSortMenu()
|
||||||
});
|
});
|
||||||
if (this.modalRef) {
|
if (this.modalRef) {
|
||||||
this.modalRef.open();
|
this.modalRef.open();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the current sort mode.
|
* Sets the current sort mode.
|
||||||
|
@ -144,19 +134,18 @@ class ProximoListScreen extends React.Component<Props, State> {
|
||||||
this.setState({
|
this.setState({
|
||||||
currentSortMode: mode,
|
currentSortMode: mode,
|
||||||
});
|
});
|
||||||
let data = this.state.currentlyDisplayedData;
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 1:
|
case 1:
|
||||||
data.sort(sortPrice);
|
this.listData.sort(sortPrice);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
data.sort(sortPriceReverse);
|
this.listData.sort(sortPriceReverse);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
data.sort(sortName);
|
this.listData.sort(sortName);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
data.sort(sortNameReverse);
|
this.listData.sort(sortNameReverse);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (this.modalRef && mode !== this.state.currentSortMode) {
|
if (this.modalRef && mode !== this.state.currentSortMode) {
|
||||||
|
@ -181,46 +170,14 @@ class ProximoListScreen extends React.Component<Props, State> {
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sanitizes the given string to improve search performance
|
|
||||||
*
|
|
||||||
* @param str The string to sanitize
|
|
||||||
* @return {string} The sanitized string
|
|
||||||
*/
|
|
||||||
sanitizeString(str: string): string {
|
|
||||||
return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns only articles whose name contains the given string.
|
|
||||||
* Case and accents insensitive.
|
|
||||||
*
|
|
||||||
* @param str The string used to filter article names
|
|
||||||
* @returns {[]}
|
|
||||||
*/
|
|
||||||
filterData(str: string) {
|
|
||||||
let filteredData = [];
|
|
||||||
const testStr = this.sanitizeString(str);
|
|
||||||
const articles = this.originalData;
|
|
||||||
for (const article of articles) {
|
|
||||||
const name = this.sanitizeString(article.name);
|
|
||||||
if (name.includes(testStr)) {
|
|
||||||
filteredData.push(article)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return filteredData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback used when the search changes
|
* Callback used when the search changes
|
||||||
*
|
*
|
||||||
* @param str The new search string
|
* @param str The new search string
|
||||||
*/
|
*/
|
||||||
onSearchStringChange(str: string) {
|
onSearchStringChange = (str: string) => {
|
||||||
this.setState({
|
this.setState({currentSearchString: str})
|
||||||
currentlyDisplayedData: this.filterData(str)
|
};
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the modal content depending on the given article
|
* Gets the modal content depending on the given article
|
||||||
|
@ -333,23 +290,20 @@ class ProximoListScreen extends React.Component<Props, State> {
|
||||||
* @param item The article to render
|
* @param item The article to render
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
renderItem({item}: Object) {
|
renderItem = ({item}: Object) => {
|
||||||
const onPress = this.onListItemPress.bind(this, item);
|
if (stringMatchQuery(item.name, this.state.currentSearchString)) {
|
||||||
return (
|
const onPress = this.onListItemPress.bind(this, item);
|
||||||
<List.Item
|
const color = this.getStockColor(parseInt(item.quantity));
|
||||||
title={item.name}
|
return (
|
||||||
description={item.quantity + ' ' + i18n.t('proximoScreen.inStock')}
|
<ProximoListItem
|
||||||
descriptionStyle={{color: this.getStockColor(parseInt(item.quantity))}}
|
item={item}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
left={() => <Avatar.Image style={{backgroundColor: 'transparent'}} size={64}
|
color={color}
|
||||||
source={{uri: item.image}}/>}
|
/>
|
||||||
right={() =>
|
);
|
||||||
<Text style={{fontWeight: "bold"}}>
|
} else
|
||||||
{item.price}€
|
return null;
|
||||||
</Text>}
|
};
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts a key for the given article
|
* Extracts a key for the given article
|
||||||
|
@ -366,9 +320,9 @@ class ProximoListScreen extends React.Component<Props, State> {
|
||||||
*
|
*
|
||||||
* @param ref
|
* @param ref
|
||||||
*/
|
*/
|
||||||
onModalRef(ref: Object) {
|
onModalRef = (ref: Object) => {
|
||||||
this.modalRef = ref;
|
this.modalRef = ref;
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
@ -378,11 +332,12 @@ class ProximoListScreen extends React.Component<Props, State> {
|
||||||
<CustomModal onRef={this.onModalRef}>
|
<CustomModal onRef={this.onModalRef}>
|
||||||
{this.state.modalCurrentDisplayItem}
|
{this.state.modalCurrentDisplayItem}
|
||||||
</CustomModal>
|
</CustomModal>
|
||||||
<PureFlatList
|
{/*$FlowFixMe*/}
|
||||||
data={this.state.currentlyDisplayedData}
|
<FlatList
|
||||||
|
data={this.listData}
|
||||||
|
extraData={this.state.currentSearchString + this.state.currentSortMode}
|
||||||
keyExtractor={this.keyExtractor}
|
keyExtractor={this.keyExtractor}
|
||||||
renderItem={this.renderItem}
|
renderItem={this.renderItem}
|
||||||
updateData={this.state.currentSortMode}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue