From f433edf902d8cb89af58628cb232727e0073e7a5 Mon Sep 17 00:00:00 2001 From: Arnaud Vergnet Date: Tue, 7 Apr 2020 17:44:48 +0200 Subject: [PATCH] Hide side bar items in accordions --- src/components/Sidebar/SideBarSection.js | 156 ++++++++++++++++++++++ src/components/Sidebar/Sidebar.js | 159 +++++++++-------------- 2 files changed, 214 insertions(+), 101 deletions(-) create mode 100644 src/components/Sidebar/SideBarSection.js diff --git a/src/components/Sidebar/SideBarSection.js b/src/components/Sidebar/SideBarSection.js new file mode 100644 index 0000000..33fe67f --- /dev/null +++ b/src/components/Sidebar/SideBarSection.js @@ -0,0 +1,156 @@ +// @flow + +import * as React from 'react'; +import {FlatList} from "react-native"; +import {Drawer, List, withTheme} from 'react-native-paper'; +import {openBrowser} from "../../utils/WebBrowser"; + +type Props = { + navigation: Object, + startOpen: boolean, + isLoggedIn: boolean, + sectionName: string, + activeRoute: string, + listKey: string, + listData: Array, +} + +type State = { + expanded: boolean +} + +const LIST_ITEM_HEIGHT = 48; + +class SideBarSection extends React.PureComponent { + + state = { + expanded: this.props.startOpen, + }; + + colors: Object; + shouldExpand: boolean; + + constructor(props) { + super(props); + this.colors = props.theme.colors; + } + + /** + * Searches if the current route is contained in the given list data. + * If this is the case and the list is collapsed, we should expand this list. + * + * @return boolean + */ + shouldExpandList() { + for (let i = 0; i < this.props.listData.length; i++) { + if (this.props.listData[i].route === this.props.activeRoute) { + return this.state.expanded === false; + } + } + return false; + } + + /** + * Callback when a drawer item is pressed. + * It will either navigate to the associated screen, or open the browser to the associated link + * + * @param item The item pressed + */ + onListItemPress(item: Object) { + if (item.link !== undefined) + openBrowser(item.link, this.colors.primary); + else if (item.action !== undefined) + item.action(); + else + this.props.navigation.navigate(item.route); + } + + /** + * Key extractor for list items + * + * @param item The item to extract the key from + * @return {string} The extracted key + */ + listKeyExtractor = (item: Object) => item.route; + + shouldHideItem(item: Object) { + const onlyWhenLoggedOut = item.onlyWhenLoggedOut !== undefined && item.onlyWhenLoggedOut === true; + const onlyWhenLoggedIn = item.onlyWhenLoggedIn !== undefined && item.onlyWhenLoggedIn === true; + return (onlyWhenLoggedIn && !this.props.isLoggedIn || onlyWhenLoggedOut && this.props.isLoggedIn); + } + + /** + * Gets the render item for the given list item + * + * @param item The item to render + * @return {*} + */ + getRenderItem = ({item}: Object) => { + const onListItemPress = this.onListItemPress.bind(this, item); + if (this.shouldHideItem(item)) + return null; + return ( + + ); + }; + + toggleAccordion = () => { + if ((!this.state.expanded && this.shouldExpand) || !this.shouldExpand) + this.setState({expanded: !this.state.expanded}) + }; + + shouldRenderAccordion() { + let itemsToRender = 0; + for (let i = 0; i < this.props.listData.length; i++) { + if (!this.shouldHideItem(this.props.listData[i])) + itemsToRender += 1; + } + return itemsToRender > 1; + } + + itemLayout = (data, index) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index}); + + getFlatList() { + return ( + // $FlowFixMe + + ); + } + + render() { + if (this.shouldRenderAccordion()) { + this.shouldExpand = this.shouldExpandList(); + if (this.shouldExpand) + this.toggleAccordion(); + return ( + + {this.getFlatList()} + + ); + } else + return this.getFlatList(); + } +} + +export default withTheme(SideBarSection); diff --git a/src/components/Sidebar/Sidebar.js b/src/components/Sidebar/Sidebar.js index 90e3d76..906bd49 100644 --- a/src/components/Sidebar/Sidebar.js +++ b/src/components/Sidebar/Sidebar.js @@ -1,15 +1,14 @@ // @flow import * as React from 'react'; -import {Dimensions, FlatList, Image, Platform, StyleSheet, View,} from 'react-native'; +import {Dimensions, FlatList, Image, StyleSheet, View,} from 'react-native'; import i18n from "i18n-js"; -import {openBrowser} from "../../utils/WebBrowser"; -import {Drawer, TouchableRipple, withTheme} from "react-native-paper"; +import {TouchableRipple} from "react-native-paper"; import ConnectionManager from "../../managers/ConnectionManager"; import LogoutDialog from "../Amicale/LogoutDialog"; +import SideBarSection from "./SideBarSection"; const deviceWidth = Dimensions.get("window").width; -const LIST_ITEM_HEIGHT = 48; type Props = { navigation: Object, @@ -30,8 +29,6 @@ class SideBar extends React.Component { dataSet: Array; - colors: Object; - /** * Generate the dataset * @@ -40,16 +37,14 @@ class SideBar extends React.Component { constructor(props: Props) { super(props); // Dataset used to render the drawer - this.dataSet = [ + const mainData = [ { name: i18n.t('screens.home'), route: "Main", icon: "home", }, - { - name: i18n.t('sidenav.divider4'), - route: "Divider4" - }, + ]; + const amicaleData = [ { name: i18n.t('screens.login'), route: "LoginScreen", @@ -82,10 +77,8 @@ class SideBar extends React.Component { icon: "logout", onlyWhenLoggedIn: true, }, - { - name: i18n.t('sidenav.divider2'), - route: "Divider2" - }, + ]; + const servicesData = [ { name: i18n.t('screens.menuSelf'), route: "SelfMenuScreen", @@ -113,10 +106,8 @@ class SideBar extends React.Component { link: "https://ent.insa-toulouse.fr/", icon: "notebook", }, - { - name: i18n.t('sidenav.divider1'), - route: "Divider1" - }, + ]; + const websitesData = [ { name: "Amicale", route: "AmicaleScreen", @@ -141,10 +132,8 @@ class SideBar extends React.Component { link: "https://www.etud.insa-toulouse.fr/~tutorinsa/", icon: "school", }, - { - name: i18n.t('sidenav.divider3'), - route: "Divider3" - }, + ]; + const othersData = [ { name: i18n.t('screens.settings'), route: "SettingsScreen", @@ -156,7 +145,39 @@ class SideBar extends React.Component { icon: "information", }, ]; - this.colors = props.theme.colors; + + this.dataSet = [ + { + key: '1', + name: i18n.t('screens.home'), + startOpen: true, // App always starts on Main + data: mainData + }, + { + key: '2', + name: i18n.t('sidenav.divider4'), + startOpen: false, // TODO set by user preferences + data: amicaleData + }, + { + key: '3', + name: i18n.t('sidenav.divider2'), + startOpen: false, + data: servicesData + }, + { + key: '4', + name: i18n.t('sidenav.divider1'), + startOpen: false, + data: websitesData + }, + { + key: '5', + name: i18n.t('sidenav.divider3'), + startOpen: false, + data: othersData + }, + ]; ConnectionManager.getInstance().addLoginStateListener(this.onLoginStateChange); this.props.navigation.addListener('state', this.onRouteChange); this.state = { @@ -166,7 +187,7 @@ class SideBar extends React.Component { }; } - onRouteChange = (event) => { + onRouteChange = (event: Object) => { try { const state = event.data.state.routes[0].state; // Get the Drawer's state if it exists // Get the current route name. This will only show Drawer routes. @@ -174,7 +195,7 @@ class SideBar extends React.Component { const routeName = state.routeNames[state.index]; if (this.state.activeRoute !== routeName) this.setState({activeRoute: routeName}); - } catch(e) { + } catch (e) { this.setState({activeRoute: 'Main'}); } @@ -184,34 +205,8 @@ class SideBar extends React.Component { hideDisconnectDialog = () => this.setState({dialogVisible: false}); - onLoginStateChange = (isLoggedIn: boolean) => this.setState({isLoggedIn: isLoggedIn}); - /** - * Callback when a drawer item is pressed. - * It will either navigate to the associated screen, or open the browser to the associated link - * - * @param item The item pressed - */ - onListItemPress(item: Object) { - if (item.link !== undefined) - openBrowser(item.link, this.colors.primary); - else if (item.action !== undefined) - item.action(); - else - this.props.navigation.navigate(item.route); - } - - /** - * Key extractor for list items - * - * @param item The item to extract the key from - * @return {string} The extracted key - */ - listKeyExtractor(item: Object): string { - return item.route; - } - /** * Gets the render item for the given list item * @@ -219,46 +214,22 @@ class SideBar extends React.Component { * @return {*} */ getRenderItem = ({item}: Object) => { - const onListItemPress = this.onListItemPress.bind(this, item); - const onlyWhenLoggedOut = item.onlyWhenLoggedOut !== undefined && item.onlyWhenLoggedOut === true; - const onlyWhenLoggedIn = item.onlyWhenLoggedIn !== undefined && item.onlyWhenLoggedIn === true; - const shouldEmphasis = item.shouldEmphasis !== undefined && item.shouldEmphasis === true; - if (onlyWhenLoggedIn && !this.state.isLoggedIn || onlyWhenLoggedOut && this.state.isLoggedIn) - return null; - else if (item.icon !== undefined) { - return ( - - ); - } else { - return ( - - ); - } + return }; - itemLayout = (data, index) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index}); - render() { - const onPress = this.onListItemPress.bind(this, {route: 'TetrisScreen'}); return ( this.props.navigation.navigate("TetrisScreen")} > {