From 33d98b024b4507dcbd799dbe53fc22893a2f8987 Mon Sep 17 00:00:00 2001 From: Arnaud Vergnet Date: Mon, 3 Aug 2020 21:06:39 +0200 Subject: [PATCH] Improve Services components to match linter --- src/components/Lists/CardList/CardList.js | 121 +-- src/components/Lists/CardList/CardListItem.js | 81 +- .../Lists/CardList/ImageListItem.js | 95 ++- src/managers/DashboardManager.js | 4 +- src/managers/ServicesManager.js | 698 +++++++++--------- src/screens/Home/HomeScreen.js | 6 +- src/screens/Services/ServicesScreen.js | 277 +++---- src/screens/Services/ServicesSectionScreen.js | 99 +-- src/utils/Services.js | 19 + 9 files changed, 718 insertions(+), 682 deletions(-) create mode 100644 src/utils/Services.js diff --git a/src/components/Lists/CardList/CardList.js b/src/components/Lists/CardList/CardList.js index 801521e..7787a11 100644 --- a/src/components/Lists/CardList/CardList.js +++ b/src/components/Lists/CardList/CardList.js @@ -1,72 +1,73 @@ // @flow import * as React from 'react'; -import {Animated, Dimensions} from "react-native"; -import ImageListItem from "./ImageListItem"; -import CardListItem from "./CardListItem"; -import type {ViewStyle} from "react-native/Libraries/StyleSheet/StyleSheet"; +import {Animated, Dimensions} from 'react-native'; +import type {ViewStyle} from 'react-native/Libraries/StyleSheet/StyleSheet'; +import ImageListItem from './ImageListItem'; +import CardListItem from './CardListItem'; +import type {ServiceItemType} from '../../../managers/ServicesManager'; -type Props = { - dataset: Array, - isHorizontal: boolean, - contentContainerStyle?: ViewStyle, -} - -export type cardItem = { - key: string, - title: string, - subtitle: string, - image: string | number, - onPress: () => void, +type PropsType = { + dataset: Array, + isHorizontal?: boolean, + contentContainerStyle?: ViewStyle | null, }; -export type cardList = Array; +export default class CardList extends React.Component { + static defaultProps = { + isHorizontal: false, + contentContainerStyle: null, + }; + windowWidth: number; -export default class CardList extends React.Component { + horizontalItemSize: number; - static defaultProps = { - isHorizontal: false, + constructor(props: PropsType) { + super(props); + this.windowWidth = Dimensions.get('window').width; + this.horizontalItemSize = this.windowWidth / 4; // So that we can fit 3 items and a part of the 4th => user knows he can scroll + } + + getRenderItem = ({item}: {item: ServiceItemType}): React.Node => { + const {props} = this; + if (props.isHorizontal) + return ( + + ); + return ; + }; + + keyExtractor = (item: ServiceItemType): string => item.key; + + render(): React.Node { + const {props} = this; + let containerStyle = {}; + if (props.isHorizontal) { + containerStyle = { + height: this.horizontalItemSize + 50, + justifyContent: 'space-around', + }; } - - windowWidth: number; - horizontalItemSize: number; - - constructor(props: Props) { - super(props); - this.windowWidth = Dimensions.get('window').width; - this.horizontalItemSize = this.windowWidth/4; // So that we can fit 3 items and a part of the 4th => user knows he can scroll - } - - renderItem = ({item}: { item: cardItem }) => { - if (this.props.isHorizontal) - return ; - else - return ; - }; - - keyExtractor = (item: cardItem) => item.key; - - render() { - let containerStyle = {}; - if (this.props.isHorizontal) { - containerStyle = { - height: this.horizontalItemSize + 50, - justifyContent: 'space-around', - }; + return ( + - ); - } + pagingEnabled={props.isHorizontal} + snapToInterval={ + props.isHorizontal ? (this.horizontalItemSize + 5) * 3 : null + } + /> + ); + } } diff --git a/src/components/Lists/CardList/CardListItem.js b/src/components/Lists/CardList/CardListItem.js index 069d567..27ca40b 100644 --- a/src/components/Lists/CardList/CardListItem.js +++ b/src/components/Lists/CardList/CardListItem.js @@ -2,50 +2,41 @@ import * as React from 'react'; import {Caption, Card, Paragraph, TouchableRipple} from 'react-native-paper'; -import {View} from "react-native"; -import type {cardItem} from "./CardList"; +import {View} from 'react-native'; +import type {ServiceItemType} from '../../../managers/ServicesManager'; -type Props = { - item: cardItem, -} - -export default class CardListItem extends React.Component { - - shouldComponentUpdate() { - return false; - } - - render() { - const props = this.props; - const item = props.item; - const source = typeof item.image === "number" - ? item.image - : {uri: item.image}; - return ( - - - - - - {item.title} - {item.subtitle} - - - - - - ); - } +type PropsType = { + item: ServiceItemType, +}; + +export default class CardListItem extends React.Component { + shouldComponentUpdate(): boolean { + return false; + } + + render(): React.Node { + const {props} = this; + const {item} = props; + const source = + typeof item.image === 'number' ? item.image : {uri: item.image}; + return ( + + + + + + {item.title} + {item.subtitle} + + + + + ); + } } diff --git a/src/components/Lists/CardList/ImageListItem.js b/src/components/Lists/CardList/ImageListItem.js index dd17110..6dff57a 100644 --- a/src/components/Lists/CardList/ImageListItem.js +++ b/src/components/Lists/CardList/ImageListItem.js @@ -3,53 +3,52 @@ import * as React from 'react'; import {Text, TouchableRipple} from 'react-native-paper'; import {Image, View} from 'react-native'; -import type {cardItem} from "./CardList"; +import type {ServiceItemType} from '../../../managers/ServicesManager'; -type Props = { - item: cardItem, - width: number, -} - -export default class ImageListItem extends React.Component { - - shouldComponentUpdate() { - return false; - } - - render() { - const item = this.props.item; - const source = typeof item.image === "number" - ? item.image - : {uri: item.image}; - return ( - - - - - {item.title} - - - - ); - } +type PropsType = { + item: ServiceItemType, + width: number, +}; + +export default class ImageListItem extends React.Component { + shouldComponentUpdate(): boolean { + return false; + } + + render(): React.Node { + const {props} = this; + const {item} = props; + const source = + typeof item.image === 'number' ? item.image : {uri: item.image}; + return ( + + + + + {item.title} + + + + ); + } } diff --git a/src/managers/DashboardManager.js b/src/managers/DashboardManager.js index 36aa9cb..0c9c5cc 100644 --- a/src/managers/DashboardManager.js +++ b/src/managers/DashboardManager.js @@ -1,12 +1,12 @@ // @flow -import type {ServiceItem} from './ServicesManager'; +import type {ServiceItemType} from './ServicesManager'; import ServicesManager from './ServicesManager'; import {getSublistWithIds} from '../utils/Utils'; import AsyncStorageManager from './AsyncStorageManager'; export default class DashboardManager extends ServicesManager { - getCurrentDashboard(): Array { + getCurrentDashboard(): Array { const dashboardIdList = AsyncStorageManager.getObject( AsyncStorageManager.PREFERENCES.dashboardItems.key, ); diff --git a/src/managers/ServicesManager.js b/src/managers/ServicesManager.js index 046ae39..74ffc44 100644 --- a/src/managers/ServicesManager.js +++ b/src/managers/ServicesManager.js @@ -1,378 +1,384 @@ // @flow -import i18n from "i18n-js"; -import AvailableWebsites from "../constants/AvailableWebsites"; -import {StackNavigationProp} from "@react-navigation/stack"; -import ConnectionManager from "./ConnectionManager"; -import type {fullDashboard} from "../screens/Home/HomeScreen"; +import i18n from 'i18n-js'; +import {StackNavigationProp} from '@react-navigation/stack'; +import AvailableWebsites from '../constants/AvailableWebsites'; +import ConnectionManager from './ConnectionManager'; +import type {FullDashboardType} from '../screens/Home/HomeScreen'; +import getStrippedServicesList from '../utils/Services'; // AMICALE -const CLUBS_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Clubs.png"; -const PROFILE_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/ProfilAmicaliste.png"; -const EQUIPMENT_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Materiel.png"; -const VOTE_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Vote.png"; -const AMICALE_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/WebsiteAmicale.png"; +const CLUBS_IMAGE = + 'https://etud.insa-toulouse.fr/~amicale_app/images/Clubs.png'; +const PROFILE_IMAGE = + 'https://etud.insa-toulouse.fr/~amicale_app/images/ProfilAmicaliste.png'; +const EQUIPMENT_IMAGE = + 'https://etud.insa-toulouse.fr/~amicale_app/images/Materiel.png'; +const VOTE_IMAGE = 'https://etud.insa-toulouse.fr/~amicale_app/images/Vote.png'; +const AMICALE_IMAGE = + 'https://etud.insa-toulouse.fr/~amicale_app/images/WebsiteAmicale.png'; // STUDENTS -const PROXIMO_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Proximo.png" -const WIKETUD_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Wiketud.png"; -const EE_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/EEC.png"; -const TUTORINSA_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/TutorINSA.png"; +const PROXIMO_IMAGE = + 'https://etud.insa-toulouse.fr/~amicale_app/images/Proximo.png'; +const WIKETUD_IMAGE = + 'https://etud.insa-toulouse.fr/~amicale_app/images/Wiketud.png'; +const EE_IMAGE = 'https://etud.insa-toulouse.fr/~amicale_app/images/EEC.png'; +const TUTORINSA_IMAGE = + 'https://etud.insa-toulouse.fr/~amicale_app/images/TutorINSA.png'; // INSA -const BIB_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Bib.png"; -const RU_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/RU.png"; -const ROOM_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Salles.png"; -const EMAIL_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Bluemind.png"; -const ENT_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/ENT.png"; -const ACCOUNT_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Account.png"; +const BIB_IMAGE = 'https://etud.insa-toulouse.fr/~amicale_app/images/Bib.png'; +const RU_IMAGE = 'https://etud.insa-toulouse.fr/~amicale_app/images/RU.png'; +const ROOM_IMAGE = + 'https://etud.insa-toulouse.fr/~amicale_app/images/Salles.png'; +const EMAIL_IMAGE = + 'https://etud.insa-toulouse.fr/~amicale_app/images/Bluemind.png'; +const ENT_IMAGE = 'https://etud.insa-toulouse.fr/~amicale_app/images/ENT.png'; +const ACCOUNT_IMAGE = + 'https://etud.insa-toulouse.fr/~amicale_app/images/Account.png'; // SPECIAL -const WASHER_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/ProxiwashLaveLinge.png"; -const DRYER_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/ProxiwashSecheLinge.png"; +const WASHER_IMAGE = + 'https://etud.insa-toulouse.fr/~amicale_app/images/ProxiwashLaveLinge.png'; +const DRYER_IMAGE = + 'https://etud.insa-toulouse.fr/~amicale_app/images/ProxiwashSecheLinge.png'; -const AMICALE_LOGO = require("../../assets/amicale.png"); +const AMICALE_LOGO = require('../../assets/amicale.png'); export const SERVICES_KEY = { - CLUBS: "clubs", - PROFILE: "profile", - EQUIPMENT: "equipment", - AMICALE_WEBSITE: "amicale_website", - VOTE: "vote", - PROXIMO: "proximo", - WIKETUD: "wiketud", - ELUS_ETUDIANTS: "elus_etudiants", - TUTOR_INSA: "tutor_insa", - RU: "ru", - AVAILABLE_ROOMS: "available_rooms", - BIB: "bib", - EMAIL: "email", - ENT: "ent", - INSA_ACCOUNT: "insa_account", - WASHERS: "washers", - DRYERS: "dryers", -} + CLUBS: 'clubs', + PROFILE: 'profile', + EQUIPMENT: 'equipment', + AMICALE_WEBSITE: 'amicale_website', + VOTE: 'vote', + PROXIMO: 'proximo', + WIKETUD: 'wiketud', + ELUS_ETUDIANTS: 'elus_etudiants', + TUTOR_INSA: 'tutor_insa', + RU: 'ru', + AVAILABLE_ROOMS: 'available_rooms', + BIB: 'bib', + EMAIL: 'email', + ENT: 'ent', + INSA_ACCOUNT: 'insa_account', + WASHERS: 'washers', + DRYERS: 'dryers', +}; export const SERVICES_CATEGORIES_KEY = { - AMICALE: "amicale", - STUDENTS: "students", - INSA: "insa", - SPECIAL: "special", -} + AMICALE: 'amicale', + STUDENTS: 'students', + INSA: 'insa', + SPECIAL: 'special', +}; +export type ServiceItemType = { + key: string, + title: string, + subtitle: string, + image: string, + onPress: () => void, + badgeFunction?: (dashboard: FullDashboardType) => number, +}; -export type ServiceItem = { - key: string, - title: string, - subtitle: string, - image: string, - onPress: () => void, - badgeFunction?: (dashboard: fullDashboard) => number, -} - -export type ServiceCategory = { - key: string, - title: string, - subtitle: string, - image: string | number, - content: Array -} - +export type ServiceCategoryType = { + key: string, + title: string, + subtitle: string, + image: string | number, + content: Array, +}; export default class ServicesManager { + navigation: StackNavigationProp; - navigation: StackNavigationProp; + amicaleDataset: Array; - amicaleDataset: Array; - studentsDataset: Array; - insaDataset: Array; - specialDataset: Array; + studentsDataset: Array; - categoriesDataset: Array; + insaDataset: Array; - constructor(nav: StackNavigationProp) { - this.navigation = nav; - this.amicaleDataset = [ - { - key: SERVICES_KEY.CLUBS, - title: i18n.t('screens.clubs.title'), - subtitle: i18n.t('screens.services.descriptions.clubs'), - image: CLUBS_IMAGE, - onPress: () => this.onAmicaleServicePress("club-list"), - }, - { - key: SERVICES_KEY.PROFILE, - title: i18n.t('screens.profile.title'), - subtitle: i18n.t('screens.services.descriptions.profile'), - image: PROFILE_IMAGE, - onPress: () => this.onAmicaleServicePress("profile"), - }, - { - key: SERVICES_KEY.EQUIPMENT, - title: i18n.t('screens.equipment.title'), - subtitle: i18n.t('screens.services.descriptions.equipment'), - image: EQUIPMENT_IMAGE, - onPress: () => this.onAmicaleServicePress("equipment-list"), - }, - { - key: SERVICES_KEY.AMICALE_WEBSITE, - title: i18n.t('screens.websites.amicale'), - subtitle: i18n.t('screens.services.descriptions.amicaleWebsite'), - image: AMICALE_IMAGE, - onPress: () => nav.navigate("website", { - host: AvailableWebsites.websites.AMICALE, - title: i18n.t('screens.websites.amicale') - }), - }, - { - key: SERVICES_KEY.VOTE, - title: i18n.t('screens.vote.title'), - subtitle: i18n.t('screens.services.descriptions.vote'), - image: VOTE_IMAGE, - onPress: () => this.onAmicaleServicePress("vote"), - }, - ]; - this.studentsDataset = [ - { - key: SERVICES_KEY.PROXIMO, - title: i18n.t('screens.proximo.title'), - subtitle: i18n.t('screens.services.descriptions.proximo'), - image: PROXIMO_IMAGE, - onPress: () => nav.navigate("proximo"), - badgeFunction: (dashboard: fullDashboard) => dashboard.proximo_articles - }, - { - key: SERVICES_KEY.WIKETUD, - title: "Wiketud", - subtitle: i18n.t('screens.services.descriptions.wiketud'), - image: WIKETUD_IMAGE, - onPress: () => nav.navigate("website", {host: AvailableWebsites.websites.WIKETUD, title: "Wiketud"}), - }, - { - key: SERVICES_KEY.ELUS_ETUDIANTS, - title: "Élus Étudiants", - subtitle: i18n.t('screens.services.descriptions.elusEtudiants'), - image: EE_IMAGE, - onPress: () => nav.navigate("website", { - host: AvailableWebsites.websites.ELUS_ETUDIANTS, - title: "Élus Étudiants" - }), - }, - { - key: SERVICES_KEY.TUTOR_INSA, - title: "Tutor'INSA", - subtitle: i18n.t('screens.services.descriptions.tutorInsa'), - image: TUTORINSA_IMAGE, - onPress: () => nav.navigate("website", { - host: AvailableWebsites.websites.TUTOR_INSA, - title: "Tutor'INSA" - }), - badgeFunction: (dashboard: fullDashboard) => dashboard.available_tutorials - }, - ]; - this.insaDataset = [ - { - key: SERVICES_KEY.RU, - title: i18n.t('screens.menu.title'), - subtitle: i18n.t('screens.services.descriptions.self'), - image: RU_IMAGE, - onPress: () => nav.navigate("self-menu"), - badgeFunction: (dashboard: fullDashboard) => dashboard.today_menu.length - }, - { - key: SERVICES_KEY.AVAILABLE_ROOMS, - title: i18n.t('screens.websites.rooms'), - subtitle: i18n.t('screens.services.descriptions.availableRooms'), - image: ROOM_IMAGE, - onPress: () => nav.navigate("website", { - host: AvailableWebsites.websites.AVAILABLE_ROOMS, - title: i18n.t('screens.websites.rooms') - }), - }, - { - key: SERVICES_KEY.BIB, - title: i18n.t('screens.websites.bib'), - subtitle: i18n.t('screens.services.descriptions.bib'), - image: BIB_IMAGE, - onPress: () => nav.navigate("website", { - host: AvailableWebsites.websites.BIB, - title: i18n.t('screens.websites.bib') - }), - }, - { - key: SERVICES_KEY.EMAIL, - title: i18n.t('screens.websites.mails'), - subtitle: i18n.t('screens.services.descriptions.mails'), - image: EMAIL_IMAGE, - onPress: () => nav.navigate("website", { - host: AvailableWebsites.websites.BLUEMIND, - title: i18n.t('screens.websites.mails') - }), - }, - { - key: SERVICES_KEY.ENT, - title: i18n.t('screens.websites.ent'), - subtitle: i18n.t('screens.services.descriptions.ent'), - image: ENT_IMAGE, - onPress: () => nav.navigate("website", { - host: AvailableWebsites.websites.ENT, - title: i18n.t('screens.websites.ent') - }), - }, - { - key: SERVICES_KEY.INSA_ACCOUNT, - title: i18n.t('screens.insaAccount.title'), - subtitle: i18n.t('screens.services.descriptions.insaAccount'), - image: ACCOUNT_IMAGE, - onPress: () => nav.navigate("website", { - host: AvailableWebsites.websites.INSA_ACCOUNT, - title: i18n.t('screens.insaAccount.title') - }), - }, - ]; - this.specialDataset = [ - { - key: SERVICES_KEY.WASHERS, - title: i18n.t('screens.proxiwash.washers'), - subtitle: i18n.t('screens.services.descriptions.washers'), - image: WASHER_IMAGE, - onPress: () => nav.navigate("proxiwash"), - badgeFunction: (dashboard: fullDashboard) => dashboard.available_washers - }, - { - key: SERVICES_KEY.DRYERS, - title: i18n.t('screens.proxiwash.dryers'), - subtitle: i18n.t('screens.services.descriptions.washers'), - image: DRYER_IMAGE, - onPress: () => nav.navigate("proxiwash"), - badgeFunction: (dashboard: fullDashboard) => dashboard.available_dryers - } - ]; - this.categoriesDataset = [ - { - key: SERVICES_CATEGORIES_KEY.AMICALE, - title: i18n.t("screens.services.categories.amicale"), - subtitle: i18n.t("screens.services.more"), - image: AMICALE_LOGO, - content: this.amicaleDataset - }, - { - key: SERVICES_CATEGORIES_KEY.STUDENTS, - title: i18n.t("screens.services.categories.students"), - subtitle: i18n.t("screens.services.more"), - image: 'account-group', - content: this.studentsDataset - }, - { - key: SERVICES_CATEGORIES_KEY.INSA, - title: i18n.t("screens.services.categories.insa"), - subtitle: i18n.t("screens.services.more"), - image: 'school', - content: this.insaDataset - }, - { - key: SERVICES_CATEGORIES_KEY.SPECIAL, - title: i18n.t("screens.services.categories.special"), - subtitle: i18n.t("screens.services.categories.special"), - image: 'star', - content: this.specialDataset - }, - ]; - } + specialDataset: Array; - /** - * Redirects the user to the login screen if he is not logged in - * - * @param route - * @returns {null} - */ - onAmicaleServicePress(route: string) { - if (ConnectionManager.getInstance().isLoggedIn()) - this.navigation.navigate(route); - else - this.navigation.navigate("login", {nextScreen: route}); - } + categoriesDataset: Array; - /** - * Gets the given services list without items of the given ids - * - * @param idList The ids of items to remove - * @param sourceList The item list to use as source - * @returns {[]} - */ - getStrippedList(idList: Array, sourceList: Array<{key: string, [key: string]: any}>) { - let newArray = []; - for (let i = 0; i < sourceList.length; i++) { - const item = sourceList[i]; - if (!(idList.includes(item.key))) - newArray.push(item); - } - return newArray; - } + constructor(nav: StackNavigationProp) { + this.navigation = nav; + this.amicaleDataset = [ + { + key: SERVICES_KEY.CLUBS, + title: i18n.t('screens.clubs.title'), + subtitle: i18n.t('screens.services.descriptions.clubs'), + image: CLUBS_IMAGE, + onPress: (): void => this.onAmicaleServicePress('club-list'), + }, + { + key: SERVICES_KEY.PROFILE, + title: i18n.t('screens.profile.title'), + subtitle: i18n.t('screens.services.descriptions.profile'), + image: PROFILE_IMAGE, + onPress: (): void => this.onAmicaleServicePress('profile'), + }, + { + key: SERVICES_KEY.EQUIPMENT, + title: i18n.t('screens.equipment.title'), + subtitle: i18n.t('screens.services.descriptions.equipment'), + image: EQUIPMENT_IMAGE, + onPress: (): void => this.onAmicaleServicePress('equipment-list'), + }, + { + key: SERVICES_KEY.AMICALE_WEBSITE, + title: i18n.t('screens.websites.amicale'), + subtitle: i18n.t('screens.services.descriptions.amicaleWebsite'), + image: AMICALE_IMAGE, + onPress: (): void => + nav.navigate('website', { + host: AvailableWebsites.websites.AMICALE, + title: i18n.t('screens.websites.amicale'), + }), + }, + { + key: SERVICES_KEY.VOTE, + title: i18n.t('screens.vote.title'), + subtitle: i18n.t('screens.services.descriptions.vote'), + image: VOTE_IMAGE, + onPress: (): void => this.onAmicaleServicePress('vote'), + }, + ]; + this.studentsDataset = [ + { + key: SERVICES_KEY.PROXIMO, + title: i18n.t('screens.proximo.title'), + subtitle: i18n.t('screens.services.descriptions.proximo'), + image: PROXIMO_IMAGE, + onPress: (): void => nav.navigate('proximo'), + badgeFunction: (dashboard: FullDashboardType): number => + dashboard.proximo_articles, + }, + { + key: SERVICES_KEY.WIKETUD, + title: 'Wiketud', + subtitle: i18n.t('screens.services.descriptions.wiketud'), + image: WIKETUD_IMAGE, + onPress: (): void => + nav.navigate('website', { + host: AvailableWebsites.websites.WIKETUD, + title: 'Wiketud', + }), + }, + { + key: SERVICES_KEY.ELUS_ETUDIANTS, + title: 'Élus Étudiants', + subtitle: i18n.t('screens.services.descriptions.elusEtudiants'), + image: EE_IMAGE, + onPress: (): void => + nav.navigate('website', { + host: AvailableWebsites.websites.ELUS_ETUDIANTS, + title: 'Élus Étudiants', + }), + }, + { + key: SERVICES_KEY.TUTOR_INSA, + title: "Tutor'INSA", + subtitle: i18n.t('screens.services.descriptions.tutorInsa'), + image: TUTORINSA_IMAGE, + onPress: (): void => + nav.navigate('website', { + host: AvailableWebsites.websites.TUTOR_INSA, + title: "Tutor'INSA", + }), + badgeFunction: (dashboard: FullDashboardType): number => + dashboard.available_tutorials, + }, + ]; + this.insaDataset = [ + { + key: SERVICES_KEY.RU, + title: i18n.t('screens.menu.title'), + subtitle: i18n.t('screens.services.descriptions.self'), + image: RU_IMAGE, + onPress: (): void => nav.navigate('self-menu'), + badgeFunction: (dashboard: FullDashboardType): number => + dashboard.today_menu.length, + }, + { + key: SERVICES_KEY.AVAILABLE_ROOMS, + title: i18n.t('screens.websites.rooms'), + subtitle: i18n.t('screens.services.descriptions.availableRooms'), + image: ROOM_IMAGE, + onPress: (): void => + nav.navigate('website', { + host: AvailableWebsites.websites.AVAILABLE_ROOMS, + title: i18n.t('screens.websites.rooms'), + }), + }, + { + key: SERVICES_KEY.BIB, + title: i18n.t('screens.websites.bib'), + subtitle: i18n.t('screens.services.descriptions.bib'), + image: BIB_IMAGE, + onPress: (): void => + nav.navigate('website', { + host: AvailableWebsites.websites.BIB, + title: i18n.t('screens.websites.bib'), + }), + }, + { + key: SERVICES_KEY.EMAIL, + title: i18n.t('screens.websites.mails'), + subtitle: i18n.t('screens.services.descriptions.mails'), + image: EMAIL_IMAGE, + onPress: (): void => + nav.navigate('website', { + host: AvailableWebsites.websites.BLUEMIND, + title: i18n.t('screens.websites.mails'), + }), + }, + { + key: SERVICES_KEY.ENT, + title: i18n.t('screens.websites.ent'), + subtitle: i18n.t('screens.services.descriptions.ent'), + image: ENT_IMAGE, + onPress: (): void => + nav.navigate('website', { + host: AvailableWebsites.websites.ENT, + title: i18n.t('screens.websites.ent'), + }), + }, + { + key: SERVICES_KEY.INSA_ACCOUNT, + title: i18n.t('screens.insaAccount.title'), + subtitle: i18n.t('screens.services.descriptions.insaAccount'), + image: ACCOUNT_IMAGE, + onPress: (): void => + nav.navigate('website', { + host: AvailableWebsites.websites.INSA_ACCOUNT, + title: i18n.t('screens.insaAccount.title'), + }), + }, + ]; + this.specialDataset = [ + { + key: SERVICES_KEY.WASHERS, + title: i18n.t('screens.proxiwash.washers'), + subtitle: i18n.t('screens.services.descriptions.washers'), + image: WASHER_IMAGE, + onPress: (): void => nav.navigate('proxiwash'), + badgeFunction: (dashboard: FullDashboardType): number => + dashboard.available_washers, + }, + { + key: SERVICES_KEY.DRYERS, + title: i18n.t('screens.proxiwash.dryers'), + subtitle: i18n.t('screens.services.descriptions.washers'), + image: DRYER_IMAGE, + onPress: (): void => nav.navigate('proxiwash'), + badgeFunction: (dashboard: FullDashboardType): number => + dashboard.available_dryers, + }, + ]; + this.categoriesDataset = [ + { + key: SERVICES_CATEGORIES_KEY.AMICALE, + title: i18n.t('screens.services.categories.amicale'), + subtitle: i18n.t('screens.services.more'), + image: AMICALE_LOGO, + content: this.amicaleDataset, + }, + { + key: SERVICES_CATEGORIES_KEY.STUDENTS, + title: i18n.t('screens.services.categories.students'), + subtitle: i18n.t('screens.services.more'), + image: 'account-group', + content: this.studentsDataset, + }, + { + key: SERVICES_CATEGORIES_KEY.INSA, + title: i18n.t('screens.services.categories.insa'), + subtitle: i18n.t('screens.services.more'), + image: 'school', + content: this.insaDataset, + }, + { + key: SERVICES_CATEGORIES_KEY.SPECIAL, + title: i18n.t('screens.services.categories.special'), + subtitle: i18n.t('screens.services.categories.special'), + image: 'star', + content: this.specialDataset, + }, + ]; + } - /** - * Gets the list of amicale's services - * - * @param excludedItems Ids of items to exclude from the returned list - * @returns {Array} - */ - getAmicaleServices(excludedItems?: Array) { - if (excludedItems != null) - return this.getStrippedList(excludedItems, this.amicaleDataset) - else - return this.amicaleDataset; - } + /** + * Redirects the user to the login screen if he is not logged in + * + * @param route + * @returns {null} + */ + onAmicaleServicePress(route: string) { + if (ConnectionManager.getInstance().isLoggedIn()) + this.navigation.navigate(route); + else this.navigation.navigate('login', {nextScreen: route}); + } - /** - * Gets the list of students' services - * - * @param excludedItems Ids of items to exclude from the returned list - * @returns {Array} - */ - getStudentServices(excludedItems?: Array) { - if (excludedItems != null) - return this.getStrippedList(excludedItems, this.studentsDataset) - else - return this.studentsDataset; - } + /** + * Gets the list of amicale's services + * + * @param excludedItems Ids of items to exclude from the returned list + * @returns {Array} + */ + getAmicaleServices(excludedItems?: Array): Array { + if (excludedItems != null) + return getStrippedServicesList(excludedItems, this.amicaleDataset); + return this.amicaleDataset; + } - /** - * Gets the list of INSA's services - * - * @param excludedItems Ids of items to exclude from the returned list - * @returns {Array} - */ - getINSAServices(excludedItems?: Array) { - if (excludedItems != null) - return this.getStrippedList(excludedItems, this.insaDataset) - else - return this.insaDataset; - } + /** + * Gets the list of students' services + * + * @param excludedItems Ids of items to exclude from the returned list + * @returns {Array} + */ + getStudentServices(excludedItems?: Array): Array { + if (excludedItems != null) + return getStrippedServicesList(excludedItems, this.studentsDataset); + return this.studentsDataset; + } - /** - * Gets the list of special services - * - * @param excludedItems Ids of items to exclude from the returned list - * @returns {Array} - */ - getSpecialServices(excludedItems?: Array) { - if (excludedItems != null) - return this.getStrippedList(excludedItems, this.specialDataset) - else - return this.specialDataset; - } + /** + * Gets the list of INSA's services + * + * @param excludedItems Ids of items to exclude from the returned list + * @returns {Array} + */ + getINSAServices(excludedItems?: Array): Array { + if (excludedItems != null) + return getStrippedServicesList(excludedItems, this.insaDataset); + return this.insaDataset; + } - /** - * Gets all services sorted by category - * - * @param excludedItems Ids of categories to exclude from the returned list - * @returns {Array} - */ - getCategories(excludedItems?: Array) { - if (excludedItems != null) - return this.getStrippedList(excludedItems, this.categoriesDataset) - else - return this.categoriesDataset; - } + /** + * Gets the list of special services + * + * @param excludedItems Ids of items to exclude from the returned list + * @returns {Array} + */ + getSpecialServices(excludedItems?: Array): Array { + if (excludedItems != null) + return getStrippedServicesList(excludedItems, this.specialDataset); + return this.specialDataset; + } + /** + * Gets all services sorted by category + * + * @param excludedItems Ids of categories to exclude from the returned list + * @returns {Array} + */ + getCategories(excludedItems?: Array): Array { + if (excludedItems != null) + return getStrippedServicesList(excludedItems, this.categoriesDataset); + return this.categoriesDataset; + } } diff --git a/src/screens/Home/HomeScreen.js b/src/screens/Home/HomeScreen.js index 07082b2..0ff2582 100644 --- a/src/screens/Home/HomeScreen.js +++ b/src/screens/Home/HomeScreen.js @@ -26,7 +26,7 @@ import AsyncStorageManager from '../../managers/AsyncStorageManager'; import {MASCOT_STYLE} from '../../components/Mascot/Mascot'; import MascotPopup from '../../components/Mascot/MascotPopup'; import DashboardManager from '../../managers/DashboardManager'; -import type {ServiceItem} from '../../managers/ServicesManager'; +import type {ServiceItemType} from '../../managers/ServicesManager'; import {getDisplayEvent, getFutureEvents} from '../../utils/Home'; // import DATA from "../dashboard_data.json"; @@ -230,7 +230,7 @@ class HomeScreen extends React.Component { * @param content * @return {*} */ - getDashboardRow(content: Array): React.Node { + getDashboardRow(content: Array): React.Node { return ( // $FlowFixMe { getDashboardRowRenderItem = ({ item, }: { - item: ServiceItem | null, + item: ServiceItemType | null, }): React.Node => { if (item != null) return ( diff --git a/src/screens/Services/ServicesScreen.js b/src/screens/Services/ServicesScreen.js index 0f6fa7a..e54e9d4 100644 --- a/src/screens/Services/ServicesScreen.js +++ b/src/screens/Services/ServicesScreen.js @@ -1,149 +1,162 @@ // @flow import * as React from 'react'; -import type {cardList} from "../../components/Lists/CardList/CardList"; -import CardList from "../../components/Lists/CardList/CardList"; -import {Image, View} from "react-native"; -import {Avatar, Card, Divider, List, TouchableRipple, withTheme} from "react-native-paper"; -import type {CustomTheme} from "../../managers/ThemeManager"; +import {Image, View} from 'react-native'; +import { + Avatar, + Card, + Divider, + List, + TouchableRipple, + withTheme, +} from 'react-native-paper'; import i18n from 'i18n-js'; -import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton"; -import {StackNavigationProp} from "@react-navigation/stack"; -import {MASCOT_STYLE} from "../../components/Mascot/Mascot"; -import MascotPopup from "../../components/Mascot/MascotPopup"; -import AsyncStorageManager from "../../managers/AsyncStorageManager"; -import ServicesManager, {SERVICES_CATEGORIES_KEY} from "../../managers/ServicesManager"; -import CollapsibleFlatList from "../../components/Collapsible/CollapsibleFlatList"; +import {StackNavigationProp} from '@react-navigation/stack'; +import CardList from '../../components/Lists/CardList/CardList'; +import type {CustomTheme} from '../../managers/ThemeManager'; +import MaterialHeaderButtons, { + Item, +} from '../../components/Overrides/CustomHeaderButton'; +import {MASCOT_STYLE} from '../../components/Mascot/Mascot'; +import MascotPopup from '../../components/Mascot/MascotPopup'; +import AsyncStorageManager from '../../managers/AsyncStorageManager'; +import ServicesManager, { + SERVICES_CATEGORIES_KEY, +} from '../../managers/ServicesManager'; +import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList'; +import type {ServiceCategoryType} from '../../managers/ServicesManager'; -type Props = { - navigation: StackNavigationProp, - theme: CustomTheme, -} +type PropsType = { + navigation: StackNavigationProp, + theme: CustomTheme, +}; -export type listItem = { - title: string, - description: string, - image: string | number, - content: cardList, -} +class ServicesScreen extends React.Component { + finalDataset: Array; + constructor(props: PropsType) { + super(props); + const services = new ServicesManager(props.navigation); + this.finalDataset = services.getCategories([ + SERVICES_CATEGORIES_KEY.SPECIAL, + ]); + } -class ServicesScreen extends React.Component { + componentDidMount() { + const {props} = this; + props.navigation.setOptions({ + headerRight: this.getAboutButton, + }); + } - finalDataset: Array + getAboutButton = (): React.Node => ( + + + + ); - constructor(props) { - super(props); - const services = new ServicesManager(props.navigation); - this.finalDataset = services.getCategories([SERVICES_CATEGORIES_KEY.SPECIAL]) - } + onAboutPress = () => { + const {props} = this; + props.navigation.navigate('amicale-contact'); + }; - componentDidMount() { - this.props.navigation.setOptions({ - headerRight: this.getAboutButton, - }); - } + /** + * Gets the list title image for the list. + * + * If the source is a string, we are using an icon. + * If the source is a number, we are using an internal image. + * + * @param source The source image to display. Can be a string for icons or a number for local images + * @returns {*} + */ + getListTitleImage(source: string | number): React.Node { + const {props} = this; + if (typeof source === 'number') + return ( + + ); + return ( + + ); + } - getAboutButton = () => - - - ; + /** + * A list item showing a list of available services for the current category + * + * @param item + * @returns {*} + */ + getRenderItem = ({item}: {item: ServiceCategoryType}): React.Node => { + const {props} = this; + return ( + { + props.navigation.navigate('services-section', {data: item}); + }}> + + this.getListTitleImage(item.image)} + right={({size}: {size: number}): React.Node => ( + + )} + /> + + + + ); + }; - onAboutPress = () => this.props.navigation.navigate('amicale-contact'); + keyExtractor = (item: ServiceCategoryType): string => item.title; - /** - * Gets the list title image for the list. - * - * If the source is a string, we are using an icon. - * If the source is a number, we are using an internal image. - * - * @param props Props to pass to the component - * @param source The source image to display. Can be a string for icons or a number for local images - * @returns {*} - */ - getListTitleImage(props, source: string | number) { - if (typeof source === "number") - return - else - return - } - - /** - * A list item showing a list of available services for the current category - * - * @param item - * @returns {*} - */ - renderItem = ({item}: { item: listItem }) => { - return ( - this.props.navigation.navigate("services-section", {data: item})} - > - - this.getListTitleImage(props, item.image)} - right={(props) => } - /> - - - - - ); - }; - - keyExtractor = (item: listItem) => { - return item.title; - } - - render() { - return ( - - } - hasTab={true} - /> - - - ); - } + render(): React.Node { + return ( + + } + hasTab + /> + + + ); + } } export default withTheme(ServicesScreen); diff --git a/src/screens/Services/ServicesSectionScreen.js b/src/screens/Services/ServicesSectionScreen.js index 1c071a2..d35d779 100644 --- a/src/screens/Services/ServicesSectionScreen.js +++ b/src/screens/Services/ServicesSectionScreen.js @@ -1,58 +1,65 @@ // @flow import * as React from 'react'; -import CardList from "../../components/Lists/CardList/CardList"; -import CustomTabBar from "../../components/Tabbar/CustomTabBar"; -import {withCollapsible} from "../../utils/withCollapsible"; -import {Collapsible} from "react-navigation-collapsible"; -import {CommonActions} from "@react-navigation/native"; -import type {listItem} from "./ServicesScreen"; -import {StackNavigationProp} from "@react-navigation/stack"; +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 Props = { - navigation: StackNavigationProp, - route: { params: { data: listItem | null } }, - collapsibleStack: Collapsible, -} +type PropsType = { + navigation: StackNavigationProp, + route: {params: {data: ServiceCategoryType | null}}, + collapsibleStack: Collapsible, +}; -class ServicesSectionScreen extends React.Component { +class ServicesSectionScreen extends React.Component { + finalDataset: ServiceCategoryType; - finalDataset: listItem; + constructor(props: PropsType) { + super(props); + this.handleNavigationParams(); + } - constructor(props) { - super(props); - this.handleNavigationParams(); + /** + * Recover the list to display from navigation parameters + */ + handleNavigationParams() { + const {props} = this; + 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, + }); + } } + } - /** - * Recover the list to display from navigation parameters - */ - handleNavigationParams() { - if (this.props.route.params != null) { - if (this.props.route.params.data != null) { - this.finalDataset = this.props.route.params.data; - // reset params to prevent infinite loop - this.props.navigation.dispatch(CommonActions.setParams({data: null})); - this.props.navigation.setOptions({ - headerTitle: this.finalDataset.title, - }); - } - } - } - - render() { - const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack; - return - } + render(): React.Node { + const {props} = this; + const { + containerPaddingTop, + scrollIndicatorInsetTop, + onScroll, + } = props.collapsibleStack; + return ( + + ); + } } export default withCollapsible(ServicesSectionScreen); diff --git a/src/utils/Services.js b/src/utils/Services.js new file mode 100644 index 0000000..4ee3a3e --- /dev/null +++ b/src/utils/Services.js @@ -0,0 +1,19 @@ +// @flow + +/** + * Gets the given services list without items of the given ids + * + * @param idList The ids of items to remove + * @param sourceList The item list to use as source + * @returns {[]} + */ +export default function getStrippedServicesList( + idList: Array, + sourceList: Array<{key: string, ...T}>, +): Array<{key: string, ...T}> { + const newArray = []; + sourceList.forEach((item: {key: string, ...T}) => { + if (!idList.includes(item.key)) newArray.push(item); + }); + return newArray; +}