forked from vergnet/application-amicale
		
	Improved project structure
This commit is contained in:
		
							parent
							
								
									fac9d8208e
								
							
						
					
					
						commit
						7e90b80ca2
					
				
					 30 changed files with 672 additions and 698 deletions
				
			
		
							
								
								
									
										12
									
								
								App.js
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								App.js
									
									
									
									
									
								
							|  | @ -2,17 +2,17 @@ | |||
| 
 | ||||
| import * as React from 'react'; | ||||
| import {Platform, StatusBar} from 'react-native'; | ||||
| import LocaleManager from './utils/LocaleManager'; | ||||
| import AsyncStorageManager from "./utils/AsyncStorageManager"; | ||||
| import LocaleManager from './managers/LocaleManager'; | ||||
| import AsyncStorageManager from "./managers/AsyncStorageManager"; | ||||
| import CustomIntroSlider from "./components/CustomIntroSlider"; | ||||
| import {SplashScreen} from 'expo'; | ||||
| import ThemeManager from './utils/ThemeManager'; | ||||
| import ThemeManager from './managers/ThemeManager'; | ||||
| import {NavigationContainer} from '@react-navigation/native'; | ||||
| import {createStackNavigator} from '@react-navigation/stack'; | ||||
| import DrawerNavigator from './navigation/DrawerNavigator'; | ||||
| import NotificationsManager from "./utils/NotificationsManager"; | ||||
| import {initExpoToken} from "./utils/Notifications"; | ||||
| import {Provider as PaperProvider} from 'react-native-paper'; | ||||
| import AprilFoolsManager from "./utils/AprilFoolsManager"; | ||||
| import AprilFoolsManager from "./managers/AprilFoolsManager"; | ||||
| import Update from "./constants/Update"; | ||||
| 
 | ||||
| type Props = {}; | ||||
|  | @ -90,7 +90,7 @@ export default class App extends React.Component<Props, State> { | |||
|         // Wait for custom fonts to be loaded before showing the app
 | ||||
|         await AsyncStorageManager.getInstance().loadPreferences(); | ||||
|         ThemeManager.getInstance().setUpdateThemeCallback(this.onUpdateTheme); | ||||
|         await NotificationsManager.initExpoToken(); | ||||
|         await initExpoToken(); | ||||
|         this.onLoadFinished(); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										210
									
								
								__tests__/utils/PlanningEventManager.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								__tests__/utils/PlanningEventManager.test.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,210 @@ | |||
| import React from 'react'; | ||||
| import * as Planning from "../../utils/Planning"; | ||||
| 
 | ||||
| test('isDescriptionEmpty', () => { | ||||
|     expect(Planning.isDescriptionEmpty("")).toBeTrue(); | ||||
|     expect(Planning.isDescriptionEmpty("   ")).toBeTrue(); | ||||
|     // noinspection CheckTagEmptyBody
 | ||||
|     expect(Planning.isDescriptionEmpty("<p></p>")).toBeTrue(); | ||||
|     expect(Planning.isDescriptionEmpty("<p>   </p>")).toBeTrue(); | ||||
|     expect(Planning.isDescriptionEmpty("<p><br></p>")).toBeTrue(); | ||||
|     expect(Planning.isDescriptionEmpty("<p><br></p><p><br></p>")).toBeTrue(); | ||||
|     expect(Planning.isDescriptionEmpty("<p><br><br><br></p>")).toBeTrue(); | ||||
|     expect(Planning.isDescriptionEmpty("<p><br>")).toBeTrue(); | ||||
|     expect(Planning.isDescriptionEmpty(null)).toBeTrue(); | ||||
|     expect(Planning.isDescriptionEmpty(undefined)).toBeTrue(); | ||||
|     expect(Planning.isDescriptionEmpty("coucou")).toBeFalse(); | ||||
|     expect(Planning.isDescriptionEmpty("<p>coucou</p>")).toBeFalse(); | ||||
| }); | ||||
| 
 | ||||
| test('isEventDateStringFormatValid', () => { | ||||
|     expect(Planning.isEventDateStringFormatValid("2020-03-21 09:00")).toBeTrue(); | ||||
|     expect(Planning.isEventDateStringFormatValid("3214-64-12 01:16")).toBeTrue(); | ||||
| 
 | ||||
|     expect(Planning.isEventDateStringFormatValid("3214-64-12 01:16:00")).toBeFalse(); | ||||
|     expect(Planning.isEventDateStringFormatValid("3214-64-12 1:16")).toBeFalse(); | ||||
|     expect(Planning.isEventDateStringFormatValid("3214-f4-12 01:16")).toBeFalse(); | ||||
|     expect(Planning.isEventDateStringFormatValid("sqdd 09:00")).toBeFalse(); | ||||
|     expect(Planning.isEventDateStringFormatValid("2020-03-21")).toBeFalse(); | ||||
|     expect(Planning.isEventDateStringFormatValid("2020-03-21 truc")).toBeFalse(); | ||||
|     expect(Planning.isEventDateStringFormatValid("3214-64-12 1:16:65")).toBeFalse(); | ||||
|     expect(Planning.isEventDateStringFormatValid("garbage")).toBeFalse(); | ||||
|     expect(Planning.isEventDateStringFormatValid("")).toBeFalse(); | ||||
|     expect(Planning.isEventDateStringFormatValid(undefined)).toBeFalse(); | ||||
|     expect(Planning.isEventDateStringFormatValid(null)).toBeFalse(); | ||||
| }); | ||||
| 
 | ||||
| test('stringToDate', () => { | ||||
|     let testDate = new Date(); | ||||
|     expect(Planning.stringToDate(undefined)).toBeNull(); | ||||
|     expect(Planning.stringToDate("")).toBeNull(); | ||||
|     expect(Planning.stringToDate("garbage")).toBeNull(); | ||||
|     expect(Planning.stringToDate("2020-03-21")).toBeNull(); | ||||
|     expect(Planning.stringToDate("09:00:00")).toBeNull(); | ||||
|     expect(Planning.stringToDate("2020-03-21 09:g0")).toBeNull(); | ||||
|     expect(Planning.stringToDate("2020-03-21 09:g0:")).toBeNull(); | ||||
|     testDate.setFullYear(2020, 2, 21); | ||||
|     testDate.setHours(9, 0, 0, 0); | ||||
|     expect(Planning.stringToDate("2020-03-21 09:00")).toEqual(testDate); | ||||
|     testDate.setFullYear(2020, 0, 31); | ||||
|     testDate.setHours(18, 30, 0, 0); | ||||
|     expect(Planning.stringToDate("2020-01-31 18:30")).toEqual(testDate); | ||||
|     testDate.setFullYear(2020, 50, 50); | ||||
|     testDate.setHours(65, 65, 0, 0); | ||||
|     expect(Planning.stringToDate("2020-51-50 65:65")).toEqual(testDate); | ||||
| }); | ||||
| 
 | ||||
| test('getFormattedEventTime', () => { | ||||
|     expect(Planning.getFormattedEventTime(null, null)) | ||||
|         .toBe('/ - /'); | ||||
|     expect(Planning.getFormattedEventTime(undefined, undefined)) | ||||
|         .toBe('/ - /'); | ||||
|     expect(Planning.getFormattedEventTime("20:30", "23:00")) | ||||
|         .toBe('/ - /'); | ||||
|     expect(Planning.getFormattedEventTime("2020-03-30", "2020-03-31")) | ||||
|         .toBe('/ - /'); | ||||
| 
 | ||||
| 
 | ||||
|     expect(Planning.getFormattedEventTime("2020-03-21 09:00", "2020-03-21 09:00")) | ||||
|         .toBe('09:00'); | ||||
|     expect(Planning.getFormattedEventTime("2020-03-21 09:00", "2020-03-22 17:00")) | ||||
|         .toBe('09:00 - 23:59'); | ||||
|     expect(Planning.getFormattedEventTime("2020-03-30 20:30", "2020-03-30 23:00")) | ||||
|         .toBe('20:30 - 23:00'); | ||||
| }); | ||||
| 
 | ||||
| test('getDateOnlyString', () => { | ||||
|     expect(Planning.getDateOnlyString("2020-03-21 09:00")).toBe("2020-03-21"); | ||||
|     expect(Planning.getDateOnlyString("2021-12-15 09:00")).toBe("2021-12-15"); | ||||
|     expect(Planning.getDateOnlyString("2021-12-o5 09:00")).toBeNull(); | ||||
|     expect(Planning.getDateOnlyString("2021-12-15 09:")).toBeNull(); | ||||
|     expect(Planning.getDateOnlyString("2021-12-15")).toBeNull(); | ||||
|     expect(Planning.getDateOnlyString("garbage")).toBeNull(); | ||||
| }); | ||||
| 
 | ||||
| test('isEventBefore', () => { | ||||
|     expect(Planning.isEventBefore( | ||||
|         "2020-03-21 09:00", "2020-03-21 10:00")).toBeTrue(); | ||||
|     expect(Planning.isEventBefore( | ||||
|         "2020-03-21 10:00", "2020-03-21 10:15")).toBeTrue(); | ||||
|     expect(Planning.isEventBefore( | ||||
|         "2020-03-21 10:15", "2021-03-21 10:15")).toBeTrue(); | ||||
|     expect(Planning.isEventBefore( | ||||
|         "2020-03-21 10:15", "2020-05-21 10:15")).toBeTrue(); | ||||
|     expect(Planning.isEventBefore( | ||||
|         "2020-03-21 10:15", "2020-03-30 10:15")).toBeTrue(); | ||||
| 
 | ||||
|     expect(Planning.isEventBefore( | ||||
|         "2020-03-21 10:00", "2020-03-21 10:00")).toBeFalse(); | ||||
|     expect(Planning.isEventBefore( | ||||
|         "2020-03-21 10:00", "2020-03-21 09:00")).toBeFalse(); | ||||
|     expect(Planning.isEventBefore( | ||||
|         "2020-03-21 10:15", "2020-03-21 10:00")).toBeFalse(); | ||||
|     expect(Planning.isEventBefore( | ||||
|         "2021-03-21 10:15", "2020-03-21 10:15")).toBeFalse(); | ||||
|     expect(Planning.isEventBefore( | ||||
|         "2020-05-21 10:15", "2020-03-21 10:15")).toBeFalse(); | ||||
|     expect(Planning.isEventBefore( | ||||
|         "2020-03-30 10:15", "2020-03-21 10:15")).toBeFalse(); | ||||
| 
 | ||||
|     expect(Planning.isEventBefore( | ||||
|         "garbage", "2020-03-21 10:15")).toBeFalse(); | ||||
|     expect(Planning.isEventBefore( | ||||
|         undefined, undefined)).toBeFalse(); | ||||
| }); | ||||
| 
 | ||||
| test('dateToString', () => { | ||||
|     let testDate = new Date(); | ||||
|     testDate.setFullYear(2020, 2, 21); | ||||
|     testDate.setHours(9, 0, 0, 0); | ||||
|     expect(Planning.dateToString(testDate)).toBe("2020-03-21 09:00"); | ||||
|     testDate.setFullYear(2021, 0, 12); | ||||
|     testDate.setHours(9, 10, 0, 0); | ||||
|     expect(Planning.dateToString(testDate)).toBe("2021-01-12 09:10"); | ||||
|     testDate.setFullYear(2022, 11, 31); | ||||
|     testDate.setHours(9, 10, 15, 0); | ||||
|     expect(Planning.dateToString(testDate)).toBe("2022-12-31 09:10"); | ||||
| }); | ||||
| 
 | ||||
| test('generateEmptyCalendar', () => { | ||||
|     jest.spyOn(Date, 'now') | ||||
|         .mockImplementation(() => | ||||
|             new Date('2020-01-14T00:00:00.000Z').getTime() | ||||
|         ); | ||||
|     let calendar = Planning.generateEmptyCalendar(1); | ||||
|     expect(calendar).toHaveProperty("2020-01-14"); | ||||
|     expect(calendar).toHaveProperty("2020-01-20"); | ||||
|     expect(calendar).toHaveProperty("2020-02-10"); | ||||
|     expect(Object.keys(calendar).length).toBe(32); | ||||
|     calendar = Planning.generateEmptyCalendar(3); | ||||
|     expect(calendar).toHaveProperty("2020-01-14"); | ||||
|     expect(calendar).toHaveProperty("2020-01-20"); | ||||
|     expect(calendar).toHaveProperty("2020-02-10"); | ||||
|     expect(calendar).toHaveProperty("2020-02-14"); | ||||
|     expect(calendar).toHaveProperty("2020-03-20"); | ||||
|     expect(calendar).toHaveProperty("2020-04-12"); | ||||
|     expect(Object.keys(calendar).length).toBe(92); | ||||
| }); | ||||
| 
 | ||||
| test('pushEventInOrder', () => { | ||||
|     let eventArray = []; | ||||
|     let event1 = {date_begin: "2020-01-14 09:15"}; | ||||
|     Planning.pushEventInOrder(eventArray, event1); | ||||
|     expect(eventArray.length).toBe(1); | ||||
|     expect(eventArray[0]).toBe(event1); | ||||
| 
 | ||||
|     let event2 = {date_begin: "2020-01-14 10:15"}; | ||||
|     Planning.pushEventInOrder(eventArray, event2); | ||||
|     expect(eventArray.length).toBe(2); | ||||
|     expect(eventArray[0]).toBe(event1); | ||||
|     expect(eventArray[1]).toBe(event2); | ||||
| 
 | ||||
|     let event3 = {date_begin: "2020-01-14 10:15", title: "garbage"}; | ||||
|     Planning.pushEventInOrder(eventArray, event3); | ||||
|     expect(eventArray.length).toBe(3); | ||||
|     expect(eventArray[0]).toBe(event1); | ||||
|     expect(eventArray[1]).toBe(event2); | ||||
|     expect(eventArray[2]).toBe(event3); | ||||
| 
 | ||||
|     let event4 = {date_begin: "2020-01-13 09:00"}; | ||||
|     Planning.pushEventInOrder(eventArray, event4); | ||||
|     expect(eventArray.length).toBe(4); | ||||
|     expect(eventArray[0]).toBe(event4); | ||||
|     expect(eventArray[1]).toBe(event1); | ||||
|     expect(eventArray[2]).toBe(event2); | ||||
|     expect(eventArray[3]).toBe(event3); | ||||
| }); | ||||
| 
 | ||||
| test('generateEventAgenda', () => { | ||||
|     jest.spyOn(Date, 'now') | ||||
|         .mockImplementation(() => | ||||
|             new Date('2020-01-14T00:00:00.000Z').getTime() | ||||
|         ); | ||||
|     let eventList = [ | ||||
|         {date_begin: "2020-01-14 09:15"}, | ||||
|         {date_begin: "2020-02-01 09:15"}, | ||||
|         {date_begin: "2020-01-15 09:15"}, | ||||
|         {date_begin: "2020-02-01 09:30"}, | ||||
|         {date_begin: "2020-02-01 08:30"}, | ||||
|     ]; | ||||
|     const calendar = Planning.generateEventAgenda(eventList, 2); | ||||
|     expect(calendar["2020-01-14"].length).toBe(1); | ||||
|     expect(calendar["2020-01-14"][0]).toBe(eventList[0]); | ||||
|     expect(calendar["2020-01-15"].length).toBe(1); | ||||
|     expect(calendar["2020-01-15"][0]).toBe(eventList[2]); | ||||
|     expect(calendar["2020-02-01"].length).toBe(3); | ||||
|     expect(calendar["2020-02-01"][0]).toBe(eventList[4]); | ||||
|     expect(calendar["2020-02-01"][1]).toBe(eventList[1]); | ||||
|     expect(calendar["2020-02-01"][2]).toBe(eventList[3]); | ||||
| }); | ||||
| 
 | ||||
| test('getCurrentDateString', () => { | ||||
|     jest.spyOn(Date, 'now') | ||||
|         .mockImplementation(() => { | ||||
|             let date = new Date(); | ||||
|             date.setFullYear(2020, 0, 14); | ||||
|             date.setHours(15, 30, 54, 65); | ||||
|             return date.getTime(); | ||||
|         }); | ||||
|     expect(Planning.getCurrentDateString()).toBe('2020-01-14 15:30'); | ||||
| }); | ||||
|  | @ -5,7 +5,7 @@ import {StyleSheet, View} from "react-native"; | |||
| import HTML from "react-native-render-html"; | ||||
| import i18n from "i18n-js"; | ||||
| import {Avatar, Button, Card, withTheme} from 'react-native-paper'; | ||||
| import PlanningEventManager from "../utils/PlanningEventManager"; | ||||
| import {getFormattedEventTime, isDescriptionEmpty} from "../utils/Planning"; | ||||
| 
 | ||||
| /** | ||||
|  * Component used to display an event preview if an event is available | ||||
|  | @ -17,7 +17,7 @@ function PreviewEventDashboardItem(props) { | |||
|     const {colors} = props.theme; | ||||
|     const isEmpty = props.event === undefined | ||||
|         ? true | ||||
|         : PlanningEventManager.isDescriptionEmpty(props.event['description']); | ||||
|         : isDescriptionEmpty(props.event['description']); | ||||
| 
 | ||||
|     if (props.event !== undefined && props.event !== null) { | ||||
|         const hasImage = props.event['logo'] !== '' && props.event['logo'] !== null; | ||||
|  | @ -34,12 +34,12 @@ function PreviewEventDashboardItem(props) { | |||
|                 {hasImage ? | ||||
|                     <Card.Title | ||||
|                         title={props.event['title']} | ||||
|                         subtitle={PlanningEventManager.getFormattedEventTime(props.event['date_begin'], props.event['date_end'])} | ||||
|                         subtitle={getFormattedEventTime(props.event['date_begin'], props.event['date_end'])} | ||||
|                         left={getImage} | ||||
|                     /> : | ||||
|                     <Card.Title | ||||
|                         title={props.event['title']} | ||||
|                         subtitle={PlanningEventManager.getFormattedEventTime(props.event['date_begin'], props.event['date_end'])} | ||||
|                         subtitle={getFormattedEventTime(props.event['date_begin'], props.event['date_end'])} | ||||
|                     />} | ||||
|                 {!isEmpty ? | ||||
|                     <Card.Content style={styles.content}> | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| // @flow
 | ||||
| 
 | ||||
| import * as React from 'react'; | ||||
| import WebDataManager from "../utils/WebDataManager"; | ||||
| import {readData} from "../utils/WebData"; | ||||
| import i18n from "i18n-js"; | ||||
| import {Snackbar} from 'react-native-paper'; | ||||
| import {RefreshControl, SectionList, View} from "react-native"; | ||||
|  | @ -42,8 +42,6 @@ export default class WebSectionList extends React.PureComponent<Props, State> { | |||
|         updateData: 0, | ||||
|     }; | ||||
| 
 | ||||
|     webDataManager: WebDataManager; | ||||
| 
 | ||||
|     refreshInterval: IntervalID; | ||||
|     lastRefresh: Date; | ||||
| 
 | ||||
|  | @ -79,7 +77,6 @@ export default class WebSectionList extends React.PureComponent<Props, State> { | |||
|      * Allows to detect when the screen is focused | ||||
|      */ | ||||
|     componentDidMount() { | ||||
|         this.webDataManager = new WebDataManager(this.props.fetchUrl); | ||||
|         const onScreenFocus = this.onScreenFocus.bind(this); | ||||
|         const onScreenBlur = this.onScreenBlur.bind(this); | ||||
|         this.props.navigation.addListener('focus', onScreenFocus); | ||||
|  | @ -144,7 +141,7 @@ export default class WebSectionList extends React.PureComponent<Props, State> { | |||
|             canRefresh = true; | ||||
|         if (canRefresh) { | ||||
|             this.setState({refreshing: true}); | ||||
|             this.webDataManager.readData() | ||||
|             readData(this.props.fetchUrl) | ||||
|                 .then(this.onFetchSuccess) | ||||
|                 .catch(this.onFetchError); | ||||
|         } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| <coverage generated="1584889501493" clover="3.2.0"> | ||||
|   <project timestamp="1584889501493" name="All files"> | ||||
|     <metrics statements="65" coveredstatements="65" conditionals="39" coveredconditionals="36" methods="11" coveredmethods="11" elements="115" coveredelements="112" complexity="0" loc="65" ncloc="65" packages="1" files="1" classes="1"/> | ||||
|     <file name="PlanningEventManager.js" path="/home/keplyx/expo-projects/application-amicale/utils/PlanningEventManager.js"> | ||||
|     <file name="Planning.js" path="/home/keplyx/expo-projects/application-amicale/utils/Planning.js"> | ||||
|       <metrics statements="65" coveredstatements="65" conditionals="39" coveredconditionals="36" methods="11" coveredmethods="11"/> | ||||
|       <line num="18" count="1" type="stmt"/> | ||||
|       <line num="27" count="1" type="stmt"/> | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| <html lang="en"> | ||||
| 
 | ||||
| <head> | ||||
|     <title>Code coverage report for PlanningEventManager.js</title> | ||||
|     <title>Code coverage report for Planning.js</title> | ||||
|     <meta charset="utf-8" /> | ||||
|     <link rel="stylesheet" href="prettify.css" /> | ||||
|     <link rel="stylesheet" href="base.css" /> | ||||
|  | @ -15,41 +15,41 @@ | |||
|         } | ||||
|     </style> | ||||
| </head> | ||||
|      | ||||
| 
 | ||||
| <body> | ||||
| <div class='wrapper'> | ||||
|     <div class='pad1'> | ||||
|         <h1><a href="index.html">All files</a> PlanningEventManager.js</h1> | ||||
|         <h1><a href="index.html">All files</a> Planning.js</h1> | ||||
|         <div class='clearfix'> | ||||
|              | ||||
| 
 | ||||
|             <div class='fl pad1y space-right2'> | ||||
|                 <span class="strong">100% </span> | ||||
|                 <span class="quiet">Statements</span> | ||||
|                 <span class='fraction'>68/68</span> | ||||
|             </div> | ||||
|          | ||||
|              | ||||
| 
 | ||||
| 
 | ||||
|             <div class='fl pad1y space-right2'> | ||||
|                 <span class="strong">92.31% </span> | ||||
|                 <span class="quiet">Branches</span> | ||||
|                 <span class='fraction'>36/39</span> | ||||
|             </div> | ||||
|          | ||||
|              | ||||
| 
 | ||||
| 
 | ||||
|             <div class='fl pad1y space-right2'> | ||||
|                 <span class="strong">100% </span> | ||||
|                 <span class="quiet">Functions</span> | ||||
|                 <span class='fraction'>11/11</span> | ||||
|             </div> | ||||
|          | ||||
|              | ||||
| 
 | ||||
| 
 | ||||
|             <div class='fl pad1y space-right2'> | ||||
|                 <span class="strong">100% </span> | ||||
|                 <span class="quiet">Lines</span> | ||||
|                 <span class='fraction'>65/65</span> | ||||
|             </div> | ||||
|          | ||||
|              | ||||
| 
 | ||||
| 
 | ||||
|         </div> | ||||
|         <p class="quiet"> | ||||
|             Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block. | ||||
|  | @ -806,4 +806,3 @@ export default class PlanningEventManager { | |||
|         <script src="block-navigation.js"></script> | ||||
|     </body> | ||||
| </html> | ||||
|      | ||||
|  | @ -15,41 +15,41 @@ | |||
|         } | ||||
|     </style> | ||||
| </head> | ||||
|      | ||||
| 
 | ||||
| <body> | ||||
| <div class='wrapper'> | ||||
|     <div class='pad1'> | ||||
|         <h1>All files</h1> | ||||
|         <div class='clearfix'> | ||||
|              | ||||
| 
 | ||||
|             <div class='fl pad1y space-right2'> | ||||
|                 <span class="strong">100% </span> | ||||
|                 <span class="quiet">Statements</span> | ||||
|                 <span class='fraction'>68/68</span> | ||||
|             </div> | ||||
|          | ||||
|              | ||||
| 
 | ||||
| 
 | ||||
|             <div class='fl pad1y space-right2'> | ||||
|                 <span class="strong">92.31% </span> | ||||
|                 <span class="quiet">Branches</span> | ||||
|                 <span class='fraction'>36/39</span> | ||||
|             </div> | ||||
|          | ||||
|              | ||||
| 
 | ||||
| 
 | ||||
|             <div class='fl pad1y space-right2'> | ||||
|                 <span class="strong">100% </span> | ||||
|                 <span class="quiet">Functions</span> | ||||
|                 <span class='fraction'>11/11</span> | ||||
|             </div> | ||||
|          | ||||
|              | ||||
| 
 | ||||
| 
 | ||||
|             <div class='fl pad1y space-right2'> | ||||
|                 <span class="strong">100% </span> | ||||
|                 <span class="quiet">Lines</span> | ||||
|                 <span class='fraction'>65/65</span> | ||||
|             </div> | ||||
|          | ||||
|              | ||||
| 
 | ||||
| 
 | ||||
|         </div> | ||||
|         <p class="quiet"> | ||||
|             Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block. | ||||
|  | @ -73,7 +73,7 @@ | |||
| </tr> | ||||
| </thead> | ||||
| <tbody><tr> | ||||
| 	<td class="file high" data-value="PlanningEventManager.js"><a href="PlanningEventManager.js.html">PlanningEventManager.js</a></td> | ||||
| 	<td class="file high" data-value="Planning.js"><a href="PlanningEventManager.js.html">Planning.js</a></td> | ||||
| 	<td data-value="100" class="pic high"> | ||||
| 	<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div> | ||||
| 	</td> | ||||
|  | @ -108,4 +108,3 @@ | |||
|         <script src="block-navigation.js"></script> | ||||
|     </body> | ||||
| </html> | ||||
|      | ||||
|  | @ -1,5 +1,5 @@ | |||
| TN: | ||||
| SF:utils/PlanningEventManager.js | ||||
| SF:utils/Planning.js | ||||
| FN:26,(anonymous_0) | ||||
| FN:37,(anonymous_1) | ||||
| FN:53,(anonymous_2) | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ import ProximoListScreen from "../screens/Proximo/ProximoListScreen"; | |||
| import ProximoAboutScreen from "../screens/Proximo/ProximoAboutScreen"; | ||||
| import PlanexScreen from '../screens/Websites/PlanexScreen'; | ||||
| import {MaterialCommunityIcons} from "@expo/vector-icons"; | ||||
| import AsyncStorageManager from "../utils/AsyncStorageManager"; | ||||
| import AsyncStorageManager from "../managers/AsyncStorageManager"; | ||||
| import HeaderButton from "../components/HeaderButton"; | ||||
| import {withTheme} from 'react-native-paper'; | ||||
| import i18n from "i18n-js"; | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ import * as React from 'react'; | |||
| import {FlatList, Linking, Platform, View} from 'react-native'; | ||||
| import i18n from "i18n-js"; | ||||
| import appJson from '../../app'; | ||||
| import AsyncStorageManager from "../../utils/AsyncStorageManager"; | ||||
| import AsyncStorageManager from "../../managers/AsyncStorageManager"; | ||||
| import CustomModal from "../../components/CustomModal"; | ||||
| import {Avatar, Button, Card, List, Text, Title, withTheme} from 'react-native-paper'; | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| import * as React from 'react'; | ||||
| import {ScrollView, View} from "react-native"; | ||||
| import AsyncStorageManager from "../../utils/AsyncStorageManager"; | ||||
| import AsyncStorageManager from "../../managers/AsyncStorageManager"; | ||||
| import CustomModal from "../../components/CustomModal"; | ||||
| import {Button, Card, List, Subheading, TextInput, Title, withTheme} from 'react-native-paper'; | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ import {Text, withTheme} from 'react-native-paper'; | |||
| import FeedItem from "../components/FeedItem"; | ||||
| import SquareDashboardItem from "../components/SquareDashboardItem"; | ||||
| import PreviewEventDashboardItem from "../components/PreviewEventDashboardItem"; | ||||
| import PlanningEventManager from "../utils/PlanningEventManager"; | ||||
| import {stringToDate} from "../utils/Planning"; | ||||
| // import DATA from "../dashboard_data.json";
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -201,8 +201,8 @@ class HomeScreen extends React.Component<Props> { | |||
|      * @return {number} The number of milliseconds | ||||
|      */ | ||||
|     getEventDuration(event: Object): number { | ||||
|         let start = PlanningEventManager.stringToDate(event['date_begin']); | ||||
|         let end = PlanningEventManager.stringToDate(event['date_end']); | ||||
|         let start = stringToDate(event['date_begin']); | ||||
|         let end = stringToDate(event['date_end']); | ||||
|         let duration = 0; | ||||
|         if (start !== undefined && start !== null && end !== undefined && end !== null) | ||||
|             duration = end - start; | ||||
|  | @ -219,7 +219,7 @@ class HomeScreen extends React.Component<Props> { | |||
|     getEventsAfterLimit(events: Object, limit: Date): Array<Object> { | ||||
|         let validEvents = []; | ||||
|         for (let event of events) { | ||||
|             let startDate = PlanningEventManager.stringToDate(event['date_begin']); | ||||
|             let startDate = stringToDate(event['date_begin']); | ||||
|             if (startDate !== undefined && startDate !== null && startDate >= limit) { | ||||
|                 validEvents.push(event); | ||||
|             } | ||||
|  | @ -255,8 +255,8 @@ class HomeScreen extends React.Component<Props> { | |||
|         let validEvents = []; | ||||
|         let now = new Date(); | ||||
|         for (let event of events) { | ||||
|             let startDate = PlanningEventManager.stringToDate(event['date_begin']); | ||||
|             let endDate = PlanningEventManager.stringToDate(event['date_end']); | ||||
|             let startDate = stringToDate(event['date_begin']); | ||||
|             let endDate = stringToDate(event['date_end']); | ||||
|             if (startDate !== undefined && startDate !== null) { | ||||
|                 if (startDate > now) | ||||
|                     validEvents.push(event); | ||||
|  |  | |||
|  | @ -4,9 +4,9 @@ import * as React from 'react'; | |||
| import {Image, ScrollView, View} from 'react-native'; | ||||
| import HTML from "react-native-render-html"; | ||||
| import {Linking} from "expo"; | ||||
| import PlanningEventManager from '../../utils/PlanningEventManager'; | ||||
| import {getDateOnlyString, getFormattedEventTime} from '../../utils/Planning'; | ||||
| import {Card, withTheme} from 'react-native-paper'; | ||||
| import DateManager from "../../utils/DateManager"; | ||||
| import DateManager from "../../managers/DateManager"; | ||||
| 
 | ||||
| type Props = { | ||||
|     navigation: Object, | ||||
|  | @ -33,9 +33,9 @@ class PlanningDisplayScreen extends React.Component<Props> { | |||
| 
 | ||||
|     render() { | ||||
|         // console.log("rendering planningDisplayScreen");
 | ||||
|         let subtitle = PlanningEventManager.getFormattedEventTime( | ||||
|         let subtitle = getFormattedEventTime( | ||||
|             this.displayData["date_begin"], this.displayData["date_end"]); | ||||
|         let dateString = PlanningEventManager.getDateOnlyString(this.displayData["date_begin"]); | ||||
|         let dateString = getDateOnlyString(this.displayData["date_begin"]); | ||||
|         if (dateString !== null) | ||||
|             subtitle += ' | ' + DateManager.getInstance().getTranslatedDate(dateString); | ||||
|         return ( | ||||
|  |  | |||
|  | @ -4,9 +4,14 @@ import * as React from 'react'; | |||
| import {BackHandler, View} from 'react-native'; | ||||
| import i18n from "i18n-js"; | ||||
| import {LocaleConfig} from 'react-native-calendars'; | ||||
| import WebDataManager from "../../utils/WebDataManager"; | ||||
| import type {eventObject} from "../../utils/PlanningEventManager"; | ||||
| import PlanningEventManager from '../../utils/PlanningEventManager'; | ||||
| import {readData} from "../../utils/WebData"; | ||||
| import type {eventObject} from "../../utils/Planning"; | ||||
| import { | ||||
|     generateEventAgenda, | ||||
|     getCurrentDateString, | ||||
|     getDateOnlyString, | ||||
|     getFormattedEventTime, | ||||
| } from '../../utils/Planning'; | ||||
| import {Avatar, Divider, List} from 'react-native-paper'; | ||||
| import CustomAgenda from "../../components/CustomAgenda"; | ||||
| 
 | ||||
|  | @ -38,7 +43,6 @@ const AGENDA_MONTH_SPAN = 3; | |||
| export default class PlanningScreen extends React.Component<Props, State> { | ||||
| 
 | ||||
|     agendaRef: Object; | ||||
|     webDataManager: WebDataManager; | ||||
| 
 | ||||
|     lastRefresh: Date; | ||||
|     minTimeBetweenRefresh = 60; | ||||
|  | @ -59,11 +63,10 @@ export default class PlanningScreen extends React.Component<Props, State> { | |||
|     onAgendaRef: Function; | ||||
|     onCalendarToggled: Function; | ||||
|     onBackButtonPressAndroid: Function; | ||||
|     currentDate = PlanningEventManager.getDateOnlyString(PlanningEventManager.getCurrentDateString()); | ||||
|     currentDate = getDateOnlyString(getCurrentDateString()); | ||||
| 
 | ||||
|     constructor(props: any) { | ||||
|         super(props); | ||||
|         this.webDataManager = new WebDataManager(FETCH_URL); | ||||
|         if (i18n.currentLocale().startsWith("fr")) { | ||||
|             LocaleConfig.defaultLocale = 'fr'; | ||||
|         } | ||||
|  | @ -141,11 +144,11 @@ export default class PlanningScreen extends React.Component<Props, State> { | |||
| 
 | ||||
|         if (canRefresh) { | ||||
|             this.setState({refreshing: true}); | ||||
|             this.webDataManager.readData() | ||||
|             readData(FETCH_URL) | ||||
|                 .then((fetchedData) => { | ||||
|                     this.setState({ | ||||
|                         refreshing: false, | ||||
|                         agendaItems: PlanningEventManager.generateEventAgenda(fetchedData, AGENDA_MONTH_SPAN) | ||||
|                         agendaItems: generateEventAgenda(fetchedData, AGENDA_MONTH_SPAN) | ||||
|                     }); | ||||
|                     this.lastRefresh = new Date(); | ||||
|                 }) | ||||
|  | @ -189,7 +192,7 @@ export default class PlanningScreen extends React.Component<Props, State> { | |||
|                     <Divider/> | ||||
|                     <List.Item | ||||
|                         title={item.title} | ||||
|                         description={PlanningEventManager.getFormattedEventTime(item["date_begin"], item["date_end"])} | ||||
|                         description={getFormattedEventTime(item["date_begin"], item["date_end"])} | ||||
|                         left={() => <Avatar.Image | ||||
|                             source={{uri: item.logo}} | ||||
|                             style={{backgroundColor: 'transparent'}} | ||||
|  | @ -204,7 +207,7 @@ export default class PlanningScreen extends React.Component<Props, State> { | |||
|                     <Divider/> | ||||
|                     <List.Item | ||||
|                         title={item.title} | ||||
|                         description={PlanningEventManager.getFormattedEventTime(item["date_begin"], item["date_end"])} | ||||
|                         description={getFormattedEventTime(item["date_begin"], item["date_end"])} | ||||
|                         onPress={onPress} | ||||
|                     /> | ||||
|                 </View> | ||||
|  |  | |||
|  | @ -4,15 +4,15 @@ import * as React from 'react'; | |||
| import {Alert, Platform, View} from 'react-native'; | ||||
| import i18n from "i18n-js"; | ||||
| import WebSectionList from "../../components/WebSectionList"; | ||||
| import NotificationsManager from "../../utils/NotificationsManager"; | ||||
| import AsyncStorageManager from "../../utils/AsyncStorageManager"; | ||||
| import * as Notifications from "../../utils/Notifications"; | ||||
| import AsyncStorageManager from "../../managers/AsyncStorageManager"; | ||||
| import * as Expo from "expo"; | ||||
| import {Avatar, Banner, Button, Card, Text, withTheme} from 'react-native-paper'; | ||||
| import HeaderButton from "../../components/HeaderButton"; | ||||
| import ProxiwashListItem from "../../components/ProxiwashListItem"; | ||||
| import ProxiwashConstants from "../../constants/ProxiwashConstants"; | ||||
| import CustomModal from "../../components/CustomModal"; | ||||
| import AprilFoolsManager from "../../utils/AprilFoolsManager"; | ||||
| import AprilFoolsManager from "../../managers/AprilFoolsManager"; | ||||
| 
 | ||||
| const DATA_URL = "https://etud.insa-toulouse.fr/~amicale_app/washinsa/washinsa.json"; | ||||
| 
 | ||||
|  | @ -118,12 +118,12 @@ class ProxiwashScreen extends React.Component<Props, State> { | |||
|         }); | ||||
|         if (AsyncStorageManager.getInstance().preferences.expoToken.current !== '') { | ||||
|             // Get latest watchlist from server
 | ||||
|             NotificationsManager.getMachineNotificationWatchlist((fetchedList) => { | ||||
|             Notifications.getMachineNotificationWatchlist((fetchedList) => { | ||||
|                 this.setState({machinesWatched: fetchedList}) | ||||
|             }); | ||||
|             // Get updated watchlist after received notification
 | ||||
|             Expo.Notifications.addListener(() => { | ||||
|                 NotificationsManager.getMachineNotificationWatchlist((fetchedList) => { | ||||
|                 Notifications.getMachineNotificationWatchlist((fetchedList) => { | ||||
|                     this.setState({machinesWatched: fetchedList}) | ||||
|                 }); | ||||
|             }); | ||||
|  | @ -175,7 +175,7 @@ class ProxiwashScreen extends React.Component<Props, State> { | |||
|     setupNotifications(machineId: string) { | ||||
|         if (AsyncStorageManager.getInstance().preferences.expoToken.current !== '') { | ||||
|             if (!this.isMachineWatched(machineId)) { | ||||
|                 NotificationsManager.setupMachineNotification(machineId, true); | ||||
|                 Notifications.setupMachineNotification(machineId, true); | ||||
|                 this.saveNotificationToState(machineId); | ||||
|             } else | ||||
|                 this.disableNotification(machineId); | ||||
|  | @ -205,7 +205,7 @@ class ProxiwashScreen extends React.Component<Props, State> { | |||
|         if (data.length > 0) { | ||||
|             let arrayIndex = data.indexOf(machineId); | ||||
|             if (arrayIndex !== -1) { | ||||
|                 NotificationsManager.setupMachineNotification(machineId, false); | ||||
|                 Notifications.setupMachineNotification(machineId, false); | ||||
|                 this.removeNotificationFroState(arrayIndex); | ||||
|             } | ||||
|         } | ||||
|  |  | |||
|  | @ -2,10 +2,10 @@ | |||
| 
 | ||||
| import * as React from 'react'; | ||||
| import {View} from 'react-native'; | ||||
| import DateManager from "../utils/DateManager"; | ||||
| import DateManager from "../managers/DateManager"; | ||||
| import WebSectionList from "../components/WebSectionList"; | ||||
| import {Card, Text, withTheme} from 'react-native-paper'; | ||||
| import AprilFoolsManager from "../utils/AprilFoolsManager"; | ||||
| import AprilFoolsManager from "../managers/AprilFoolsManager"; | ||||
| 
 | ||||
| const DATA_URL = "https://etud.insa-toulouse.fr/~amicale_app/menu/menu_data.json"; | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,10 +2,10 @@ | |||
| 
 | ||||
| import * as React from 'react'; | ||||
| import {ScrollView} from "react-native"; | ||||
| import ThemeManager from '../utils/ThemeManager'; | ||||
| import ThemeManager from '../managers/ThemeManager'; | ||||
| import i18n from "i18n-js"; | ||||
| import AsyncStorageManager from "../utils/AsyncStorageManager"; | ||||
| import NotificationsManager from "../utils/NotificationsManager"; | ||||
| import AsyncStorageManager from "../managers/AsyncStorageManager"; | ||||
| import {setMachineReminderNotificationTime} from "../utils/Notifications"; | ||||
| import {Card, List, Switch, ToggleButton} from 'react-native-paper'; | ||||
| import {Appearance} from "react-native-appearance"; | ||||
| 
 | ||||
|  | @ -60,7 +60,7 @@ export default class SettingsScreen extends React.Component<Props, State> { | |||
|             let intVal = 0; | ||||
|             if (value !== 'never') | ||||
|                 intVal = parseInt(value); | ||||
|             NotificationsManager.setMachineReminderNotificationTime(intVal); | ||||
|             setMachineReminderNotificationTime(intVal); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,12 +1,12 @@ | |||
| // @flow
 | ||||
| 
 | ||||
| import * as React from 'react'; | ||||
| import ThemeManager from "../../utils/ThemeManager"; | ||||
| import ThemeManager from "../../managers/ThemeManager"; | ||||
| import WebViewScreen from "../../components/WebViewScreen"; | ||||
| import {Avatar, Banner} from "react-native-paper"; | ||||
| import i18n from "i18n-js"; | ||||
| import {View} from "react-native"; | ||||
| import AsyncStorageManager from "../../utils/AsyncStorageManager"; | ||||
| import AsyncStorageManager from "../../managers/AsyncStorageManager"; | ||||
| 
 | ||||
| type Props = { | ||||
|     navigation: Object, | ||||
|  |  | |||
							
								
								
									
										125
									
								
								utils/Notifications.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								utils/Notifications.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,125 @@ | |||
| // @flow
 | ||||
| 
 | ||||
| import * as Permissions from 'expo-permissions'; | ||||
| import {Notifications} from 'expo'; | ||||
| import AsyncStorageManager from "../managers/AsyncStorageManager"; | ||||
| import LocaleManager from "../managers/LocaleManager"; | ||||
| import passwords from "../passwords"; | ||||
| 
 | ||||
| const EXPO_TOKEN_SERVER = 'https://etud.insa-toulouse.fr/~amicale_app/expo_notifications/save_token.php'; | ||||
| 
 | ||||
| /** | ||||
|  * Async function asking permission to send notifications to the user | ||||
|  * | ||||
|  * @returns {Promise} | ||||
|  */ | ||||
| export async function askPermissions() { | ||||
|     const {status: existingStatus} = await Permissions.getAsync(Permissions.NOTIFICATIONS); | ||||
|     let finalStatus = existingStatus; | ||||
|     if (existingStatus !== 'granted') { | ||||
|         const {status} = await Permissions.askAsync(Permissions.NOTIFICATIONS); | ||||
|         finalStatus = status; | ||||
|     } | ||||
|     return finalStatus === 'granted'; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Save expo token to allow sending notifications to this device. | ||||
|  * This token is unique for each device and won't change. | ||||
|  * It only needs to be fetched once, then it will be saved in storage. | ||||
|  * | ||||
|  * @return {Promise<void>} | ||||
|  */ | ||||
| export async function initExpoToken() { | ||||
|     let token = AsyncStorageManager.getInstance().preferences.expoToken.current; | ||||
|     if (token === '') { | ||||
|         try { | ||||
|             await askPermissions(); | ||||
|             let expoToken = await Notifications.getExpoPushTokenAsync(); | ||||
|             // Save token for instant use later on
 | ||||
|             AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.expoToken.key, expoToken); | ||||
|         } catch (e) { | ||||
|             console.log(e); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Gets the machines watched from the server | ||||
|  * | ||||
|  * @param callback Function to execute with the fetched data | ||||
|  */ | ||||
| export function getMachineNotificationWatchlist(callback: Function) { | ||||
|     let token = AsyncStorageManager.getInstance().preferences.expoToken.current; | ||||
|     if (token !== '') { | ||||
|         let data = { | ||||
|             function: 'get_machine_watchlist', | ||||
|             password: passwords.expoNotifications, | ||||
|             token: token, | ||||
|         }; | ||||
|         fetch(EXPO_TOKEN_SERVER, { | ||||
|             method: 'POST', | ||||
|             headers: new Headers({ | ||||
|                 Accept: 'application/json', | ||||
|                 'Content-Type': 'application/json', | ||||
|             }), | ||||
|             body: JSON.stringify(data) // <-- Post parameters
 | ||||
|         }).then((response) => response.json()) | ||||
|             .then((responseJson) => { | ||||
|                 callback(responseJson); | ||||
|             }); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Asks the server to enable/disable notifications for the specified machine | ||||
|  * | ||||
|  * @param machineID The machine ID | ||||
|  * @param isEnabled True to enable notifications, false to disable | ||||
|  */ | ||||
| export function setupMachineNotification(machineID: string, isEnabled: boolean) { | ||||
|     let token = AsyncStorageManager.getInstance().preferences.expoToken.current; | ||||
|     if (token !== '') { | ||||
|         let data = { | ||||
|             function: 'setup_machine_notification', | ||||
|             password: passwords.expoNotifications, | ||||
|             locale: LocaleManager.getCurrentLocale(), | ||||
|             token: token, | ||||
|             machine_id: machineID, | ||||
|             enabled: isEnabled | ||||
|         }; | ||||
|         fetch(EXPO_TOKEN_SERVER, { | ||||
|             method: 'POST', | ||||
|             headers: new Headers({ | ||||
|                 Accept: 'application/json', | ||||
|                 'Content-Type': 'application/json', | ||||
|             }), | ||||
|             body: JSON.stringify(data) // <-- Post parameters
 | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Sends the selected reminder time for notifications to the server | ||||
|  * | ||||
|  * @param time The reminder time to use | ||||
|  */ | ||||
| export function setMachineReminderNotificationTime(time: number) { | ||||
|     let token = AsyncStorageManager.getInstance().preferences.expoToken.current; | ||||
|     if (token !== '') { | ||||
|         let data = { | ||||
|             function: 'set_machine_reminder', | ||||
|             password: passwords.expoNotifications, | ||||
|             token: token, | ||||
|             time: time, | ||||
|         }; | ||||
|         fetch(EXPO_TOKEN_SERVER, { | ||||
|             method: 'POST', | ||||
|             headers: new Headers({ | ||||
|                 Accept: 'application/json', | ||||
|                 'Content-Type': 'application/json', | ||||
|             }), | ||||
|             body: JSON.stringify(data) // <-- Post parameters
 | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | @ -1,131 +0,0 @@ | |||
| // @flow
 | ||||
| 
 | ||||
| import * as Permissions from 'expo-permissions'; | ||||
| import {Notifications} from 'expo'; | ||||
| import AsyncStorageManager from "./AsyncStorageManager"; | ||||
| import LocaleManager from "./LocaleManager"; | ||||
| import passwords from "../passwords"; | ||||
| 
 | ||||
| const EXPO_TOKEN_SERVER = 'https://etud.insa-toulouse.fr/~amicale_app/expo_notifications/save_token.php'; | ||||
| 
 | ||||
| /** | ||||
|  * Static class used to manage notifications sent to the user | ||||
|  */ | ||||
| export default class NotificationsManager { | ||||
| 
 | ||||
|     /** | ||||
|      * Async function asking permission to send notifications to the user | ||||
|      * | ||||
|      * @returns {Promise} | ||||
|      */ | ||||
|     static async askPermissions() { | ||||
|         const {status: existingStatus} = await Permissions.getAsync(Permissions.NOTIFICATIONS); | ||||
|         let finalStatus = existingStatus; | ||||
|         if (existingStatus !== 'granted') { | ||||
|             const {status} = await Permissions.askAsync(Permissions.NOTIFICATIONS); | ||||
|             finalStatus = status; | ||||
|         } | ||||
|         return finalStatus === 'granted'; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Save expo token to allow sending notifications to this device. | ||||
|      * This token is unique for each device and won't change. | ||||
|      * It only needs to be fetched once, then it will be saved in storage. | ||||
|      * | ||||
|      * @return {Promise<void>} | ||||
|      */ | ||||
|     static async initExpoToken() { | ||||
|         let token = AsyncStorageManager.getInstance().preferences.expoToken.current; | ||||
|         if (token === '') { | ||||
|             try { | ||||
|                 await NotificationsManager.askPermissions(); | ||||
|                 let expoToken = await Notifications.getExpoPushTokenAsync(); | ||||
|                 // Save token for instant use later on
 | ||||
|                 AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.expoToken.key, expoToken); | ||||
|             } catch(e) { | ||||
|                 console.log(e); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the machines watched from the server | ||||
|      * | ||||
|      * @param callback Function to execute with the fetched data | ||||
|      */ | ||||
|     static getMachineNotificationWatchlist(callback: Function) { | ||||
|         let token = AsyncStorageManager.getInstance().preferences.expoToken.current; | ||||
|         if (token !== '') { | ||||
|             let data = { | ||||
|                 function: 'get_machine_watchlist', | ||||
|                 password: passwords.expoNotifications, | ||||
|                 token: token, | ||||
|             }; | ||||
|             fetch(EXPO_TOKEN_SERVER, { | ||||
|                 method: 'POST', | ||||
|                 headers: new Headers({ | ||||
|                     Accept: 'application/json', | ||||
|                     'Content-Type': 'application/json', | ||||
|                 }), | ||||
|                 body: JSON.stringify(data) // <-- Post parameters
 | ||||
|             }).then((response) => response.json()) | ||||
|                 .then((responseJson) => { | ||||
|                     callback(responseJson); | ||||
|                 }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Asks the server to enable/disable notifications for the specified machine | ||||
|      * | ||||
|      * @param machineID The machine ID | ||||
|      * @param isEnabled True to enable notifications, false to disable | ||||
|      */ | ||||
|     static setupMachineNotification(machineID: string, isEnabled: boolean) { | ||||
|         let token = AsyncStorageManager.getInstance().preferences.expoToken.current; | ||||
|         if (token !== '') { | ||||
|             let data = { | ||||
|                 function: 'setup_machine_notification', | ||||
|                 password: passwords.expoNotifications, | ||||
|                 locale: LocaleManager.getCurrentLocale(), | ||||
|                 token: token, | ||||
|                 machine_id: machineID, | ||||
|                 enabled: isEnabled | ||||
|             }; | ||||
|             fetch(EXPO_TOKEN_SERVER, { | ||||
|                 method: 'POST', | ||||
|                 headers: new Headers({ | ||||
|                     Accept: 'application/json', | ||||
|                     'Content-Type': 'application/json', | ||||
|                 }), | ||||
|                 body: JSON.stringify(data) // <-- Post parameters
 | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sends the selected reminder time for notifications to the server | ||||
|      * | ||||
|      * @param time The reminder time to use | ||||
|      */ | ||||
|     static setMachineReminderNotificationTime(time: number) { | ||||
|         let token = AsyncStorageManager.getInstance().preferences.expoToken.current; | ||||
|         if (token !== '') { | ||||
|             let data = { | ||||
|                 function: 'set_machine_reminder', | ||||
|                 password: passwords.expoNotifications, | ||||
|                 token: token, | ||||
|                 time: time, | ||||
|             }; | ||||
|             fetch(EXPO_TOKEN_SERVER, { | ||||
|                 method: 'POST', | ||||
|                 headers: new Headers({ | ||||
|                     Accept: 'application/json', | ||||
|                     'Content-Type': 'application/json', | ||||
|                 }), | ||||
|                 body: JSON.stringify(data) // <-- Post parameters
 | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										240
									
								
								utils/Planning.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								utils/Planning.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,240 @@ | |||
| // @flow
 | ||||
| 
 | ||||
| export type eventObject = { | ||||
|     id: number, | ||||
|     title: string, | ||||
|     logo: string, | ||||
|     date_begin: string, | ||||
|     date_end: string, | ||||
|     description: string, | ||||
|     club: string, | ||||
|     category_id: number, | ||||
|     url: string, | ||||
| }; | ||||
| 
 | ||||
| // Regex used to check date string validity
 | ||||
| const dateRegExp = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/; | ||||
| 
 | ||||
| /** | ||||
|  * Gets the current day string representation in the format | ||||
|  * YYYY-MM-DD | ||||
|  * | ||||
|  * @return {string} The string representation | ||||
|  */ | ||||
| export function getCurrentDateString(): string { | ||||
|     return dateToString(new Date(Date.now())); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Checks if the given date is before the other. | ||||
|  * | ||||
|  * @param event1Date Event 1 date in format YYYY-MM-DD HH:MM | ||||
|  * @param event2Date Event 2 date in format YYYY-MM-DD HH:MM | ||||
|  * @return {boolean} | ||||
|  */ | ||||
| export function isEventBefore(event1Date: string, event2Date: string): boolean { | ||||
|     let date1 = stringToDate(event1Date); | ||||
|     let date2 = stringToDate(event2Date); | ||||
|     if (date1 !== null && date2 !== null) | ||||
|         return date1 < date2; | ||||
|     else | ||||
|         return false; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Gets only the date part of the given event date string in the format | ||||
|  * YYYY-MM-DD HH:MM | ||||
|  * | ||||
|  * @param dateString The string to get the date from | ||||
|  * @return {string|null} Date in format YYYY:MM:DD or null if given string is invalid | ||||
|  */ | ||||
| export function getDateOnlyString(dateString: string): string | null { | ||||
|     if (isEventDateStringFormatValid(dateString)) | ||||
|         return dateString.split(" ")[0]; | ||||
|     else | ||||
|         return null; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Checks if the given date string is in the format | ||||
|  * YYYY-MM-DD HH:MM | ||||
|  * | ||||
|  * @param dateString The string to check | ||||
|  * @return {boolean} | ||||
|  */ | ||||
| export function isEventDateStringFormatValid(dateString: ?string): boolean { | ||||
|     return dateString !== undefined | ||||
|         && dateString !== null | ||||
|         && dateRegExp.test(dateString); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Converts the given date string to a date object.<br> | ||||
|  * Accepted format: YYYY-MM-DD HH:MM | ||||
|  * | ||||
|  * @param dateString The string to convert | ||||
|  * @return {Date|null} The date object or null if the given string is invalid | ||||
|  */ | ||||
| export function stringToDate(dateString: string): Date | null { | ||||
|     let date = new Date(); | ||||
|     if (isEventDateStringFormatValid(dateString)) { | ||||
|         let stringArray = dateString.split(' '); | ||||
|         let dateArray = stringArray[0].split('-'); | ||||
|         let timeArray = stringArray[1].split(':'); | ||||
|         date.setFullYear( | ||||
|             parseInt(dateArray[0]), | ||||
|             parseInt(dateArray[1]) - 1, // Month range from 0 to 11
 | ||||
|             parseInt(dateArray[2]) | ||||
|         ); | ||||
|         date.setHours( | ||||
|             parseInt(timeArray[0]), | ||||
|             parseInt(timeArray[1]), | ||||
|             0, | ||||
|             0, | ||||
|         ); | ||||
|     } else | ||||
|         date = null; | ||||
| 
 | ||||
|     return date; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Converts a date object to a string in the format | ||||
|  * YYYY-MM-DD HH-MM-SS | ||||
|  * | ||||
|  * @param date The date object to convert | ||||
|  * @return {string} The converted string | ||||
|  */ | ||||
| export function dateToString(date: Date): string { | ||||
|     const day = String(date.getDate()).padStart(2, '0'); | ||||
|     const month = String(date.getMonth() + 1).padStart(2, '0'); //January is 0!
 | ||||
|     const year = date.getFullYear(); | ||||
|     const hours = String(date.getHours()).padStart(2, '0'); | ||||
|     const minutes = String(date.getMinutes()).padStart(2, '0'); | ||||
|     return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Returns a string corresponding to the event start and end times in the following format: | ||||
|  * | ||||
|  * HH:MM - HH:MM | ||||
|  * | ||||
|  * If the end date is not specified or is equal to start time, only start time will be shown. | ||||
|  * | ||||
|  * If the end date is not on the same day, 23:59 will be shown as end time | ||||
|  * | ||||
|  * @param start Start time in YYYY-MM-DD HH:MM:SS format | ||||
|  * @param end End time in YYYY-MM-DD HH:MM:SS format | ||||
|  * @return {string} Formatted string or "/ - /" on error | ||||
|  */ | ||||
| export function getFormattedEventTime(start: string, end: string): string { | ||||
|     let formattedStr = '/ - /'; | ||||
|     let startDate = stringToDate(start); | ||||
|     let endDate = stringToDate(end); | ||||
| 
 | ||||
|     if (startDate !== null && endDate !== null && startDate.getTime() !== endDate.getTime()) { | ||||
|         formattedStr = String(startDate.getHours()).padStart(2, '0') + ':' | ||||
|             + String(startDate.getMinutes()).padStart(2, '0') + ' - '; | ||||
|         if (endDate.getFullYear() > startDate.getFullYear() | ||||
|             || endDate.getMonth() > startDate.getMonth() | ||||
|             || endDate.getDate() > startDate.getDate()) | ||||
|             formattedStr += '23:59'; | ||||
|         else | ||||
|             formattedStr += String(endDate.getHours()).padStart(2, '0') + ':' | ||||
|                 + String(endDate.getMinutes()).padStart(2, '0'); | ||||
|     } else if (startDate !== null) | ||||
|         formattedStr = | ||||
|             String(startDate.getHours()).padStart(2, '0') + ':' | ||||
|             + String(startDate.getMinutes()).padStart(2, '0'); | ||||
| 
 | ||||
|     return formattedStr | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Checks if the given description can be considered empty. | ||||
|  * <br> | ||||
|  * An empty description is composed only of whitespace, <b>br</b> or <b>p</b> tags | ||||
|  * | ||||
|  * | ||||
|  * @param description The text to check | ||||
|  * @return {boolean} | ||||
|  */ | ||||
| export function isDescriptionEmpty(description: ?string): boolean { | ||||
|     if (description !== undefined && description !== null) { | ||||
|         return description | ||||
|             .split('<p>').join('') // Equivalent to a replace all
 | ||||
|             .split('</p>').join('') | ||||
|             .split('<br>').join('').trim() === ''; | ||||
|     } else | ||||
|         return true; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Generates an object with an empty array for each key. | ||||
|  * Each key is a date string in the format | ||||
|  * YYYY-MM-DD | ||||
|  * | ||||
|  * @param numberOfMonths The number of months to create, starting from the current date | ||||
|  * @return {Object} | ||||
|  */ | ||||
| export function generateEmptyCalendar(numberOfMonths: number): Object { | ||||
|     let end = new Date(Date.now()); | ||||
|     end.setMonth(end.getMonth() + numberOfMonths); | ||||
|     let daysOfYear = {}; | ||||
|     for (let d = new Date(Date.now()); d <= end; d.setDate(d.getDate() + 1)) { | ||||
|         const dateString = getDateOnlyString( | ||||
|             dateToString(new Date(d))); | ||||
|         if (dateString !== null) | ||||
|             daysOfYear[dateString] = [] | ||||
|     } | ||||
|     return daysOfYear; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Generates an object with an array of eventObject at each key. | ||||
|  * Each key is a date string in the format | ||||
|  * YYYY-MM-DD. | ||||
|  * | ||||
|  * If no event is available at the given key, the array will be empty | ||||
|  * | ||||
|  * @param eventList The list of events to map to the agenda | ||||
|  * @param numberOfMonths The number of months to create the agenda for | ||||
|  * @return {Object} | ||||
|  */ | ||||
| export function generateEventAgenda(eventList: Array<eventObject>, numberOfMonths: number): Object { | ||||
|     let agendaItems = generateEmptyCalendar(numberOfMonths); | ||||
|     for (let i = 0; i < eventList.length; i++) { | ||||
|         const dateString = getDateOnlyString(eventList[i].date_begin); | ||||
|         if (dateString !== null) { | ||||
|             const eventArray = agendaItems[dateString]; | ||||
|             if (eventArray !== undefined) | ||||
|                 this.pushEventInOrder(eventArray, eventList[i]); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
|     return agendaItems; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Adds events to the given array depending on their starting date. | ||||
|  * | ||||
|  * Events starting before are added at the front. | ||||
|  * | ||||
|  * @param eventArray The array to hold sorted events | ||||
|  * @param event The event to add to the array | ||||
|  */ | ||||
| export function pushEventInOrder(eventArray: Array<eventObject>, event: eventObject): Object { | ||||
|     if (eventArray.length === 0) | ||||
|         eventArray.push(event); | ||||
|     else { | ||||
|         for (let i = 0; i < eventArray.length; i++) { | ||||
|             if (isEventBefore(event.date_begin, eventArray[i].date_begin)) { | ||||
|                 eventArray.splice(i, 0, event); | ||||
|                 break; | ||||
|             } else if (i === eventArray.length - 1) { | ||||
|                 eventArray.push(event); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,243 +0,0 @@ | |||
| // @flow
 | ||||
| 
 | ||||
| export type eventObject = { | ||||
|     id: number, | ||||
|     title: string, | ||||
|     logo: string, | ||||
|     date_begin: string, | ||||
|     date_end: string, | ||||
|     description: string, | ||||
|     club: string, | ||||
|     category_id: number, | ||||
|     url: string, | ||||
| }; | ||||
| 
 | ||||
| export default class PlanningEventManager { | ||||
| 
 | ||||
|     // Regex used to check date string validity
 | ||||
|     static dateRegExp = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/; | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the current day string representation in the format | ||||
|      * YYYY-MM-DD | ||||
|      * | ||||
|      * @return {string} The string representation | ||||
|      */ | ||||
|     static getCurrentDateString(): string { | ||||
|         return PlanningEventManager.dateToString(new Date(Date.now())); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Checks if the given date is before the other. | ||||
|      * | ||||
|      * @param event1Date Event 1 date in format YYYY-MM-DD HH:MM | ||||
|      * @param event2Date Event 2 date in format YYYY-MM-DD HH:MM | ||||
|      * @return {boolean} | ||||
|      */ | ||||
|     static isEventBefore(event1Date: string, event2Date: string): boolean { | ||||
|         let date1 = PlanningEventManager.stringToDate(event1Date); | ||||
|         let date2 = PlanningEventManager.stringToDate(event2Date); | ||||
|         if (date1 !== null && date2 !== null) | ||||
|             return date1 < date2; | ||||
|         else | ||||
|             return false; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets only the date part of the given event date string in the format | ||||
|      * YYYY-MM-DD HH:MM | ||||
|      * | ||||
|      * @param dateString The string to get the date from | ||||
|      * @return {string|null} Date in format YYYY:MM:DD or null if given string is invalid | ||||
|      */ | ||||
|     static getDateOnlyString(dateString: string): string | null { | ||||
|         if (PlanningEventManager.isEventDateStringFormatValid(dateString)) | ||||
|             return dateString.split(" ")[0]; | ||||
|         else | ||||
|             return null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Checks if the given date string is in the format | ||||
|      * YYYY-MM-DD HH:MM | ||||
|      * | ||||
|      * @param dateString The string to check | ||||
|      * @return {boolean} | ||||
|      */ | ||||
|     static isEventDateStringFormatValid(dateString: ?string): boolean { | ||||
|         return dateString !== undefined | ||||
|             && dateString !== null | ||||
|             && PlanningEventManager.dateRegExp.test(dateString); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Converts the given date string to a date object.<br> | ||||
|      * Accepted format: YYYY-MM-DD HH:MM | ||||
|      * | ||||
|      * @param dateString The string to convert | ||||
|      * @return {Date|null} The date object or null if the given string is invalid | ||||
|      */ | ||||
|     static stringToDate(dateString: string): Date | null { | ||||
|         let date = new Date(); | ||||
|         if (PlanningEventManager.isEventDateStringFormatValid(dateString)) { | ||||
|             let stringArray = dateString.split(' '); | ||||
|             let dateArray = stringArray[0].split('-'); | ||||
|             let timeArray = stringArray[1].split(':'); | ||||
|             date.setFullYear( | ||||
|                 parseInt(dateArray[0]), | ||||
|                 parseInt(dateArray[1]) - 1, // Month range from 0 to 11
 | ||||
|                 parseInt(dateArray[2]) | ||||
|             ); | ||||
|             date.setHours( | ||||
|                 parseInt(timeArray[0]), | ||||
|                 parseInt(timeArray[1]), | ||||
|                 0, | ||||
|                 0, | ||||
|             ); | ||||
|         } else | ||||
|             date = null; | ||||
| 
 | ||||
|         return date; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Converts a date object to a string in the format | ||||
|      * YYYY-MM-DD HH-MM-SS | ||||
|      * | ||||
|      * @param date The date object to convert | ||||
|      * @return {string} The converted string | ||||
|      */ | ||||
|     static dateToString(date: Date): string { | ||||
|         const day = String(date.getDate()).padStart(2, '0'); | ||||
|         const month = String(date.getMonth() + 1).padStart(2, '0'); //January is 0!
 | ||||
|         const year = date.getFullYear(); | ||||
|         const hours = String(date.getHours()).padStart(2, '0'); | ||||
|         const minutes = String(date.getMinutes()).padStart(2, '0'); | ||||
|         return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns a string corresponding to the event start and end times in the following format: | ||||
|      * | ||||
|      * HH:MM - HH:MM | ||||
|      * | ||||
|      * If the end date is not specified or is equal to start time, only start time will be shown. | ||||
|      * | ||||
|      * If the end date is not on the same day, 23:59 will be shown as end time | ||||
|      * | ||||
|      * @param start Start time in YYYY-MM-DD HH:MM:SS format | ||||
|      * @param end End time in YYYY-MM-DD HH:MM:SS format | ||||
|      * @return {string} Formatted string or "/ - /" on error | ||||
|      */ | ||||
|     static getFormattedEventTime(start: string, end: string): string { | ||||
|         let formattedStr = '/ - /'; | ||||
|         let startDate = PlanningEventManager.stringToDate(start); | ||||
|         let endDate = PlanningEventManager.stringToDate(end); | ||||
| 
 | ||||
|         if (startDate !== null && endDate !== null && startDate.getTime() !== endDate.getTime()) { | ||||
|             formattedStr = String(startDate.getHours()).padStart(2, '0') + ':' | ||||
|                 + String(startDate.getMinutes()).padStart(2, '0') + ' - '; | ||||
|             if (endDate.getFullYear() > startDate.getFullYear() | ||||
|                 || endDate.getMonth() > startDate.getMonth() | ||||
|                 || endDate.getDate() > startDate.getDate()) | ||||
|                 formattedStr += '23:59'; | ||||
|             else | ||||
|                 formattedStr += String(endDate.getHours()).padStart(2, '0') + ':' | ||||
|                     + String(endDate.getMinutes()).padStart(2, '0'); | ||||
|         } else if (startDate !== null) | ||||
|             formattedStr = | ||||
|                 String(startDate.getHours()).padStart(2, '0') + ':' | ||||
|                 + String(startDate.getMinutes()).padStart(2, '0'); | ||||
| 
 | ||||
|         return formattedStr | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Checks if the given description can be considered empty. | ||||
|      * <br> | ||||
|      * An empty description is composed only of whitespace, <b>br</b> or <b>p</b> tags | ||||
|      * | ||||
|      * | ||||
|      * @param description The text to check | ||||
|      * @return {boolean} | ||||
|      */ | ||||
|     static isDescriptionEmpty(description: ?string): boolean { | ||||
|         if (description !== undefined && description !== null) { | ||||
|             return description | ||||
|                 .split('<p>').join('') // Equivalent to a replace all
 | ||||
|                 .split('</p>').join('') | ||||
|                 .split('<br>').join('').trim() === ''; | ||||
|         } else | ||||
|             return true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Generates an object with an empty array for each key. | ||||
|      * Each key is a date string in the format | ||||
|      * YYYY-MM-DD | ||||
|      * | ||||
|      * @param numberOfMonths The number of months to create, starting from the current date | ||||
|      * @return {Object} | ||||
|      */ | ||||
|     static generateEmptyCalendar(numberOfMonths: number): Object { | ||||
|         let end = new Date(Date.now()); | ||||
|         end.setMonth(end.getMonth() + numberOfMonths); | ||||
|         let daysOfYear = {}; | ||||
|         for (let d = new Date(Date.now()); d <= end; d.setDate(d.getDate() + 1)) { | ||||
|             const dateString = PlanningEventManager.getDateOnlyString( | ||||
|                 PlanningEventManager.dateToString(new Date(d))); | ||||
|             if (dateString !== null) | ||||
|                 daysOfYear[dateString] = [] | ||||
|         } | ||||
|         return daysOfYear; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Generates an object with an array of eventObject at each key. | ||||
|      * Each key is a date string in the format | ||||
|      * YYYY-MM-DD. | ||||
|      * | ||||
|      * If no event is available at the given key, the array will be empty | ||||
|      * | ||||
|      * @param eventList The list of events to map to the agenda | ||||
|      * @param numberOfMonths The number of months to create the agenda for | ||||
|      * @return {Object} | ||||
|      */ | ||||
|     static generateEventAgenda(eventList: Array<eventObject>, numberOfMonths: number): Object { | ||||
|         let agendaItems = PlanningEventManager.generateEmptyCalendar(numberOfMonths); | ||||
|         for (let i = 0; i < eventList.length; i++) { | ||||
|             const dateString = PlanningEventManager.getDateOnlyString(eventList[i].date_begin); | ||||
|             if (dateString !== null) { | ||||
|                 const eventArray = agendaItems[dateString]; | ||||
|                 if (eventArray !== undefined) | ||||
|                     this.pushEventInOrder(eventArray, eventList[i]); | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|         return agendaItems; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Adds events to the given array depending on their starting date. | ||||
|      * | ||||
|      * Events starting before are added at the front. | ||||
|      * | ||||
|      * @param eventArray The array to hold sorted events | ||||
|      * @param event The event to add to the array | ||||
|      */ | ||||
|     static pushEventInOrder(eventArray: Array<eventObject>, event: eventObject): Object { | ||||
|         if (eventArray.length === 0) | ||||
|             eventArray.push(event); | ||||
|         else { | ||||
|             for (let i = 0; i < eventArray.length; i++) { | ||||
|                 if (PlanningEventManager.isEventBefore(event.date_begin, eventArray[i].date_begin)) { | ||||
|                     eventArray.splice(i, 0, event); | ||||
|                     break; | ||||
|                 } else if (i === eventArray.length - 1) { | ||||
|                     eventArray.push(event); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										19
									
								
								utils/WebData.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								utils/WebData.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| // @flow
 | ||||
| 
 | ||||
| /** | ||||
|  * Read data from FETCH_URL and return it. | ||||
|  * If no data was found, returns an empty object | ||||
|  * | ||||
|  * @param url The urls to fetch data from | ||||
|  * @return {Promise<Object>} | ||||
|  */ | ||||
| export async function readData(url: string) { | ||||
|     let fetchedData: Object = {}; | ||||
|     try { | ||||
|         let response = await fetch(url); | ||||
|         fetchedData = await response.json(); | ||||
|     } catch (error) { | ||||
|         throw new Error('Could not read FetchedData from server'); | ||||
|     } | ||||
|     return fetchedData; | ||||
| } | ||||
|  | @ -1,34 +0,0 @@ | |||
| // @flow
 | ||||
| 
 | ||||
| /** | ||||
|  * Class used to get json data from the web | ||||
|  */ | ||||
| export default class WebDataManager { | ||||
| 
 | ||||
|     FETCH_URL: string; | ||||
|     lastDataFetched: Object = {}; | ||||
| 
 | ||||
| 
 | ||||
|     constructor(url: string) { | ||||
|         this.FETCH_URL = url; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Read data from FETCH_URL and return it. | ||||
|      * If no data was found, returns an empty object | ||||
|      * | ||||
|      * @return {Promise<Object>} | ||||
|      */ | ||||
|     async readData() { | ||||
|         let fetchedData: Object = {}; | ||||
|         try { | ||||
|             let response = await fetch(this.FETCH_URL); | ||||
|             fetchedData = await response.json(); | ||||
|         } catch (error) { | ||||
|             throw new Error('Could not read FetchedData from server'); | ||||
|         } | ||||
|         this.lastDataFetched = fetchedData; | ||||
|         return fetchedData; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -1,210 +0,0 @@ | |||
| import React from 'react'; | ||||
| import PlanningEventManager from "../PlanningEventManager"; | ||||
| 
 | ||||
| test('isDescriptionEmpty', () => { | ||||
|     expect(PlanningEventManager.isDescriptionEmpty("")).toBeTrue(); | ||||
|     expect(PlanningEventManager.isDescriptionEmpty("   ")).toBeTrue(); | ||||
|     // noinspection CheckTagEmptyBody
 | ||||
|     expect(PlanningEventManager.isDescriptionEmpty("<p></p>")).toBeTrue(); | ||||
|     expect(PlanningEventManager.isDescriptionEmpty("<p>   </p>")).toBeTrue(); | ||||
|     expect(PlanningEventManager.isDescriptionEmpty("<p><br></p>")).toBeTrue(); | ||||
|     expect(PlanningEventManager.isDescriptionEmpty("<p><br></p><p><br></p>")).toBeTrue(); | ||||
|     expect(PlanningEventManager.isDescriptionEmpty("<p><br><br><br></p>")).toBeTrue(); | ||||
|     expect(PlanningEventManager.isDescriptionEmpty("<p><br>")).toBeTrue(); | ||||
|     expect(PlanningEventManager.isDescriptionEmpty(null)).toBeTrue(); | ||||
|     expect(PlanningEventManager.isDescriptionEmpty(undefined)).toBeTrue(); | ||||
|     expect(PlanningEventManager.isDescriptionEmpty("coucou")).toBeFalse(); | ||||
|     expect(PlanningEventManager.isDescriptionEmpty("<p>coucou</p>")).toBeFalse(); | ||||
| }); | ||||
| 
 | ||||
| test('isEventDateStringFormatValid', () => { | ||||
|     expect(PlanningEventManager.isEventDateStringFormatValid("2020-03-21 09:00")).toBeTrue(); | ||||
|     expect(PlanningEventManager.isEventDateStringFormatValid("3214-64-12 01:16")).toBeTrue(); | ||||
| 
 | ||||
|     expect(PlanningEventManager.isEventDateStringFormatValid("3214-64-12 01:16:00")).toBeFalse(); | ||||
|     expect(PlanningEventManager.isEventDateStringFormatValid("3214-64-12 1:16")).toBeFalse(); | ||||
|     expect(PlanningEventManager.isEventDateStringFormatValid("3214-f4-12 01:16")).toBeFalse(); | ||||
|     expect(PlanningEventManager.isEventDateStringFormatValid("sqdd 09:00")).toBeFalse(); | ||||
|     expect(PlanningEventManager.isEventDateStringFormatValid("2020-03-21")).toBeFalse(); | ||||
|     expect(PlanningEventManager.isEventDateStringFormatValid("2020-03-21 truc")).toBeFalse(); | ||||
|     expect(PlanningEventManager.isEventDateStringFormatValid("3214-64-12 1:16:65")).toBeFalse(); | ||||
|     expect(PlanningEventManager.isEventDateStringFormatValid("garbage")).toBeFalse(); | ||||
|     expect(PlanningEventManager.isEventDateStringFormatValid("")).toBeFalse(); | ||||
|     expect(PlanningEventManager.isEventDateStringFormatValid(undefined)).toBeFalse(); | ||||
|     expect(PlanningEventManager.isEventDateStringFormatValid(null)).toBeFalse(); | ||||
| }); | ||||
| 
 | ||||
| test('stringToDate', () => { | ||||
|     let testDate = new Date(); | ||||
|     expect(PlanningEventManager.stringToDate(undefined)).toBeNull(); | ||||
|     expect(PlanningEventManager.stringToDate("")).toBeNull(); | ||||
|     expect(PlanningEventManager.stringToDate("garbage")).toBeNull(); | ||||
|     expect(PlanningEventManager.stringToDate("2020-03-21")).toBeNull(); | ||||
|     expect(PlanningEventManager.stringToDate("09:00:00")).toBeNull(); | ||||
|     expect(PlanningEventManager.stringToDate("2020-03-21 09:g0")).toBeNull(); | ||||
|     expect(PlanningEventManager.stringToDate("2020-03-21 09:g0:")).toBeNull(); | ||||
|     testDate.setFullYear(2020, 2, 21); | ||||
|     testDate.setHours(9, 0, 0, 0); | ||||
|     expect(PlanningEventManager.stringToDate("2020-03-21 09:00")).toEqual(testDate); | ||||
|     testDate.setFullYear(2020, 0, 31); | ||||
|     testDate.setHours(18, 30, 0, 0); | ||||
|     expect(PlanningEventManager.stringToDate("2020-01-31 18:30")).toEqual(testDate); | ||||
|     testDate.setFullYear(2020, 50, 50); | ||||
|     testDate.setHours(65, 65, 0, 0); | ||||
|     expect(PlanningEventManager.stringToDate("2020-51-50 65:65")).toEqual(testDate); | ||||
| }); | ||||
| 
 | ||||
| test('getFormattedEventTime', () => { | ||||
|     expect(PlanningEventManager.getFormattedEventTime(null, null)) | ||||
|         .toBe('/ - /'); | ||||
|     expect(PlanningEventManager.getFormattedEventTime(undefined, undefined)) | ||||
|         .toBe('/ - /'); | ||||
|     expect(PlanningEventManager.getFormattedEventTime("20:30", "23:00")) | ||||
|         .toBe('/ - /'); | ||||
|     expect(PlanningEventManager.getFormattedEventTime("2020-03-30", "2020-03-31")) | ||||
|         .toBe('/ - /'); | ||||
| 
 | ||||
| 
 | ||||
|     expect(PlanningEventManager.getFormattedEventTime("2020-03-21 09:00", "2020-03-21 09:00")) | ||||
|         .toBe('09:00'); | ||||
|     expect(PlanningEventManager.getFormattedEventTime("2020-03-21 09:00", "2020-03-22 17:00")) | ||||
|         .toBe('09:00 - 23:59'); | ||||
|     expect(PlanningEventManager.getFormattedEventTime("2020-03-30 20:30", "2020-03-30 23:00")) | ||||
|         .toBe('20:30 - 23:00'); | ||||
| }); | ||||
| 
 | ||||
| test('getDateOnlyString', () => { | ||||
|     expect(PlanningEventManager.getDateOnlyString("2020-03-21 09:00")).toBe("2020-03-21"); | ||||
|     expect(PlanningEventManager.getDateOnlyString("2021-12-15 09:00")).toBe("2021-12-15"); | ||||
|     expect(PlanningEventManager.getDateOnlyString("2021-12-o5 09:00")).toBeNull(); | ||||
|     expect(PlanningEventManager.getDateOnlyString("2021-12-15 09:")).toBeNull(); | ||||
|     expect(PlanningEventManager.getDateOnlyString("2021-12-15")).toBeNull(); | ||||
|     expect(PlanningEventManager.getDateOnlyString("garbage")).toBeNull(); | ||||
| }); | ||||
| 
 | ||||
| test('isEventBefore', () => { | ||||
|     expect(PlanningEventManager.isEventBefore( | ||||
|         "2020-03-21 09:00", "2020-03-21 10:00")).toBeTrue(); | ||||
|     expect(PlanningEventManager.isEventBefore( | ||||
|         "2020-03-21 10:00", "2020-03-21 10:15")).toBeTrue(); | ||||
|     expect(PlanningEventManager.isEventBefore( | ||||
|         "2020-03-21 10:15", "2021-03-21 10:15")).toBeTrue(); | ||||
|     expect(PlanningEventManager.isEventBefore( | ||||
|         "2020-03-21 10:15", "2020-05-21 10:15")).toBeTrue(); | ||||
|     expect(PlanningEventManager.isEventBefore( | ||||
|         "2020-03-21 10:15", "2020-03-30 10:15")).toBeTrue(); | ||||
| 
 | ||||
|     expect(PlanningEventManager.isEventBefore( | ||||
|         "2020-03-21 10:00", "2020-03-21 10:00")).toBeFalse(); | ||||
|     expect(PlanningEventManager.isEventBefore( | ||||
|         "2020-03-21 10:00", "2020-03-21 09:00")).toBeFalse(); | ||||
|     expect(PlanningEventManager.isEventBefore( | ||||
|         "2020-03-21 10:15", "2020-03-21 10:00")).toBeFalse(); | ||||
|     expect(PlanningEventManager.isEventBefore( | ||||
|         "2021-03-21 10:15", "2020-03-21 10:15")).toBeFalse(); | ||||
|     expect(PlanningEventManager.isEventBefore( | ||||
|         "2020-05-21 10:15", "2020-03-21 10:15")).toBeFalse(); | ||||
|     expect(PlanningEventManager.isEventBefore( | ||||
|         "2020-03-30 10:15", "2020-03-21 10:15")).toBeFalse(); | ||||
| 
 | ||||
|     expect(PlanningEventManager.isEventBefore( | ||||
|         "garbage", "2020-03-21 10:15")).toBeFalse(); | ||||
|     expect(PlanningEventManager.isEventBefore( | ||||
|         undefined, undefined)).toBeFalse(); | ||||
| }); | ||||
| 
 | ||||
| test('dateToString', () => { | ||||
|     let testDate = new Date(); | ||||
|     testDate.setFullYear(2020, 2, 21); | ||||
|     testDate.setHours(9, 0, 0, 0); | ||||
|     expect(PlanningEventManager.dateToString(testDate)).toBe("2020-03-21 09:00"); | ||||
|     testDate.setFullYear(2021, 0, 12); | ||||
|     testDate.setHours(9, 10, 0, 0); | ||||
|     expect(PlanningEventManager.dateToString(testDate)).toBe("2021-01-12 09:10"); | ||||
|     testDate.setFullYear(2022, 11, 31); | ||||
|     testDate.setHours(9, 10, 15, 0); | ||||
|     expect(PlanningEventManager.dateToString(testDate)).toBe("2022-12-31 09:10"); | ||||
| }); | ||||
| 
 | ||||
| test('generateEmptyCalendar', () => { | ||||
|     jest.spyOn(Date, 'now') | ||||
|         .mockImplementation(() => | ||||
|             new Date('2020-01-14T00:00:00.000Z').getTime() | ||||
|         ); | ||||
|     let calendar = PlanningEventManager.generateEmptyCalendar(1); | ||||
|     expect(calendar).toHaveProperty("2020-01-14"); | ||||
|     expect(calendar).toHaveProperty("2020-01-20"); | ||||
|     expect(calendar).toHaveProperty("2020-02-10"); | ||||
|     expect(Object.keys(calendar).length).toBe(32); | ||||
|     calendar = PlanningEventManager.generateEmptyCalendar(3); | ||||
|     expect(calendar).toHaveProperty("2020-01-14"); | ||||
|     expect(calendar).toHaveProperty("2020-01-20"); | ||||
|     expect(calendar).toHaveProperty("2020-02-10"); | ||||
|     expect(calendar).toHaveProperty("2020-02-14"); | ||||
|     expect(calendar).toHaveProperty("2020-03-20"); | ||||
|     expect(calendar).toHaveProperty("2020-04-12"); | ||||
|     expect(Object.keys(calendar).length).toBe(92); | ||||
| }); | ||||
| 
 | ||||
| test('pushEventInOrder', () => { | ||||
|     let eventArray = []; | ||||
|     let event1 = {date_begin: "2020-01-14 09:15"}; | ||||
|     PlanningEventManager.pushEventInOrder(eventArray, event1); | ||||
|     expect(eventArray.length).toBe(1); | ||||
|     expect(eventArray[0]).toBe(event1); | ||||
| 
 | ||||
|     let event2 = {date_begin: "2020-01-14 10:15"}; | ||||
|     PlanningEventManager.pushEventInOrder(eventArray, event2); | ||||
|     expect(eventArray.length).toBe(2); | ||||
|     expect(eventArray[0]).toBe(event1); | ||||
|     expect(eventArray[1]).toBe(event2); | ||||
| 
 | ||||
|     let event3 = {date_begin: "2020-01-14 10:15", title: "garbage"}; | ||||
|     PlanningEventManager.pushEventInOrder(eventArray, event3); | ||||
|     expect(eventArray.length).toBe(3); | ||||
|     expect(eventArray[0]).toBe(event1); | ||||
|     expect(eventArray[1]).toBe(event2); | ||||
|     expect(eventArray[2]).toBe(event3); | ||||
| 
 | ||||
|     let event4 = {date_begin: "2020-01-13 09:00"}; | ||||
|     PlanningEventManager.pushEventInOrder(eventArray, event4); | ||||
|     expect(eventArray.length).toBe(4); | ||||
|     expect(eventArray[0]).toBe(event4); | ||||
|     expect(eventArray[1]).toBe(event1); | ||||
|     expect(eventArray[2]).toBe(event2); | ||||
|     expect(eventArray[3]).toBe(event3); | ||||
| }); | ||||
| 
 | ||||
| test('generateEventAgenda', () => { | ||||
|     jest.spyOn(Date, 'now') | ||||
|         .mockImplementation(() => | ||||
|             new Date('2020-01-14T00:00:00.000Z').getTime() | ||||
|         ); | ||||
|     let eventList = [ | ||||
|         {date_begin: "2020-01-14 09:15"}, | ||||
|         {date_begin: "2020-02-01 09:15"}, | ||||
|         {date_begin: "2020-01-15 09:15"}, | ||||
|         {date_begin: "2020-02-01 09:30"}, | ||||
|         {date_begin: "2020-02-01 08:30"}, | ||||
|     ]; | ||||
|     const calendar = PlanningEventManager.generateEventAgenda(eventList, 2); | ||||
|     expect(calendar["2020-01-14"].length).toBe(1); | ||||
|     expect(calendar["2020-01-14"][0]).toBe(eventList[0]); | ||||
|     expect(calendar["2020-01-15"].length).toBe(1); | ||||
|     expect(calendar["2020-01-15"][0]).toBe(eventList[2]); | ||||
|     expect(calendar["2020-02-01"].length).toBe(3); | ||||
|     expect(calendar["2020-02-01"][0]).toBe(eventList[4]); | ||||
|     expect(calendar["2020-02-01"][1]).toBe(eventList[1]); | ||||
|     expect(calendar["2020-02-01"][2]).toBe(eventList[3]); | ||||
| }); | ||||
| 
 | ||||
| test('getCurrentDateString', () => { | ||||
|     jest.spyOn(Date, 'now') | ||||
|         .mockImplementation(() => { | ||||
|             let date = new Date(); | ||||
|             date.setFullYear(2020, 0, 14); | ||||
|             date.setHours(15, 30, 54, 65); | ||||
|             return date.getTime(); | ||||
|         }); | ||||
|     expect(PlanningEventManager.getCurrentDateString()).toBe('2020-01-14 15:30'); | ||||
| }); | ||||
		Loading…
	
		Reference in a new issue