diff --git a/app.json b/app.json index f617336..a276e0e 100644 --- a/app.json +++ b/app.json @@ -10,7 +10,7 @@ "android", "web" ], - "version": "1.0.2", + "version": "1.0.3", "orientation": "portrait", "primaryColor": "#be1522", "icon": "./assets/android.icon.png", @@ -36,7 +36,7 @@ }, "android": { "package": "fr.amicaleinsat.application", - "versionCode": 5, + "versionCode": 6, "icon": "./assets/android.icon.png", "adaptiveIcon": { "foregroundImage": "./assets/android.adaptive-icon.png", diff --git a/native-base-theme/variables/platform.js b/native-base-theme/variables/platform.js index 991484b..5f83cf7 100644 --- a/native-base-theme/variables/platform.js +++ b/native-base-theme/variables/platform.js @@ -242,6 +242,7 @@ export default { // Text textColor: "#000", + textDisabledColor: "#d9e1e8", inverseTextColor: "#fff", noteFontSize: 14, get defaultTextColor() { @@ -260,6 +261,10 @@ export default { customMaterialIconColor: "#5d5d5d", fetchedDataSectionListErrorText: "#898989", + // Calendar/Agenda + agendaBackgroundColor: '#f3f3f4', + agendaEmptyLine: '#dbdbdc', + // PROXIWASH proxiwashFinishedColor: "rgba(54,165,22,0.31)", proxiwashReadyColor: "transparent", diff --git a/native-base-theme/variables/platformDark.js b/native-base-theme/variables/platformDark.js index 352dfec..6abea3a 100644 --- a/native-base-theme/variables/platformDark.js +++ b/native-base-theme/variables/platformDark.js @@ -243,6 +243,7 @@ export default { // Text textColor: "#ebebeb", + textDisabledColor: "#3b3f42", inverseTextColor: "#000", noteFontSize: 14, get defaultTextColor() { @@ -261,6 +262,10 @@ export default { customMaterialIconColor: "#b3b3b3", fetchedDataSectionListErrorText: "#acacac", + // Calendar/Agenda + agendaBackgroundColor: '#373737', + agendaEmptyLine: '#464646', + // PROXIWASH proxiwashFinishedColor: "rgba(17,149,32,0.53)", proxiwashReadyColor: "transparent", diff --git a/package-lock.json b/package-lock.json index 1b5c1e6..b1403f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1866,6 +1866,16 @@ "node-int64": "^0.4.0" } }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, "buffer-alloc": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", @@ -2504,11 +2514,54 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "dom-serializer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.1.tgz", + "integrity": "sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q==", + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" + }, + "entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==" + } + } + }, "dom-walk": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2540,6 +2593,11 @@ "once": "^1.4.0" } }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, "envinfo": { "version": "5.12.1", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-5.12.1.tgz", @@ -2597,6 +2655,11 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, "exec-sh": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz", @@ -4067,6 +4130,36 @@ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" }, + "html-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=" + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "http-errors": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", @@ -4092,6 +4185,11 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, "image-size": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz", @@ -6131,6 +6229,16 @@ "resolved": "https://registry.npmjs.org/react-native-branch/-/react-native-branch-2.2.5.tgz", "integrity": "sha1-QHTdY7SXPmOX2c5Q6XtXx3pRjp0=" }, + "react-native-calendars": { + "version": "1.212.0", + "resolved": "https://registry.npmjs.org/react-native-calendars/-/react-native-calendars-1.212.0.tgz", + "integrity": "sha512-FrWabRJlUeTjCF8qx8cYAId4SbCMmVmO5eE791ykQaTG2/jvQJz2O3MF5QRjJHyaS/QdeEilKf9w/Mk43bM42Q==", + "requires": { + "lodash": "^4.0.0", + "prop-types": "^15.5.10", + "xdate": "^0.8.0" + } + }, "react-native-drawer": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/react-native-drawer/-/react-native-drawer-2.5.1.tgz", @@ -6242,6 +6350,17 @@ "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-1.0.1.tgz", "integrity": "sha512-RENoo6/sJc3FApP7vJ1Js7WyDuTVh97bbr5aMjJyw3kqpR2/JDHyL/dQFfOvSSAc+VjitpR9/CfPPad7tLRiIA==" }, + "react-native-render-html": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/react-native-render-html/-/react-native-render-html-4.1.2.tgz", + "integrity": "sha512-lIW7GfNCsqOzdv0hxiZms24uC9Hu8hqufMyD4FBjfs8HSc4pUKnvBgOljiqEXmjGhDEJM6oY7QGglAV0hL69bQ==", + "requires": { + "buffer": "^4.5.1", + "events": "^1.1.0", + "html-entities": "^1.2.0", + "htmlparser2": "^3.10.1" + } + }, "react-native-safe-area-view": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/react-native-safe-area-view/-/react-native-safe-area-view-0.12.0.tgz", @@ -8208,6 +8327,11 @@ "uuid": "^3.3.2" } }, + "xdate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/xdate/-/xdate-0.8.2.tgz", + "integrity": "sha1-17AzwASF0CaVuvAET06s2j/JYaM=" + }, "xmlbuilder": { "version": "9.0.7", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", diff --git a/package.json b/package.json index d7b7f4b..77371fe 100644 --- a/package.json +++ b/package.json @@ -23,10 +23,12 @@ "react-native": "^0.59.9", "react-native-app-intro-slider": "^3.0.0", "react-native-autolink": "^1.8.1", + "react-native-calendars": "^1.212.0", "react-native-modal": "^11.3.1", "react-native-modalize": "^1.2.1", "react-native-paper": "^2.16.0", "react-native-platform-touchable": "^1.1.1", + "react-native-render-html": "^4.1.2", "react-native-side-menu": "^1.1.3", "react-native-status-bar-height": "^2.3.1", "react-native-webview": "^5.8.1", diff --git a/screens/PlanningScreen.js b/screens/PlanningScreen.js index b8ccd77..536db7a 100644 --- a/screens/PlanningScreen.js +++ b/screens/PlanningScreen.js @@ -1,18 +1,33 @@ // @flow import * as React from 'react'; -import {Button, H3, Text} from 'native-base'; +import {Content, H1, H2, H3, Text} from 'native-base'; import i18n from "i18n-js"; -import {View, Platform} from "react-native"; -import CustomMaterialIcon from "../components/CustomMaterialIcon"; +import {View, Image} from "react-native"; import ThemeManager from "../utils/ThemeManager"; import {Linking} from "expo"; import BaseContainer from "../components/BaseContainer"; +import {Agenda} 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"; + type Props = { navigation: Object, } +type State = { + modalCurrentDisplayItem: Object, + refreshing: boolean, + agendaItems: Object, +}; + +const FETCH_URL = "https://amicale-insat.fr/event/json/list"; + +const AGENDA_MONTH_SPAN = 6; + /** * Opens a link in the device's browser * @param link The link to open @@ -24,67 +39,286 @@ function openWebLink(link) { /** * Class defining the app's planning screen */ -export default class PlanningScreen extends React.Component { +export default class PlanningScreen extends React.Component { + modalRef: { current: null | Modalize }; + webDataManager: WebDataManager; + + constructor(props: any) { + super(props); + this.modalRef = React.createRef(); + this.webDataManager = new WebDataManager(FETCH_URL); + } + + componentDidMount() { + this._onRefresh(); + } + + state = { + modalCurrentDisplayItem: {}, + refreshing: false, + agendaItems: {} + }; + + getCurrentDate() { + let today = new Date(); + return this.getFormattedDate(today); + } + + getFormattedDate(date: Date) { + let dd = String(date.getDate()).padStart(2, '0'); + let mm = String(date.getMonth() + 1).padStart(2, '0'); //January is 0! + let yyyy = date.getFullYear(); + return yyyy + '-' + mm + '-' + dd; + } + + generateEmptyCalendar() { + let end = new Date(new Date().setMonth(new Date().getMonth() + AGENDA_MONTH_SPAN + 1)); + let daysOfYear = {}; + for (let d = new Date(2019, 8, 1); d <= end; d.setDate(d.getDate() + 1)) { + daysOfYear[this.getFormattedDate(new Date(d))] = [] + } + return daysOfYear; + } + + 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}, + div: {color: ThemeManager.getCurrentThemeVariables().textColor} + }}/> + : } + +
+ ); + } + + showItemDetails(item: Object) { + this.setState({ + modalCurrentDisplayItem: item + }); + if (this.modalRef.current) { + this.modalRef.current.open(); + } + } + + getRenderItem(item: Object) { + return ( + this.showItemDetails(item)}> + + + + {this.getFormattedTime(item)} + +

{item.title}

+
+ + {item.logo !== null ? + + : } + + +
+ ); + } + + getRenderEmptyDate() { + return ( + + + + ); + } + + rowHasChanged(r1: Object, r2: Object) { + if (r1 !== undefined && r2 !== undefined) + return r1.title !== r2.title; + else return !(r1 === undefined && r2 === undefined); + } + + /** + * Refresh data and show a toast if any error occurred + * @private + */ + _onRefresh = () => { + this.setState({refreshing: true}); + this.webDataManager.readData() + .then((fetchedData) => { + this.setState({ + refreshing: false, + }); + this.generateEventAgenda(fetchedData); + }) + .catch((err) => { + this.setState({ + refreshing: false, + }); + console.log(err); + }); + }; + + generateEventAgenda(eventList: Array) { + let agendaItems = this.generateEmptyCalendar(); + for (let i = 0; i < eventList.length; i++) { + if (agendaItems[this.getEventStartDate(eventList[i])] !== undefined) + agendaItems[this.getEventStartDate(eventList[i])].push(eventList[i]); + } + this.setState({agendaItems: agendaItems}) + } + + 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]; + } render() { const nav = this.props.navigation; return ( - - { Platform.OS === 'android' ? - - - - - -

- {i18n.t('planningScreen.wipTitle')} -

- - {i18n.t('planningScreen.wipSubtitle')} - -
- : - - - } - - - - - + + {this.getModalContent()} + + this._onRefresh()} + // Set this true while waiting for new data from a refresh + refreshing={this.state.refreshing} + renderItem={(item) => this.getRenderItem(item)} + renderEmptyDate={() => this.getRenderEmptyDate()} + rowHasChanged={() => this.rowHasChanged()} + // agenda theme + theme={{ + backgroundColor: ThemeManager.getCurrentThemeVariables().agendaBackgroundColor, + calendarBackground: ThemeManager.getCurrentThemeVariables().containerBgColor, + textSectionTitleColor: ThemeManager.getCurrentThemeVariables().listNoteColor, + selectedDayBackgroundColor: ThemeManager.getCurrentThemeVariables().brandPrimary, + selectedDayTextColor: '#ffffff', + todayTextColor: ThemeManager.getCurrentThemeVariables().brandPrimary, + dayTextColor: ThemeManager.getCurrentThemeVariables().textColor, + textDisabledColor: ThemeManager.getCurrentThemeVariables().textDisabledColor, + dotColor: ThemeManager.getCurrentThemeVariables().brandPrimary, + selectedDotColor: '#ffffff', + arrowColor: 'orange', + monthTextColor: ThemeManager.getCurrentThemeVariables().brandPrimary, + indicatorColor: ThemeManager.getCurrentThemeVariables().brandPrimary, + textDayFontWeight: '300', + textMonthFontWeight: 'bold', + textDayHeaderFontWeight: '300', + textDayFontSize: 16, + textMonthFontSize: 16, + textDayHeaderFontSize: 16, + agendaDayTextColor: ThemeManager.getCurrentThemeVariables().listNoteColor, + 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' + } + } + }} + />
); } } -