Update proxiwash screens to use TypeScript

This commit is contained in:
Arnaud Vergnet 2020-09-22 22:29:39 +02:00
parent b78357968a
commit 9f4dcda7d9
5 changed files with 192 additions and 206 deletions

View file

@ -38,6 +38,7 @@ import CollapsibleSectionList from '../Collapsible/CollapsibleSectionList';
export type SectionListDataType<ItemT> = Array<{ export type SectionListDataType<ItemT> = Array<{
title: string; title: string;
icon?: string;
data: Array<ItemT>; data: Array<ItemT>;
keyExtractor?: (data: ItemT) => string; keyExtractor?: (data: ItemT) => string;
}>; }>;

View file

@ -17,16 +17,17 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/ */
export enum MachineStates {
AVAILABLE,
RUNNING,
RUNNING_NOT_STARTED,
FINISHED,
UNAVAILABLE,
ERROR,
UNKNOWN,
}
export default { export default {
machineStates: {
AVAILABLE: 0,
RUNNING: 1,
RUNNING_NOT_STARTED: 2,
FINISHED: 3,
UNAVAILABLE: 4,
ERROR: 5,
UNKNOWN: 6,
},
stateIcons: [ stateIcons: [
'radiobox-blank', 'radiobox-blank',
'progress-check', 'progress-check',

View file

@ -1,122 +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, View} from 'react-native';
import i18n from 'i18n-js';
import {Card, Avatar, Paragraph, Title} from 'react-native-paper';
import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrollView';
import type {CardTitleIconPropsType} from '../../constants/PaperStyles';
import ProxiwashConstants from '../../constants/ProxiwashConstants';
const LOGO = 'https://etud.insa-toulouse.fr/~amicale_app/images/Proxiwash.png';
export type LaundromatType = {
id: string,
title: string,
subtitle: string,
description: string,
tarif: string,
paymentMethods: string,
icon: string,
url: string,
};
/**
* Class defining the proxiwash about screen.
*/
export default class ProxiwashAboutScreen extends React.Component<null> {
static getCardItem(item: LaundromatType): React.Node {
return (
<Card style={{margin: 5}}>
<Card.Title
title={i18n.t(item.title)}
subtitle={i18n.t(item.subtitle)}
left={(iconProps: CardTitleIconPropsType): React.Node => (
<Avatar.Icon size={iconProps.size} icon={item.icon} />
)}
/>
<Card.Content>
<Paragraph>{i18n.t(item.description)}</Paragraph>
<Title>{i18n.t('screens.proxiwash.tariffs')}</Title>
<Paragraph>{i18n.t(item.tarif)}</Paragraph>
<Title>{i18n.t('screens.proxiwash.paymentMethods')}</Title>
<Paragraph>{i18n.t(item.paymentMethods)}</Paragraph>
</Card.Content>
</Card>
);
}
render(): React.Node {
return (
<CollapsibleScrollView style={{padding: 5}} hasTab>
<View
style={{
width: '100%',
height: 100,
marginTop: 20,
marginBottom: 20,
justifyContent: 'center',
alignItems: 'center',
}}>
<Image
source={{uri: LOGO}}
style={{height: '100%', width: '100%', resizeMode: 'contain'}}
/>
</View>
{ProxiwashAboutScreen.getCardItem(ProxiwashConstants.washinsa)}
{ProxiwashAboutScreen.getCardItem(ProxiwashConstants.tripodeB)}
<Card style={{margin: 5}}>
<Card.Title
title={i18n.t('screens.proxiwash.dryer')}
left={(iconProps: CardTitleIconPropsType): React.Node => (
<Avatar.Icon size={iconProps.size} icon="tumble-dryer" />
)}
/>
<Card.Content>
<Title>{i18n.t('screens.proxiwash.procedure')}</Title>
<Paragraph>{i18n.t('screens.proxiwash.dryerProcedure')}</Paragraph>
<Title>{i18n.t('screens.proxiwash.tips')}</Title>
<Paragraph>{i18n.t('screens.proxiwash.dryerTips')}</Paragraph>
</Card.Content>
</Card>
<Card style={{margin: 5}}>
<Card.Title
title={i18n.t('screens.proxiwash.washer')}
left={(iconProps: CardTitleIconPropsType): React.Node => (
<Avatar.Icon size={iconProps.size} icon="washing-machine" />
)}
/>
<Card.Content>
<Title>{i18n.t('screens.proxiwash.procedure')}</Title>
<Paragraph>{i18n.t('screens.proxiwash.washerProcedure')}</Paragraph>
<Title>{i18n.t('screens.proxiwash.tips')}</Title>
<Paragraph>{i18n.t('screens.proxiwash.washerTips')}</Paragraph>
</Card.Content>
</Card>
</CollapsibleScrollView>
);
}
}

View file

@ -0,0 +1,117 @@
/*
* 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, View} from 'react-native';
import i18n from 'i18n-js';
import {Card, Avatar, Paragraph, Title} from 'react-native-paper';
import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrollView';
import ProxiwashConstants from '../../constants/ProxiwashConstants';
const LOGO = 'https://etud.insa-toulouse.fr/~amicale_app/images/Proxiwash.png';
export type LaundromatType = {
id: string;
title: string;
subtitle: string;
description: string;
tarif: string;
paymentMethods: string;
icon: string;
url: string;
};
function getCardItem(item: LaundromatType) {
return (
<Card style={{margin: 5}}>
<Card.Title
title={i18n.t(item.title)}
subtitle={i18n.t(item.subtitle)}
left={(iconProps) => (
<Avatar.Icon size={iconProps.size} icon={item.icon} />
)}
/>
<Card.Content>
<Paragraph>{i18n.t(item.description)}</Paragraph>
<Title>{i18n.t('screens.proxiwash.tariffs')}</Title>
<Paragraph>{i18n.t(item.tarif)}</Paragraph>
<Title>{i18n.t('screens.proxiwash.paymentMethods')}</Title>
<Paragraph>{i18n.t(item.paymentMethods)}</Paragraph>
</Card.Content>
</Card>
);
}
/**
* Class defining the proxiwash about screen.
*/
export default function ProxiwashAboutScreen() {
return (
<CollapsibleScrollView style={{padding: 5}} hasTab>
<View
style={{
width: '100%',
height: 100,
marginTop: 20,
marginBottom: 20,
justifyContent: 'center',
alignItems: 'center',
}}>
<Image
source={{uri: LOGO}}
style={{height: '100%', width: '100%', resizeMode: 'contain'}}
/>
</View>
{getCardItem(ProxiwashConstants.washinsa)}
{getCardItem(ProxiwashConstants.tripodeB)}
<Card style={{margin: 5}}>
<Card.Title
title={i18n.t('screens.proxiwash.dryer')}
left={(iconProps) => (
<Avatar.Icon size={iconProps.size} icon="tumble-dryer" />
)}
/>
<Card.Content>
<Title>{i18n.t('screens.proxiwash.procedure')}</Title>
<Paragraph>{i18n.t('screens.proxiwash.dryerProcedure')}</Paragraph>
<Title>{i18n.t('screens.proxiwash.tips')}</Title>
<Paragraph>{i18n.t('screens.proxiwash.dryerTips')}</Paragraph>
</Card.Content>
</Card>
<Card style={{margin: 5}}>
<Card.Title
title={i18n.t('screens.proxiwash.washer')}
left={(iconProps) => (
<Avatar.Icon size={iconProps.size} icon="washing-machine" />
)}
/>
<Card.Content>
<Title>{i18n.t('screens.proxiwash.procedure')}</Title>
<Paragraph>{i18n.t('screens.proxiwash.washerProcedure')}</Paragraph>
<Title>{i18n.t('screens.proxiwash.tips')}</Title>
<Paragraph>{i18n.t('screens.proxiwash.washerTips')}</Paragraph>
</Card.Content>
</Card>
</CollapsibleScrollView>
);
}

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 {Alert, View} from 'react-native'; import {Alert, View} from 'react-native';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
@ -29,14 +27,15 @@ import WebSectionList from '../../components/Screens/WebSectionList';
import * as Notifications from '../../utils/Notifications'; import * as Notifications from '../../utils/Notifications';
import AsyncStorageManager from '../../managers/AsyncStorageManager'; import AsyncStorageManager from '../../managers/AsyncStorageManager';
import ProxiwashListItem from '../../components/Lists/Proxiwash/ProxiwashListItem'; import ProxiwashListItem from '../../components/Lists/Proxiwash/ProxiwashListItem';
import ProxiwashConstants from '../../constants/ProxiwashConstants'; import ProxiwashConstants, {
MachineStates,
} from '../../constants/ProxiwashConstants';
import CustomModal from '../../components/Overrides/CustomModal'; import CustomModal from '../../components/Overrides/CustomModal';
import AprilFoolsManager from '../../managers/AprilFoolsManager'; import AprilFoolsManager from '../../managers/AprilFoolsManager';
import MaterialHeaderButtons, { import MaterialHeaderButtons, {
Item, Item,
} from '../../components/Overrides/CustomHeaderButton'; } from '../../components/Overrides/CustomHeaderButton';
import ProxiwashSectionHeader from '../../components/Lists/Proxiwash/ProxiwashSectionHeader'; import ProxiwashSectionHeader from '../../components/Lists/Proxiwash/ProxiwashSectionHeader';
import type {CustomThemeType} from '../../managers/ThemeManager';
import { import {
getCleanedMachineWatched, getCleanedMachineWatched,
getMachineEndDate, getMachineEndDate,
@ -47,31 +46,41 @@ import MascotPopup from '../../components/Mascot/MascotPopup';
import type {SectionListDataType} from '../../components/Screens/WebSectionList'; import type {SectionListDataType} from '../../components/Screens/WebSectionList';
import type {LaundromatType} from './ProxiwashAboutScreen'; import type {LaundromatType} from './ProxiwashAboutScreen';
const modalStateStrings = {}; const modalStateStrings: {[key in MachineStates]: string} = {
[MachineStates.AVAILABLE]: i18n.t('screens.proxiwash.modal.ready'),
[MachineStates.RUNNING]: i18n.t('screens.proxiwash.modal.running'),
[MachineStates.RUNNING_NOT_STARTED]: i18n.t(
'screens.proxiwash.modal.runningNotStarted',
),
[MachineStates.FINISHED]: i18n.t('screens.proxiwash.modal.finished'),
[MachineStates.UNAVAILABLE]: i18n.t('screens.proxiwash.modal.broken'),
[MachineStates.ERROR]: i18n.t('screens.proxiwash.modal.error'),
[MachineStates.UNKNOWN]: i18n.t('screens.proxiwash.modal.unknown'),
};
const REFRESH_TIME = 1000 * 10; // Refresh every 10 seconds const REFRESH_TIME = 1000 * 10; // Refresh every 10 seconds
const LIST_ITEM_HEIGHT = 64; const LIST_ITEM_HEIGHT = 64;
export type ProxiwashMachineType = { export type ProxiwashMachineType = {
number: string, number: string;
state: string, state: MachineStates;
maxWeight: number, maxWeight: number;
startTime: string, startTime: string;
endTime: string, endTime: string;
donePercent: string, donePercent: string;
remainingTime: string, remainingTime: string;
program: string, program: string;
}; };
type PropsType = { type PropsType = {
navigation: StackNavigationProp, navigation: StackNavigationProp<any>;
theme: CustomThemeType, theme: ReactNativePaper.Theme;
}; };
type StateType = { type StateType = {
modalCurrentDisplayItem: React.Node, modalCurrentDisplayItem: React.ReactNode;
machinesWatched: Array<ProxiwashMachineType>, machinesWatched: Array<ProxiwashMachineType>;
selectedWash: string, selectedWash: string;
}; };
/** /**
@ -92,15 +101,17 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
modalRef: null | Modalize; modalRef: null | Modalize;
fetchedData: { fetchedData: {
dryers: Array<ProxiwashMachineType>, dryers: Array<ProxiwashMachineType>;
washers: Array<ProxiwashMachineType>, washers: Array<ProxiwashMachineType>;
}; };
/** /**
* Creates machine state parameters using current theme and translations * Creates machine state parameters using current theme and translations
*/ */
constructor() { constructor(props: PropsType) {
super(); super(props);
this.modalRef = null;
this.fetchedData = {dryers: [], washers: []};
this.state = { this.state = {
modalCurrentDisplayItem: null, modalCurrentDisplayItem: null,
machinesWatched: AsyncStorageManager.getObject( machinesWatched: AsyncStorageManager.getObject(
@ -110,27 +121,6 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
AsyncStorageManager.PREFERENCES.selectedWash.key, AsyncStorageManager.PREFERENCES.selectedWash.key,
), ),
}; };
modalStateStrings[ProxiwashConstants.machineStates.AVAILABLE] = i18n.t(
'screens.proxiwash.modal.ready',
);
modalStateStrings[ProxiwashConstants.machineStates.RUNNING] = i18n.t(
'screens.proxiwash.modal.running',
);
modalStateStrings[
ProxiwashConstants.machineStates.RUNNING_NOT_STARTED
] = i18n.t('screens.proxiwash.modal.runningNotStarted');
modalStateStrings[ProxiwashConstants.machineStates.FINISHED] = i18n.t(
'screens.proxiwash.modal.finished',
);
modalStateStrings[ProxiwashConstants.machineStates.UNAVAILABLE] = i18n.t(
'screens.proxiwash.modal.broken',
);
modalStateStrings[ProxiwashConstants.machineStates.ERROR] = i18n.t(
'screens.proxiwash.modal.error',
);
modalStateStrings[ProxiwashConstants.machineStates.UNKNOWN] = i18n.t(
'screens.proxiwash.modal.unknown',
);
} }
/** /**
@ -139,12 +129,12 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
componentDidMount() { componentDidMount() {
const {navigation} = this.props; const {navigation} = this.props;
navigation.setOptions({ navigation.setOptions({
headerRight: (): React.Node => ( headerRight: () => (
<MaterialHeaderButtons> <MaterialHeaderButtons>
<Item <Item
title="switch" title="switch"
iconName="swap-horizontal" iconName="swap-horizontal"
onPress={():void => navigation.navigate('settings')} onPress={(): void => navigation.navigate('settings')}
/> />
<Item <Item
title="information" title="information"
@ -164,10 +154,10 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
); );
if (selected !== state.selectedWash) { if (selected !== state.selectedWash) {
this.setState({ this.setState({
selectedWash: selected selectedWash: selected,
}); });
} }
} };
/** /**
* Callback used when pressing the about button. * Callback used when pressing the about button.
@ -208,29 +198,27 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
* @param isDryer True if the given item is a dryer * @param isDryer True if the given item is a dryer
* @return {*} * @return {*}
*/ */
getModalContent( getModalContent(title: string, item: ProxiwashMachineType, isDryer: boolean) {
title: string,
item: ProxiwashMachineType,
isDryer: boolean,
): React.Node {
const {props, state} = this; const {props, state} = this;
let button = { let button: {text: string; icon: string; onPress: () => void} = {
text: i18n.t('screens.proxiwash.modal.ok'), text: i18n.t('screens.proxiwash.modal.ok'),
icon: '', icon: '',
onPress: undefined, onPress: () => undefined,
}; };
let message = modalStateStrings[item.state]; let message = modalStateStrings[item.state];
const onPress = this.onSetupNotificationsPress.bind(this, item); const onPress = () => this.onSetupNotificationsPress(item);
if (item.state === ProxiwashConstants.machineStates.RUNNING) { if (item.state === MachineStates.RUNNING) {
let remainingTime = parseInt(item.remainingTime, 10); let remainingTime = parseInt(item.remainingTime, 10);
if (remainingTime < 0) remainingTime = 0; if (remainingTime < 0) {
remainingTime = 0;
}
button = { button = {
text: isMachineWatched(item, state.machinesWatched) text: isMachineWatched(item, state.machinesWatched)
? i18n.t('screens.proxiwash.modal.disableNotifications') ? i18n.t('screens.proxiwash.modal.disableNotifications')
: i18n.t('screens.proxiwash.modal.enableNotifications'), : i18n.t('screens.proxiwash.modal.enableNotifications'),
icon: '', icon: '',
onPress, onPress: onPress,
}; };
message = i18n.t('screens.proxiwash.modal.running', { message = i18n.t('screens.proxiwash.modal.running', {
start: item.startTime, start: item.startTime,
@ -247,7 +235,7 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
}}> }}>
<Card.Title <Card.Title
title={title} title={title}
left={(): React.Node => ( left={() => (
<Avatar.Icon <Avatar.Icon
icon={isDryer ? 'tumble-dryer' : 'washing-machine'} icon={isDryer ? 'tumble-dryer' : 'washing-machine'}
color={props.theme.colors.text} color={props.theme.colors.text}
@ -259,7 +247,7 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
<Text>{message}</Text> <Text>{message}</Text>
</Card.Content> </Card.Content>
{button.onPress !== undefined ? ( {button.onPress ? (
<Card.Actions> <Card.Actions>
<Button <Button
icon={button.icon} icon={button.icon}
@ -280,11 +268,7 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
* @param section The section to render * @param section The section to render
* @return {*} * @return {*}
*/ */
getRenderSectionHeader = ({ getRenderSectionHeader = ({section}: {section: {title: string}}) => {
section,
}: {
section: {title: string},
}): React.Node => {
const isDryer = section.title === i18n.t('screens.proxiwash.dryers'); const isDryer = section.title === i18n.t('screens.proxiwash.dryers');
const nbAvailable = this.getMachineAvailableNumber(isDryer); const nbAvailable = this.getMachineAvailableNumber(isDryer);
return ( return (
@ -307,9 +291,9 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
item, item,
section, section,
}: { }: {
item: ProxiwashMachineType, item: ProxiwashMachineType;
section: {title: string}, section: {title: string};
}): React.Node => { }) => {
const {machinesWatched} = this.state; const {machinesWatched} = this.state;
const isDryer = section.title === i18n.t('screens.proxiwash.dryers'); const isDryer = section.title === i18n.t('screens.proxiwash.dryers');
return ( return (
@ -369,12 +353,16 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
*/ */
getMachineAvailableNumber(isDryer: boolean): number { getMachineAvailableNumber(isDryer: boolean): number {
let data; let data;
if (isDryer) data = this.fetchedData.dryers; if (isDryer) {
else data = this.fetchedData.washers; data = this.fetchedData.dryers;
} else {
data = this.fetchedData.washers;
}
let count = 0; let count = 0;
data.forEach((machine: ProxiwashMachineType) => { data.forEach((machine: ProxiwashMachineType) => {
if (machine.state === ProxiwashConstants.machineStates.AVAILABLE) if (machine.state === MachineStates.AVAILABLE) {
count += 1; count += 1;
}
}); });
return count; return count;
} }
@ -386,8 +374,8 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
* @return {*} * @return {*}
*/ */
createDataset = (fetchedData: { createDataset = (fetchedData: {
dryers: Array<ProxiwashMachineType>, dryers: Array<ProxiwashMachineType>;
washers: Array<ProxiwashMachineType>, washers: Array<ProxiwashMachineType>;
}): SectionListDataType<ProxiwashMachineType> => { }): SectionListDataType<ProxiwashMachineType> => {
const {state} = this; const {state} = this;
let data = fetchedData; let data = fetchedData;
@ -466,8 +454,9 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
if ( if (
machine.number === selectedMachine.number && machine.number === selectedMachine.number &&
machine.endTime === selectedMachine.endTime machine.endTime === selectedMachine.endTime
) ) {
newList.splice(index, 1); newList.splice(index, 1);
}
}); });
this.saveNewWatchedList(newList); this.saveNewWatchedList(newList);
} }
@ -480,7 +469,7 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
); );
} }
render(): React.Node { render() {
const {state} = this; const {state} = this;
const {navigation} = this.props; const {navigation} = this.props;
let data: LaundromatType; let data: LaundromatType;