Compare commits
No commits in common. "38afbf02a383c152cd51e176ba38d8bf2b6e4584" and "b78357968a08d75ebbf478994ee7b9e4929869cb" have entirely different histories.
38afbf02a3
...
b78357968a
13 changed files with 444 additions and 431 deletions
|
|
@ -38,7 +38,6 @@ import CollapsibleSectionList from '../Collapsible/CollapsibleSectionList';
|
|||
|
||||
export type SectionListDataType<ItemT> = Array<{
|
||||
title: string;
|
||||
icon?: string;
|
||||
data: Array<ItemT>;
|
||||
keyExtractor?: (data: ItemT) => string;
|
||||
}>;
|
||||
|
|
|
|||
|
|
@ -17,17 +17,16 @@
|
|||
* 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 {
|
||||
machineStates: {
|
||||
AVAILABLE: 0,
|
||||
RUNNING: 1,
|
||||
RUNNING_NOT_STARTED: 2,
|
||||
FINISHED: 3,
|
||||
UNAVAILABLE: 4,
|
||||
ERROR: 5,
|
||||
UNKNOWN: 6,
|
||||
},
|
||||
stateIcons: [
|
||||
'radiobox-blank',
|
||||
'progress-check',
|
||||
|
|
|
|||
122
src/screens/Proxiwash/ProxiwashAboutScreen.js
Normal file
122
src/screens/Proxiwash/ProxiwashAboutScreen.js
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,117 +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/>.
|
||||
*/
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
|
@ -17,6 +17,8 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Alert, View} from 'react-native';
|
||||
import i18n from 'i18n-js';
|
||||
|
|
@ -27,15 +29,14 @@ import WebSectionList from '../../components/Screens/WebSectionList';
|
|||
import * as Notifications from '../../utils/Notifications';
|
||||
import AsyncStorageManager from '../../managers/AsyncStorageManager';
|
||||
import ProxiwashListItem from '../../components/Lists/Proxiwash/ProxiwashListItem';
|
||||
import ProxiwashConstants, {
|
||||
MachineStates,
|
||||
} from '../../constants/ProxiwashConstants';
|
||||
import ProxiwashConstants from '../../constants/ProxiwashConstants';
|
||||
import CustomModal from '../../components/Overrides/CustomModal';
|
||||
import AprilFoolsManager from '../../managers/AprilFoolsManager';
|
||||
import MaterialHeaderButtons, {
|
||||
Item,
|
||||
} from '../../components/Overrides/CustomHeaderButton';
|
||||
import ProxiwashSectionHeader from '../../components/Lists/Proxiwash/ProxiwashSectionHeader';
|
||||
import type {CustomThemeType} from '../../managers/ThemeManager';
|
||||
import {
|
||||
getCleanedMachineWatched,
|
||||
getMachineEndDate,
|
||||
|
|
@ -46,41 +47,31 @@ import MascotPopup from '../../components/Mascot/MascotPopup';
|
|||
import type {SectionListDataType} from '../../components/Screens/WebSectionList';
|
||||
import type {LaundromatType} from './ProxiwashAboutScreen';
|
||||
|
||||
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 modalStateStrings = {};
|
||||
|
||||
const REFRESH_TIME = 1000 * 10; // Refresh every 10 seconds
|
||||
const LIST_ITEM_HEIGHT = 64;
|
||||
|
||||
export type ProxiwashMachineType = {
|
||||
number: string;
|
||||
state: MachineStates;
|
||||
maxWeight: number;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
donePercent: string;
|
||||
remainingTime: string;
|
||||
program: string;
|
||||
number: string,
|
||||
state: string,
|
||||
maxWeight: number,
|
||||
startTime: string,
|
||||
endTime: string,
|
||||
donePercent: string,
|
||||
remainingTime: string,
|
||||
program: string,
|
||||
};
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp<any>;
|
||||
theme: ReactNativePaper.Theme;
|
||||
navigation: StackNavigationProp,
|
||||
theme: CustomThemeType,
|
||||
};
|
||||
|
||||
type StateType = {
|
||||
modalCurrentDisplayItem: React.ReactNode;
|
||||
machinesWatched: Array<ProxiwashMachineType>;
|
||||
selectedWash: string;
|
||||
modalCurrentDisplayItem: React.Node,
|
||||
machinesWatched: Array<ProxiwashMachineType>,
|
||||
selectedWash: string,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -101,17 +92,15 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
|
|||
modalRef: null | Modalize;
|
||||
|
||||
fetchedData: {
|
||||
dryers: Array<ProxiwashMachineType>;
|
||||
washers: Array<ProxiwashMachineType>;
|
||||
dryers: Array<ProxiwashMachineType>,
|
||||
washers: Array<ProxiwashMachineType>,
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates machine state parameters using current theme and translations
|
||||
*/
|
||||
constructor(props: PropsType) {
|
||||
super(props);
|
||||
this.modalRef = null;
|
||||
this.fetchedData = {dryers: [], washers: []};
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
modalCurrentDisplayItem: null,
|
||||
machinesWatched: AsyncStorageManager.getObject(
|
||||
|
|
@ -121,6 +110,27 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
|
|||
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',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -129,12 +139,12 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
|
|||
componentDidMount() {
|
||||
const {navigation} = this.props;
|
||||
navigation.setOptions({
|
||||
headerRight: () => (
|
||||
headerRight: (): React.Node => (
|
||||
<MaterialHeaderButtons>
|
||||
<Item
|
||||
title="switch"
|
||||
iconName="swap-horizontal"
|
||||
onPress={(): void => navigation.navigate('settings')}
|
||||
onPress={():void => navigation.navigate('settings')}
|
||||
/>
|
||||
<Item
|
||||
title="information"
|
||||
|
|
@ -154,10 +164,10 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
|
|||
);
|
||||
if (selected !== state.selectedWash) {
|
||||
this.setState({
|
||||
selectedWash: selected,
|
||||
selectedWash: selected
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used when pressing the about button.
|
||||
|
|
@ -198,27 +208,29 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
|
|||
* @param isDryer True if the given item is a dryer
|
||||
* @return {*}
|
||||
*/
|
||||
getModalContent(title: string, item: ProxiwashMachineType, isDryer: boolean) {
|
||||
getModalContent(
|
||||
title: string,
|
||||
item: ProxiwashMachineType,
|
||||
isDryer: boolean,
|
||||
): React.Node {
|
||||
const {props, state} = this;
|
||||
let button: {text: string; icon: string; onPress: () => void} = {
|
||||
let button = {
|
||||
text: i18n.t('screens.proxiwash.modal.ok'),
|
||||
icon: '',
|
||||
onPress: () => undefined,
|
||||
onPress: undefined,
|
||||
};
|
||||
let message = modalStateStrings[item.state];
|
||||
const onPress = () => this.onSetupNotificationsPress(item);
|
||||
if (item.state === MachineStates.RUNNING) {
|
||||
const onPress = this.onSetupNotificationsPress.bind(this, item);
|
||||
if (item.state === ProxiwashConstants.machineStates.RUNNING) {
|
||||
let remainingTime = parseInt(item.remainingTime, 10);
|
||||
if (remainingTime < 0) {
|
||||
remainingTime = 0;
|
||||
}
|
||||
if (remainingTime < 0) remainingTime = 0;
|
||||
|
||||
button = {
|
||||
text: isMachineWatched(item, state.machinesWatched)
|
||||
? i18n.t('screens.proxiwash.modal.disableNotifications')
|
||||
: i18n.t('screens.proxiwash.modal.enableNotifications'),
|
||||
icon: '',
|
||||
onPress: onPress,
|
||||
onPress,
|
||||
};
|
||||
message = i18n.t('screens.proxiwash.modal.running', {
|
||||
start: item.startTime,
|
||||
|
|
@ -235,7 +247,7 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
|
|||
}}>
|
||||
<Card.Title
|
||||
title={title}
|
||||
left={() => (
|
||||
left={(): React.Node => (
|
||||
<Avatar.Icon
|
||||
icon={isDryer ? 'tumble-dryer' : 'washing-machine'}
|
||||
color={props.theme.colors.text}
|
||||
|
|
@ -247,7 +259,7 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
|
|||
<Text>{message}</Text>
|
||||
</Card.Content>
|
||||
|
||||
{button.onPress ? (
|
||||
{button.onPress !== undefined ? (
|
||||
<Card.Actions>
|
||||
<Button
|
||||
icon={button.icon}
|
||||
|
|
@ -268,7 +280,11 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
|
|||
* @param section The section to render
|
||||
* @return {*}
|
||||
*/
|
||||
getRenderSectionHeader = ({section}: {section: {title: string}}) => {
|
||||
getRenderSectionHeader = ({
|
||||
section,
|
||||
}: {
|
||||
section: {title: string},
|
||||
}): React.Node => {
|
||||
const isDryer = section.title === i18n.t('screens.proxiwash.dryers');
|
||||
const nbAvailable = this.getMachineAvailableNumber(isDryer);
|
||||
return (
|
||||
|
|
@ -291,9 +307,9 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
|
|||
item,
|
||||
section,
|
||||
}: {
|
||||
item: ProxiwashMachineType;
|
||||
section: {title: string};
|
||||
}) => {
|
||||
item: ProxiwashMachineType,
|
||||
section: {title: string},
|
||||
}): React.Node => {
|
||||
const {machinesWatched} = this.state;
|
||||
const isDryer = section.title === i18n.t('screens.proxiwash.dryers');
|
||||
return (
|
||||
|
|
@ -353,16 +369,12 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
|
|||
*/
|
||||
getMachineAvailableNumber(isDryer: boolean): number {
|
||||
let data;
|
||||
if (isDryer) {
|
||||
data = this.fetchedData.dryers;
|
||||
} else {
|
||||
data = this.fetchedData.washers;
|
||||
}
|
||||
if (isDryer) data = this.fetchedData.dryers;
|
||||
else data = this.fetchedData.washers;
|
||||
let count = 0;
|
||||
data.forEach((machine: ProxiwashMachineType) => {
|
||||
if (machine.state === MachineStates.AVAILABLE) {
|
||||
if (machine.state === ProxiwashConstants.machineStates.AVAILABLE)
|
||||
count += 1;
|
||||
}
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
|
@ -374,8 +386,8 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
|
|||
* @return {*}
|
||||
*/
|
||||
createDataset = (fetchedData: {
|
||||
dryers: Array<ProxiwashMachineType>;
|
||||
washers: Array<ProxiwashMachineType>;
|
||||
dryers: Array<ProxiwashMachineType>,
|
||||
washers: Array<ProxiwashMachineType>,
|
||||
}): SectionListDataType<ProxiwashMachineType> => {
|
||||
const {state} = this;
|
||||
let data = fetchedData;
|
||||
|
|
@ -454,9 +466,8 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
|
|||
if (
|
||||
machine.number === selectedMachine.number &&
|
||||
machine.endTime === selectedMachine.endTime
|
||||
) {
|
||||
)
|
||||
newList.splice(index, 1);
|
||||
}
|
||||
});
|
||||
this.saveNewWatchedList(newList);
|
||||
}
|
||||
|
|
@ -469,7 +480,7 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
|
|||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
render(): React.Node {
|
||||
const {state} = this;
|
||||
const {navigation} = this.props;
|
||||
let data: LaundromatType;
|
||||
83
src/screens/Services/Proximo/ProximoAboutScreen.js
Normal file
83
src/screens/Services/Proximo/ProximoAboutScreen.js
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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, Text} from 'react-native-paper';
|
||||
import CustomTabBar from '../../../components/Tabbar/CustomTabBar';
|
||||
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
|
||||
import type {CardTitleIconPropsType} from '../../../constants/PaperStyles';
|
||||
|
||||
const LOGO = 'https://etud.insa-toulouse.fr/~amicale_app/images/Proximo.png';
|
||||
|
||||
/**
|
||||
* Class defining the proximo about screen.
|
||||
*/
|
||||
// eslint-disable-next-line react/prefer-stateless-function
|
||||
export default class ProximoAboutScreen extends React.Component<null> {
|
||||
render(): React.Node {
|
||||
return (
|
||||
<CollapsibleScrollView style={{padding: 5}}>
|
||||
<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>
|
||||
<Text>{i18n.t('screens.proximo.description')}</Text>
|
||||
<Card style={{margin: 5}}>
|
||||
<Card.Title
|
||||
title={i18n.t('screens.proximo.openingHours')}
|
||||
left={(iconProps: CardTitleIconPropsType): React.Node => (
|
||||
<Avatar.Icon size={iconProps.size} icon="clock-outline" />
|
||||
)}
|
||||
/>
|
||||
<Card.Content>
|
||||
<Paragraph>18h30 - 19h30</Paragraph>
|
||||
</Card.Content>
|
||||
</Card>
|
||||
<Card
|
||||
style={{margin: 5, marginBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}>
|
||||
<Card.Title
|
||||
title={i18n.t('screens.proximo.paymentMethods')}
|
||||
left={(iconProps: CardTitleIconPropsType): React.Node => (
|
||||
<Avatar.Icon size={iconProps.size} icon="cash" />
|
||||
)}
|
||||
/>
|
||||
<Card.Content>
|
||||
<Paragraph>
|
||||
{i18n.t('screens.proximo.paymentMethodsDescription')}
|
||||
</Paragraph>
|
||||
</Card.Content>
|
||||
</Card>
|
||||
</CollapsibleScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +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, Text} from 'react-native-paper';
|
||||
import CustomTabBar from '../../../components/Tabbar/CustomTabBar';
|
||||
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
|
||||
|
||||
const LOGO = 'https://etud.insa-toulouse.fr/~amicale_app/images/Proximo.png';
|
||||
|
||||
/**
|
||||
* Class defining the proximo about screen.
|
||||
*/
|
||||
export default function ProximoAboutScreen() {
|
||||
return (
|
||||
<CollapsibleScrollView style={{padding: 5}}>
|
||||
<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>
|
||||
<Text>{i18n.t('screens.proximo.description')}</Text>
|
||||
<Card style={{margin: 5}}>
|
||||
<Card.Title
|
||||
title={i18n.t('screens.proximo.openingHours')}
|
||||
left={(iconProps) => (
|
||||
<Avatar.Icon size={iconProps.size} icon="clock-outline" />
|
||||
)}
|
||||
/>
|
||||
<Card.Content>
|
||||
<Paragraph>18h30 - 19h30</Paragraph>
|
||||
</Card.Content>
|
||||
</Card>
|
||||
<Card style={{margin: 5, marginBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}>
|
||||
<Card.Title
|
||||
title={i18n.t('screens.proximo.paymentMethods')}
|
||||
left={(iconProps) => (
|
||||
<Avatar.Icon size={iconProps.size} icon="cash" />
|
||||
)}
|
||||
/>
|
||||
<Card.Content>
|
||||
<Paragraph>
|
||||
{i18n.t('screens.proximo.paymentMethodsDescription')}
|
||||
</Paragraph>
|
||||
</Card.Content>
|
||||
</Card>
|
||||
</CollapsibleScrollView>
|
||||
);
|
||||
}
|
||||
|
|
@ -17,6 +17,8 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Image, Platform, ScrollView, View} from 'react-native';
|
||||
import i18n from 'i18n-js';
|
||||
|
|
@ -36,6 +38,7 @@ import ProximoListItem from '../../../components/Lists/Proximo/ProximoListItem';
|
|||
import MaterialHeaderButtons, {
|
||||
Item,
|
||||
} from '../../../components/Overrides/CustomHeaderButton';
|
||||
import type {CustomThemeType} from '../../../managers/ThemeManager';
|
||||
import CollapsibleFlatList from '../../../components/Collapsible/CollapsibleFlatList';
|
||||
import type {ProximoArticleType} from './ProximoMainScreen';
|
||||
|
||||
|
|
@ -51,42 +54,34 @@ function sortPriceReverse(
|
|||
}
|
||||
|
||||
function sortName(a: ProximoArticleType, b: ProximoArticleType): number {
|
||||
if (a.name.toLowerCase() < b.name.toLowerCase()) {
|
||||
return -1;
|
||||
}
|
||||
if (a.name.toLowerCase() > b.name.toLowerCase()) {
|
||||
return 1;
|
||||
}
|
||||
if (a.name.toLowerCase() < b.name.toLowerCase()) return -1;
|
||||
if (a.name.toLowerCase() > b.name.toLowerCase()) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function sortNameReverse(a: ProximoArticleType, b: ProximoArticleType): number {
|
||||
if (a.name.toLowerCase() < b.name.toLowerCase()) {
|
||||
return 1;
|
||||
}
|
||||
if (a.name.toLowerCase() > b.name.toLowerCase()) {
|
||||
return -1;
|
||||
}
|
||||
if (a.name.toLowerCase() < b.name.toLowerCase()) return 1;
|
||||
if (a.name.toLowerCase() > b.name.toLowerCase()) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const LIST_ITEM_HEIGHT = 84;
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp<any>;
|
||||
navigation: StackNavigationProp,
|
||||
route: {
|
||||
params: {
|
||||
data: {data: Array<ProximoArticleType>};
|
||||
shouldFocusSearchBar: boolean;
|
||||
};
|
||||
};
|
||||
theme: ReactNativePaper.Theme;
|
||||
data: {data: Array<ProximoArticleType>},
|
||||
shouldFocusSearchBar: boolean,
|
||||
},
|
||||
},
|
||||
theme: CustomThemeType,
|
||||
};
|
||||
|
||||
type StateType = {
|
||||
currentSortMode: number;
|
||||
modalCurrentDisplayItem: React.ReactNode;
|
||||
currentSearchString: string;
|
||||
currentSortMode: number,
|
||||
modalCurrentDisplayItem: React.Node,
|
||||
currentSearchString: string,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -101,7 +96,6 @@ class ProximoListScreen extends React.Component<PropsType, StateType> {
|
|||
|
||||
constructor(props: PropsType) {
|
||||
super(props);
|
||||
this.modalRef = null;
|
||||
this.listData = props.route.params.data.data.sort(sortName);
|
||||
this.shouldFocusSearchBar = props.route.params.shouldFocusSearchBar;
|
||||
this.state = {
|
||||
|
|
@ -192,9 +186,7 @@ class ProximoListScreen extends React.Component<PropsType, StateType> {
|
|||
this.listData.sort(sortName);
|
||||
break;
|
||||
}
|
||||
if (this.modalRef && currentMode !== currentSortMode) {
|
||||
this.modalRef.close();
|
||||
}
|
||||
if (this.modalRef && currentMode !== currentSortMode) this.modalRef.close();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -206,13 +198,9 @@ class ProximoListScreen extends React.Component<PropsType, StateType> {
|
|||
getStockColor(availableStock: number): string {
|
||||
const {theme} = this.props;
|
||||
let color: string;
|
||||
if (availableStock > 3) {
|
||||
color = theme.colors.success;
|
||||
} else if (availableStock > 0) {
|
||||
color = theme.colors.warning;
|
||||
} else {
|
||||
color = theme.colors.danger;
|
||||
}
|
||||
if (availableStock > 3) color = theme.colors.success;
|
||||
else if (availableStock > 0) color = theme.colors.warning;
|
||||
else color = theme.colors.danger;
|
||||
return color;
|
||||
}
|
||||
|
||||
|
|
@ -221,7 +209,7 @@ class ProximoListScreen extends React.Component<PropsType, StateType> {
|
|||
*
|
||||
* @return {*}
|
||||
*/
|
||||
getSortMenuButton = () => {
|
||||
getSortMenuButton = (): React.Node => {
|
||||
return (
|
||||
<MaterialHeaderButtons>
|
||||
<Item title="main" iconName="sort" onPress={this.onSortMenuPress} />
|
||||
|
|
@ -234,9 +222,8 @@ class ProximoListScreen extends React.Component<PropsType, StateType> {
|
|||
*
|
||||
* @return {*}
|
||||
*/
|
||||
getSearchBar = () => {
|
||||
getSearchBar = (): React.Node => {
|
||||
return (
|
||||
// @ts-ignore
|
||||
<Searchbar
|
||||
placeholder={i18n.t('screens.proximo.search')}
|
||||
onChangeText={this.onSearchStringChange}
|
||||
|
|
@ -250,7 +237,7 @@ class ProximoListScreen extends React.Component<PropsType, StateType> {
|
|||
* @param item The article to display
|
||||
* @return {*}
|
||||
*/
|
||||
getModalItemContent(item: ProximoArticleType) {
|
||||
getModalItemContent(item: ProximoArticleType): React.Node {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
|
|
@ -297,7 +284,7 @@ class ProximoListScreen extends React.Component<PropsType, StateType> {
|
|||
*
|
||||
* @return {*}
|
||||
*/
|
||||
getModalSortMenu() {
|
||||
getModalSortMenu(): React.Node {
|
||||
const {currentSortMode} = this.state;
|
||||
return (
|
||||
<View
|
||||
|
|
@ -312,22 +299,22 @@ class ProximoListScreen extends React.Component<PropsType, StateType> {
|
|||
onValueChange={(value: string) => {
|
||||
this.setSortMode(value);
|
||||
}}
|
||||
value={currentSortMode.toString()}>
|
||||
value={currentSortMode}>
|
||||
<RadioButton.Item
|
||||
label={i18n.t('screens.proximo.sortPrice')}
|
||||
value={'1'}
|
||||
value={1}
|
||||
/>
|
||||
<RadioButton.Item
|
||||
label={i18n.t('screens.proximo.sortPriceReverse')}
|
||||
value={'2'}
|
||||
value={2}
|
||||
/>
|
||||
<RadioButton.Item
|
||||
label={i18n.t('screens.proximo.sortName')}
|
||||
value={'3'}
|
||||
value={3}
|
||||
/>
|
||||
<RadioButton.Item
|
||||
label={i18n.t('screens.proximo.sortNameReverse')}
|
||||
value={'4'}
|
||||
value={4}
|
||||
/>
|
||||
</RadioButton.Group>
|
||||
</View>
|
||||
|
|
@ -340,7 +327,7 @@ class ProximoListScreen extends React.Component<PropsType, StateType> {
|
|||
* @param item The article to render
|
||||
* @return {*}
|
||||
*/
|
||||
getRenderItem = ({item}: {item: ProximoArticleType}) => {
|
||||
getRenderItem = ({item}: {item: ProximoArticleType}): React.Node => {
|
||||
const {currentSearchString} = this.state;
|
||||
if (stringMatchQuery(item.name, currentSearchString)) {
|
||||
const onPress = () => {
|
||||
|
|
@ -377,15 +364,15 @@ class ProximoListScreen extends React.Component<PropsType, StateType> {
|
|||
};
|
||||
|
||||
itemLayout = (
|
||||
data: Array<ProximoArticleType> | null | undefined,
|
||||
data: ProximoArticleType,
|
||||
index: number,
|
||||
): {length: number; offset: number; index: number} => ({
|
||||
): {length: number, offset: number, index: number} => ({
|
||||
length: LIST_ITEM_HEIGHT,
|
||||
offset: LIST_ITEM_HEIGHT * index,
|
||||
index,
|
||||
});
|
||||
|
||||
render() {
|
||||
render(): React.Node {
|
||||
const {state} = this;
|
||||
return (
|
||||
<View
|
||||
|
|
@ -27,41 +27,43 @@ import WebSectionList from '../../../components/Screens/WebSectionList';
|
|||
import MaterialHeaderButtons, {
|
||||
Item,
|
||||
} from '../../../components/Overrides/CustomHeaderButton';
|
||||
import type {CustomThemeType} from '../../../managers/ThemeManager';
|
||||
import type {SectionListDataType} from '../../../components/Screens/WebSectionList';
|
||||
import type {ListIconPropsType} from '../../../constants/PaperStyles';
|
||||
|
||||
const DATA_URL = 'https://etud.insa-toulouse.fr/~proximo/data/stock-v2.json';
|
||||
const LIST_ITEM_HEIGHT = 84;
|
||||
|
||||
export type ProximoCategoryType = {
|
||||
name: string;
|
||||
icon: string;
|
||||
id: string;
|
||||
name: string,
|
||||
icon: string,
|
||||
id: string,
|
||||
};
|
||||
|
||||
export type ProximoArticleType = {
|
||||
name: string;
|
||||
description: string;
|
||||
quantity: string;
|
||||
price: string;
|
||||
code: string;
|
||||
id: string;
|
||||
type: Array<string>;
|
||||
image: string;
|
||||
name: string,
|
||||
description: string,
|
||||
quantity: string,
|
||||
price: string,
|
||||
code: string,
|
||||
id: string,
|
||||
type: Array<string>,
|
||||
image: string,
|
||||
};
|
||||
|
||||
export type ProximoMainListItemType = {
|
||||
type: ProximoCategoryType;
|
||||
data: Array<ProximoArticleType>;
|
||||
type: ProximoCategoryType,
|
||||
data: Array<ProximoArticleType>,
|
||||
};
|
||||
|
||||
export type ProximoDataType = {
|
||||
types: Array<ProximoCategoryType>;
|
||||
articles: Array<ProximoArticleType>;
|
||||
types: Array<ProximoCategoryType>,
|
||||
articles: Array<ProximoArticleType>,
|
||||
};
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp<any>;
|
||||
theme: ReactNativePaper.Theme;
|
||||
navigation: StackNavigationProp,
|
||||
theme: CustomThemeType,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -85,20 +87,12 @@ class ProximoMainScreen extends React.Component<PropsType> {
|
|||
const str2 = b.type.name.toLowerCase();
|
||||
|
||||
// Make 'All' category with id -1 stick to the top
|
||||
if (a.type.id === '-1') {
|
||||
return -1;
|
||||
}
|
||||
if (b.type.id === '-1') {
|
||||
return 1;
|
||||
}
|
||||
if (a.type.id === -1) return -1;
|
||||
if (b.type.id === -1) return 1;
|
||||
|
||||
// Sort others by name ascending
|
||||
if (str1 < str2) {
|
||||
return -1;
|
||||
}
|
||||
if (str1 > str2) {
|
||||
return 1;
|
||||
}
|
||||
if (str1 < str2) return -1;
|
||||
if (str1 > str2) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -111,17 +105,16 @@ class ProximoMainScreen extends React.Component<PropsType> {
|
|||
*/
|
||||
static getAvailableArticles(
|
||||
articles: Array<ProximoArticleType> | null,
|
||||
type?: ProximoCategoryType,
|
||||
type: ?ProximoCategoryType,
|
||||
): Array<ProximoArticleType> {
|
||||
const availableArticles: Array<ProximoArticleType> = [];
|
||||
const availableArticles = [];
|
||||
if (articles != null) {
|
||||
articles.forEach((article: ProximoArticleType) => {
|
||||
if (
|
||||
((type != null && article.type.includes(type.id)) || type == null) &&
|
||||
parseInt(article.quantity, 10) > 0
|
||||
) {
|
||||
)
|
||||
availableArticles.push(article);
|
||||
}
|
||||
});
|
||||
}
|
||||
return availableArticles;
|
||||
|
|
@ -129,18 +122,13 @@ class ProximoMainScreen extends React.Component<PropsType> {
|
|||
|
||||
articles: Array<ProximoArticleType> | null;
|
||||
|
||||
constructor(props: PropsType) {
|
||||
super(props);
|
||||
this.articles = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates header button
|
||||
*/
|
||||
componentDidMount() {
|
||||
const {navigation} = this.props;
|
||||
navigation.setOptions({
|
||||
headerRight: () => this.getHeaderButtons(),
|
||||
headerRight: (): React.Node => this.getHeaderButtons(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +168,7 @@ class ProximoMainScreen extends React.Component<PropsType> {
|
|||
* Gets the header buttons
|
||||
* @return {*}
|
||||
*/
|
||||
getHeaderButtons() {
|
||||
getHeaderButtons(): React.Node {
|
||||
return (
|
||||
<MaterialHeaderButtons>
|
||||
<Item
|
||||
|
|
@ -211,7 +199,7 @@ class ProximoMainScreen extends React.Component<PropsType> {
|
|||
* @param item The category to render
|
||||
* @return {*}
|
||||
*/
|
||||
getRenderItem = ({item}: {item: ProximoMainListItemType}) => {
|
||||
getRenderItem = ({item}: {item: ProximoMainListItemType}): React.Node => {
|
||||
const {navigation, theme} = this.props;
|
||||
const dataToSend = {
|
||||
shouldFocusSearchBar: false,
|
||||
|
|
@ -231,14 +219,14 @@ class ProximoMainScreen extends React.Component<PropsType> {
|
|||
title={item.type.name}
|
||||
description={subtitle}
|
||||
onPress={onPress}
|
||||
left={(props) => (
|
||||
left={(props: ListIconPropsType): React.Node => (
|
||||
<List.Icon
|
||||
style={props.style}
|
||||
icon={item.type.icon}
|
||||
color={theme.colors.primary}
|
||||
/>
|
||||
)}
|
||||
right={(props) => (
|
||||
right={(props: ListIconPropsType): React.Node => (
|
||||
<List.Icon
|
||||
color={props.color}
|
||||
style={props.style}
|
||||
|
|
@ -307,7 +295,7 @@ class ProximoMainScreen extends React.Component<PropsType> {
|
|||
return finalData;
|
||||
}
|
||||
|
||||
render() {
|
||||
render(): React.Node {
|
||||
const {navigation} = this.props;
|
||||
return (
|
||||
<WebSectionList
|
||||
|
|
@ -17,6 +17,8 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import {Card, Text, withTheme} from 'react-native-paper';
|
||||
|
|
@ -24,31 +26,32 @@ import {StackNavigationProp} from '@react-navigation/stack';
|
|||
import i18n from 'i18n-js';
|
||||
import DateManager from '../../managers/DateManager';
|
||||
import WebSectionList from '../../components/Screens/WebSectionList';
|
||||
import type {CustomThemeType} from '../../managers/ThemeManager';
|
||||
import type {SectionListDataType} from '../../components/Screens/WebSectionList';
|
||||
|
||||
const DATA_URL =
|
||||
'https://etud.insa-toulouse.fr/~amicale_app/menu/menu_data.json';
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp<any>;
|
||||
theme: ReactNativePaper.Theme;
|
||||
navigation: StackNavigationProp,
|
||||
theme: CustomThemeType,
|
||||
};
|
||||
|
||||
export type RuFoodCategoryType = {
|
||||
name: string;
|
||||
dishes: Array<{name: string}>;
|
||||
name: string,
|
||||
dishes: Array<{name: string}>,
|
||||
};
|
||||
|
||||
type RuMealType = {
|
||||
name: string;
|
||||
foodcategory: Array<RuFoodCategoryType>;
|
||||
name: string,
|
||||
foodcategory: Array<RuFoodCategoryType>,
|
||||
};
|
||||
|
||||
type RawRuMenuType = {
|
||||
restaurant_id: number;
|
||||
id: number;
|
||||
date: string;
|
||||
meal: Array<RuMealType>;
|
||||
restaurant_id: number,
|
||||
id: number,
|
||||
date: string,
|
||||
meal: Array<RuMealType>,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -74,7 +77,7 @@ class SelfMenuScreen extends React.Component<PropsType> {
|
|||
createDataset = (
|
||||
fetchedData: Array<RawRuMenuType>,
|
||||
): SectionListDataType<RuFoodCategoryType> => {
|
||||
let result: SectionListDataType<RuFoodCategoryType> = [];
|
||||
let result = [];
|
||||
if (fetchedData == null || fetchedData.length === 0) {
|
||||
result = [
|
||||
{
|
||||
|
|
@ -101,7 +104,11 @@ class SelfMenuScreen extends React.Component<PropsType> {
|
|||
* @param section The section to render the header from
|
||||
* @return {*}
|
||||
*/
|
||||
getRenderSectionHeader = ({section}: {section: {title: string}}) => {
|
||||
getRenderSectionHeader = ({
|
||||
section,
|
||||
}: {
|
||||
section: {title: string},
|
||||
}): React.Node => {
|
||||
return (
|
||||
<Card
|
||||
style={{
|
||||
|
|
@ -134,7 +141,7 @@ class SelfMenuScreen extends React.Component<PropsType> {
|
|||
* @param item The item to render
|
||||
* @return {*}
|
||||
*/
|
||||
getRenderItem = ({item}: {item: RuFoodCategoryType}) => {
|
||||
getRenderItem = ({item}: {item: RuFoodCategoryType}): React.Node => {
|
||||
const {theme} = this.props;
|
||||
return (
|
||||
<Card
|
||||
|
|
@ -156,7 +163,7 @@ class SelfMenuScreen extends React.Component<PropsType> {
|
|||
}}
|
||||
/>
|
||||
<Card.Content>
|
||||
{item.dishes.map((object: {name: string}) =>
|
||||
{item.dishes.map((object: {name: string}): React.Node =>
|
||||
object.name !== '' ? (
|
||||
<Text
|
||||
style={{
|
||||
|
|
@ -181,7 +188,7 @@ class SelfMenuScreen extends React.Component<PropsType> {
|
|||
*/
|
||||
getKeyExtractor = (item: RuFoodCategoryType): string => item.name;
|
||||
|
||||
render() {
|
||||
render(): React.Node {
|
||||
const {navigation} = this.props;
|
||||
return (
|
||||
<WebSectionList
|
||||
|
|
@ -17,6 +17,8 @@
|
|||
* 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 {
|
||||
|
|
@ -30,6 +32,7 @@ import {
|
|||
import i18n from 'i18n-js';
|
||||
import {StackNavigationProp} from '@react-navigation/stack';
|
||||
import CardList from '../../components/Lists/CardList/CardList';
|
||||
import type {CustomThemeType} from '../../managers/ThemeManager';
|
||||
import MaterialHeaderButtons, {
|
||||
Item,
|
||||
} from '../../components/Overrides/CustomHeaderButton';
|
||||
|
|
@ -43,8 +46,8 @@ import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatLis
|
|||
import type {ServiceCategoryType} from '../../managers/ServicesManager';
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp<any>;
|
||||
theme: ReactNativePaper.Theme;
|
||||
navigation: StackNavigationProp,
|
||||
theme: CustomThemeType,
|
||||
};
|
||||
|
||||
class ServicesScreen extends React.Component<PropsType> {
|
||||
|
|
@ -65,7 +68,7 @@ class ServicesScreen extends React.Component<PropsType> {
|
|||
});
|
||||
}
|
||||
|
||||
getAboutButton = () => (
|
||||
getAboutButton = (): React.Node => (
|
||||
<MaterialHeaderButtons>
|
||||
<Item
|
||||
title="information"
|
||||
|
|
@ -89,11 +92,12 @@ class ServicesScreen extends React.Component<PropsType> {
|
|||
* @param source The source image to display. Can be a string for icons or a number for local images
|
||||
* @returns {*}
|
||||
*/
|
||||
getListTitleImage(source: string | number) {
|
||||
getListTitleImage(source: string | number): React.Node {
|
||||
const {props} = this;
|
||||
if (typeof source === 'number') {
|
||||
if (typeof source === 'number')
|
||||
return (
|
||||
<Image
|
||||
size={48}
|
||||
source={source}
|
||||
style={{
|
||||
width: 48,
|
||||
|
|
@ -101,7 +105,6 @@ class ServicesScreen extends React.Component<PropsType> {
|
|||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Avatar.Icon
|
||||
size={48}
|
||||
|
|
@ -118,7 +121,7 @@ class ServicesScreen extends React.Component<PropsType> {
|
|||
* @param item
|
||||
* @returns {*}
|
||||
*/
|
||||
getRenderItem = ({item}: {item: ServiceCategoryType}) => {
|
||||
getRenderItem = ({item}: {item: ServiceCategoryType}): React.Node => {
|
||||
const {props} = this;
|
||||
return (
|
||||
<TouchableRipple
|
||||
|
|
@ -133,8 +136,8 @@ class ServicesScreen extends React.Component<PropsType> {
|
|||
<Card.Title
|
||||
title={item.title}
|
||||
subtitle={item.subtitle}
|
||||
left={() => this.getListTitleImage(item.image)}
|
||||
right={() => <List.Icon icon="chevron-right" />}
|
||||
left={(): React.Node => this.getListTitleImage(item.image)}
|
||||
right={(): React.Node => <List.Icon icon="chevron-right" />}
|
||||
/>
|
||||
<CardList dataset={item.content} isHorizontal />
|
||||
</View>
|
||||
|
|
@ -144,14 +147,14 @@ class ServicesScreen extends React.Component<PropsType> {
|
|||
|
||||
keyExtractor = (item: ServiceCategoryType): string => item.title;
|
||||
|
||||
render() {
|
||||
render(): React.Node {
|
||||
return (
|
||||
<View>
|
||||
<CollapsibleFlatList
|
||||
data={this.finalDataset}
|
||||
renderItem={this.getRenderItem}
|
||||
keyExtractor={this.keyExtractor}
|
||||
ItemSeparatorComponent={() => <Divider />}
|
||||
ItemSeparatorComponent={(): React.Node => <Divider />}
|
||||
hasTab
|
||||
/>
|
||||
<MascotPopup
|
||||
|
|
@ -160,6 +163,7 @@ class ServicesScreen extends React.Component<PropsType> {
|
|||
message={i18n.t('screens.services.mascotDialog.message')}
|
||||
icon="cloud-question"
|
||||
buttons={{
|
||||
action: null,
|
||||
cancel: {
|
||||
message: i18n.t('screens.services.mascotDialog.button'),
|
||||
icon: 'check',
|
||||
|
|
@ -17,25 +17,28 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Collapsible} from 'react-navigation-collapsible';
|
||||
import {CommonActions} from '@react-navigation/native';
|
||||
import {StackNavigationProp} from '@react-navigation/stack';
|
||||
import CardList from '../../components/Lists/CardList/CardList';
|
||||
import CustomTabBar from '../../components/Tabbar/CustomTabBar';
|
||||
import withCollapsible from '../../utils/withCollapsible';
|
||||
import type {ServiceCategoryType} from '../../managers/ServicesManager';
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp<any>;
|
||||
route: {params: {data: ServiceCategoryType | null}};
|
||||
collapsibleStack: Collapsible;
|
||||
navigation: StackNavigationProp,
|
||||
route: {params: {data: ServiceCategoryType | null}},
|
||||
collapsibleStack: Collapsible,
|
||||
};
|
||||
|
||||
class ServicesSectionScreen extends React.Component<PropsType> {
|
||||
finalDataset: null | ServiceCategoryType;
|
||||
finalDataset: ServiceCategoryType;
|
||||
|
||||
constructor(props: PropsType) {
|
||||
super(props);
|
||||
this.finalDataset = null;
|
||||
this.handleNavigationParams();
|
||||
}
|
||||
|
||||
|
|
@ -44,24 +47,38 @@ class ServicesSectionScreen extends React.Component<PropsType> {
|
|||
*/
|
||||
handleNavigationParams() {
|
||||
const {props} = this;
|
||||
if (props.route.params.data) {
|
||||
this.finalDataset = props.route.params.data;
|
||||
// reset params to prevent infinite loop
|
||||
props.navigation.dispatch(CommonActions.setParams({data: null}));
|
||||
props.navigation.setOptions({
|
||||
headerTitle: this.finalDataset.title,
|
||||
});
|
||||
if (props.route.params != null) {
|
||||
if (props.route.params.data != null) {
|
||||
this.finalDataset = props.route.params.data;
|
||||
// reset params to prevent infinite loop
|
||||
props.navigation.dispatch(CommonActions.setParams({data: null}));
|
||||
props.navigation.setOptions({
|
||||
headerTitle: this.finalDataset.title,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.finalDataset) {
|
||||
return null;
|
||||
}
|
||||
render(): React.Node {
|
||||
const {props} = this;
|
||||
const {
|
||||
containerPaddingTop,
|
||||
scrollIndicatorInsetTop,
|
||||
onScroll,
|
||||
} = props.collapsibleStack;
|
||||
return (
|
||||
<CardList dataset={this.finalDataset.content} isHorizontal={false} />
|
||||
<CardList
|
||||
dataset={this.finalDataset.content}
|
||||
isHorizontal={false}
|
||||
onScroll={onScroll}
|
||||
contentContainerStyle={{
|
||||
paddingTop: containerPaddingTop,
|
||||
paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20,
|
||||
}}
|
||||
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ServicesSectionScreen;
|
||||
export default withCollapsible(ServicesSectionScreen);
|
||||
|
|
@ -17,6 +17,8 @@
|
|||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {StackNavigationProp} from '@react-navigation/stack';
|
||||
import WebViewScreen from '../../components/Screens/WebViewScreen';
|
||||
|
|
@ -24,24 +26,21 @@ import AvailableWebsites from '../../constants/AvailableWebsites';
|
|||
import BasicLoadingScreen from '../../components/Screens/BasicLoadingScreen';
|
||||
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp<any>;
|
||||
route: {params: {host: string; path: string | null; title: string}};
|
||||
navigation: StackNavigationProp,
|
||||
route: {params: {host: string, path: string | null, title: string}},
|
||||
};
|
||||
|
||||
const ENABLE_MOBILE_STRING =
|
||||
'<meta name="viewport" content="width=device-width, initial-scale=1.0">';
|
||||
const ENABLE_MOBILE_STRING = `<meta name="viewport" content="width=device-width, initial-scale=1.0">`;
|
||||
|
||||
const AVAILABLE_ROOMS_STYLE =
|
||||
'<style>body,body>.container2{padding-top:0;width:100%}b,body>.container2>h1,body>.container2>h3,br,header{display:none}.table-bordered td,.table-bordered th{border:none;border-right:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.table{padding:0;margin:0;width:200%;max-width:200%;display:block}tbody{display:block;width:100%}thead{display:block;width:100%}.table tbody tr,tbody tr[bgcolor],thead tr{width:100%;display:inline-flex}.table tbody td,.table thead td[colspan]{padding:0;flex:1;height:50px;margin:0}.table tbody td[bgcolor=white],.table thead td,.table>tbody>tr>td:nth-child(1){flex:0 0 150px;height:50px}</style>';
|
||||
const BIB_STYLE =
|
||||
'<style>.hero-unit,.navbar,footer{display:none}.hero-unit-form,.hero-unit2,.hero-unit3{background-color:#fff;box-shadow:none;padding:0;margin:0}.hero-unit-form h4{font-size:2rem;line-height:2rem}.btn{font-size:1.5rem;line-height:1.5rem;padding:20px}.btn-danger{background-image:none;background-color:#be1522}.table{font-size:.8rem}.table td{padding:0;height:18.2333px;border:none;border-bottom:1px solid #c1c1c1}.table td[style="max-width:55px;"]{max-width:110px!important}.table-bordered{min-width:50px}th{height:50px}.table-bordered{border-collapse:collapse}</style>';
|
||||
const AVAILABLE_ROOMS_STYLE = `<style>body,body>.container2{padding-top:0;width:100%}b,body>.container2>h1,body>.container2>h3,br,header{display:none}.table-bordered td,.table-bordered th{border:none;border-right:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.table{padding:0;margin:0;width:200%;max-width:200%;display:block}tbody{display:block;width:100%}thead{display:block;width:100%}.table tbody tr,tbody tr[bgcolor],thead tr{width:100%;display:inline-flex}.table tbody td,.table thead td[colspan]{padding:0;flex:1;height:50px;margin:0}.table tbody td[bgcolor=white],.table thead td,.table>tbody>tr>td:nth-child(1){flex:0 0 150px;height:50px}</style>`;
|
||||
const BIB_STYLE = `<style>.hero-unit,.navbar,footer{display:none}.hero-unit-form,.hero-unit2,.hero-unit3{background-color:#fff;box-shadow:none;padding:0;margin:0}.hero-unit-form h4{font-size:2rem;line-height:2rem}.btn{font-size:1.5rem;line-height:1.5rem;padding:20px}.btn-danger{background-image:none;background-color:#be1522}.table{font-size:.8rem}.table td{padding:0;height:18.2333px;border:none;border-bottom:1px solid #c1c1c1}.table td[style="max-width:55px;"]{max-width:110px!important}.table-bordered{min-width:50px}th{height:50px}.table-bordered{border-collapse:collapse}</style>`;
|
||||
|
||||
const BIB_BACK_BUTTON =
|
||||
"<div style='width: 100%; display: flex'>" +
|
||||
`<div style='width: 100%; display: flex'>` +
|
||||
`<a style='margin: auto' href='${AvailableWebsites.websites.BIB}'>` +
|
||||
"<button id='customBackButton' class='btn btn-primary'>Retour</button>" +
|
||||
'</a>' +
|
||||
'</div>';
|
||||
`<button id='customBackButton' class='btn btn-primary'>Retour</button>` +
|
||||
`</a>` +
|
||||
`</div>`;
|
||||
|
||||
class WebsiteScreen extends React.Component<PropsType> {
|
||||
fullUrl: string;
|
||||
|
|
@ -54,8 +53,6 @@ class WebsiteScreen extends React.Component<PropsType> {
|
|||
|
||||
constructor(props: PropsType) {
|
||||
super(props);
|
||||
this.fullUrl = '';
|
||||
this.host = '';
|
||||
props.navigation.addListener('focus', this.onScreenFocus);
|
||||
this.injectedJS = {};
|
||||
this.customPaddingFunctions = {};
|
||||
|
|
@ -66,7 +63,7 @@ class WebsiteScreen extends React.Component<PropsType> {
|
|||
this.injectedJS[AvailableWebsites.websites.BIB] =
|
||||
`document.querySelector('head').innerHTML += '${ENABLE_MOBILE_STRING}';` +
|
||||
`document.querySelector('head').innerHTML += '${BIB_STYLE}';` +
|
||||
'if ($(".hero-unit-form").length > 0 && $("#customBackButton").length === 0)' +
|
||||
`if ($(".hero-unit-form").length > 0 && $("#customBackButton").length === 0)` +
|
||||
`$(".hero-unit-form").append("${BIB_BACK_BUTTON}");true;`;
|
||||
|
||||
this.customPaddingFunctions[AvailableWebsites.websites.BLUEMIND] = (
|
||||
|
|
@ -75,7 +72,7 @@ class WebsiteScreen extends React.Component<PropsType> {
|
|||
return (
|
||||
`$('head').append('${ENABLE_MOBILE_STRING}');` +
|
||||
`$('.minwidth').css('top', ${padding}` +
|
||||
"$('#mailview-bottom').css('min-height', 500);"
|
||||
`$('#mailview-bottom').css('min-height', 500);`
|
||||
);
|
||||
};
|
||||
this.customPaddingFunctions[AvailableWebsites.websites.WIKETUD] = (
|
||||
|
|
@ -106,26 +103,20 @@ class WebsiteScreen extends React.Component<PropsType> {
|
|||
if (this.host != null && path != null) {
|
||||
path = path.replace(this.host, '');
|
||||
this.fullUrl = this.host + path;
|
||||
} else {
|
||||
this.fullUrl = this.host;
|
||||
}
|
||||
} else this.fullUrl = this.host;
|
||||
|
||||
if (title != null) {
|
||||
navigation.setOptions({title});
|
||||
}
|
||||
if (title != null) navigation.setOptions({title});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
render(): React.Node {
|
||||
const {navigation} = this.props;
|
||||
let injectedJavascript = '';
|
||||
let customPadding = null;
|
||||
if (this.host != null && this.injectedJS[this.host] != null) {
|
||||
if (this.host != null && this.injectedJS[this.host] != null)
|
||||
injectedJavascript = this.injectedJS[this.host];
|
||||
}
|
||||
if (this.host != null && this.customPaddingFunctions[this.host] != null) {
|
||||
if (this.host != null && this.customPaddingFunctions[this.host] != null)
|
||||
customPadding = this.customPaddingFunctions[this.host];
|
||||
}
|
||||
|
||||
if (this.fullUrl != null) {
|
||||
return (
|
||||
Loading…
Reference in a new issue