Update list components to use TypeScript
This commit is contained in:
parent
acc4f8cdcc
commit
e4adcd0057
21 changed files with 648 additions and 705 deletions
|
@ -17,19 +17,16 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Animated, Dimensions} from 'react-native';
|
||||
import type {ViewStyle} from 'react-native/Libraries/StyleSheet/StyleSheet';
|
||||
import {Animated, Dimensions, ViewStyle} from 'react-native';
|
||||
import ImageListItem from './ImageListItem';
|
||||
import CardListItem from './CardListItem';
|
||||
import type {ServiceItemType} from '../../../managers/ServicesManager';
|
||||
|
||||
type PropsType = {
|
||||
dataset: Array<ServiceItemType>,
|
||||
isHorizontal?: boolean,
|
||||
contentContainerStyle?: ViewStyle | null,
|
||||
dataset: Array<ServiceItemType>;
|
||||
isHorizontal?: boolean;
|
||||
contentContainerStyle?: ViewStyle;
|
||||
};
|
||||
|
||||
export default class CardList extends React.Component<PropsType> {
|
||||
|
@ -45,12 +42,12 @@ export default class CardList extends React.Component<PropsType> {
|
|||
constructor(props: PropsType) {
|
||||
super(props);
|
||||
this.windowWidth = Dimensions.get('window').width;
|
||||
this.horizontalItemSize = this.windowWidth / 4; // So that we can fit 3 items and a part of the 4th => user knows he can scroll
|
||||
this.horizontalItemSize = this.windowWidth / 4; // So that we can fit 3 items, and a part of the 4th => user knows he can scroll
|
||||
}
|
||||
|
||||
getRenderItem = ({item}: {item: ServiceItemType}): React.Node => {
|
||||
getRenderItem = ({item}: {item: ServiceItemType}) => {
|
||||
const {props} = this;
|
||||
if (props.isHorizontal)
|
||||
if (props.isHorizontal) {
|
||||
return (
|
||||
<ImageListItem
|
||||
item={item}
|
||||
|
@ -58,12 +55,13 @@ export default class CardList extends React.Component<PropsType> {
|
|||
width={this.horizontalItemSize}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <CardListItem item={item} key={item.title} />;
|
||||
};
|
||||
|
||||
keyExtractor = (item: ServiceItemType): string => item.key;
|
||||
|
||||
render(): React.Node {
|
||||
render() {
|
||||
const {props} = this;
|
||||
let containerStyle = {};
|
||||
if (props.isHorizontal) {
|
||||
|
@ -84,7 +82,7 @@ export default class CardList extends React.Component<PropsType> {
|
|||
}
|
||||
pagingEnabled={props.isHorizontal}
|
||||
snapToInterval={
|
||||
props.isHorizontal ? (this.horizontalItemSize + 5) * 3 : null
|
||||
props.isHorizontal ? (this.horizontalItemSize + 5) * 3 : undefined
|
||||
}
|
||||
/>
|
||||
);
|
|
@ -17,45 +17,38 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Caption, Card, Paragraph, TouchableRipple} from 'react-native-paper';
|
||||
import {View} from 'react-native';
|
||||
import type {ServiceItemType} from '../../../managers/ServicesManager';
|
||||
|
||||
type PropsType = {
|
||||
item: ServiceItemType,
|
||||
item: ServiceItemType;
|
||||
};
|
||||
|
||||
export default class CardListItem extends React.Component<PropsType> {
|
||||
shouldComponentUpdate(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
const {props} = this;
|
||||
const {item} = props;
|
||||
const source =
|
||||
typeof item.image === 'number' ? item.image : {uri: item.image};
|
||||
return (
|
||||
<Card
|
||||
style={{
|
||||
width: '40%',
|
||||
margin: 5,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}>
|
||||
<TouchableRipple style={{flex: 1}} onPress={item.onPress}>
|
||||
<View>
|
||||
<Card.Cover style={{height: 80}} source={source} />
|
||||
<Card.Content>
|
||||
<Paragraph>{item.title}</Paragraph>
|
||||
<Caption>{item.subtitle}</Caption>
|
||||
</Card.Content>
|
||||
</View>
|
||||
</TouchableRipple>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
function CardListItem(props: PropsType) {
|
||||
const {item} = props;
|
||||
const source =
|
||||
typeof item.image === 'number' ? item.image : {uri: item.image};
|
||||
return (
|
||||
<Card
|
||||
style={{
|
||||
width: '40%',
|
||||
margin: 5,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}>
|
||||
<TouchableRipple style={{flex: 1}} onPress={item.onPress}>
|
||||
<View>
|
||||
<Card.Cover style={{height: 80}} source={source} />
|
||||
<Card.Content>
|
||||
<Paragraph>{item.title}</Paragraph>
|
||||
<Caption>{item.subtitle}</Caption>
|
||||
</Card.Content>
|
||||
</View>
|
||||
</TouchableRipple>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(CardListItem, () => true);
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Text, TouchableRipple} from 'react-native-paper';
|
||||
import {Image, View} from 'react-native';
|
||||
import type {ServiceItemType} from '../../../managers/ServicesManager';
|
||||
|
||||
type PropsType = {
|
||||
item: ServiceItemType,
|
||||
width: number,
|
||||
};
|
||||
|
||||
export default class ImageListItem extends React.Component<PropsType> {
|
||||
shouldComponentUpdate(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
const {props} = this;
|
||||
const {item} = props;
|
||||
const source =
|
||||
typeof item.image === 'number' ? item.image : {uri: item.image};
|
||||
return (
|
||||
<TouchableRipple
|
||||
style={{
|
||||
width: props.width,
|
||||
height: props.width + 40,
|
||||
margin: 5,
|
||||
}}
|
||||
onPress={item.onPress}>
|
||||
<View>
|
||||
<Image
|
||||
style={{
|
||||
width: props.width - 20,
|
||||
height: props.width - 20,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
source={source}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
marginTop: 5,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
{item.title}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableRipple>
|
||||
);
|
||||
}
|
||||
}
|
66
src/components/Lists/CardList/ImageListItem.tsx
Normal file
66
src/components/Lists/CardList/ImageListItem.tsx
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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 * as React from 'react';
|
||||
import {Text, TouchableRipple} from 'react-native-paper';
|
||||
import {Image, View} from 'react-native';
|
||||
import type {ServiceItemType} from '../../../managers/ServicesManager';
|
||||
|
||||
type PropsType = {
|
||||
item: ServiceItemType;
|
||||
width: number;
|
||||
};
|
||||
|
||||
function ImageListItem(props: PropsType) {
|
||||
const {item} = props;
|
||||
const source =
|
||||
typeof item.image === 'number' ? item.image : {uri: item.image};
|
||||
return (
|
||||
<TouchableRipple
|
||||
style={{
|
||||
width: props.width,
|
||||
height: props.width + 40,
|
||||
margin: 5,
|
||||
}}
|
||||
onPress={item.onPress}>
|
||||
<View>
|
||||
<Image
|
||||
style={{
|
||||
width: props.width - 20,
|
||||
height: props.width - 20,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
source={source}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
marginTop: 5,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
{item.title}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableRipple>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(ImageListItem, () => true);
|
|
@ -17,8 +17,6 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Card, Chip, List, Text} from 'react-native-paper';
|
||||
import {StyleSheet, View} from 'react-native';
|
||||
|
@ -26,12 +24,11 @@ import i18n from 'i18n-js';
|
|||
import AnimatedAccordion from '../../Animations/AnimatedAccordion';
|
||||
import {isItemInCategoryFilter} from '../../../utils/Search';
|
||||
import type {ClubCategoryType} from '../../../screens/Amicale/Clubs/ClubListScreen';
|
||||
import type {ListIconPropsType} from '../../../constants/PaperStyles';
|
||||
|
||||
type PropsType = {
|
||||
categories: Array<ClubCategoryType>,
|
||||
onChipSelect: (id: number) => void,
|
||||
selectedCategories: Array<number>,
|
||||
categories: Array<ClubCategoryType>;
|
||||
onChipSelect: (id: number) => void;
|
||||
selectedCategories: Array<number>;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
@ -54,16 +51,8 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
});
|
||||
|
||||
class ClubListHeader extends React.Component<PropsType> {
|
||||
shouldComponentUpdate(nextProps: PropsType): boolean {
|
||||
const {props} = this;
|
||||
return (
|
||||
nextProps.selectedCategories.length !== props.selectedCategories.length
|
||||
);
|
||||
}
|
||||
|
||||
getChipRender = (category: ClubCategoryType, key: string): React.Node => {
|
||||
const {props} = this;
|
||||
function ClubListHeader(props: PropsType) {
|
||||
const getChipRender = (category: ClubCategoryType, key: string) => {
|
||||
const onPress = (): void => props.onChipSelect(category.id);
|
||||
return (
|
||||
<Chip
|
||||
|
@ -80,32 +69,39 @@ class ClubListHeader extends React.Component<PropsType> {
|
|||
);
|
||||
};
|
||||
|
||||
getCategoriesRender(): React.Node {
|
||||
const {props} = this;
|
||||
const final = [];
|
||||
const getCategoriesRender = () => {
|
||||
const final: Array<React.ReactNode> = [];
|
||||
props.categories.forEach((cat: ClubCategoryType) => {
|
||||
final.push(this.getChipRender(cat, cat.id.toString()));
|
||||
final.push(getChipRender(cat, cat.id.toString()));
|
||||
});
|
||||
return final;
|
||||
}
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
return (
|
||||
<Card style={styles.card}>
|
||||
<AnimatedAccordion
|
||||
title={i18n.t('screens.clubs.categories')}
|
||||
left={(props: ListIconPropsType): React.Node => (
|
||||
<List.Icon color={props.color} style={props.style} icon="star" />
|
||||
)}
|
||||
opened>
|
||||
<Text style={styles.text}>
|
||||
{i18n.t('screens.clubs.categoriesFilterMessage')}
|
||||
</Text>
|
||||
<View style={styles.chipContainer}>{this.getCategoriesRender()}</View>
|
||||
</AnimatedAccordion>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Card style={styles.card}>
|
||||
<AnimatedAccordion
|
||||
title={i18n.t('screens.clubs.categories')}
|
||||
left={(iconProps) => (
|
||||
<List.Icon
|
||||
color={iconProps.color}
|
||||
style={iconProps.style}
|
||||
icon="star"
|
||||
/>
|
||||
)}
|
||||
opened>
|
||||
<Text style={styles.text}>
|
||||
{i18n.t('screens.clubs.categoriesFilterMessage')}
|
||||
</Text>
|
||||
<View style={styles.chipContainer}>{getCategoriesRender()}</View>
|
||||
</AnimatedAccordion>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default ClubListHeader;
|
||||
const areEqual = (prevProps: PropsType, nextProps: PropsType): boolean => {
|
||||
return (
|
||||
prevProps.selectedCategories.length === nextProps.selectedCategories.length
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ClubListHeader, areEqual);
|
|
@ -17,8 +17,6 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Avatar, Chip, List, withTheme} from 'react-native-paper';
|
||||
import {View} from 'react-native';
|
||||
|
@ -26,14 +24,13 @@ import type {
|
|||
ClubCategoryType,
|
||||
ClubType,
|
||||
} from '../../../screens/Amicale/Clubs/ClubListScreen';
|
||||
import type {CustomThemeType} from '../../../managers/ThemeManager';
|
||||
|
||||
type PropsType = {
|
||||
onPress: () => void,
|
||||
categoryTranslator: (id: number) => ClubCategoryType,
|
||||
item: ClubType,
|
||||
height: number,
|
||||
theme: CustomThemeType,
|
||||
onPress: () => void;
|
||||
categoryTranslator: (id: number) => ClubCategoryType;
|
||||
item: ClubType;
|
||||
height: number;
|
||||
theme: ReactNativePaper.Theme;
|
||||
};
|
||||
|
||||
class ClubListItem extends React.Component<PropsType> {
|
||||
|
@ -48,9 +45,9 @@ class ClubListItem extends React.Component<PropsType> {
|
|||
return false;
|
||||
}
|
||||
|
||||
getCategoriesRender(categories: Array<number | null>): React.Node {
|
||||
getCategoriesRender(categories: Array<number | null>) {
|
||||
const {props} = this;
|
||||
const final = [];
|
||||
const final: Array<React.ReactNode> = [];
|
||||
categories.forEach((cat: number | null) => {
|
||||
if (cat != null) {
|
||||
const category: ClubCategoryType = props.categoryTranslator(cat);
|
||||
|
@ -66,9 +63,9 @@ class ClubListItem extends React.Component<PropsType> {
|
|||
return <View style={{flexDirection: 'row'}}>{final}</View>;
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
render() {
|
||||
const {props} = this;
|
||||
const categoriesRender = (): React.Node =>
|
||||
const categoriesRender = () =>
|
||||
this.getCategoriesRender(props.item.category);
|
||||
const {colors} = props.theme;
|
||||
return (
|
||||
|
@ -76,7 +73,7 @@ class ClubListItem extends React.Component<PropsType> {
|
|||
title={props.item.name}
|
||||
description={categoriesRender}
|
||||
onPress={props.onPress}
|
||||
left={(): React.Node => (
|
||||
left={() => (
|
||||
<Avatar.Image
|
||||
style={{
|
||||
backgroundColor: 'transparent',
|
||||
|
@ -87,7 +84,7 @@ class ClubListItem extends React.Component<PropsType> {
|
|||
source={{uri: props.item.logo}}
|
||||
/>
|
||||
)}
|
||||
right={(): React.Node => (
|
||||
right={() => (
|
||||
<Avatar.Icon
|
||||
style={{
|
||||
marginTop: 'auto',
|
|
@ -1,108 +0,0 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {withTheme} from 'react-native-paper';
|
||||
import {FlatList, Image, View} from 'react-native';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import DashboardEditItem from './DashboardEditItem';
|
||||
import AnimatedAccordion from '../../Animations/AnimatedAccordion';
|
||||
import type {
|
||||
ServiceCategoryType,
|
||||
ServiceItemType,
|
||||
} from '../../../managers/ServicesManager';
|
||||
import type {CustomThemeType} from '../../../managers/ThemeManager';
|
||||
|
||||
type PropsType = {
|
||||
item: ServiceCategoryType,
|
||||
activeDashboard: Array<string>,
|
||||
onPress: (service: ServiceItemType) => void,
|
||||
theme: CustomThemeType,
|
||||
};
|
||||
|
||||
const LIST_ITEM_HEIGHT = 64;
|
||||
|
||||
class DashboardEditAccordion extends React.Component<PropsType> {
|
||||
getRenderItem = ({item}: {item: ServiceItemType}): React.Node => {
|
||||
const {props} = this;
|
||||
return (
|
||||
<DashboardEditItem
|
||||
height={LIST_ITEM_HEIGHT}
|
||||
item={item}
|
||||
isActive={props.activeDashboard.includes(item.key)}
|
||||
onPress={() => {
|
||||
props.onPress(item);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
getItemLayout = (
|
||||
data: ?Array<ServiceItemType>,
|
||||
index: number,
|
||||
): {length: number, offset: number, index: number} => ({
|
||||
length: LIST_ITEM_HEIGHT,
|
||||
offset: LIST_ITEM_HEIGHT * index,
|
||||
index,
|
||||
});
|
||||
|
||||
render(): React.Node {
|
||||
const {props} = this;
|
||||
const {item} = props;
|
||||
return (
|
||||
<View>
|
||||
<AnimatedAccordion
|
||||
title={item.title}
|
||||
left={(): React.Node =>
|
||||
typeof item.image === 'number' ? (
|
||||
<Image
|
||||
source={item.image}
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<MaterialCommunityIcons
|
||||
// $FlowFixMe
|
||||
name={item.image}
|
||||
color={props.theme.colors.primary}
|
||||
size={40}
|
||||
/>
|
||||
)
|
||||
}>
|
||||
{/* $FlowFixMe */}
|
||||
<FlatList
|
||||
data={item.content}
|
||||
extraData={props.activeDashboard.toString()}
|
||||
renderItem={this.getRenderItem}
|
||||
listKey={item.key}
|
||||
// Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
|
||||
getItemLayout={this.getItemLayout}
|
||||
removeClippedSubviews
|
||||
/>
|
||||
</AnimatedAccordion>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(DashboardEditAccordion);
|
100
src/components/Lists/DashboardEdit/DashboardEditAccordion.tsx
Normal file
100
src/components/Lists/DashboardEdit/DashboardEditAccordion.tsx
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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 * as React from 'react';
|
||||
import {useTheme} from 'react-native-paper';
|
||||
import {FlatList, Image, View} from 'react-native';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import DashboardEditItem from './DashboardEditItem';
|
||||
import AnimatedAccordion from '../../Animations/AnimatedAccordion';
|
||||
import type {
|
||||
ServiceCategoryType,
|
||||
ServiceItemType,
|
||||
} from '../../../managers/ServicesManager';
|
||||
|
||||
type PropsType = {
|
||||
item: ServiceCategoryType;
|
||||
activeDashboard: Array<string>;
|
||||
onPress: (service: ServiceItemType) => void;
|
||||
};
|
||||
|
||||
const LIST_ITEM_HEIGHT = 64;
|
||||
|
||||
function DashboardEditAccordion(props: PropsType) {
|
||||
const theme = useTheme();
|
||||
|
||||
const getRenderItem = ({item}: {item: ServiceItemType}) => {
|
||||
return (
|
||||
<DashboardEditItem
|
||||
height={LIST_ITEM_HEIGHT}
|
||||
item={item}
|
||||
isActive={props.activeDashboard.includes(item.key)}
|
||||
onPress={() => {
|
||||
props.onPress(item);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const getItemLayout = (
|
||||
data: Array<ServiceItemType> | null | undefined,
|
||||
index: number,
|
||||
): {length: number; offset: number; index: number} => ({
|
||||
length: LIST_ITEM_HEIGHT,
|
||||
offset: LIST_ITEM_HEIGHT * index,
|
||||
index,
|
||||
});
|
||||
|
||||
const {item} = props;
|
||||
return (
|
||||
<View>
|
||||
<AnimatedAccordion
|
||||
title={item.title}
|
||||
left={() =>
|
||||
typeof item.image === 'number' ? (
|
||||
<Image
|
||||
source={item.image}
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<MaterialCommunityIcons
|
||||
name={item.image}
|
||||
color={theme.colors.primary}
|
||||
size={40}
|
||||
/>
|
||||
)
|
||||
}>
|
||||
<FlatList
|
||||
data={item.content}
|
||||
extraData={props.activeDashboard.toString()}
|
||||
renderItem={getRenderItem}
|
||||
listKey={item.key}
|
||||
// Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
|
||||
getItemLayout={getItemLayout}
|
||||
removeClippedSubviews
|
||||
/>
|
||||
</AnimatedAccordion>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default DashboardEditAccordion;
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Image} from 'react-native';
|
||||
import {List, withTheme} from 'react-native-paper';
|
||||
import type {CustomThemeType} from '../../../managers/ThemeManager';
|
||||
import type {ServiceItemType} from '../../../managers/ServicesManager';
|
||||
import type {ListIconPropsType} from '../../../constants/PaperStyles';
|
||||
|
||||
type PropsType = {
|
||||
item: ServiceItemType,
|
||||
isActive: boolean,
|
||||
height: number,
|
||||
onPress: () => void,
|
||||
theme: CustomThemeType,
|
||||
};
|
||||
|
||||
class DashboardEditItem extends React.Component<PropsType> {
|
||||
shouldComponentUpdate(nextProps: PropsType): boolean {
|
||||
const {isActive} = this.props;
|
||||
return nextProps.isActive !== isActive;
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
const {item, onPress, height, isActive, theme} = this.props;
|
||||
return (
|
||||
<List.Item
|
||||
title={item.title}
|
||||
description={item.subtitle}
|
||||
onPress={isActive ? null : onPress}
|
||||
left={(): React.Node => (
|
||||
<Image
|
||||
source={{uri: item.image}}
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
right={(props: ListIconPropsType): React.Node =>
|
||||
isActive ? (
|
||||
<List.Icon
|
||||
style={props.style}
|
||||
icon="check"
|
||||
color={theme.colors.success}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
style={{
|
||||
height,
|
||||
justifyContent: 'center',
|
||||
paddingLeft: 30,
|
||||
backgroundColor: isActive
|
||||
? theme.colors.proxiwashFinishedColor
|
||||
: 'transparent',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(DashboardEditItem);
|
76
src/components/Lists/DashboardEdit/DashboardEditItem.tsx
Normal file
76
src/components/Lists/DashboardEdit/DashboardEditItem.tsx
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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 * as React from 'react';
|
||||
import {Image} from 'react-native';
|
||||
import {List, useTheme} from 'react-native-paper';
|
||||
import type {ServiceItemType} from '../../../managers/ServicesManager';
|
||||
|
||||
type PropsType = {
|
||||
item: ServiceItemType;
|
||||
isActive: boolean;
|
||||
height: number;
|
||||
onPress: () => void;
|
||||
};
|
||||
|
||||
function DashboardEditItem(props: PropsType) {
|
||||
const theme = useTheme();
|
||||
const {item, onPress, height, isActive} = props;
|
||||
return (
|
||||
<List.Item
|
||||
title={item.title}
|
||||
description={item.subtitle}
|
||||
onPress={isActive ? undefined : onPress}
|
||||
left={() => (
|
||||
<Image
|
||||
source={
|
||||
typeof item.image === 'string' ? {uri: item.image} : item.image
|
||||
}
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
right={(iconProps) =>
|
||||
isActive ? (
|
||||
<List.Icon
|
||||
style={iconProps.style}
|
||||
icon="check"
|
||||
color={theme.colors.success}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
style={{
|
||||
height,
|
||||
justifyContent: 'center',
|
||||
paddingLeft: 30,
|
||||
backgroundColor: isActive
|
||||
? theme.colors.proxiwashFinishedColor
|
||||
: 'transparent',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const areEqual = (prevProps: PropsType, nextProps: PropsType): boolean => {
|
||||
return nextProps.isActive !== prevProps.isActive;
|
||||
};
|
||||
|
||||
export default React.memo(DashboardEditItem, areEqual);
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {TouchableRipple, withTheme} from 'react-native-paper';
|
||||
import {Dimensions, Image, View} from 'react-native';
|
||||
import type {CustomThemeType} from '../../../managers/ThemeManager';
|
||||
|
||||
type PropsType = {
|
||||
image: string,
|
||||
isActive: boolean,
|
||||
onPress: () => void,
|
||||
theme: CustomThemeType,
|
||||
};
|
||||
|
||||
/**
|
||||
* Component used to render a small dashboard item
|
||||
*/
|
||||
class DashboardEditPreviewItem extends React.Component<PropsType> {
|
||||
itemSize: number;
|
||||
|
||||
constructor(props: PropsType) {
|
||||
super(props);
|
||||
this.itemSize = Dimensions.get('window').width / 8;
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
const {props} = this;
|
||||
return (
|
||||
<TouchableRipple
|
||||
onPress={props.onPress}
|
||||
borderless
|
||||
style={{
|
||||
marginLeft: 5,
|
||||
marginRight: 5,
|
||||
backgroundColor: props.isActive
|
||||
? props.theme.colors.textDisabled
|
||||
: 'transparent',
|
||||
borderRadius: 5,
|
||||
}}>
|
||||
<View
|
||||
style={{
|
||||
width: this.itemSize,
|
||||
height: this.itemSize,
|
||||
}}>
|
||||
<Image
|
||||
source={{uri: props.image}}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</TouchableRipple>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(DashboardEditPreviewItem);
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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 * as React from 'react';
|
||||
import {TouchableRipple, useTheme} from 'react-native-paper';
|
||||
import {Dimensions, Image, View} from 'react-native';
|
||||
|
||||
type PropsType = {
|
||||
image: string;
|
||||
isActive: boolean;
|
||||
onPress: () => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Component used to render a small dashboard item
|
||||
*/
|
||||
function DashboardEditPreviewItem(props: PropsType) {
|
||||
const theme = useTheme();
|
||||
const itemSize = Dimensions.get('window').width / 8;
|
||||
|
||||
return (
|
||||
<TouchableRipple
|
||||
onPress={props.onPress}
|
||||
borderless
|
||||
style={{
|
||||
marginLeft: 5,
|
||||
marginRight: 5,
|
||||
backgroundColor: props.isActive
|
||||
? theme.colors.textDisabled
|
||||
: 'transparent',
|
||||
borderRadius: 5,
|
||||
}}>
|
||||
<View
|
||||
style={{
|
||||
width: itemSize,
|
||||
height: itemSize,
|
||||
}}>
|
||||
<Image
|
||||
source={{uri: props.image}}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</TouchableRipple>
|
||||
);
|
||||
}
|
||||
|
||||
export default DashboardEditPreviewItem;
|
|
@ -1,132 +0,0 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Avatar, List, withTheme} from 'react-native-paper';
|
||||
import i18n from 'i18n-js';
|
||||
import {StackNavigationProp} from '@react-navigation/stack';
|
||||
import type {CustomThemeType} from '../../../managers/ThemeManager';
|
||||
import type {DeviceType} from '../../../screens/Amicale/Equipment/EquipmentListScreen';
|
||||
import {
|
||||
getFirstEquipmentAvailability,
|
||||
getRelativeDateString,
|
||||
isEquipmentAvailable,
|
||||
} from '../../../utils/EquipmentBooking';
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp,
|
||||
userDeviceRentDates: [string, string],
|
||||
item: DeviceType,
|
||||
height: number,
|
||||
theme: CustomThemeType,
|
||||
};
|
||||
|
||||
class EquipmentListItem extends React.Component<PropsType> {
|
||||
shouldComponentUpdate(nextProps: PropsType): boolean {
|
||||
const {userDeviceRentDates} = this.props;
|
||||
return nextProps.userDeviceRentDates !== userDeviceRentDates;
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
const {item, userDeviceRentDates, navigation, height, theme} = this.props;
|
||||
const isRented = userDeviceRentDates != null;
|
||||
const isAvailable = isEquipmentAvailable(item);
|
||||
const firstAvailability = getFirstEquipmentAvailability(item);
|
||||
|
||||
let onPress;
|
||||
if (isRented)
|
||||
onPress = () => {
|
||||
navigation.navigate('equipment-confirm', {
|
||||
item,
|
||||
dates: userDeviceRentDates,
|
||||
});
|
||||
};
|
||||
else
|
||||
onPress = () => {
|
||||
navigation.navigate('equipment-rent', {item});
|
||||
};
|
||||
|
||||
let description;
|
||||
if (isRented) {
|
||||
const start = new Date(userDeviceRentDates[0]);
|
||||
const end = new Date(userDeviceRentDates[1]);
|
||||
if (start.getTime() !== end.getTime())
|
||||
description = i18n.t('screens.equipment.bookingPeriod', {
|
||||
begin: getRelativeDateString(start),
|
||||
end: getRelativeDateString(end),
|
||||
});
|
||||
else
|
||||
description = i18n.t('screens.equipment.bookingDay', {
|
||||
date: getRelativeDateString(start),
|
||||
});
|
||||
} else if (isAvailable)
|
||||
description = i18n.t('screens.equipment.bail', {cost: item.caution});
|
||||
else
|
||||
description = i18n.t('screens.equipment.available', {
|
||||
date: getRelativeDateString(firstAvailability),
|
||||
});
|
||||
|
||||
let icon;
|
||||
if (isRented) icon = 'bookmark-check';
|
||||
else if (isAvailable) icon = 'check-circle-outline';
|
||||
else icon = 'update';
|
||||
|
||||
let color;
|
||||
if (isRented) color = theme.colors.warning;
|
||||
else if (isAvailable) color = theme.colors.success;
|
||||
else color = theme.colors.primary;
|
||||
|
||||
return (
|
||||
<List.Item
|
||||
title={item.name}
|
||||
description={description}
|
||||
onPress={onPress}
|
||||
left={({size}: {size: number}): React.Node => (
|
||||
<Avatar.Icon
|
||||
size={size}
|
||||
style={{
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
icon={icon}
|
||||
color={color}
|
||||
/>
|
||||
)}
|
||||
right={(): React.Node => (
|
||||
<Avatar.Icon
|
||||
style={{
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
size={48}
|
||||
icon="chevron-right"
|
||||
/>
|
||||
)}
|
||||
style={{
|
||||
height,
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(EquipmentListItem);
|
136
src/components/Lists/Equipment/EquipmentListItem.tsx
Normal file
136
src/components/Lists/Equipment/EquipmentListItem.tsx
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* 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 * as React from 'react';
|
||||
import {Avatar, List, useTheme} from 'react-native-paper';
|
||||
import i18n from 'i18n-js';
|
||||
import {StackNavigationProp} from '@react-navigation/stack';
|
||||
import type {DeviceType} from '../../../screens/Amicale/Equipment/EquipmentListScreen';
|
||||
import {
|
||||
getFirstEquipmentAvailability,
|
||||
getRelativeDateString,
|
||||
isEquipmentAvailable,
|
||||
} from '../../../utils/EquipmentBooking';
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp<any>;
|
||||
userDeviceRentDates: [string, string];
|
||||
item: DeviceType;
|
||||
height: number;
|
||||
};
|
||||
|
||||
function EquipmentListItem(props: PropsType) {
|
||||
const theme = useTheme();
|
||||
const {item, userDeviceRentDates, navigation, height} = props;
|
||||
const isRented = userDeviceRentDates != null;
|
||||
const isAvailable = isEquipmentAvailable(item);
|
||||
const firstAvailability = getFirstEquipmentAvailability(item);
|
||||
|
||||
let onPress;
|
||||
if (isRented) {
|
||||
onPress = () => {
|
||||
navigation.navigate('equipment-confirm', {
|
||||
item,
|
||||
dates: userDeviceRentDates,
|
||||
});
|
||||
};
|
||||
} else {
|
||||
onPress = () => {
|
||||
navigation.navigate('equipment-rent', {item});
|
||||
};
|
||||
}
|
||||
|
||||
let description;
|
||||
if (isRented) {
|
||||
const start = new Date(userDeviceRentDates[0]);
|
||||
const end = new Date(userDeviceRentDates[1]);
|
||||
if (start.getTime() !== end.getTime()) {
|
||||
description = i18n.t('screens.equipment.bookingPeriod', {
|
||||
begin: getRelativeDateString(start),
|
||||
end: getRelativeDateString(end),
|
||||
});
|
||||
} else {
|
||||
description = i18n.t('screens.equipment.bookingDay', {
|
||||
date: getRelativeDateString(start),
|
||||
});
|
||||
}
|
||||
} else if (isAvailable) {
|
||||
description = i18n.t('screens.equipment.bail', {cost: item.caution});
|
||||
} else {
|
||||
description = i18n.t('screens.equipment.available', {
|
||||
date: getRelativeDateString(firstAvailability),
|
||||
});
|
||||
}
|
||||
|
||||
let icon: string;
|
||||
if (isRented) {
|
||||
icon = 'bookmark-check';
|
||||
} else if (isAvailable) {
|
||||
icon = 'check-circle-outline';
|
||||
} else {
|
||||
icon = 'update';
|
||||
}
|
||||
|
||||
let color: string;
|
||||
if (isRented) {
|
||||
color = theme.colors.warning;
|
||||
} else if (isAvailable) {
|
||||
color = theme.colors.success;
|
||||
} else {
|
||||
color = theme.colors.primary;
|
||||
}
|
||||
|
||||
return (
|
||||
<List.Item
|
||||
title={item.name}
|
||||
description={description}
|
||||
onPress={onPress}
|
||||
left={() => (
|
||||
<Avatar.Icon
|
||||
style={{
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
icon={icon}
|
||||
color={color}
|
||||
/>
|
||||
)}
|
||||
right={() => (
|
||||
<Avatar.Icon
|
||||
style={{
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
size={48}
|
||||
icon="chevron-right"
|
||||
/>
|
||||
)}
|
||||
style={{
|
||||
height,
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const areEqual = (prevProps: PropsType, nextProps: PropsType): boolean => {
|
||||
return nextProps.userDeviceRentDates !== prevProps.userDeviceRentDates;
|
||||
};
|
||||
|
||||
export default React.memo(EquipmentListItem, areEqual);
|
|
@ -17,8 +17,6 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {List, withTheme} from 'react-native-paper';
|
||||
import {FlatList, View} from 'react-native';
|
||||
|
@ -29,17 +27,15 @@ import type {
|
|||
PlanexGroupType,
|
||||
PlanexGroupCategoryType,
|
||||
} from '../../../screens/Planex/GroupSelectionScreen';
|
||||
import type {CustomThemeType} from '../../../managers/ThemeManager';
|
||||
import type {ListIconPropsType} from '../../../constants/PaperStyles';
|
||||
|
||||
type PropsType = {
|
||||
item: PlanexGroupCategoryType,
|
||||
favorites: Array<PlanexGroupType>,
|
||||
onGroupPress: (PlanexGroupType) => void,
|
||||
onFavoritePress: (PlanexGroupType) => void,
|
||||
currentSearchString: string,
|
||||
height: number,
|
||||
theme: CustomThemeType,
|
||||
item: PlanexGroupCategoryType;
|
||||
favorites: Array<PlanexGroupType>;
|
||||
onGroupPress: (data: PlanexGroupType) => void;
|
||||
onFavoritePress: (data: PlanexGroupType) => void;
|
||||
currentSearchString: string;
|
||||
height: number;
|
||||
theme: ReactNativePaper.Theme;
|
||||
};
|
||||
|
||||
const LIST_ITEM_HEIGHT = 64;
|
||||
|
@ -55,7 +51,7 @@ class GroupListAccordion extends React.Component<PropsType> {
|
|||
);
|
||||
}
|
||||
|
||||
getRenderItem = ({item}: {item: PlanexGroupType}): React.Node => {
|
||||
getRenderItem = ({item}: {item: PlanexGroupType}) => {
|
||||
const {props} = this;
|
||||
const onPress = () => {
|
||||
props.onGroupPress(item);
|
||||
|
@ -77,18 +73,19 @@ class GroupListAccordion extends React.Component<PropsType> {
|
|||
getData(): Array<PlanexGroupType> {
|
||||
const {props} = this;
|
||||
const originalData = props.item.content;
|
||||
const displayData = [];
|
||||
const displayData: Array<PlanexGroupType> = [];
|
||||
originalData.forEach((data: PlanexGroupType) => {
|
||||
if (stringMatchQuery(data.name, props.currentSearchString))
|
||||
if (stringMatchQuery(data.name, props.currentSearchString)) {
|
||||
displayData.push(data);
|
||||
}
|
||||
});
|
||||
return displayData;
|
||||
}
|
||||
|
||||
itemLayout = (
|
||||
data: ?Array<PlanexGroupType>,
|
||||
data: Array<PlanexGroupType> | null | undefined,
|
||||
index: number,
|
||||
): {length: number, offset: number, index: number} => ({
|
||||
): {length: number; offset: number; index: number} => ({
|
||||
length: LIST_ITEM_HEIGHT,
|
||||
offset: LIST_ITEM_HEIGHT * index,
|
||||
index,
|
||||
|
@ -96,7 +93,7 @@ class GroupListAccordion extends React.Component<PropsType> {
|
|||
|
||||
keyExtractor = (item: PlanexGroupType): string => item.id.toString();
|
||||
|
||||
render(): React.Node {
|
||||
render() {
|
||||
const {props} = this;
|
||||
const {item} = this.props;
|
||||
return (
|
||||
|
@ -107,7 +104,7 @@ class GroupListAccordion extends React.Component<PropsType> {
|
|||
height: props.height,
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
left={(iconProps: ListIconPropsType): React.Node =>
|
||||
left={(iconProps) =>
|
||||
item.id === 0 ? (
|
||||
<List.Icon
|
||||
style={iconProps.style}
|
|
@ -17,23 +17,19 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {List, TouchableRipple, withTheme} from 'react-native-paper';
|
||||
import * as Animatable from 'react-native-animatable';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import type {CustomThemeType} from '../../../managers/ThemeManager';
|
||||
import type {PlanexGroupType} from '../../../screens/Planex/GroupSelectionScreen';
|
||||
import type {ListIconPropsType} from '../../../constants/PaperStyles';
|
||||
|
||||
type PropsType = {
|
||||
theme: CustomThemeType,
|
||||
onPress: () => void,
|
||||
onStarPress: () => void,
|
||||
item: PlanexGroupType,
|
||||
favorites: Array<PlanexGroupType>,
|
||||
height: number,
|
||||
theme: ReactNativePaper.Theme;
|
||||
onPress: () => void;
|
||||
onStarPress: () => void;
|
||||
item: PlanexGroupType;
|
||||
favorites: Array<PlanexGroupType>;
|
||||
height: number;
|
||||
};
|
||||
|
||||
const REPLACE_REGEX = /_/g;
|
||||
|
@ -41,10 +37,11 @@ const REPLACE_REGEX = /_/g;
|
|||
class GroupListItem extends React.Component<PropsType> {
|
||||
isFav: boolean;
|
||||
|
||||
starRef: null | Animatable.View;
|
||||
starRef: {current: null | Animatable.View};
|
||||
|
||||
constructor(props: PropsType) {
|
||||
super(props);
|
||||
this.starRef = React.createRef<Animatable.View>();
|
||||
this.isFav = this.isGroupInFavorites(props.favorites);
|
||||
}
|
||||
|
||||
|
@ -52,7 +49,9 @@ class GroupListItem extends React.Component<PropsType> {
|
|||
const {favorites} = this.props;
|
||||
const favChanged = favorites.length !== nextProps.favorites.length;
|
||||
let newFavState = this.isFav;
|
||||
if (favChanged) newFavState = this.isGroupInFavorites(nextProps.favorites);
|
||||
if (favChanged) {
|
||||
newFavState = this.isGroupInFavorites(nextProps.favorites);
|
||||
}
|
||||
const shouldUpdate = this.isFav !== newFavState;
|
||||
this.isFav = newFavState;
|
||||
return shouldUpdate;
|
||||
|
@ -61,9 +60,12 @@ class GroupListItem extends React.Component<PropsType> {
|
|||
onStarPress = () => {
|
||||
const {props} = this;
|
||||
const ref = this.starRef;
|
||||
if (ref != null) {
|
||||
if (this.isFav) ref.rubberBand();
|
||||
else ref.swing();
|
||||
if (ref.current) {
|
||||
if (this.isFav) {
|
||||
ref.current.rubberBand();
|
||||
} else {
|
||||
ref.current.swing();
|
||||
}
|
||||
}
|
||||
props.onStarPress();
|
||||
};
|
||||
|
@ -71,31 +73,29 @@ class GroupListItem extends React.Component<PropsType> {
|
|||
isGroupInFavorites(favorites: Array<PlanexGroupType>): boolean {
|
||||
const {item} = this.props;
|
||||
for (let i = 0; i < favorites.length; i += 1) {
|
||||
if (favorites[i].id === item.id) return true;
|
||||
if (favorites[i].id === item.id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
render() {
|
||||
const {props} = this;
|
||||
const {colors} = props.theme;
|
||||
return (
|
||||
<List.Item
|
||||
title={props.item.name.replace(REPLACE_REGEX, ' ')}
|
||||
onPress={props.onPress}
|
||||
left={(iconProps: ListIconPropsType): React.Node => (
|
||||
left={(iconProps) => (
|
||||
<List.Icon
|
||||
color={iconProps.color}
|
||||
style={iconProps.style}
|
||||
icon="chevron-right"
|
||||
/>
|
||||
)}
|
||||
right={(iconProps: ListIconPropsType): React.Node => (
|
||||
<Animatable.View
|
||||
ref={(ref: Animatable.View) => {
|
||||
this.starRef = ref;
|
||||
}}
|
||||
useNativeDriver>
|
||||
right={(iconProps) => (
|
||||
<Animatable.View ref={this.starRef} useNativeDriver>
|
||||
<TouchableRipple
|
||||
onPress={this.onStarPress}
|
||||
style={{
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Avatar, List, Text, withTheme} from 'react-native-paper';
|
||||
import i18n from 'i18n-js';
|
||||
import type {ProximoArticleType} from '../../../screens/Services/Proximo/ProximoMainScreen';
|
||||
|
||||
type PropsType = {
|
||||
onPress: () => void,
|
||||
color: string,
|
||||
item: ProximoArticleType,
|
||||
height: number,
|
||||
};
|
||||
|
||||
class ProximoListItem extends React.Component<PropsType> {
|
||||
shouldComponentUpdate(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
const {props} = this;
|
||||
return (
|
||||
<List.Item
|
||||
title={props.item.name}
|
||||
description={`${props.item.quantity} ${i18n.t(
|
||||
'screens.proximo.inStock',
|
||||
)}`}
|
||||
descriptionStyle={{color: props.color}}
|
||||
onPress={props.onPress}
|
||||
left={(): React.Node => (
|
||||
<Avatar.Image
|
||||
style={{backgroundColor: 'transparent'}}
|
||||
size={64}
|
||||
source={{uri: props.item.image}}
|
||||
/>
|
||||
)}
|
||||
right={(): React.Node => (
|
||||
<Text style={{fontWeight: 'bold'}}>{props.item.price}€</Text>
|
||||
)}
|
||||
style={{
|
||||
height: props.height,
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(ProximoListItem);
|
59
src/components/Lists/Proximo/ProximoListItem.tsx
Normal file
59
src/components/Lists/Proximo/ProximoListItem.tsx
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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 * as React from 'react';
|
||||
import {Avatar, List, Text} from 'react-native-paper';
|
||||
import i18n from 'i18n-js';
|
||||
import type {ProximoArticleType} from '../../../screens/Services/Proximo/ProximoMainScreen';
|
||||
|
||||
type PropsType = {
|
||||
onPress: () => void;
|
||||
color: string;
|
||||
item: ProximoArticleType;
|
||||
height: number;
|
||||
};
|
||||
|
||||
function ProximoListItem(props: PropsType) {
|
||||
return (
|
||||
<List.Item
|
||||
title={props.item.name}
|
||||
description={`${props.item.quantity} ${i18n.t(
|
||||
'screens.proximo.inStock',
|
||||
)}`}
|
||||
descriptionStyle={{color: props.color}}
|
||||
onPress={props.onPress}
|
||||
left={() => (
|
||||
<Avatar.Image
|
||||
style={{backgroundColor: 'transparent'}}
|
||||
size={64}
|
||||
source={{uri: props.item.image}}
|
||||
/>
|
||||
)}
|
||||
right={() => (
|
||||
<Text style={{fontWeight: 'bold'}}>{props.item.price}€</Text>
|
||||
)}
|
||||
style={{
|
||||
height: props.height,
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(ProximoListItem, () => true);
|
|
@ -17,8 +17,6 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Avatar,
|
||||
|
@ -34,20 +32,19 @@ import i18n from 'i18n-js';
|
|||
import * as Animatable from 'react-native-animatable';
|
||||
import ProxiwashConstants from '../../../constants/ProxiwashConstants';
|
||||
import AprilFoolsManager from '../../../managers/AprilFoolsManager';
|
||||
import type {CustomThemeType} from '../../../managers/ThemeManager';
|
||||
import type {ProxiwashMachineType} from '../../../screens/Proxiwash/ProxiwashScreen';
|
||||
|
||||
type PropsType = {
|
||||
item: ProxiwashMachineType,
|
||||
theme: CustomThemeType,
|
||||
item: ProxiwashMachineType;
|
||||
theme: ReactNativePaper.Theme;
|
||||
onPress: (
|
||||
title: string,
|
||||
item: ProxiwashMachineType,
|
||||
isDryer: boolean,
|
||||
) => void,
|
||||
isWatched: boolean,
|
||||
isDryer: boolean,
|
||||
height: number,
|
||||
) => void;
|
||||
isWatched: boolean;
|
||||
isDryer: boolean;
|
||||
height: number;
|
||||
};
|
||||
|
||||
const AnimatedIcon = Animatable.createAnimatableComponent(Avatar.Icon);
|
||||
|
@ -89,10 +86,11 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
|||
|
||||
let displayNumber = props.item.number;
|
||||
const displayMaxWeight = props.item.maxWeight;
|
||||
if (AprilFoolsManager.getInstance().isAprilFoolsEnabled())
|
||||
if (AprilFoolsManager.getInstance().isAprilFoolsEnabled()) {
|
||||
displayNumber = AprilFoolsManager.getProxiwashMachineDisplayNumber(
|
||||
parseInt(props.item.number, 10),
|
||||
);
|
||||
}
|
||||
|
||||
this.title = props.isDryer
|
||||
? i18n.t('screens.proxiwash.dryer')
|
||||
|
@ -159,10 +157,10 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
|||
colors.proxiwashUnknownColor;
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
render() {
|
||||
const {props} = this;
|
||||
const {colors} = props.theme;
|
||||
const machineState = props.item.state;
|
||||
const machineState = parseInt(props.item.state, 10);
|
||||
const isRunning = machineState === ProxiwashConstants.machineStates.RUNNING;
|
||||
const isReady = machineState === ProxiwashConstants.machineStates.AVAILABLE;
|
||||
const description = isRunning
|
||||
|
@ -171,10 +169,13 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
|||
const stateIcon = ProxiwashConstants.stateIcons[machineState];
|
||||
const stateString = this.stateStrings[machineState];
|
||||
let progress;
|
||||
if (isRunning && props.item.donePercent !== '')
|
||||
if (isRunning && props.item.donePercent !== '') {
|
||||
progress = parseFloat(props.item.donePercent) / 100;
|
||||
else if (isRunning) progress = 0;
|
||||
else progress = 1;
|
||||
} else if (isRunning) {
|
||||
progress = 0;
|
||||
} else {
|
||||
progress = 1;
|
||||
}
|
||||
|
||||
const icon = props.isWatched ? (
|
||||
<AnimatedIcon
|
||||
|
@ -224,8 +225,8 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
|||
justifyContent: 'center',
|
||||
}}
|
||||
onPress={this.onListItemPress}
|
||||
left={(): React.Node => icon}
|
||||
right={(): React.Node => (
|
||||
left={() => icon}
|
||||
right={() => (
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<View style={{justifyContent: 'center'}}>
|
||||
<Text
|
|
@ -17,19 +17,16 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Avatar, Text, withTheme} from 'react-native-paper';
|
||||
import {StyleSheet, View} from 'react-native';
|
||||
import i18n from 'i18n-js';
|
||||
import type {CustomThemeType} from '../../../managers/ThemeManager';
|
||||
|
||||
type PropsType = {
|
||||
theme: CustomThemeType,
|
||||
title: string,
|
||||
isDryer: boolean,
|
||||
nbAvailable: number,
|
||||
theme: ReactNativePaper.Theme;
|
||||
title: string;
|
||||
isDryer: boolean;
|
||||
nbAvailable: number;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
@ -61,7 +58,7 @@ class ProxiwashListItem extends React.Component<PropsType> {
|
|||
);
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
render() {
|
||||
const {props} = this;
|
||||
const subtitle = `${props.nbAvailable} ${
|
||||
props.nbAvailable <= 1
|
|
@ -94,7 +94,7 @@ export type ServiceItemType = {
|
|||
key: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
image: string;
|
||||
image: string | number;
|
||||
onPress: () => void;
|
||||
badgeFunction?: (dashboard: FullDashboardType) => number;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue