Compare commits

...

20 commits

Author SHA1 Message Date
docjyJ
b7bc46f800 Update ui to go to proxiwash Settings
Remove the header of proxiwash screen
Add a button on proxiwash screen tool bar
2020-09-10 11:54:25 +02:00
docjyJ
f3e6bfe583 Update tariff language key for washinsa and tripode B 2020-09-10 07:55:30 +02:00
docjyJ
775fac4e5c Update Docs 2020-09-10 07:55:30 +02:00
docjyJ
8199a27f87 Update Proxiwash About Screen 2020-09-10 07:55:30 +02:00
docjyJ
6ff17ffde2 Add new Screen to select wash 2020-09-10 07:55:29 +02:00
docjyJ
2f75309f4e Update Language File 2020-09-10 07:55:29 +02:00
docjyJ
c9e1d0bba2 Update Settigns Screen 2020-09-10 07:55:29 +02:00
docjyJ
e8ad955d28 add settings selection 2020-09-10 07:55:29 +02:00
Arnaud Vergnet
c3d324549d Update contributors translations 2020-09-09 17:26:14 +02:00
Arnaud Vergnet
182360aabd Improve structure and flow types 2020-09-09 17:12:23 +02:00
docjyJ
3eabd84b58 Remove Useless var 2020-09-03 13:00:58 +02:00
docjyJ
e819e94395 Update About Screen 2020-09-03 12:45:40 +02:00
docjyJ
70dcb5fe97 Update Language file 2020-09-03 12:45:06 +02:00
docjyJ
3ff5a40f8d Add Thanks key 2020-09-03 12:01:05 +02:00
docjyJ
955eb11b84 Update Screen 2020-09-03 12:01:05 +02:00
docjyJ
47e749b528 Update Lang 2020-09-03 12:01:05 +02:00
docjyJ
2f94be64e5 Update Options Dialog to support Icon 2020-09-03 12:01:05 +02:00
docjyJ
a9a2b9150b Update About Screen 2020-09-03 12:01:05 +02:00
docjyJ
a8b9fd49b7 Add Thanks key 2020-09-03 12:00:59 +02:00
docjyJ
2ed4fbd780 Add Thaks card 2020-09-03 12:00:59 +02:00
10 changed files with 519 additions and 116 deletions

View file

@ -41,14 +41,10 @@
"washer": "Washer", "washer": "Washer",
"washers": "Washers", "washers": "Washers",
"min": "min", "min": "min",
"description": "This is the washing service operated by Promologis for INSA's residences (We don't mind if you do not live on the campus and you do your laundry here). The room is right next to the R2, with 3 dryers and 9 washers, is open 7d/7 24h/24 ! Here you can check their availability ! You can bring your own detergent, use the one given on site or buy it at the Proximo (cheaper than the one given by the machines ). You can pay by credit card or cash.",
"informationTab": "Information", "informationTab": "Information",
"paymentTab": "Payment", "paymentTab": "Payment",
"tariffs": "Tariffs", "tariffs": "Tariffs",
"washersTariff": "3€ the washer + 0.80€ with detergent.",
"dryersTariff": "0.35€ for 5min of dryer usage.",
"paymentMethods": "Payment Methods", "paymentMethods": "Payment Methods",
"paymentMethodsDescription": "Cash up until 10€.\nCredit Card also accepted.",
"washerProcedure": "Put your laundry in the tumble without tamping it and by respecting charge limits.\n\nClose the machine's door.\n\nChoose a program using one of the four favorite program buttons.\n\nPay to the command central, then press the START button on the machine.\n\nWhen the program is finished, the screen indicates 'Programme terminé', press the yellow button to open the lid and retrieve your laundry.", "washerProcedure": "Put your laundry in the tumble without tamping it and by respecting charge limits.\n\nClose the machine's door.\n\nChoose a program using one of the four favorite program buttons.\n\nPay to the command central, then press the START button on the machine.\n\nWhen the program is finished, the screen indicates 'Programme terminé', press the yellow button to open the lid and retrieve your laundry.",
"washerTips": "Program 'blanc/couleur': 6kg of dry laundry (cotton linen, linen, underwear, sheets, jeans, towels).\n\nProgram 'non repassable': 3,5 kg of dry laundry (synthetic fibre linen, cotton and polyester mixed).\n\nProgram 'fin 30°C': 2,5 kg of dry laundry (delicate linen in synthetic fibres).\n\nProgram 'laine 30°C': 2,5 kg of dry laundry (wool textiles).", "washerTips": "Program 'blanc/couleur': 6kg of dry laundry (cotton linen, linen, underwear, sheets, jeans, towels).\n\nProgram 'non repassable': 3,5 kg of dry laundry (synthetic fibre linen, cotton and polyester mixed).\n\nProgram 'fin 30°C': 2,5 kg of dry laundry (delicate linen in synthetic fibres).\n\nProgram 'laine 30°C': 2,5 kg of dry laundry (wool textiles).",
"dryerProcedure": "Put your laundry in the tumble without tamping it and by respecting charge limits.\n\nClose the machine's door.\n\nChoose a program using one of the four favorite program buttons.\n\nPay to the command central, then press the START button on the machine.", "dryerProcedure": "Put your laundry in the tumble without tamping it and by respecting charge limits.\n\nClose the machine's door.\n\nChoose a program using one of the four favorite program buttons.\n\nPay to the command central, then press the START button on the machine.",
@ -57,6 +53,20 @@
"tips": "Tips", "tips": "Tips",
"numAvailable": "available", "numAvailable": "available",
"numAvailablePlural": "available", "numAvailablePlural": "available",
"washinsa": {
"title": "INSA laundromat",
"subtitle": "Your favorite laundromat !!",
"description": "This is the washing service operated by Promologis for INSA's residences (We don't mind if you do not live on the campus and you do your laundry here). The room is right next to the R2, with 3 dryers and 9 washers, is open 7d/7 24h/24 ! You can bring your own detergent, use the one given on site or buy it at the Proximo (cheaper than the one given by the machines ).",
"tariff": "Washers 6kg: 3€ the washer + 0.80€ with detergent.\nDryers 14kg: 0.35€ for 5min of dryer usage.",
"paymentMethods": "Cash up until 10€.\nCredit Card also accepted."
},
"tripodeB": {
"title": "Tripode B laundromat",
"subtitle": "[A TRADUIRE] Celle de ceux qui habite prés du métro.",
"description": "[A TRADUIRE] C'est le service de laverie proposé par le crous pour les résidences Tripode B et C ainsi que Thalès et Pythagore. Le local situé au pied du Tripode B en face de de la résidence Pythagore avec ses 2 sèche-linges et 6 machines est ouvert 7J/7 de 7h à 23h. En plus des machine 6kg il y as une machine de 10kg.",
"tariff": "Washers 6kg: 2.60€ the washer + 0.90€ with detergent.\nWashers 10kg: 4.90€ the washer + 1.50€ with detergent.\nDryers 14kg: 0.40€ for 5min of dryer usage.",
"paymentMethods": "Carte bancaire acceptée."
},
"modal": { "modal": {
"enableNotifications": "Notify me", "enableNotifications": "Notify me",
"disableNotifications": "Stop notifications", "disableNotifications": "Stop notifications",
@ -197,7 +207,6 @@
"message": "An Amicale account allows you to take part in several activities around campus. You can join a club, or even create your own!\n\nLogging into your Amicale account on the app will allow you to see all available clubs on the campus, vote for the upcoming elections, and more to come!\n\nNo Account? Go to the Amicale's building during open hours to create one.", "message": "An Amicale account allows you to take part in several activities around campus. You can join a club, or even create your own!\n\nLogging into your Amicale account on the app will allow you to see all available clubs on the campus, vote for the upcoming elections, and more to come!\n\nNo Account? Go to the Amicale's building during open hours to create one.",
"button": "OK" "button": "OK"
} }
}, },
"profile": { "profile": {
"title": "Profile", "title": "Profile",
@ -346,12 +355,20 @@
"license": "License", "license": "License",
"debug": "Debug", "debug": "Debug",
"team": "Team", "team": "Team",
"author": "Author and maintainer",
"authorMail": "Send an email",
"additionalDev": "Thanks",
"technologies": "Technologies", "technologies": "Technologies",
"reactNative": "Made with React Native", "reactNative": "Made with React Native",
"libs": "Libraries used" "libs": "Libraries used",
"thanks": "Thanks",
"user": {
"you": "You ?",
"arnaud": "Student in IR (2020). He is the creator of this beautiful app you use everyday. Some say he is handsome as well.",
"yohan": "Student in IR (2020). He helped to fix bugs. I think he is handsome as well but I don't know him personally.",
"beranger": "Student in AE (2020) and president of the Amicale when the app was created. The app was his idea. He helped a lot to find bugs, new features and communication.",
"celine": "Student in GPE (2020). Without her, everything would be less cute. She helped to write the text, for communication, and also to create the mascot 🦊.",
"damien": "Student in IR (2020) and creator of the 2020 version of the Amicale's website. Thanks to his help, integrating Amicale's services into the app was child's play.",
"titouan": "Student in IR (2020). He helped a lot in finding bugs and new features.",
"theo": "Student in AE (2020). If the app works on iOS, this is all thanks to his help during his numerous tests."
}
}, },
"feedback": { "feedback": {
"title": "Contribute", "title": "Contribute",

View file

@ -41,14 +41,10 @@
"washer": "Lave-Linge", "washer": "Lave-Linge",
"washers": "Lave-Linges", "washers": "Lave-Linges",
"min": "min", "min": "min",
"description": "C'est le service de laverie proposé par Promologis pour les résidences INSA (On t'en voudra pas si tu loges pas sur le campus et que tu fais ta machine ici). Le local situé au pied du R2 avec ses 3 sèche-linges et 9 machines est ouvert 7J/7 24h/24 ! Ici tu peux vérifier leur disponibilité ! Tu peux amener ta lessive, la prendre sur place ou encore mieux l'acheter au Proximo (moins chère qu'à la laverie directement). Tu peux payer par CB ou espèces.",
"informationTab": "Informations", "informationTab": "Informations",
"paymentTab": "Paiement", "paymentTab": "Paiement",
"tariffs": "Tarifs", "tariffs": "Tarifs",
"washersTariff": "3€ la machine + 0.80€ avec la lessive.",
"dryersTariff": "0.35€ pour 5min de sèche linge.",
"paymentMethods": "Moyens de Paiement", "paymentMethods": "Moyens de Paiement",
"paymentMethodsDescription": "Toute monnaie jusqu'à 10€.\nCarte bancaire acceptée.",
"washerProcedure": "Déposer le linge dans le tambour sans le tasser et en respectant les charges.\n\nFermer la porte de l'appareil.\n\nSélectionner un programme avec l'une des quatre touches de programme favori standard.\n\nAprès avoir payé à la centrale de commande, appuyer sur le bouton marqué START du lave-linge.\n\nDès que le programme est terminé, lafficheur indique 'Programme terminé', appuyer sur le bouton jaune douverture du hublot pour récupérer le linge.", "washerProcedure": "Déposer le linge dans le tambour sans le tasser et en respectant les charges.\n\nFermer la porte de l'appareil.\n\nSélectionner un programme avec l'une des quatre touches de programme favori standard.\n\nAprès avoir payé à la centrale de commande, appuyer sur le bouton marqué START du lave-linge.\n\nDès que le programme est terminé, lafficheur indique 'Programme terminé', appuyer sur le bouton jaune douverture du hublot pour récupérer le linge.",
"washerTips": "Programme blanc/couleur : 6kg de linge sec (textiles en coton, lin, linge de corps, draps, jeans,serviettes de toilettes).\n\nProgramme non repassable : 3,5 kg de linge sec (textiles en fibres synthétiques, coton et polyester mélangés).\n\nProgramme fin 30°C : 2,5 kg de linge sec (textiles délicats en fibres synthétiques, rayonne).\n\nProgramme laine 30°C : 2,5 kg de linge sec (textiles en laine et lainages lavables).", "washerTips": "Programme blanc/couleur : 6kg de linge sec (textiles en coton, lin, linge de corps, draps, jeans,serviettes de toilettes).\n\nProgramme non repassable : 3,5 kg de linge sec (textiles en fibres synthétiques, coton et polyester mélangés).\n\nProgramme fin 30°C : 2,5 kg de linge sec (textiles délicats en fibres synthétiques, rayonne).\n\nProgramme laine 30°C : 2,5 kg de linge sec (textiles en laine et lainages lavables).",
"dryerProcedure": "Déposer le linge dans le tambour sans le tasser et en respectant les charges.\n\nFermer la porte de l'appareil.\n\nSélectionner un programme avec l'une des quatre touches de programme favori standard.\n\nAprès avoir payé à la centrale de commande, appuyer sur le bouton marqué START du lave-linge.", "dryerProcedure": "Déposer le linge dans le tambour sans le tasser et en respectant les charges.\n\nFermer la porte de l'appareil.\n\nSélectionner un programme avec l'une des quatre touches de programme favori standard.\n\nAprès avoir payé à la centrale de commande, appuyer sur le bouton marqué START du lave-linge.",
@ -57,6 +53,20 @@
"tips": "Conseils", "tips": "Conseils",
"numAvailable": "disponible", "numAvailable": "disponible",
"numAvailablePlural": "disponibles", "numAvailablePlural": "disponibles",
"washinsa": {
"title": "Laverie INSA",
"subtitle": "Ta laverie préférer !!",
"description": "C'est le service de laverie proposé par Promologis pour les résidences INSA (On t'en voudra pas si tu loges pas sur le campus et que tu fais ta machine ici). Le local situé au pied du R2 avec ses 3 sèche-linges et 9 machines est ouvert 7J/7 24h/24 ! Tu peux amener ta lessive, la prendre sur place ou encore mieux l'acheter au Proximo (moins chère qu'à la laverie directement).",
"tariff": "Lave-Linges 6kg: 3€ la machine + 0.80€ avec la lessive.\nSèche-Linges 14kg: 0.35€ pour 5min de sèche linge.",
"paymentMethods": "Toute monnaie jusqu'à 10€.\nCarte bancaire acceptée."
},
"tripodeB": {
"title": "Laverie Tripode B",
"subtitle": "Celle de ceux qui habite prés du métro.",
"description": "C'est le service de laverie proposé par le crous pour les résidences Tripode B et C ainsi que Thalès et Pythagore. Le local situé au pied du Tripode B en face de de la résidence Pythagore avec ses 2 sèche-linges et 6 machines est ouvert 7J/7 de 7h à 23h. En plus des machine 6kg il y as une machine de 10kg.",
"tariff": "Lave-Linges 6kg: 2.60€ la machine + 0.90€ avec la lessive.\nLave-Linges 10kg: 4.90€ la machine + 1.50€ avec la lessive.\nSèche-Linges 14kg: 0.40€ pour 5min de sèche linge.",
"paymentMethods": "Carte bancaire acceptée."
},
"modal": { "modal": {
"enableNotifications": "Me Notifier", "enableNotifications": "Me Notifier",
"disableNotifications": "Désactiver les notifications", "disableNotifications": "Désactiver les notifications",
@ -345,12 +355,20 @@
"license": "Licence", "license": "Licence",
"debug": "Debug", "debug": "Debug",
"team": "Équipe", "team": "Équipe",
"author": "Auteur et mainteneur",
"authorMail": "Envoyer un mail",
"additionalDev": "Remerciements",
"technologies": "Technologies", "technologies": "Technologies",
"reactNative": "Créé avec React Native", "reactNative": "Créé avec React Native",
"libs": "Librairies utilisées" "libs": "Librairies utilisées",
"thanks": "Remerciements",
"user": {
"you": "Toi ?",
"arnaud": "Étudiant en IR (2020). C'est le créateur de cette magnifique application que t'utilises tous les jour. Et il est vraiment BG aussi.",
"yohan": "Étudiant en IR (2020). Il a aidé à corriger des bug. Et j'imagine aussi qu'il est BG mais je le connait pas.",
"beranger": "Étudiant en AE (2020) et Président de lAmicale au moment de la création et du lancement du projet. Lapplication, cétait son idée. Il a beaucoup aidé pour trouver des bugs, de nouvelles fonctionnalités et faire de la com.",
"celine": "Étudiante en GPE (2020). Sans elle, tout serait moins mignon. Elle a aidé pour écrire le texte, faire de la com, et aussi à créer la mascotte 🦊.",
"damien": "Étudiant en IR (2020) et créateur de la dernière version du site de lAmicale. Grâce à son aide, intégrer les services de lAmicale à lapplication a été très simple.",
"titouan": "Étudiant en IR (2020). Il a beaucoup aidé pour trouver des bugs et proposer des nouvelles fonctionnalités.",
"theo": "Étudiant en AE (2020). Si lapplication marche sur iOS, cest grâce à son aide lors de ses nombreux tests."
}
}, },
"feedback": { "feedback": {
"title": "Participer", "title": "Participer",

View file

@ -6,6 +6,7 @@ import {FlatList} from 'react-native';
export type OptionsDialogButtonType = { export type OptionsDialogButtonType = {
title: string, title: string,
icon?: string,
onPress: () => void, onPress: () => void,
}; };
@ -19,10 +20,19 @@ type PropsType = {
class OptionsDialog extends React.PureComponent<PropsType> { class OptionsDialog extends React.PureComponent<PropsType> {
getButtonRender = ({item}: {item: OptionsDialogButtonType}): React.Node => { getButtonRender = ({item}: {item: OptionsDialogButtonType}): React.Node => {
return <Button onPress={item.onPress}>{item.title}</Button>; return (
<Button onPress={item.onPress} icon={item.icon}>
{item.title}
</Button>
);
}; };
keyExtractor = (item: OptionsDialogButtonType): string => item.title; keyExtractor = (item: OptionsDialogButtonType): string => {
if (item.icon != null) {
return item.title + item.icon;
}
return item.title;
};
render(): React.Node { render(): React.Node {
const {props} = this; const {props} = this;

View file

@ -59,6 +59,8 @@ class ProxiwashListItem extends React.Component<PropsType> {
title: string; title: string;
titlePopUp: string;
constructor(props: PropsType) { constructor(props: PropsType) {
super(props); super(props);
this.stateColors = {}; this.stateColors = {};
@ -67,6 +69,7 @@ class ProxiwashListItem extends React.Component<PropsType> {
this.updateStateStrings(); this.updateStateStrings();
let displayNumber = props.item.number; let displayNumber = props.item.number;
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),
@ -76,6 +79,7 @@ class ProxiwashListItem extends React.Component<PropsType> {
? i18n.t('screens.proxiwash.dryer') ? i18n.t('screens.proxiwash.dryer')
: i18n.t('screens.proxiwash.washer'); : i18n.t('screens.proxiwash.washer');
this.title += `${displayNumber}`; this.title += `${displayNumber}`;
this.titlePopUp = `${this.title} - ${displayMaxWeight} kg`;
} }
shouldComponentUpdate(nextProps: PropsType): boolean { shouldComponentUpdate(nextProps: PropsType): boolean {
@ -90,7 +94,7 @@ class ProxiwashListItem extends React.Component<PropsType> {
onListItemPress = () => { onListItemPress = () => {
const {props} = this; const {props} = this;
props.onPress(this.title, props.item, props.isDryer); props.onPress(this.titlePopUp, props.item, props.isDryer);
}; };
updateStateStrings() { updateStateStrings() {

View file

@ -107,6 +107,10 @@ export default class AsyncStorageManager {
key: 'gameScores', key: 'gameScores',
default: '[]', default: '[]',
}, },
selectedWash: {
key: 'selectedWash',
default: 'washinsa',
},
}; };
#currentPreferences: {[key: string]: string}; #currentPreferences: {[key: string]: string};

View file

@ -29,6 +29,7 @@ import {
getWebsiteStack, getWebsiteStack,
} from '../utils/CollapsibleUtils'; } from '../utils/CollapsibleUtils';
import Mascot, {MASCOT_STYLE} from '../components/Mascot/Mascot'; import Mascot, {MASCOT_STYLE} from '../components/Mascot/Mascot';
import ProxiwashSettingsScreen from '../screens/Proxiwash/ProxiwashSettingsScreen';
const modalTransition = const modalTransition =
Platform.OS === 'ios' Platform.OS === 'ios'
@ -91,6 +92,12 @@ function ProxiwashStackComponent(): React.Node {
ProxiwashAboutScreen, ProxiwashAboutScreen,
i18n.t('screens.proxiwash.title'), i18n.t('screens.proxiwash.title'),
)} )}
{createScreenCollapsibleStack(
'proxiwash-settings',
ProxiwashStack,
ProxiwashSettingsScreen,
i18n.t('screens.proxiwash.title'),
)}
</ProxiwashStack.Navigator> </ProxiwashStack.Navigator>
); );
} }

View file

@ -1,17 +1,19 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {FlatList, Linking, Platform, Image} from 'react-native'; import {FlatList, Linking, Platform, Image, View} from 'react-native';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import {Avatar, Card, List, Title, withTheme} from 'react-native-paper'; import {Avatar, Card, List, withTheme} from 'react-native-paper';
import {StackNavigationProp} from '@react-navigation/stack'; import {StackNavigationProp} from '@react-navigation/stack';
import packageJson from '../../../package.json'; import packageJson from '../../../package.json';
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList'; import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
import APP_LOGO from '../../../assets/android.icon.png'; import APP_LOGO from '../../../assets/android.icon.round.png';
import type { import type {
CardTitleIconPropsType, CardTitleIconPropsType,
ListIconPropsType, ListIconPropsType,
} from '../../constants/PaperStyles'; } from '../../constants/PaperStyles';
import OptionsDialog from '../../components/Dialogs/OptionsDialog';
import type {OptionsDialogButtonType} from '../../components/Dialogs/OptionsDialog';
type ListItemType = { type ListItemType = {
onPressCallback: () => void, onPressCallback: () => void,
@ -20,6 +22,15 @@ type ListItemType = {
showChevron: boolean, showChevron: boolean,
}; };
type MemberItemType = {
name: string,
message: string,
icon: string,
trollLink?: string,
linkedin?: string,
mail?: string,
};
const links = { const links = {
appstore: 'https://apps.apple.com/us/app/campus-amicale-insat/id1477722148', appstore: 'https://apps.apple.com/us/app/campus-amicale-insat/id1477722148',
playstore: playstore:
@ -30,28 +41,20 @@ const links = {
'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/Changelog.md', 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/Changelog.md',
license: license:
'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/LICENSE', 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/LICENSE',
authorMail:
'mailto:vergnet@etud.insa-toulouse.fr?' +
'subject=' +
'Application Amicale INSA Toulouse' +
'&body=' +
'Coucou !\n\n',
authorLinkedin: 'https://www.linkedin.com/in/arnaud-vergnet-434ba5179/',
yohanMail:
'mailto:ysimard@etud.insa-toulouse.fr?' +
'subject=' +
'Application Amicale INSA Toulouse' +
'&body=' +
'Coucou !\n\n',
yohanLinkedin: 'https://www.linkedin.com/in/yohan-simard',
react: 'https://facebook.github.io/react-native/', react: 'https://facebook.github.io/react-native/',
meme: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
}; };
type PropsType = { type PropsType = {
navigation: StackNavigationProp, navigation: StackNavigationProp,
}; };
type StateType = {
dialogVisible: boolean,
dialogTitle: string,
dialogMessage: string,
dialogButtons: Array<OptionsDialogButtonType>,
};
/** /**
* Opens a link in the device's browser * Opens a link in the device's browser
* @param link The link to open * @param link The link to open
@ -60,14 +63,76 @@ function openWebLink(link: string) {
Linking.openURL(link); Linking.openURL(link);
} }
/**
* Object containing data relative to major contributors
*/
const majorContributors: {[key: string]: MemberItemType} = {
arnaud: {
name: 'Arnaud Vergnet',
message: i18n.t('screens.about.user.arnaud'),
icon: 'crown',
trollLink: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
linkedin: 'https://www.linkedin.com/in/arnaud-vergnet-434ba5179/',
mail:
'mailto:vergnet@etud.insa-toulouse.fr?' +
'subject=' +
'Application Amicale INSA Toulouse' +
'&body=' +
'Coucou !\n\n',
},
yohan: {
name: 'Yohan Simard',
message: i18n.t('screens.about.user.yohan'),
icon: 'xml',
linkedin: 'https://www.linkedin.com/in/yohan-simard',
mail: 'mailto:ysimard@etud.insa-toulouse.fr?' +
'subject=' +
'Application Amicale INSA Toulouse' +
'&body=' +
'Coucou !\n\n',
},
};
/**
* Object containing data relative to users who helped during development
*/
const helpfulUsers: {[key: string]: MemberItemType} = {
beranger: {
name: 'Béranger Quintana Y Arciosana',
message: i18n.t('screens.about.user.beranger'),
icon: 'account-heart',
},
celine: {
name: 'Céline Tassin',
message: i18n.t('screens.about.user.celine'),
icon: 'brush',
},
damien: {
name: 'Damien Molina',
message: i18n.t('screens.about.user.damien'),
icon: 'web',
},
titouan: {
name: 'Titouan Labourdette',
message: i18n.t('screens.about.user.titouan'),
icon: 'shield-bug',
},
theo: {
name: 'Théo Tami',
message: i18n.t('screens.about.user.theo'),
icon: 'food-apple',
},
};
/** /**
* Class defining an about screen. This screen shows the user information about the app and it's author. * Class defining an about screen. This screen shows the user information about the app and it's author.
*/ */
class AboutScreen extends React.Component<PropsType> { class AboutScreen extends React.Component<PropsType, StateType> {
/** /**
* Data to be displayed in the app card * Data to be displayed in the app card
*/ */
appData = [ appData: Array<ListItemType> = [
{ {
onPressCallback: () => { onPressCallback: () => {
openWebLink(Platform.OS === 'ios' ? links.appstore : links.playstore); openWebLink(Platform.OS === 'ios' ? links.appstore : links.playstore);
@ -115,60 +180,79 @@ class AboutScreen extends React.Component<PropsType> {
]; ];
/** /**
* Data to be displayed in the author card * Data to be displayed in the team card
*/ */
authorData = [ teamData: Array<ListItemType> = [
{ {
onPressCallback: () => { onPressCallback: () => {
openWebLink(links.meme); this.onContributorListItemPress(majorContributors.arnaud);
}, },
icon: 'account-circle', icon: majorContributors.arnaud.icon,
text: 'Arnaud VERGNET', text: majorContributors.arnaud.name,
showChevron: false, showChevron: false,
}, },
{ {
onPressCallback: () => { onPressCallback: () => {
openWebLink(links.authorMail); this.onContributorListItemPress(majorContributors.yohan);
}, },
icon: 'email', icon: majorContributors.yohan.icon,
text: i18n.t('screens.about.authorMail'), text: majorContributors.yohan.name,
showChevron: true, showChevron: false,
}, },
{ {
onPressCallback: () => { onPressCallback: () => {
openWebLink(links.authorLinkedin); const {navigation} = this.props;
navigation.navigate('feedback');
}, },
icon: 'linkedin', icon: 'hand-pointing-right',
text: 'Linkedin', text: i18n.t('screens.about.user.you'),
showChevron: true, showChevron: true,
}, },
]; ];
/** /**
* Data to be displayed in the additional developer card * Data to be displayed in the thanks card
*/ */
additionalDevData = [ thanksData: Array<ListItemType> = [
{ {
onPressCallback: () => {}, onPressCallback: () => {
icon: 'account', this.onContributorListItemPress(helpfulUsers.beranger);
text: 'Yohan SIMARD', },
icon: helpfulUsers.beranger.icon,
text: helpfulUsers.beranger.name,
showChevron: false, showChevron: false,
}, },
{ {
onPressCallback: () => { onPressCallback: () => {
openWebLink(links.yohanMail); this.onContributorListItemPress(helpfulUsers.celine);
}, },
icon: 'email', icon: helpfulUsers.celine.icon,
text: i18n.t('screens.about.authorMail'), text: helpfulUsers.celine.name,
showChevron: true, showChevron: false,
}, },
{ {
onPressCallback: () => { onPressCallback: () => {
openWebLink(links.yohanLinkedin); this.onContributorListItemPress(helpfulUsers.damien);
}, },
icon: 'linkedin', icon: helpfulUsers.damien.icon,
text: 'Linkedin', text: helpfulUsers.damien.name,
showChevron: true, showChevron: false,
},
{
onPressCallback: () => {
this.onContributorListItemPress(helpfulUsers.titouan);
},
icon: helpfulUsers.titouan.icon,
text: helpfulUsers.titouan.name,
showChevron: false,
},
{
onPressCallback: () => {
this.onContributorListItemPress(helpfulUsers.theo);
},
icon: helpfulUsers.theo.icon,
text: helpfulUsers.theo.name,
showChevron: false,
}, },
]; ];
@ -205,11 +289,72 @@ class AboutScreen extends React.Component<PropsType> {
{ {
id: 'team', id: 'team',
}, },
{
id: 'thanks',
},
{ {
id: 'techno', id: 'techno',
}, },
]; ];
constructor(props: PropsType) {
super(props);
this.state = {
dialogVisible: false,
dialogTitle: '',
dialogMessage: '',
dialogButtons: [],
};
}
/**
* Callback used when clicking a member in the list
* It opens a dialog to show detailed information this member
*
* @param user The member to show information for
*/
onContributorListItemPress(user: MemberItemType) {
const dialogBtn = [
{
title: 'OK',
onPress: this.onDialogDismiss,
},
];
const {linkedin, trollLink, mail} = user;
if (linkedin != null) {
dialogBtn.push({
title: '',
icon: 'linkedin',
onPress: () => {
openWebLink(linkedin);
},
});
}
if (mail) {
dialogBtn.push({
title: '',
icon: 'email-edit',
onPress: () => {
openWebLink(mail);
},
});
}
if (trollLink) {
dialogBtn.push({
title: 'SWAG',
onPress: () => {
openWebLink(trollLink);
},
});
}
this.setState({
dialogVisible: true,
dialogTitle: user.name,
dialogMessage: user.message,
dialogButtons: dialogBtn,
});
}
/** /**
* Gets the app card showing information and links about the app. * Gets the app card showing information and links about the app.
* *
@ -255,18 +400,34 @@ class AboutScreen extends React.Component<PropsType> {
)} )}
/> />
<Card.Content> <Card.Content>
<Title>{i18n.t('screens.about.author')}</Title>
<FlatList <FlatList
data={this.authorData} data={this.teamData}
keyExtractor={this.keyExtractor} keyExtractor={this.keyExtractor}
listKey="1"
renderItem={this.getCardItem} renderItem={this.getCardItem}
/> />
<Title>{i18n.t('screens.about.additionalDev')}</Title> </Card.Content>
</Card>
);
}
/**
* Get the thank you card showing support information and links
*
* @return {*}
*/
getThanksCard(): React.Node {
return (
<Card style={{marginBottom: 10}}>
<Card.Title
title={i18n.t('screens.about.thanks')}
left={(iconProps: CardTitleIconPropsType): React.Node => (
<Avatar.Icon size={iconProps.size} icon="hand-heart" />
)}
/>
<Card.Content>
<FlatList <FlatList
data={this.additionalDevData} data={this.thanksData}
keyExtractor={this.keyExtractor} keyExtractor={this.keyExtractor}
listKey="2"
renderItem={this.getCardItem} renderItem={this.getCardItem}
/> />
</Card.Content> </Card.Content>
@ -282,8 +443,13 @@ class AboutScreen extends React.Component<PropsType> {
getTechnoCard(): React.Node { getTechnoCard(): React.Node {
return ( return (
<Card style={{marginBottom: 10}}> <Card style={{marginBottom: 10}}>
<Card.Title
title={i18n.t('screens.about.technologies')}
left={(iconProps: CardTitleIconPropsType): React.Node => (
<Avatar.Icon size={iconProps.size} icon="wrench" />
)}
/>
<Card.Content> <Card.Content>
<Title>{i18n.t('screens.about.technologies')}</Title>
<FlatList <FlatList
data={this.technoData} data={this.technoData}
keyExtractor={this.keyExtractor} keyExtractor={this.keyExtractor}
@ -358,6 +524,8 @@ class AboutScreen extends React.Component<PropsType> {
return this.getAppCard(); return this.getAppCard();
case 'team': case 'team':
return this.getTeamCard(); return this.getTeamCard();
case 'thanks':
return this.getThanksCard();
case 'techno': case 'techno':
return this.getTechnoCard(); return this.getTechnoCard();
default: default:
@ -365,6 +533,10 @@ class AboutScreen extends React.Component<PropsType> {
} }
}; };
onDialogDismiss = () => {
this.setState({dialogVisible: false});
};
/** /**
* Extracts a key from the given item * Extracts a key from the given item
* *
@ -374,12 +546,25 @@ class AboutScreen extends React.Component<PropsType> {
keyExtractor = (item: ListItemType): string => item.icon; keyExtractor = (item: ListItemType): string => item.icon;
render(): React.Node { render(): React.Node {
const {state} = this;
return ( return (
<View
style={{
height: '100%',
}}>
<CollapsibleFlatList <CollapsibleFlatList
style={{padding: 5}} style={{padding: 5}}
data={this.dataOrder} data={this.dataOrder}
renderItem={this.getMainCard} renderItem={this.getMainCard}
/> />
<OptionsDialog
visible={state.dialogVisible}
title={state.dialogTitle}
message={state.dialogMessage}
buttons={state.dialogButtons}
onDismiss={this.onDialogDismiss}
/>
</View>
); );
} }
} }

View file

@ -3,8 +3,7 @@
import * as React from 'react'; import * as React from 'react';
import {Image, View} from 'react-native'; import {Image, View} from 'react-native';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import {Card, Avatar, Paragraph, Text, Title} from 'react-native-paper'; import {Card, Avatar, Paragraph, Title} from 'react-native-paper';
import CustomTabBar from '../../components/Tabbar/CustomTabBar';
import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrollView'; import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrollView';
import type {CardTitleIconPropsType} from '../../constants/PaperStyles'; import type {CardTitleIconPropsType} from '../../constants/PaperStyles';
@ -32,7 +31,7 @@ export default class ProxiwashAboutScreen extends React.Component<null> {
style={{height: '100%', width: '100%', resizeMode: 'contain'}} style={{height: '100%', width: '100%', resizeMode: 'contain'}}
/> />
</View> </View>
<Text>{i18n.t('screens.proxiwash.description')}</Text>
<Card style={{margin: 5}}> <Card style={{margin: 5}}>
<Card.Title <Card.Title
title={i18n.t('screens.proxiwash.dryer')} title={i18n.t('screens.proxiwash.dryer')}
@ -62,33 +61,6 @@ export default class ProxiwashAboutScreen extends React.Component<null> {
<Paragraph>{i18n.t('screens.proxiwash.washerTips')}</Paragraph> <Paragraph>{i18n.t('screens.proxiwash.washerTips')}</Paragraph>
</Card.Content> </Card.Content>
</Card> </Card>
<Card style={{margin: 5}}>
<Card.Title
title={i18n.t('screens.proxiwash.tariffs')}
left={(iconProps: CardTitleIconPropsType): React.Node => (
<Avatar.Icon size={iconProps.size} icon="circle-multiple" />
)}
/>
<Card.Content>
<Paragraph>{i18n.t('screens.proxiwash.washersTariff')}</Paragraph>
<Paragraph>{i18n.t('screens.proxiwash.dryersTariff')}</Paragraph>
</Card.Content>
</Card>
<Card
style={{margin: 5, marginBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}>
<Card.Title
title={i18n.t('screens.proxiwash.paymentMethods')}
left={(iconProps: CardTitleIconPropsType): React.Node => (
<Avatar.Icon size={iconProps.size} icon="cash" />
)}
/>
<Card.Content>
<Paragraph>
{i18n.t('screens.proxiwash.paymentMethodsDescription')}
</Paragraph>
</Card.Content>
</Card>
</CollapsibleScrollView> </CollapsibleScrollView>
); );
} }

View file

@ -3,7 +3,7 @@
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';
import {Avatar, Button, Card, Text, withTheme} from 'react-native-paper'; import {Avatar, Button, Card, List, Text, withTheme} from 'react-native-paper';
import {StackNavigationProp} from '@react-navigation/stack'; import {StackNavigationProp} from '@react-navigation/stack';
import {Modalize} from 'react-native-modalize'; import {Modalize} from 'react-native-modalize';
import WebSectionList from '../../components/Screens/WebSectionList'; import WebSectionList from '../../components/Screens/WebSectionList';
@ -26,9 +26,8 @@ import {
import {MASCOT_STYLE} from '../../components/Mascot/Mascot'; import {MASCOT_STYLE} from '../../components/Mascot/Mascot';
import MascotPopup from '../../components/Mascot/MascotPopup'; import MascotPopup from '../../components/Mascot/MascotPopup';
import type {SectionListDataType} from '../../components/Screens/WebSectionList'; import type {SectionListDataType} from '../../components/Screens/WebSectionList';
import type {ListIconPropsType} from '../../constants/PaperStyles';
const DATA_URL = import {PROXIWASH_DATA} from './ProxiwashSettingsScreen';
'https://etud.insa-toulouse.fr/~amicale_app/v2/washinsa/washinsa_data.json';
const modalStateStrings = {}; const modalStateStrings = {};
@ -38,6 +37,7 @@ const LIST_ITEM_HEIGHT = 64;
export type ProxiwashMachineType = { export type ProxiwashMachineType = {
number: string, number: string,
state: string, state: string,
'maxWeight ': number,
startTime: string, startTime: string,
endTime: string, endTime: string,
donePercent: string, donePercent: string,
@ -53,6 +53,7 @@ type PropsType = {
type StateType = { type StateType = {
modalCurrentDisplayItem: React.Node, modalCurrentDisplayItem: React.Node,
machinesWatched: Array<ProxiwashMachineType>, machinesWatched: Array<ProxiwashMachineType>,
selectedWash: string,
}; };
/** /**
@ -87,6 +88,9 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
machinesWatched: AsyncStorageManager.getObject( machinesWatched: AsyncStorageManager.getObject(
AsyncStorageManager.PREFERENCES.proxiwashWatchedMachines.key, AsyncStorageManager.PREFERENCES.proxiwashWatchedMachines.key,
), ),
selectedWash: AsyncStorageManager.getString(
AsyncStorageManager.PREFERENCES.selectedWash.key,
),
}; };
modalStateStrings[ProxiwashConstants.machineStates.AVAILABLE] = i18n.t( modalStateStrings[ProxiwashConstants.machineStates.AVAILABLE] = i18n.t(
'screens.proxiwash.modal.ready', 'screens.proxiwash.modal.ready',
@ -124,6 +128,11 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
iconName="information" iconName="information"
onPress={this.onAboutPress} onPress={this.onAboutPress}
/> />
<Item
title="settings"
iconName="settings"
onPress={this.onSettingsPress}
/>
</MaterialHeaderButtons> </MaterialHeaderButtons>
), ),
}); });
@ -138,6 +147,15 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
navigation.navigate('proxiwash-about'); navigation.navigate('proxiwash-about');
}; };
/**
* Callback used when pressing the settings button.
* This will open the ProxiwashSettingsScreen.
*/
onSettingsPress = () => {
const {navigation} = this.props;
navigation.navigate('proxiwash-settings');
};
/** /**
* Callback used when the user clicks on enable notifications for a machine * Callback used when the user clicks on enable notifications for a machine
* *
@ -198,9 +216,6 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
remaining: remainingTime, remaining: remainingTime,
program: item.program, program: item.program,
}); });
} else if (item.state === ProxiwashConstants.machineStates.AVAILABLE) {
if (isDryer) message += `\n${i18n.t('screens.proxiwash.dryersTariff')}`;
else message += `\n${i18n.t('screens.proxiwash.washersTariff')}`;
} }
return ( return (
<View <View
@ -342,6 +357,31 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
return count; return count;
} }
/**
* Gets a chevron icon
*
* @param props
* @return {*}
*/
static getChevronIcon(props: ListIconPropsType): React.Node {
return (
<List.Icon color={props.color} style={props.style} icon="chevron-right" />
);
}
/**
* Gets a custom list item icon
*
* @param item The item to show the icon for
* @param props
* @return {*}
*/
static getItemIcon(item: ListItemType, props: ListIconPropsType): React.Node {
return (
<List.Icon color={props.color} style={props.style} icon={item.icon} />
);
}
/** /**
* Creates the dataset to be used by the FlatList * Creates the dataset to be used by the FlatList
* *
@ -437,8 +477,19 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
render(): React.Node { render(): React.Node {
const {state} = this; const {state} = this;
const {navigation} = this.props; const {navigation} = this.props;
let data: LaverieType;
switch (state.selectedWash) {
case 'tripodeB':
data = PROXIWASH_DATA.tripodeB;
break;
default:
data = PROXIWASH_DATA.washinsa;
}
return ( return (
<View style={{flex: 1}}> <View
style={{
flex: 1,
}}>
<View <View
style={{ style={{
position: 'absolute', position: 'absolute',
@ -448,7 +499,7 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
<WebSectionList <WebSectionList
createDataset={this.createDataset} createDataset={this.createDataset}
navigation={navigation} navigation={navigation}
fetchUrl={DATA_URL} fetchUrl={data.url}
renderItem={this.getRenderItem} renderItem={this.getRenderItem}
renderSectionHeader={this.getRenderSectionHeader} renderSectionHeader={this.getRenderSectionHeader}
autoRefreshTime={REFRESH_TIME} autoRefreshTime={REFRESH_TIME}

View file

@ -0,0 +1,135 @@
// @flow
import * as React from 'react';
import {
Title,
Button,
Card,
Avatar,
withTheme,
Paragraph,
} from 'react-native-paper';
import i18n from 'i18n-js';
import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrollView';
import type {CardTitleIconPropsType} from '../../constants/PaperStyles';
import AsyncStorageManager from '../../managers/AsyncStorageManager';
import ThemeManager from '../../managers/ThemeManager';
import type {CustomThemeType} from '../../managers/ThemeManager';
export type LaverieType = {
id: string,
title: string,
subtitle: string,
description: string,
tarif: string,
paymentMethods: string,
icon: string,
url: string,
};
export const PROXIWASH_DATA = {
washinsa: {
id: 'washinsa',
title: i18n.t('screens.proxiwash.washinsa.title'),
subtitle: i18n.t('screens.proxiwash.washinsa.subtitle'),
description: i18n.t('screens.proxiwash.washinsa.description'),
tarif: i18n.t('screens.proxiwash.washinsa.tariff'),
paymentMethods: i18n.t('screens.proxiwash.washinsa.paymentMethods'),
icon: 'school-outline',
url:
'https://etud.insa-toulouse.fr/~amicale_app/v2/washinsa/washinsa_data.json',
},
tripodeB: {
id: 'tripodeB',
title: i18n.t('screens.proxiwash.tripodeB.title'),
subtitle: i18n.t('screens.proxiwash.tripodeB.subtitle'),
description: i18n.t('screens.proxiwash.tripodeB.description'),
tarif: i18n.t('screens.proxiwash.tripodeB.tariff'),
paymentMethods: i18n.t('screens.proxiwash.tripodeB.paymentMethods'),
icon: 'domain',
url:
'https://etud.insa-toulouse.fr/~amicale_app/v2/washinsa/tripode_b_data.json',
},
};
type StateType = {
selectedWash: string,
currentTheme: CustomThemeType,
};
/**
* Class defining the proxiwash settings screen.
*/
class ProxiwashSettingsScreen extends React.Component<null, StateType> {
constructor() {
super();
this.state = {
selectedWash: AsyncStorageManager.getString(
AsyncStorageManager.PREFERENCES.selectedWash.key,
),
};
}
/**
* Saves the value for the proxiwash selected wash
*
* @param value The value to store
*/
onSelectWashValueChange = (value: string) => {
if (value != null) {
this.setState({selectedWash: value});
AsyncStorageManager.set(
AsyncStorageManager.PREFERENCES.selectedWash.key,
value,
);
}
};
getCardItem(item: LaverieType): React.Node {
const {selectedWash} = this.state;
const onSelectWashValueChange = (): void =>
this.onSelectWashValueChange(item.id);
let cardStyle = {
margin: 5,
};
if (selectedWash === item.id) {
cardStyle = {
margin: 5,
backgroundColor: ThemeManager.getCurrentTheme().colors
.proxiwashUnknownColor,
};
}
return (
<Card style={cardStyle}>
<Card.Title
title={item.title}
subtitle={item.subtitle}
left={(iconProps: CardTitleIconPropsType): React.Node => (
<Avatar.Icon size={iconProps.size} icon={item.icon} />
)}
/>
<Card.Content>
<Paragraph>{item.description}</Paragraph>
<Title>{i18n.t('screens.proxiwash.tariffs')}</Title>
<Paragraph>{item.tarif}</Paragraph>
<Title>{i18n.t('screens.proxiwash.paymentMethods')}</Title>
<Paragraph>{item.paymentMethods}</Paragraph>
</Card.Content>
<Card.Actions>
<Button onPress={onSelectWashValueChange}>Select</Button>
</Card.Actions>
</Card>
);
}
render(): React.Node {
return (
<CollapsibleScrollView style={{padding: 5}} hasTab>
{this.getCardItem(PROXIWASH_DATA.washinsa)}
{this.getCardItem(PROXIWASH_DATA.tripodeB)}
</CollapsibleScrollView>
);
}
}
export default withTheme(ProxiwashSettingsScreen);