Update list components to use TypeScript

This commit is contained in:
Arnaud Vergnet 2020-09-22 15:06:38 +02:00
parent acc4f8cdcc
commit e4adcd0057
21 changed files with 648 additions and 705 deletions

View file

@ -17,19 +17,16 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/ */
// @flow
import * as React from 'react'; import * as React from 'react';
import {Animated, Dimensions} from 'react-native'; import {Animated, Dimensions, ViewStyle} from 'react-native';
import type {ViewStyle} from 'react-native/Libraries/StyleSheet/StyleSheet';
import ImageListItem from './ImageListItem'; import ImageListItem from './ImageListItem';
import CardListItem from './CardListItem'; import CardListItem from './CardListItem';
import type {ServiceItemType} from '../../../managers/ServicesManager'; import type {ServiceItemType} from '../../../managers/ServicesManager';
type PropsType = { type PropsType = {
dataset: Array<ServiceItemType>, dataset: Array<ServiceItemType>;
isHorizontal?: boolean, isHorizontal?: boolean;
contentContainerStyle?: ViewStyle | null, contentContainerStyle?: ViewStyle;
}; };
export default class CardList extends React.Component<PropsType> { export default class CardList extends React.Component<PropsType> {
@ -45,12 +42,12 @@ export default class CardList extends React.Component<PropsType> {
constructor(props: PropsType) { constructor(props: PropsType) {
super(props); super(props);
this.windowWidth = Dimensions.get('window').width; 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; const {props} = this;
if (props.isHorizontal) if (props.isHorizontal) {
return ( return (
<ImageListItem <ImageListItem
item={item} item={item}
@ -58,12 +55,13 @@ export default class CardList extends React.Component<PropsType> {
width={this.horizontalItemSize} width={this.horizontalItemSize}
/> />
); );
}
return <CardListItem item={item} key={item.title} />; return <CardListItem item={item} key={item.title} />;
}; };
keyExtractor = (item: ServiceItemType): string => item.key; keyExtractor = (item: ServiceItemType): string => item.key;
render(): React.Node { render() {
const {props} = this; const {props} = this;
let containerStyle = {}; let containerStyle = {};
if (props.isHorizontal) { if (props.isHorizontal) {
@ -84,7 +82,7 @@ export default class CardList extends React.Component<PropsType> {
} }
pagingEnabled={props.isHorizontal} pagingEnabled={props.isHorizontal}
snapToInterval={ snapToInterval={
props.isHorizontal ? (this.horizontalItemSize + 5) * 3 : null props.isHorizontal ? (this.horizontalItemSize + 5) * 3 : undefined
} }
/> />
); );

View file

@ -17,45 +17,38 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/ */
// @flow
import * as React from 'react'; import * as React from 'react';
import {Caption, Card, Paragraph, TouchableRipple} from 'react-native-paper'; import {Caption, Card, Paragraph, TouchableRipple} from 'react-native-paper';
import {View} from 'react-native'; import {View} from 'react-native';
import type {ServiceItemType} from '../../../managers/ServicesManager'; import type {ServiceItemType} from '../../../managers/ServicesManager';
type PropsType = { type PropsType = {
item: ServiceItemType, item: ServiceItemType;
}; };
export default class CardListItem extends React.Component<PropsType> { function CardListItem(props: PropsType) {
shouldComponentUpdate(): boolean { const {item} = props;
return false; const source =
} typeof item.image === 'number' ? item.image : {uri: item.image};
return (
render(): React.Node { <Card
const {props} = this; style={{
const {item} = props; width: '40%',
const source = margin: 5,
typeof item.image === 'number' ? item.image : {uri: item.image}; marginLeft: 'auto',
return ( marginRight: 'auto',
<Card }}>
style={{ <TouchableRipple style={{flex: 1}} onPress={item.onPress}>
width: '40%', <View>
margin: 5, <Card.Cover style={{height: 80}} source={source} />
marginLeft: 'auto', <Card.Content>
marginRight: 'auto', <Paragraph>{item.title}</Paragraph>
}}> <Caption>{item.subtitle}</Caption>
<TouchableRipple style={{flex: 1}} onPress={item.onPress}> </Card.Content>
<View> </View>
<Card.Cover style={{height: 80}} source={source} /> </TouchableRipple>
<Card.Content> </Card>
<Paragraph>{item.title}</Paragraph> );
<Caption>{item.subtitle}</Caption>
</Card.Content>
</View>
</TouchableRipple>
</Card>
);
}
} }
export default React.memo(CardListItem, () => true);

View file

@ -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>
);
}
}

View 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);

View file

@ -17,8 +17,6 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/ */
// @flow
import * as React from 'react'; import * as React from 'react';
import {Card, Chip, List, Text} from 'react-native-paper'; import {Card, Chip, List, Text} from 'react-native-paper';
import {StyleSheet, View} from 'react-native'; import {StyleSheet, View} from 'react-native';
@ -26,12 +24,11 @@ import i18n from 'i18n-js';
import AnimatedAccordion from '../../Animations/AnimatedAccordion'; import AnimatedAccordion from '../../Animations/AnimatedAccordion';
import {isItemInCategoryFilter} from '../../../utils/Search'; import {isItemInCategoryFilter} from '../../../utils/Search';
import type {ClubCategoryType} from '../../../screens/Amicale/Clubs/ClubListScreen'; import type {ClubCategoryType} from '../../../screens/Amicale/Clubs/ClubListScreen';
import type {ListIconPropsType} from '../../../constants/PaperStyles';
type PropsType = { type PropsType = {
categories: Array<ClubCategoryType>, categories: Array<ClubCategoryType>;
onChipSelect: (id: number) => void, onChipSelect: (id: number) => void;
selectedCategories: Array<number>, selectedCategories: Array<number>;
}; };
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@ -54,16 +51,8 @@ const styles = StyleSheet.create({
}, },
}); });
class ClubListHeader extends React.Component<PropsType> { function ClubListHeader(props: PropsType) {
shouldComponentUpdate(nextProps: PropsType): boolean { const getChipRender = (category: ClubCategoryType, key: string) => {
const {props} = this;
return (
nextProps.selectedCategories.length !== props.selectedCategories.length
);
}
getChipRender = (category: ClubCategoryType, key: string): React.Node => {
const {props} = this;
const onPress = (): void => props.onChipSelect(category.id); const onPress = (): void => props.onChipSelect(category.id);
return ( return (
<Chip <Chip
@ -80,32 +69,39 @@ class ClubListHeader extends React.Component<PropsType> {
); );
}; };
getCategoriesRender(): React.Node { const getCategoriesRender = () => {
const {props} = this; const final: Array<React.ReactNode> = [];
const final = [];
props.categories.forEach((cat: ClubCategoryType) => { props.categories.forEach((cat: ClubCategoryType) => {
final.push(this.getChipRender(cat, cat.id.toString())); final.push(getChipRender(cat, cat.id.toString()));
}); });
return final; return final;
} };
render(): React.Node { return (
return ( <Card style={styles.card}>
<Card style={styles.card}> <AnimatedAccordion
<AnimatedAccordion title={i18n.t('screens.clubs.categories')}
title={i18n.t('screens.clubs.categories')} left={(iconProps) => (
left={(props: ListIconPropsType): React.Node => ( <List.Icon
<List.Icon color={props.color} style={props.style} icon="star" /> color={iconProps.color}
)} style={iconProps.style}
opened> icon="star"
<Text style={styles.text}> />
{i18n.t('screens.clubs.categoriesFilterMessage')} )}
</Text> opened>
<View style={styles.chipContainer}>{this.getCategoriesRender()}</View> <Text style={styles.text}>
</AnimatedAccordion> {i18n.t('screens.clubs.categoriesFilterMessage')}
</Card> </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);

View file

@ -17,8 +17,6 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/ */
// @flow
import * as React from 'react'; import * as React from 'react';
import {Avatar, Chip, List, withTheme} from 'react-native-paper'; import {Avatar, Chip, List, withTheme} from 'react-native-paper';
import {View} from 'react-native'; import {View} from 'react-native';
@ -26,14 +24,13 @@ import type {
ClubCategoryType, ClubCategoryType,
ClubType, ClubType,
} from '../../../screens/Amicale/Clubs/ClubListScreen'; } from '../../../screens/Amicale/Clubs/ClubListScreen';
import type {CustomThemeType} from '../../../managers/ThemeManager';
type PropsType = { type PropsType = {
onPress: () => void, onPress: () => void;
categoryTranslator: (id: number) => ClubCategoryType, categoryTranslator: (id: number) => ClubCategoryType;
item: ClubType, item: ClubType;
height: number, height: number;
theme: CustomThemeType, theme: ReactNativePaper.Theme;
}; };
class ClubListItem extends React.Component<PropsType> { class ClubListItem extends React.Component<PropsType> {
@ -48,9 +45,9 @@ class ClubListItem extends React.Component<PropsType> {
return false; return false;
} }
getCategoriesRender(categories: Array<number | null>): React.Node { getCategoriesRender(categories: Array<number | null>) {
const {props} = this; const {props} = this;
const final = []; const final: Array<React.ReactNode> = [];
categories.forEach((cat: number | null) => { categories.forEach((cat: number | null) => {
if (cat != null) { if (cat != null) {
const category: ClubCategoryType = props.categoryTranslator(cat); const category: ClubCategoryType = props.categoryTranslator(cat);
@ -66,9 +63,9 @@ class ClubListItem extends React.Component<PropsType> {
return <View style={{flexDirection: 'row'}}>{final}</View>; return <View style={{flexDirection: 'row'}}>{final}</View>;
} }
render(): React.Node { render() {
const {props} = this; const {props} = this;
const categoriesRender = (): React.Node => const categoriesRender = () =>
this.getCategoriesRender(props.item.category); this.getCategoriesRender(props.item.category);
const {colors} = props.theme; const {colors} = props.theme;
return ( return (
@ -76,7 +73,7 @@ class ClubListItem extends React.Component<PropsType> {
title={props.item.name} title={props.item.name}
description={categoriesRender} description={categoriesRender}
onPress={props.onPress} onPress={props.onPress}
left={(): React.Node => ( left={() => (
<Avatar.Image <Avatar.Image
style={{ style={{
backgroundColor: 'transparent', backgroundColor: 'transparent',
@ -87,7 +84,7 @@ class ClubListItem extends React.Component<PropsType> {
source={{uri: props.item.logo}} source={{uri: props.item.logo}}
/> />
)} )}
right={(): React.Node => ( right={() => (
<Avatar.Icon <Avatar.Icon
style={{ style={{
marginTop: 'auto', marginTop: 'auto',

View file

@ -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);

View 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;

View file

@ -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);

View 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);

View file

@ -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);

View 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 {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;

View file

@ -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);

View 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);

View file

@ -17,8 +17,6 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/ */
// @flow
import * as React from 'react'; import * as React from 'react';
import {List, withTheme} from 'react-native-paper'; import {List, withTheme} from 'react-native-paper';
import {FlatList, View} from 'react-native'; import {FlatList, View} from 'react-native';
@ -29,17 +27,15 @@ import type {
PlanexGroupType, PlanexGroupType,
PlanexGroupCategoryType, PlanexGroupCategoryType,
} from '../../../screens/Planex/GroupSelectionScreen'; } from '../../../screens/Planex/GroupSelectionScreen';
import type {CustomThemeType} from '../../../managers/ThemeManager';
import type {ListIconPropsType} from '../../../constants/PaperStyles';
type PropsType = { type PropsType = {
item: PlanexGroupCategoryType, item: PlanexGroupCategoryType;
favorites: Array<PlanexGroupType>, favorites: Array<PlanexGroupType>;
onGroupPress: (PlanexGroupType) => void, onGroupPress: (data: PlanexGroupType) => void;
onFavoritePress: (PlanexGroupType) => void, onFavoritePress: (data: PlanexGroupType) => void;
currentSearchString: string, currentSearchString: string;
height: number, height: number;
theme: CustomThemeType, theme: ReactNativePaper.Theme;
}; };
const LIST_ITEM_HEIGHT = 64; 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 {props} = this;
const onPress = () => { const onPress = () => {
props.onGroupPress(item); props.onGroupPress(item);
@ -77,18 +73,19 @@ class GroupListAccordion extends React.Component<PropsType> {
getData(): Array<PlanexGroupType> { getData(): Array<PlanexGroupType> {
const {props} = this; const {props} = this;
const originalData = props.item.content; const originalData = props.item.content;
const displayData = []; const displayData: Array<PlanexGroupType> = [];
originalData.forEach((data: PlanexGroupType) => { originalData.forEach((data: PlanexGroupType) => {
if (stringMatchQuery(data.name, props.currentSearchString)) if (stringMatchQuery(data.name, props.currentSearchString)) {
displayData.push(data); displayData.push(data);
}
}); });
return displayData; return displayData;
} }
itemLayout = ( itemLayout = (
data: ?Array<PlanexGroupType>, data: Array<PlanexGroupType> | null | undefined,
index: number, index: number,
): {length: number, offset: number, index: number} => ({ ): {length: number; offset: number; index: number} => ({
length: LIST_ITEM_HEIGHT, length: LIST_ITEM_HEIGHT,
offset: LIST_ITEM_HEIGHT * index, offset: LIST_ITEM_HEIGHT * index,
index, index,
@ -96,7 +93,7 @@ class GroupListAccordion extends React.Component<PropsType> {
keyExtractor = (item: PlanexGroupType): string => item.id.toString(); keyExtractor = (item: PlanexGroupType): string => item.id.toString();
render(): React.Node { render() {
const {props} = this; const {props} = this;
const {item} = this.props; const {item} = this.props;
return ( return (
@ -107,7 +104,7 @@ class GroupListAccordion extends React.Component<PropsType> {
height: props.height, height: props.height,
justifyContent: 'center', justifyContent: 'center',
}} }}
left={(iconProps: ListIconPropsType): React.Node => left={(iconProps) =>
item.id === 0 ? ( item.id === 0 ? (
<List.Icon <List.Icon
style={iconProps.style} style={iconProps.style}

View file

@ -17,23 +17,19 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/ */
// @flow
import * as React from 'react'; import * as React from 'react';
import {List, TouchableRipple, withTheme} from 'react-native-paper'; import {List, TouchableRipple, withTheme} from 'react-native-paper';
import * as Animatable from 'react-native-animatable'; import * as Animatable from 'react-native-animatable';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import type {CustomThemeType} from '../../../managers/ThemeManager';
import type {PlanexGroupType} from '../../../screens/Planex/GroupSelectionScreen'; import type {PlanexGroupType} from '../../../screens/Planex/GroupSelectionScreen';
import type {ListIconPropsType} from '../../../constants/PaperStyles';
type PropsType = { type PropsType = {
theme: CustomThemeType, theme: ReactNativePaper.Theme;
onPress: () => void, onPress: () => void;
onStarPress: () => void, onStarPress: () => void;
item: PlanexGroupType, item: PlanexGroupType;
favorites: Array<PlanexGroupType>, favorites: Array<PlanexGroupType>;
height: number, height: number;
}; };
const REPLACE_REGEX = /_/g; const REPLACE_REGEX = /_/g;
@ -41,10 +37,11 @@ const REPLACE_REGEX = /_/g;
class GroupListItem extends React.Component<PropsType> { class GroupListItem extends React.Component<PropsType> {
isFav: boolean; isFav: boolean;
starRef: null | Animatable.View; starRef: {current: null | Animatable.View};
constructor(props: PropsType) { constructor(props: PropsType) {
super(props); super(props);
this.starRef = React.createRef<Animatable.View>();
this.isFav = this.isGroupInFavorites(props.favorites); this.isFav = this.isGroupInFavorites(props.favorites);
} }
@ -52,7 +49,9 @@ class GroupListItem extends React.Component<PropsType> {
const {favorites} = this.props; const {favorites} = this.props;
const favChanged = favorites.length !== nextProps.favorites.length; const favChanged = favorites.length !== nextProps.favorites.length;
let newFavState = this.isFav; let newFavState = this.isFav;
if (favChanged) newFavState = this.isGroupInFavorites(nextProps.favorites); if (favChanged) {
newFavState = this.isGroupInFavorites(nextProps.favorites);
}
const shouldUpdate = this.isFav !== newFavState; const shouldUpdate = this.isFav !== newFavState;
this.isFav = newFavState; this.isFav = newFavState;
return shouldUpdate; return shouldUpdate;
@ -61,9 +60,12 @@ class GroupListItem extends React.Component<PropsType> {
onStarPress = () => { onStarPress = () => {
const {props} = this; const {props} = this;
const ref = this.starRef; const ref = this.starRef;
if (ref != null) { if (ref.current) {
if (this.isFav) ref.rubberBand(); if (this.isFav) {
else ref.swing(); ref.current.rubberBand();
} else {
ref.current.swing();
}
} }
props.onStarPress(); props.onStarPress();
}; };
@ -71,31 +73,29 @@ class GroupListItem extends React.Component<PropsType> {
isGroupInFavorites(favorites: Array<PlanexGroupType>): boolean { isGroupInFavorites(favorites: Array<PlanexGroupType>): boolean {
const {item} = this.props; const {item} = this.props;
for (let i = 0; i < favorites.length; i += 1) { 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; return false;
} }
render(): React.Node { render() {
const {props} = this; const {props} = this;
const {colors} = props.theme; const {colors} = props.theme;
return ( return (
<List.Item <List.Item
title={props.item.name.replace(REPLACE_REGEX, ' ')} title={props.item.name.replace(REPLACE_REGEX, ' ')}
onPress={props.onPress} onPress={props.onPress}
left={(iconProps: ListIconPropsType): React.Node => ( left={(iconProps) => (
<List.Icon <List.Icon
color={iconProps.color} color={iconProps.color}
style={iconProps.style} style={iconProps.style}
icon="chevron-right" icon="chevron-right"
/> />
)} )}
right={(iconProps: ListIconPropsType): React.Node => ( right={(iconProps) => (
<Animatable.View <Animatable.View ref={this.starRef} useNativeDriver>
ref={(ref: Animatable.View) => {
this.starRef = ref;
}}
useNativeDriver>
<TouchableRipple <TouchableRipple
onPress={this.onStarPress} onPress={this.onStarPress}
style={{ style={{

View file

@ -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);

View 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);

View file

@ -17,8 +17,6 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/ */
// @flow
import * as React from 'react'; import * as React from 'react';
import { import {
Avatar, Avatar,
@ -34,20 +32,19 @@ import i18n from 'i18n-js';
import * as Animatable from 'react-native-animatable'; import * as Animatable from 'react-native-animatable';
import ProxiwashConstants from '../../../constants/ProxiwashConstants'; import ProxiwashConstants from '../../../constants/ProxiwashConstants';
import AprilFoolsManager from '../../../managers/AprilFoolsManager'; import AprilFoolsManager from '../../../managers/AprilFoolsManager';
import type {CustomThemeType} from '../../../managers/ThemeManager';
import type {ProxiwashMachineType} from '../../../screens/Proxiwash/ProxiwashScreen'; import type {ProxiwashMachineType} from '../../../screens/Proxiwash/ProxiwashScreen';
type PropsType = { type PropsType = {
item: ProxiwashMachineType, item: ProxiwashMachineType;
theme: CustomThemeType, theme: ReactNativePaper.Theme;
onPress: ( onPress: (
title: string, title: string,
item: ProxiwashMachineType, item: ProxiwashMachineType,
isDryer: boolean, isDryer: boolean,
) => void, ) => void;
isWatched: boolean, isWatched: boolean;
isDryer: boolean, isDryer: boolean;
height: number, height: number;
}; };
const AnimatedIcon = Animatable.createAnimatableComponent(Avatar.Icon); const AnimatedIcon = Animatable.createAnimatableComponent(Avatar.Icon);
@ -89,10 +86,11 @@ class ProxiwashListItem extends React.Component<PropsType> {
let displayNumber = props.item.number; let displayNumber = props.item.number;
const displayMaxWeight = props.item.maxWeight; const displayMaxWeight = props.item.maxWeight;
if (AprilFoolsManager.getInstance().isAprilFoolsEnabled()) if (AprilFoolsManager.getInstance().isAprilFoolsEnabled()) {
displayNumber = AprilFoolsManager.getProxiwashMachineDisplayNumber( displayNumber = AprilFoolsManager.getProxiwashMachineDisplayNumber(
parseInt(props.item.number, 10), parseInt(props.item.number, 10),
); );
}
this.title = props.isDryer this.title = props.isDryer
? i18n.t('screens.proxiwash.dryer') ? i18n.t('screens.proxiwash.dryer')
@ -159,10 +157,10 @@ class ProxiwashListItem extends React.Component<PropsType> {
colors.proxiwashUnknownColor; colors.proxiwashUnknownColor;
} }
render(): React.Node { render() {
const {props} = this; const {props} = this;
const {colors} = props.theme; const {colors} = props.theme;
const machineState = props.item.state; const machineState = parseInt(props.item.state, 10);
const isRunning = machineState === ProxiwashConstants.machineStates.RUNNING; const isRunning = machineState === ProxiwashConstants.machineStates.RUNNING;
const isReady = machineState === ProxiwashConstants.machineStates.AVAILABLE; const isReady = machineState === ProxiwashConstants.machineStates.AVAILABLE;
const description = isRunning const description = isRunning
@ -171,10 +169,13 @@ class ProxiwashListItem extends React.Component<PropsType> {
const stateIcon = ProxiwashConstants.stateIcons[machineState]; const stateIcon = ProxiwashConstants.stateIcons[machineState];
const stateString = this.stateStrings[machineState]; const stateString = this.stateStrings[machineState];
let progress; let progress;
if (isRunning && props.item.donePercent !== '') if (isRunning && props.item.donePercent !== '') {
progress = parseFloat(props.item.donePercent) / 100; progress = parseFloat(props.item.donePercent) / 100;
else if (isRunning) progress = 0; } else if (isRunning) {
else progress = 1; progress = 0;
} else {
progress = 1;
}
const icon = props.isWatched ? ( const icon = props.isWatched ? (
<AnimatedIcon <AnimatedIcon
@ -224,8 +225,8 @@ class ProxiwashListItem extends React.Component<PropsType> {
justifyContent: 'center', justifyContent: 'center',
}} }}
onPress={this.onListItemPress} onPress={this.onListItemPress}
left={(): React.Node => icon} left={() => icon}
right={(): React.Node => ( right={() => (
<View style={{flexDirection: 'row'}}> <View style={{flexDirection: 'row'}}>
<View style={{justifyContent: 'center'}}> <View style={{justifyContent: 'center'}}>
<Text <Text

View file

@ -17,19 +17,16 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/ */
// @flow
import * as React from 'react'; import * as React from 'react';
import {Avatar, Text, withTheme} from 'react-native-paper'; import {Avatar, Text, withTheme} from 'react-native-paper';
import {StyleSheet, View} from 'react-native'; import {StyleSheet, View} from 'react-native';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import type {CustomThemeType} from '../../../managers/ThemeManager';
type PropsType = { type PropsType = {
theme: CustomThemeType, theme: ReactNativePaper.Theme;
title: string, title: string;
isDryer: boolean, isDryer: boolean;
nbAvailable: number, nbAvailable: number;
}; };
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@ -61,7 +58,7 @@ class ProxiwashListItem extends React.Component<PropsType> {
); );
} }
render(): React.Node { render() {
const {props} = this; const {props} = this;
const subtitle = `${props.nbAvailable} ${ const subtitle = `${props.nbAvailable} ${
props.nbAvailable <= 1 props.nbAvailable <= 1

View file

@ -94,7 +94,7 @@ export type ServiceItemType = {
key: string; key: string;
title: string; title: string;
subtitle: string; subtitle: string;
image: string; image: string | number;
onPress: () => void; onPress: () => void;
badgeFunction?: (dashboard: FullDashboardType) => number; badgeFunction?: (dashboard: FullDashboardType) => number;
}; };