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/>.
|
* 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
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
|
@ -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);
|
|
@ -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/>.
|
* 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);
|
|
@ -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',
|
|
@ -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/>.
|
* 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}
|
|
@ -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={{
|
|
@ -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/>.
|
* 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
|
|
@ -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
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue