diff --git a/components/BaseContainer.js b/components/BaseContainer.js index 39ca881..1e4fa85 100644 --- a/components/BaseContainer.js +++ b/components/BaseContainer.js @@ -14,6 +14,7 @@ import {NavigationActions} from "react-navigation"; type Props = { navigation: Object, headerTitle: string, + headerSubtitle: string, headerRightButton: React.Node, children: React.Node, hasTabs: boolean, @@ -37,6 +38,7 @@ export default class BaseContainer extends React.Component { hasSideMenu: true, enableRotation: false, hideHeaderOnLandscape: false, + headerSubtitle: '', }; willBlurSubscription: function; willFocusSubscription: function; @@ -96,7 +98,9 @@ export default class BaseContainer extends React.Component { {this.state.isHeaderVisible ? { searchCallback: () => null, shouldFocusSearchBar: false, title: '', + subtitle: '', leftButton: , rightButton: , hasTabs: false, @@ -52,23 +54,39 @@ export default class CustomHeader extends React.Component { getSearchBar() { return ( - - - this.props.searchCallback(text)}/> - + + + + this.props.searchCallback(text)}/> + + ); } + getHeaderTitle() { + return ( + + + {this.props.title} + + {this.props.subtitle !== '' ? {this.props.subtitle} : null} + + ); + } + + render() { let button; // Does the app have a back button or a burger menu ? @@ -93,14 +111,9 @@ export default class CustomHeader extends React.Component { {button} - - {this.props.hasSearchField ? - this.getSearchBar() : - {this.props.title}} - + {this.props.hasSearchField ? + this.getSearchBar() : + this.getHeaderTitle()} {this.props.rightButton} @@ -108,7 +121,6 @@ export default class CustomHeader extends React.Component { } }; - // Fix header in status bar on Android const styles = StyleSheet.create({ header: { diff --git a/navigation/MainTabNavigator.js b/navigation/MainTabNavigator.js index d511c8f..965b21b 100644 --- a/navigation/MainTabNavigator.js +++ b/navigation/MainTabNavigator.js @@ -4,6 +4,7 @@ import {createMaterialBottomTabNavigator} from "react-navigation-material-bottom import HomeScreen from '../screens/HomeScreen'; import PlanningScreen from '../screens/PlanningScreen'; +import PlanningDisplayScreen from '../screens/PlanningDisplayScreen'; import ProxiwashScreen from '../screens/Proxiwash/ProxiwashScreen'; import ProxiwashAboutScreen from '../screens/Proxiwash/ProxiwashAboutScreen'; import ProximoMainScreen from '../screens/Proximo/ProximoMainScreen'; @@ -57,10 +58,40 @@ const ProxiwashStack = createStackNavigator({ }, }); +const PlanningStack = createStackNavigator({ + PlanningScreen: {screen: PlanningScreen}, + PlanningDisplayScreen: {screen: PlanningDisplayScreen}, + }, + { + initialRouteName: "PlanningScreen", + mode: 'card', + headerMode: "none", + defaultNavigationOptions: { + gestureEnabled: true, + cardOverlayEnabled: true, + ...TransitionPresets.ModalPresentationIOS, + }, + }); + +const HomeStack = createStackNavigator({ + HomeScreen: {screen: HomeScreen}, + PlanningDisplayScreen: {screen: PlanningDisplayScreen}, + }, + { + initialRouteName: "HomeScreen", + mode: 'card', + headerMode: "none", + defaultNavigationOptions: { + gestureEnabled: true, + cardOverlayEnabled: true, + ...TransitionPresets.ModalPresentationIOS, + }, + }); + function createMaterialBottomTabNavigatorWithInitialRoute(initialRoute: string) { return createMaterialBottomTabNavigator({ - Home: {screen: HomeScreen}, - Planning: {screen: PlanningScreen}, + Home: HomeStack, + Planning: PlanningStack, Proxiwash: ProxiwashStack, Proximo: ProximoStack, Planex: { diff --git a/screens/HomeScreen.js b/screens/HomeScreen.js index 5fead20..f66eb60 100644 --- a/screens/HomeScreen.js +++ b/screens/HomeScreen.js @@ -310,7 +310,7 @@ export default class HomeScreen extends FetchedDataSectionList { ; } else subtitle = i18n.t('homeScreen.dashboard.todayEventsSubtitleNA'); - let clickAction = () => this.props.navigation.navigate('Planning'); + let clickAction = () => this.props.navigation.navigate('PlanningDisplayScreen', {data: displayEvent}); let displayEvent = this.getDisplayEvent(futureEvents); diff --git a/screens/PlanningDisplayScreen.js b/screens/PlanningDisplayScreen.js new file mode 100644 index 0000000..bd75d36 --- /dev/null +++ b/screens/PlanningDisplayScreen.js @@ -0,0 +1,63 @@ +// @flow + +import * as React from 'react'; +import {Image} from 'react-native'; +import {Container, Content, H1, H3, View} from 'native-base'; +import CustomHeader from "../components/CustomHeader"; +import ThemeManager from "../utils/ThemeManager"; +import HTML from "react-native-render-html"; +import {Linking} from "expo"; +import PlanningEventManager from '../utils/PlanningEventManager'; +import i18n from 'i18n-js'; + +type Props = { + navigation: Object, +}; + +function openWebLink(link) { + Linking.openURL(link).catch((err) => console.error('Error opening link', err)); +} + +/** + * Class defining an about screen. This screen shows the user information about the app and it's author. + */ +export default class PlanningDisplayScreen extends React.Component { + + render() { + const nav = this.props.navigation; + const displayData = nav.getParam('data', []); + return ( + + + +

+ {displayData.title} +

+ {displayData.logo !== null ? + + + + : } + + {displayData.description !== null ? + // Surround description with div to allow text styling if the description is not html + " + displayData.description + ""} + tagsStyles={{ + p: { + color: ThemeManager.getCurrentThemeVariables().textColor, + fontSize: ThemeManager.getCurrentThemeVariables().fontSizeBase + }, + div: {color: ThemeManager.getCurrentThemeVariables().textColor} + }} + onLinkPress={(event, link) => openWebLink(link)}/> + : } +
+
+ ); + } +} diff --git a/screens/PlanningScreen.js b/screens/PlanningScreen.js index 1fe69ac..d683cd8 100644 --- a/screens/PlanningScreen.js +++ b/screens/PlanningScreen.js @@ -1,18 +1,16 @@ // @flow import * as React from 'react'; -import {BackHandler, Image, View} from 'react-native'; -import {Button, Content, H1, H3, Text} from 'native-base'; +import {BackHandler, Image} from 'react-native'; +import {H3, Text, View} from 'native-base'; import i18n from "i18n-js"; import ThemeManager from "../utils/ThemeManager"; import {Linking} from "expo"; import BaseContainer from "../components/BaseContainer"; import {Agenda, LocaleConfig} from 'react-native-calendars'; -import HTML from 'react-native-render-html'; import Touchable from 'react-native-platform-touchable'; -import {Modalize} from 'react-native-modalize'; import WebDataManager from "../utils/WebDataManager"; -import CustomMaterialIcon from "../components/CustomMaterialIcon"; +import PlanningEventManager from '../utils/PlanningEventManager'; LocaleConfig.locales['fr'] = { monthNames: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'], @@ -28,7 +26,6 @@ type Props = { } type State = { - modalCurrentDisplayItem: Object, refreshing: boolean, agendaItems: Object, calendarShowing: boolean, @@ -51,7 +48,6 @@ function openWebLink(link) { */ export default class PlanningScreen extends React.Component { - modalRef: Modalize; agendaRef: Agenda; webDataManager: WebDataManager; @@ -61,7 +57,6 @@ export default class PlanningScreen extends React.Component { didFocusSubscription: Function; willBlurSubscription: Function; state = { - modalCurrentDisplayItem: {}, refreshing: false, agendaItems: {}, calendarShowing: false, @@ -69,7 +64,6 @@ export default class PlanningScreen extends React.Component { constructor(props: any) { super(props); - this.modalRef = React.createRef(); this.webDataManager = new WebDataManager(FETCH_URL); this.didFocusSubscription = props.navigation.addListener( 'didFocus', @@ -131,72 +125,11 @@ export default class PlanningScreen extends React.Component { return daysOfYear; } - getModalHeader() { - return ( - - - - ); - } - - getModalContent() { - return ( - -

- {this.state.modalCurrentDisplayItem.title} -

-

- {this.getFormattedTime(this.state.modalCurrentDisplayItem)} -

- - {this.state.modalCurrentDisplayItem.logo !== null ? - - - - : } - - {this.state.modalCurrentDisplayItem.description !== null ? - // Surround description with div to allow text styling if the description is not html - " + this.state.modalCurrentDisplayItem.description + ""} - tagsStyles={{ - p: { - color: ThemeManager.getCurrentThemeVariables().textColor, - fontSize: ThemeManager.getCurrentThemeVariables().fontSizeBase - }, - div: {color: ThemeManager.getCurrentThemeVariables().textColor} - }} - onLinkPress={(event, link) => openWebLink(link)}/> - : } - -
- ); - } - - showItemDetails(item: Object) { - this.setState({ - modalCurrentDisplayItem: item, - }); - if (this.modalRef.current) { - this.modalRef.current.open(); - } - } - getRenderItem(item: Object) { + let navData = { + data: item + }; + const nav = this.props.navigation; return ( { marginRight: 10, marginTop: 17, }} - onPress={() => this.showItemDetails(item)}> + onPress={() => nav.navigate('PlanningDisplayScreen', navData)}> { marginTop: 5, marginBottom: 10 }}> - {this.getFormattedTime(item)} + {PlanningEventManager.getFormattedTime(item)}

{item.title}

@@ -296,8 +229,8 @@ export default class PlanningScreen extends React.Component { generateEventAgenda(eventList: Array) { let agendaItems = this.generateEmptyCalendar(); for (let i = 0; i < eventList.length; i++) { - if (agendaItems[this.getEventStartDate(eventList[i])] !== undefined) { - this.pushEventInOrder(agendaItems, eventList[i], this.getEventStartDate(eventList[i])); + if (agendaItems[PlanningEventManager.getEventStartDate(eventList[i])] !== undefined) { + this.pushEventInOrder(agendaItems, eventList[i], PlanningEventManager.getEventStartDate(eventList[i])); } } this.setState({agendaItems: agendaItems}) @@ -308,7 +241,7 @@ export default class PlanningScreen extends React.Component { agendaItems[startDate].push(event); else { for (let i = 0; i < agendaItems[startDate].length; i++) { - if (this.isEventBefore(event, agendaItems[startDate][i])) { + if (PlanningEventManager.isEventBefore(event, agendaItems[startDate][i])) { agendaItems[startDate].splice(i, 0, event); break; } else if (i === agendaItems[startDate].length - 1) { @@ -319,65 +252,10 @@ export default class PlanningScreen extends React.Component { } } - isEventBefore(event1: Object, event2: Object) { - let date1 = new Date(); - let date2 = new Date(); - let timeArray = this.getEventStartTime(event1).split(":"); - date1.setHours(parseInt(timeArray[0]), parseInt(timeArray[1])); - timeArray = this.getEventStartTime(event2).split(":"); - date2.setHours(parseInt(timeArray[0]), parseInt(timeArray[1])); - return date1 < date2; - } - - getEventStartDate(event: Object) { - return event.date_begin.split(" ")[0]; - } - - getEventStartTime(event: Object) { - if (event !== undefined && Object.keys(event).length > 0 && event.date_begin !== null) - return this.formatTime(event.date_begin.split(" ")[1]); - else - return ""; - } - - getEventEndTime(event: Object) { - if (event !== undefined && Object.keys(event).length > 0 && event.date_end !== null) - return this.formatTime(event.date_end.split(" ")[1]); - else - return ""; - } - - getFormattedTime(event: Object) { - if (this.getEventEndTime(event) !== "") - return this.getEventStartTime(event) + " - " + this.getEventEndTime(event); - else - return this.getEventStartTime(event); - } - - formatTime(time: string) { - let array = time.split(':'); - return array[0] + ':' + array[1]; - } - - onModalClosed() { - this.setState({ - modalCurrentDisplayItem: {}, - }); - } - render() { const nav = this.props.navigation; return ( - this.getModalHeader()} - onClosed={() => this.onModalClosed()}> - {this.getModalContent()} - { agendaDayNumColor: ThemeManager.getCurrentThemeVariables().listNoteColor, agendaTodayColor: ThemeManager.getCurrentThemeVariables().brandPrimary, agendaKnobColor: ThemeManager.getCurrentThemeVariables().brandPrimary, - // Fix for days hiding behind knob - 'stylesheet.calendar.header': { - week: { - marginTop: 0, - flexDirection: 'row', - justifyContent: 'space-between' - } - } }} /> diff --git a/translations/en.json b/translations/en.json index 3cd8800..852f257 100644 --- a/translations/en.json +++ b/translations/en.json @@ -2,6 +2,7 @@ "screens": { "home": "Home", "planning": "Planning", + "planningDisplayScreen": "Event Details", "proxiwash": "Proxiwash", "proximo": "Proximo", "menuSelf": "RU Menu", diff --git a/translations/fr.json b/translations/fr.json index d2c78a1..8d506a8 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -2,6 +2,7 @@ "screens": { "home": "Accueil", "planning": "Planning", + "planningDisplayScreen": "Détails", "proxiwash": "Proxiwash", "proximo": "Proximo", "menuSelf": "Menu du RU", diff --git a/utils/PlanningEventManager.js b/utils/PlanningEventManager.js new file mode 100644 index 0000000..a853057 --- /dev/null +++ b/utils/PlanningEventManager.js @@ -0,0 +1,42 @@ + +export default class PlanningEventManager { + static isEventBefore(event1: Object, event2: Object) { + let date1 = new Date(); + let date2 = new Date(); + let timeArray = this.getEventStartTime(event1).split(":"); + date1.setHours(parseInt(timeArray[0]), parseInt(timeArray[1])); + timeArray = this.getEventStartTime(event2).split(":"); + date2.setHours(parseInt(timeArray[0]), parseInt(timeArray[1])); + return date1 < date2; + } + + static getEventStartDate(event: Object) { + return event.date_begin.split(" ")[0]; + } + + static getEventStartTime(event: Object) { + if (event !== undefined && Object.keys(event).length > 0 && event.date_begin !== null) + return this.formatTime(event.date_begin.split(" ")[1]); + else + return ""; + } + + static getEventEndTime(event: Object) { + if (event !== undefined && Object.keys(event).length > 0 && event.date_end !== null) + return this.formatTime(event.date_end.split(" ")[1]); + else + return ""; + } + + static getFormattedTime(event: Object) { + if (this.getEventEndTime(event) !== "") + return this.getEventStartTime(event) + " - " + this.getEventEndTime(event); + else + return this.getEventStartTime(event); + } + + static formatTime(time: string) { + let array = time.split(':'); + return array[0] + ':' + array[1]; + } +}