5
5
Ответвление 5
application-amicale/src/screens/Services/Proximo/ProximoListScreen.tsx
2021-05-15 11:41:17 +02:00

373 строки
9,8 КиБ
TypeScript

/*
* Copyright (c) 2019 - 2020 Arnaud Vergnet.
*
* This file is part of Campus INSAT.
*
* Campus INSAT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Campus INSAT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/
import React, { useLayoutEffect, useRef, useState } from 'react';
import { Image, Platform, ScrollView, StyleSheet, View } from 'react-native';
import i18n from 'i18n-js';
import {
RadioButton,
Searchbar,
Subheading,
Text,
Title,
useTheme,
} from 'react-native-paper';
import { Modalize } from 'react-native-modalize';
import CustomModal from '../../../components/Overrides/CustomModal';
import { stringMatchQuery } from '../../../utils/Search';
import ProximoListItem from '../../../components/Lists/Proximo/ProximoListItem';
import MaterialHeaderButtons, {
Item,
} from '../../../components/Overrides/CustomHeaderButton';
import type { ProximoArticleType } from './ProximoMainScreen';
import GENERAL_STYLES from '../../../constants/Styles';
import { useNavigation } from '@react-navigation/core';
import Urls from '../../../constants/Urls';
import WebSectionList, {
SectionListDataType,
} from '../../../components/Screens/WebSectionList';
import { readData } from '../../../utils/WebData';
import { StackScreenProps } from '@react-navigation/stack';
import {
MainRoutes,
MainStackParamsList,
} from '../../../navigation/MainNavigator';
import { useCachedProximoArticles } from '../../../context/cacheContext';
function sortPrice(a: ProximoArticleType, b: ProximoArticleType): number {
return a.price - b.price;
}
function sortPriceReverse(
a: ProximoArticleType,
b: ProximoArticleType
): number {
return b.price - a.price;
}
function sortName(a: ProximoArticleType, b: ProximoArticleType): number {
if (a.name.toLowerCase() < b.name.toLowerCase()) {
return -1;
}
if (a.name.toLowerCase() > b.name.toLowerCase()) {
return 1;
}
return 0;
}
function sortNameReverse(a: ProximoArticleType, b: ProximoArticleType): number {
if (a.name.toLowerCase() < b.name.toLowerCase()) {
return 1;
}
if (a.name.toLowerCase() > b.name.toLowerCase()) {
return -1;
}
return 0;
}
const LIST_ITEM_HEIGHT = 84;
const styles = StyleSheet.create({
modalContainer: {
flex: 1,
padding: 20,
},
modalTitleContainer: {
flexDirection: 'row',
width: '100%',
marginTop: 10,
},
modalTitle: {
marginLeft: 'auto',
},
modalContent: {
width: '100%',
height: 150,
marginTop: 20,
marginBottom: 20,
},
image: {
flex: 1,
resizeMode: 'contain',
},
sortTitle: {
marginBottom: 10,
},
});
export type ArticlesType = Array<ProximoArticleType>;
type Props = StackScreenProps<MainStackParamsList, MainRoutes.ProximoList>;
function ProximoListScreen(props: Props) {
const navigation = useNavigation();
const theme = useTheme();
const { articles, setArticles } = useCachedProximoArticles();
const modalRef = useRef<Modalize>(null);
const [currentSearchString, setCurrentSearchString] = useState('');
const [currentSortMode, setCurrentSortMode] = useState(2);
const [modalCurrentDisplayItem, setModalCurrentDisplayItem] = useState<
React.ReactChild | undefined
>();
const sortModes = [sortPrice, sortPriceReverse, sortName, sortNameReverse];
useLayoutEffect(() => {
navigation.setOptions({
headerRight: getSortMenuButton,
headerTitle: getSearchBar,
headerBackTitleVisible: false,
headerTitleContainerStyle:
Platform.OS === 'ios'
? { marginHorizontal: 0, width: '70%' }
: { marginHorizontal: 0, right: 50, left: 50 },
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [navigation, currentSortMode]);
/**
* Callback used when clicking on the sort menu button.
* It will open the modal to show a sort selection
*/
const onSortMenuPress = () => {
setModalCurrentDisplayItem(getModalSortMenu());
if (modalRef.current) {
modalRef.current.open();
}
};
/**
* Callback used when clicking an article in the list.
* It opens the modal to show detailed information about the article
*
* @param item The article pressed
*/
const onListItemPress = (item: ProximoArticleType) => {
setModalCurrentDisplayItem(getModalItemContent(item));
if (modalRef.current) {
modalRef.current.open();
}
};
/**
* Sets the current sort mode.
*
* @param mode The number representing the mode
*/
const setSortMode = (mode: string) => {
const currentMode = parseInt(mode, 10);
setCurrentSortMode(currentMode);
if (modalRef.current && currentMode !== currentSortMode) {
modalRef.current.close();
}
};
/**
* Gets a color depending on the quantity available
*
* @param availableStock The quantity available
* @return
*/
const getStockColor = (availableStock: number): string => {
let color: string;
if (availableStock > 3) {
color = theme.colors.success;
} else if (availableStock > 0) {
color = theme.colors.warning;
} else {
color = theme.colors.danger;
}
return color;
};
/**
* Gets the sort menu header button
*
* @return {*}
*/
const getSortMenuButton = () => {
return (
<MaterialHeaderButtons>
<Item title="main" iconName="sort" onPress={onSortMenuPress} />
</MaterialHeaderButtons>
);
};
/**
* Gets the header search bar
*
* @return {*}
*/
const getSearchBar = () => {
return (
// @ts-ignore
<Searchbar
placeholder={i18n.t('screens.proximo.search')}
onChangeText={setCurrentSearchString}
autoFocus={props.route.params.shouldFocusSearchBar}
/>
);
};
/**
* Gets the modal content depending on the given article
*
* @param item The article to display
* @return {*}
*/
const getModalItemContent = (item: ProximoArticleType) => {
return (
<View style={styles.modalContainer}>
<Title>{item.name}</Title>
<View style={styles.modalTitleContainer}>
<Subheading
style={{
color: getStockColor(item.quantity),
}}
>
{`${item.quantity} ${i18n.t('screens.proximo.inStock')}`}
</Subheading>
<Subheading style={styles.modalTitle}>
{item.price.toFixed(2)}
</Subheading>
</View>
<ScrollView>
<View style={styles.modalContent}>
<Image
style={styles.image}
source={{ uri: Urls.proximo.images + item.image }}
/>
</View>
<Text>{item.description}</Text>
</ScrollView>
</View>
);
};
/**
* Gets the modal content to display a sort menu
*
* @return {*}
*/
const getModalSortMenu = () => {
return (
<View style={styles.modalContainer}>
<Title style={styles.sortTitle}>
{i18n.t('screens.proximo.sortOrder')}
</Title>
<RadioButton.Group
onValueChange={setSortMode}
value={currentSortMode.toString()}
>
<RadioButton.Item
label={i18n.t('screens.proximo.sortPrice')}
value={'0'}
/>
<RadioButton.Item
label={i18n.t('screens.proximo.sortPriceReverse')}
value={'1'}
/>
<RadioButton.Item
label={i18n.t('screens.proximo.sortName')}
value={'2'}
/>
<RadioButton.Item
label={i18n.t('screens.proximo.sortNameReverse')}
value={'3'}
/>
</RadioButton.Group>
</View>
);
};
/**
* Gets a render item for the given article
*
* @param item The article to render
* @return {*}
*/
const getRenderItem = ({ item }: { item: ProximoArticleType }) => {
if (stringMatchQuery(item.name, currentSearchString)) {
const onPress = () => {
onListItemPress(item);
};
const color = getStockColor(item.quantity);
return (
<ProximoListItem
item={item}
onPress={onPress}
color={color}
height={LIST_ITEM_HEIGHT}
/>
);
}
return null;
};
/**
* Extracts a key for the given article
*
* @param item The article to extract the key from
* @return {string} The extracted key
*/
const keyExtractor = (item: ProximoArticleType): string =>
item.name + item.code;
const createDataset = (
data: ArticlesType | undefined
): SectionListDataType<ProximoArticleType> => {
if (data) {
return [
{
title: '',
data: data
.filter(
(d) =>
props.route.params.category === -1 ||
props.route.params.category === d.category_id
)
.sort(sortModes[currentSortMode]),
keyExtractor: keyExtractor,
},
];
} else {
return [];
}
};
return (
<View style={GENERAL_STYLES.flex}>
<CustomModal ref={modalRef}>{modalCurrentDisplayItem}</CustomModal>
<WebSectionList
request={() => readData<ArticlesType>(Urls.proximo.articles)}
createDataset={createDataset}
refreshOnFocus={true}
renderItem={getRenderItem}
extraData={currentSearchString + currentSortMode}
itemHeight={LIST_ITEM_HEIGHT}
cache={articles}
onCacheUpdate={setArticles}
/>
</View>
);
}
export default ProximoListScreen;