From 2022b738f5f36da1b1ea9773d3cefdd883fc5136 Mon Sep 17 00:00:00 2001 From: Arnaud Vergnet Date: Thu, 16 Jul 2020 18:25:54 +0200 Subject: [PATCH] Dashboard can now display any service from ServicesManager.js --- src/components/Home/SmallDashboardItem.js | 86 ++++++++------ src/managers/AsyncStorageManager.js | 12 ++ src/managers/DashboardManager.js | 26 +++++ src/managers/ServicesManager.js | 135 +++++++++++++++------- src/screens/Home/HomeScreen.js | 97 +++------------- src/utils/Utils.js | 37 ++++++ 6 files changed, 239 insertions(+), 154 deletions(-) create mode 100644 src/managers/DashboardManager.js create mode 100644 src/utils/Utils.js diff --git a/src/components/Home/SmallDashboardItem.js b/src/components/Home/SmallDashboardItem.js index 0c27295..e8113b5 100644 --- a/src/components/Home/SmallDashboardItem.js +++ b/src/components/Home/SmallDashboardItem.js @@ -1,17 +1,15 @@ // @flow import * as React from 'react'; -import {Badge, IconButton, withTheme} from 'react-native-paper'; -import {View} from "react-native"; +import {Badge, TouchableRipple, withTheme} from 'react-native-paper'; +import {Dimensions, Image, View} from "react-native"; import type {CustomTheme} from "../../managers/ThemeManager"; import * as Animatable from "react-native-animatable"; type Props = { - color: string, - icon: string, - clickAction: () => void, - isAvailable: boolean, - badgeNumber: number, + image: string, + onPress: () => void, + badgeCount: number | null, theme: CustomTheme, }; @@ -22,42 +20,60 @@ const AnimatableBadge = Animatable.createAnimatableComponent(Badge); */ class SmallDashboardItem extends React.Component { + itemSize: number; + + constructor(props: Props) { + super(props); + this.itemSize = Dimensions.get('window').width / 8; + } + shouldComponentUpdate(nextProps: Props) { return (nextProps.theme.dark !== this.props.theme.dark) - || (nextProps.isAvailable !== this.props.isAvailable) - || (nextProps.badgeNumber !== this.props.badgeNumber); + || (nextProps.badgeCount !== this.props.badgeCount); } render() { const props = this.props; - const colors = props.theme.colors; return ( - - - { - props.badgeNumber > 0 ? - + + - {props.badgeNumber} - : null - } - + width: "100%", + height: "100%", + }} + /> + { + props.badgeCount != null && props.badgeCount > 0 ? + + {props.badgeCount} + : null + } + + + ); } diff --git a/src/managers/AsyncStorageManager.js b/src/managers/AsyncStorageManager.js index 7f40ef9..f084c39 100644 --- a/src/managers/AsyncStorageManager.js +++ b/src/managers/AsyncStorageManager.js @@ -1,6 +1,7 @@ // @flow import AsyncStorage from '@react-native-community/async-storage'; +import {SERVICES_KEY} from "./ServicesManager"; /** * Singleton used to manage preferences. @@ -119,6 +120,17 @@ export default class AsyncStorageManager { default: '[]', current: '', }, + dashboardItems: { + key: 'dashboardItems', + default: JSON.stringify([ + SERVICES_KEY.EMAIL, + SERVICES_KEY.WASHERS, + SERVICES_KEY.PROXIMO, + SERVICES_KEY.TUTOR_INSA, + SERVICES_KEY.RU, + ]), + current: '', + }, }; /** diff --git a/src/managers/DashboardManager.js b/src/managers/DashboardManager.js new file mode 100644 index 0000000..df186f8 --- /dev/null +++ b/src/managers/DashboardManager.js @@ -0,0 +1,26 @@ +// @flow + +import type {ServiceItem} from "./ServicesManager"; +import ServicesManager from "./ServicesManager"; +import {StackNavigationProp} from "@react-navigation/stack"; +import {getSublistWithIds} from "../utils/Utils"; +import AsyncStorageManager from "./AsyncStorageManager"; + + +export default class DashboardManager extends ServicesManager{ + + constructor(nav: StackNavigationProp) { + super(nav) + } + + getCurrentDashboard(): Array { + const dashboardIdList = JSON.parse(AsyncStorageManager.getInstance().preferences.dashboardItems.current); + const allDatasets = [ + ...this.amicaleDataset, + ...this.studentsDataset, + ...this.insaDataset, + ...this.specialDataset, + ]; + return getSublistWithIds(dashboardIdList, allDatasets); + } +} diff --git a/src/managers/ServicesManager.js b/src/managers/ServicesManager.js index cb6a75e..f629fd0 100644 --- a/src/managers/ServicesManager.js +++ b/src/managers/ServicesManager.js @@ -1,22 +1,25 @@ // @flow -import type {cardList} from "../components/Lists/CardList/CardList"; 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"; +// 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"; +// 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"; +// 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"; @@ -24,9 +27,13 @@ const EMAIL_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Bluemind. 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"; + export const SERVICES_KEY = { CLUBS: "clubs", - PROFILE:"profile", + PROFILE: "profile", EQUIPMENT: "equipment", AMICALE_WEBSITE: "amicale_website", VOTE: "vote", @@ -40,15 +47,27 @@ export const SERVICES_KEY = { EMAIL: "email", ENT: "ent", INSA_ACCOUNT: "insa_account", + WASHERS: "washers", + DRYERS: "dryers", +} + +export type ServiceItem = { + key: string, + title: string, + subtitle: string, + image: string, + onPress: () => void, + badgeFunction?: (dashboard: fullDashboard) => number, } export default class ServicesManager { navigation: StackNavigationProp; - amicaleDataset: cardList; - studentsDataset: cardList; - insaDataset: cardList; + amicaleDataset: Array; + studentsDataset: Array; + insaDataset: Array; + specialDataset: Array; constructor(nav: StackNavigationProp) { this.navigation = nav; @@ -79,7 +98,10 @@ export default class ServicesManager { 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')}), + onPress: () => nav.navigate("website", { + host: AvailableWebsites.websites.AMICALE, + title: i18n.t('screens.websites.amicale') + }), }, { key: SERVICES_KEY.VOTE, @@ -96,6 +118,7 @@ export default class ServicesManager { subtitle: i18n.t('screens.services.descriptions.proximo'), image: PROXIMO_IMAGE, onPress: () => nav.navigate("proximo"), + badgeFunction: (dashboard: fullDashboard) => dashboard.proximo_articles }, { key: SERVICES_KEY.WIKETUD, @@ -109,14 +132,21 @@ export default class ServicesManager { title: "Élus Étudiants", subtitle: i18n.t('screens.services.descriptions.elusEtudiants'), image: EE_IMAGE, - onPress: () => nav.navigate("website", {host: AvailableWebsites.websites.WIKETUD, title: "Wiketud"}), + 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"}) + onPress: () => nav.navigate("website", { + host: AvailableWebsites.websites.TUTOR_INSA, + title: "Tutor'INSA" + }), + badgeFunction: (dashboard: fullDashboard) => dashboard.available_tutorials }, ]; this.insaDataset = [ @@ -126,43 +156,77 @@ export default class ServicesManager { 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')}), + 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')}), + 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')}), + 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')}), + 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')}), + 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.proxiwash.title'), // TODO add description + 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.proxiwash.title'), // TODO add description + image: DRYER_IMAGE, + onPress: () => nav.navigate("proxiwash"), + badgeFunction: (dashboard: fullDashboard) => dashboard.available_dryers + } + ] } /** @@ -185,7 +249,7 @@ export default class ServicesManager { * @param sourceList The item list to use as source * @returns {[]} */ - getStrippedList(idList: Array, sourceList: cardList) { + getStrippedList(idList: Array, sourceList: Array) { let newArray = []; for (let i = 0; i < sourceList.length; i++) { const item = sourceList[i]; @@ -195,35 +259,11 @@ export default class ServicesManager { return newArray; } - /** - * Gets a services list of items with the given ids only - * - * @param idList The ids of items to find - * @returns {[]} - */ - getServicesOfId(idList: Array) { - const allServices = [ - ...this.amicaleDataset, - ...this.studentsDataset, - ...this.insaDataset, - ] - let servicesFound = []; - for (let i = 0; i < allServices.length; i++) { - const item = allServices[i]; - if (idList.includes(item.key)) { - servicesFound.push(item); - if (servicesFound.length === idList.length) - break; - } - } - return servicesFound; - } - /** * Gets the list of amicale's services * * @param excludedItems Ids of items to exclude from the returned list - * @returns {cardList|*[]} + * @returns {Array} */ getAmicaleServices(excludedItems?: Array) { if (excludedItems != null) @@ -236,7 +276,7 @@ export default class ServicesManager { * Gets the list of students' services * * @param excludedItems Ids of items to exclude from the returned list - * @returns {cardList|*[]} + * @returns {Array} */ getStudentServices(excludedItems?: Array) { if (excludedItems != null) @@ -249,7 +289,7 @@ export default class ServicesManager { * Gets the list of INSA's services * * @param excludedItems Ids of items to exclude from the returned list - * @returns {cardList|*[]} + * @returns {Array} */ getINSAServices(excludedItems?: Array) { if (excludedItems != null) @@ -258,4 +298,17 @@ export default class ServicesManager { return this.insaDataset; } + /** + * 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; + } + } diff --git a/src/screens/Home/HomeScreen.js b/src/screens/Home/HomeScreen.js index 0655dcd..6d1429f 100644 --- a/src/screens/Home/HomeScreen.js +++ b/src/screens/Home/HomeScreen.js @@ -20,9 +20,9 @@ import {View} from "react-native-animatable"; import ConnectionManager from "../../managers/ConnectionManager"; import LogoutDialog from "../../components/Amicale/LogoutDialog"; import AsyncStorageManager from "../../managers/AsyncStorageManager"; -import AvailableWebsites from "../../constants/AvailableWebsites"; import {MASCOT_STYLE} from "../../components/Mascot/Mascot"; import MascotPopup from "../../components/Mascot/MascotPopup"; +import DashboardManager from "../../managers/DashboardManager"; // import DATA from "../dashboard_data.json"; @@ -52,7 +52,7 @@ export type feedItem = { id: string, }; -type fullDashboard = { +export type fullDashboard = { today_menu: Array<{ [key: string]: any }>, proximo_articles: number, available_dryers: number, @@ -66,15 +66,6 @@ type dashboardItem = { content: Array<{ [key: string]: any }> }; -type dashboardSmallItem = { - id: string, - data: number, - icon: string, - color: string, - onPress: () => void, - isAvailable: boolean -}; - export type event = { id: number, title: string, @@ -113,11 +104,16 @@ class HomeScreen extends React.Component { fabRef: { current: null | AnimatedFAB }; currentNewFeed: Array; + currentDashboard: fullDashboard | null; + + dashboardManager: DashboardManager; constructor(props) { super(props); this.fabRef = React.createRef(); + this.dashboardManager = new DashboardManager(this.props.navigation); this.currentNewFeed = []; + this.currentDashboard = null; this.isLoggedIn = ConnectionManager.getInstance().isLoggedIn(); this.props.navigation.setOptions({ headerRight: this.getHeaderButton, @@ -207,22 +203,6 @@ class HomeScreen extends React.Component { hideDisconnectDialog = () => this.setState({dialogVisible: false}); - onProxiwashClick = () => { - this.props.navigation.navigate("proxiwash"); - }; - - onProximoClick = () => { - this.props.navigation.navigate("proximo"); - }; - - onTutorInsaClick = () => { - this.props.navigation.navigate("website", {host: AvailableWebsites.websites.TUTOR_INSA, title: "Tutor'INSA"}); - }; - - onMenuClick = () => { - this.props.navigation.navigate('self-menu'); - }; - /** * Creates the dataset to be used in the FlatList * @@ -232,9 +212,11 @@ class HomeScreen extends React.Component { createDataset = (fetchedData: rawDashboard) => { // fetchedData = DATA; let dashboardData; - if (fetchedData.news_feed != null) { + if (fetchedData.news_feed != null) this.currentNewFeed = fetchedData.news_feed.data; - } + if (fetchedData.dashboard != null) + this.currentDashboard = fetchedData.dashboard; + if (fetchedData.dashboard != null) dashboardData = this.generateDashboardDataset(fetchedData.dashboard); else @@ -264,48 +246,7 @@ class HomeScreen extends React.Component { {id: 'actions', content: []}, { id: 'top', - content: [ - { - id: 'washers', - data: dashboardData == null ? 0 : dashboardData.available_washers, - icon: 'washing-machine', - color: this.props.theme.colors.proxiwashColor, - onPress: this.onProxiwashClick, - isAvailable: dashboardData == null ? false : dashboardData.available_washers > 0 - }, - { - id: 'dryers', - data: dashboardData == null ? 0 : dashboardData.available_dryers, - icon: 'tumble-dryer', - color: this.props.theme.colors.proxiwashColor, - onPress: this.onProxiwashClick, - isAvailable: dashboardData == null ? false : dashboardData.available_dryers > 0 - }, - { - id: 'available_tutorials', - data: dashboardData == null ? 0 : dashboardData.available_tutorials, - icon: 'school', - color: this.props.theme.colors.tutorinsaColor, - onPress: this.onTutorInsaClick, - isAvailable: dashboardData == null ? false : dashboardData.available_tutorials > 0 - }, - { - id: 'proximo_articles', - data: dashboardData == null ? 0 : dashboardData.proximo_articles, - icon: 'shopping', - color: this.props.theme.colors.proximoColor, - onPress: this.onProximoClick, - isAvailable: dashboardData == null ? false : dashboardData.proximo_articles > 0 - }, - { - id: 'today_menu', - data: dashboardData == null ? [] : dashboardData.today_menu, - icon: 'silverware-fork-knife', - color: this.props.theme.colors.menuColor, - onPress: this.onMenuClick, - isAvailable: dashboardData == null ? false : dashboardData.today_menu.length > 0 - }, - ] + content: this.dashboardManager.getCurrentDashboard(), }, { id: 'event', @@ -491,14 +432,14 @@ class HomeScreen extends React.Component { * @param item * @returns {*} */ - dashboardRowRenderItem = ({item}: { item: dashboardSmallItem }) => { + dashboardRowRenderItem = ({item}: { item: DashboardItem }) => { return ( ); }; @@ -509,7 +450,7 @@ class HomeScreen extends React.Component { * @param content * @return {*} */ - getDashboardRow(content: Array) { + getDashboardRow(content: Array) { return ( //$FlowFixMe , + originalList: Array<{ key: string, [key: string]: any }> +): Array<{ key: string, [key: string]: any }> { + let subList = []; + for (let i = 0; i < subList.length; i++) { + subList.push(null); + } + let itemsAdded = 0; + for (let i = 0; i < originalList.length; i++) { + const item = originalList[i]; + if (idList.includes(item.key)) { + subList[idList.indexOf(item.key)] = item; + itemsAdded++; + if (itemsAdded === idList.length) + break; + } + } + for (let i = 0; i < subList.length; i++) { + if (subList[i] == null) + subList.splice(i, 1); + } + + return subList; +}