forked from vergnet/application-amicale
		
	improved assets, added more details in app.json
This commit is contained in:
		
							parent
							
								
									b7ef071565
								
							
						
					
					
						commit
						405f1769d3
					
				
					 19 changed files with 243 additions and 93 deletions
				
			
		
							
								
								
									
										24
									
								
								App.js
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								App.js
									
									
									
									
									
								
							|  | @ -1,9 +1,8 @@ | |||
| // @flow
 | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import * as React from 'react'; | ||||
| import {Root, StyleProvider, Text} from 'native-base'; | ||||
| import {Ionicons} from '@expo/vector-icons'; | ||||
| import {StyleSheet, View, Image} from 'react-native' | ||||
| import {Image, StyleSheet, View} from 'react-native' | ||||
| import AppNavigator from './navigation/AppNavigator'; | ||||
| import ThemeManager from './utils/ThemeManager'; | ||||
| import LocaleManager from './utils/LocaleManager'; | ||||
|  | @ -43,6 +42,7 @@ const styles = StyleSheet.create({ | |||
|     }, | ||||
| }); | ||||
| 
 | ||||
| // Content to be used int the intro slides
 | ||||
| const slides = [ | ||||
|     { | ||||
|         key: '1', | ||||
|  | @ -61,7 +61,7 @@ const slides = [ | |||
|     { | ||||
|         key: '3', | ||||
|         title: 'Le proximo', | ||||
|         text: 'Regardez le stock de la supérette de l\'INSA depuis n\'importe où' , | ||||
|         text: 'Regardez le stock de la supérette de l\'INSA depuis n\'importe où', | ||||
|         icon: 'shopping', | ||||
|         colors: ['#f9a967', '#da5204'], | ||||
|     }, | ||||
|  | @ -101,12 +101,14 @@ export default class App extends React.Component<Props, State> { | |||
|      * @returns {Promise} | ||||
|      */ | ||||
|     async componentWillMount() { | ||||
|         // Wait for custom fonts to be loaded before showing the app
 | ||||
|         await Font.loadAsync({ | ||||
|             'Roboto': require('native-base/Fonts/Roboto.ttf'), | ||||
|             'Roboto_medium': require('native-base/Fonts/Roboto_medium.ttf'), | ||||
|         }); | ||||
|         await AsyncStorageManager.getInstance().loadPreferences(); | ||||
|         ThemeManager.getInstance().setUpdateThemeCallback(() => this.updateTheme()); | ||||
|         // Only show intro if this is the first time starting the app
 | ||||
|         this.setState({ | ||||
|             isLoading: false, | ||||
|             currentTheme: ThemeManager.getCurrentTheme(), | ||||
|  | @ -115,17 +117,20 @@ export default class App extends React.Component<Props, State> { | |||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Updates the theme and clears the cache to force reloading the app colors | ||||
|      * Updates the theme and clears the cache to force reloading the app colors. Need to edit shoutem theme for ti to work | ||||
|      */ | ||||
|     updateTheme() { | ||||
|         // console.log('update theme called');
 | ||||
|         this.setState({ | ||||
|             currentTheme: ThemeManager.getCurrentTheme() | ||||
|         }); | ||||
|         clearThemeCache(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Render item to be used for the intro slides | ||||
|      * @param item | ||||
|      * @param dimensions | ||||
|      */ | ||||
|     getIntroRenderItem(item: Object, dimensions: Object) { | ||||
|         return ( | ||||
|             <LinearGradient | ||||
|  | @ -148,6 +153,9 @@ export default class App extends React.Component<Props, State> { | |||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Callback when user ends the intro. Save in preferences to avaoid showing back the slides | ||||
|      */ | ||||
|     onIntroDone() { | ||||
|         this.setState({showIntro: false}); | ||||
|         AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.showIntro.key, '0'); | ||||
|  | @ -155,8 +163,6 @@ export default class App extends React.Component<Props, State> { | |||
| 
 | ||||
|     /** | ||||
|      * Renders the app based on loading state | ||||
|      * | ||||
|      * @returns {*} | ||||
|      */ | ||||
|     render() { | ||||
|         if (this.state.isLoading) { | ||||
|  |  | |||
							
								
								
									
										34
									
								
								app.json
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								app.json
									
									
									
									
									
								
							|  | @ -1,6 +1,7 @@ | |||
| { | ||||
|   "expo": { | ||||
|     "name": "Amicale INSAT", | ||||
|     "description": "Application mobile compatible Android et iOS pour l'Amicale INSA Toulouse. Grâce à cette application, vous avez facilement accès aux news du campus, aux emplois du temps, à l'état de la laverie, et bien d'autres services ! Ceci est une version Beta, Toutes les fonctionnalités ne sont pas encore implémentées, et il est possible de rencontrer quelques bugs.", | ||||
|     "slug": "application-amicale", | ||||
|     "privacy": "public", | ||||
|     "sdkVersion": "33.0.0", | ||||
|  | @ -9,29 +10,42 @@ | |||
|       "android", | ||||
|       "web" | ||||
|     ], | ||||
|     "version": "0.0.4", | ||||
|     "version": "0.0.5", | ||||
|     "orientation": "portrait", | ||||
|     "icon": "./assets/icon.png", | ||||
|     "primaryColor": "#e42612", | ||||
|     "icon": "./assets/icon.png", | ||||
|     "splash": { | ||||
|       "image": "./assets/splash.png", | ||||
|       "resizeMode": "cover", | ||||
|       "backgroundColor": "#fff" | ||||
|       "backgroundColor": "#fff", | ||||
|       "resizeMode": "contain", | ||||
|       "image": "./assets/splash.png" | ||||
|     }, | ||||
|     "notification": { | ||||
|       "icon": "./assets/amicale-notification.png", | ||||
|       "color": "#e42612", | ||||
|       "androidMode": "default" | ||||
|     }, | ||||
|     "updates": { | ||||
|       "fallbackToCacheTimeout": 0 | ||||
|       "enabled": false | ||||
|     }, | ||||
|     "assetBundlePatterns": [ | ||||
|       "**/*" | ||||
|     ], | ||||
|     "ios": { | ||||
|       "supportsTablet": true, | ||||
|       "icon": "./assets/ios.icon.png", | ||||
|       "bundleIdentifier": "com.test.applicationamicale" | ||||
|       "bundleIdentifier": "amicale.insat.application", | ||||
|       "buildNumber": "0.0.5", | ||||
|       "icon": "./assets/ios.icon.png" | ||||
|     }, | ||||
|     "android": { | ||||
|       "package": "amicale.insat.application", | ||||
|       "versionCode": 1, | ||||
|       "icon": "./assets/android.icon.png", | ||||
|       "package": "com.test.applicationamicale" | ||||
|       "adaptiveIcon": { | ||||
|         "foregroundImage": "./assets/android.adaptive-icon.png", | ||||
|         "backgroundColor": "#ffffff" | ||||
|       }, | ||||
|       "permissions": [ | ||||
|         "VIBRATE" | ||||
|       ] | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								assets/amicale-notification.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/amicale-notification.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 8.4 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 84 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 110 KiB | 
|  | @ -1,8 +1,8 @@ | |||
| // @flow
 | ||||
| 
 | ||||
| import * as React from "react"; | ||||
| import {Body, Header, Icon, Left, Right, Title} from "native-base"; | ||||
| import {StyleSheet, Platform} from "react-native"; | ||||
| import {Body, Header, Left, Right, Title} from "native-base"; | ||||
| import {Platform, StyleSheet} from "react-native"; | ||||
| import {getStatusBarHeight} from "react-native-status-bar-height"; | ||||
| import Touchable from 'react-native-platform-touchable'; | ||||
| import ThemeManager from "../utils/ThemeManager"; | ||||
|  | @ -34,6 +34,7 @@ export default class CustomHeader extends React.Component<Props> { | |||
| 
 | ||||
|     render() { | ||||
|         let button; | ||||
|         // Does the app have a back button or a burger menu ?
 | ||||
|         if (this.props.backButton) | ||||
|             button = | ||||
|                 <Touchable | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ type Props = { | |||
|     icon: string, | ||||
|     color: ?string, | ||||
|     fontSize: number, | ||||
|     width: number|string, | ||||
|     width: number | string, | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  |  | |||
|  | @ -20,16 +20,20 @@ type State = { | |||
|     machinesWatched: Array<Object>, | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Class used to create a basic list view using online json data. | ||||
|  * Used by inheriting from it and redefining getters. | ||||
|  */ | ||||
| export default class FetchedDataSectionList extends React.Component<Props, State> { | ||||
| 
 | ||||
|     webDataManager: WebDataManager; | ||||
| 
 | ||||
|     willFocusSubscription : function; | ||||
|     willBlurSubscription : function; | ||||
|     willFocusSubscription: function; | ||||
|     willBlurSubscription: function; | ||||
|     refreshInterval: IntervalID; | ||||
|     refreshTime: number; | ||||
| 
 | ||||
|     constructor(fetchUrl: string, refreshTime : number) { | ||||
|     constructor(fetchUrl: string, refreshTime: number) { | ||||
|         super(); | ||||
|         this.webDataManager = new WebDataManager(fetchUrl); | ||||
|         this.refreshTime = refreshTime; | ||||
|  | @ -42,16 +46,25 @@ export default class FetchedDataSectionList extends React.Component<Props, State | |||
|         machinesWatched: [], | ||||
|     }; | ||||
| 
 | ||||
|     getHeaderTranslation() { | ||||
|     /** | ||||
|      * Get the translation for the header in the current language | ||||
|      * @return {string} | ||||
|      */ | ||||
|     getHeaderTranslation() : string { | ||||
|         return "Header"; | ||||
|     } | ||||
| 
 | ||||
|     getUpdateToastTranslations() { | ||||
|     /** | ||||
|      * Get the translation for the toasts in the current language | ||||
|      * @return {string} | ||||
|      */ | ||||
|     getUpdateToastTranslations(): Array<string> { | ||||
|         return ["whoa", "nah"]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Register react navigation events on first screen load | ||||
|      * Register react navigation events on first screen load. | ||||
|      * Allows to detect when the screen is focused | ||||
|      */ | ||||
|     componentDidMount() { | ||||
|         this.willFocusSubscription = this.props.navigation.addListener( | ||||
|  | @ -68,28 +81,37 @@ export default class FetchedDataSectionList extends React.Component<Props, State | |||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Refresh data when focusing the screen and setup a refresh interval if asked to | ||||
|      */ | ||||
|     onScreenFocus() { | ||||
|         this._onRefresh(); | ||||
|         if (this.refreshTime > 0) | ||||
|             this.refreshInterval = setInterval(() => this._onRefresh(), this.refreshTime) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Remove any interval on un-focus | ||||
|      */ | ||||
|     onScreenBlur() { | ||||
|         clearInterval(this.refreshInterval); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Unregister from event when un-mounting components | ||||
|      */ | ||||
|     componentWillUnmount() { | ||||
|         if (this.willBlurSubscription !== undefined) | ||||
|             this.willBlurSubscription.remove(); | ||||
|         if (this.willFocusSubscription !== undefined) | ||||
|             this.willFocusSubscription.remove(); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Refresh data and show a toast if any error occurred | ||||
|      * @private | ||||
|      */ | ||||
|     _onRefresh = () => { | ||||
|         console.log('refresh'); | ||||
|         this.setState({refreshing: true}); | ||||
|         this.webDataManager.readData().then((fetchedData) => { | ||||
|             this.setState({ | ||||
|  | @ -101,14 +123,38 @@ export default class FetchedDataSectionList extends React.Component<Props, State | |||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     /** | ||||
|      * Get the render item to be used for display in the list. | ||||
|      * Must be overridden by inheriting class. | ||||
|      * | ||||
|      * @param item | ||||
|      * @param section | ||||
|      * @param data | ||||
|      * @return {*} | ||||
|      */ | ||||
|     getRenderItem(item: Object, section: Object, data: Object) { | ||||
|         return <View/>; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the render item to be used for the section title in the list. | ||||
|      * Must be overridden by inheriting class. | ||||
|      * | ||||
|      * @param title | ||||
|      * @return {*} | ||||
|      */ | ||||
|     getRenderSectionHeader(title: String) { | ||||
|         return <View/>; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the render item to be used when the list is empty. | ||||
|      * No need to be overridden, has good defaults. | ||||
|      * | ||||
|      * @param text | ||||
|      * @param icon | ||||
|      * @return {*} | ||||
|      */ | ||||
|     getEmptyRenderItem(text: string, icon: string) { | ||||
|         return ( | ||||
|             <View> | ||||
|  | @ -138,7 +184,9 @@ export default class FetchedDataSectionList extends React.Component<Props, State | |||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create the dataset to be used in the list from the data fetched | ||||
|      * Create the dataset to be used in the list from the data fetched. | ||||
|      * Must be overridden. | ||||
|      * | ||||
|      * @param fetchedData {Object} | ||||
|      * @return {Array} | ||||
|      */ | ||||
|  | @ -146,6 +194,12 @@ export default class FetchedDataSectionList extends React.Component<Props, State | |||
|         return []; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create the dataset when no fetched data is available. | ||||
|      * No need to be overridden, has good defaults. | ||||
|      * | ||||
|      * @return | ||||
|      */ | ||||
|     createEmptyDataset() { | ||||
|         return [ | ||||
|             { | ||||
|  | @ -165,10 +219,23 @@ export default class FetchedDataSectionList extends React.Component<Props, State | |||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Should the app use a tab layout instead of a section list ? | ||||
|      * If yes, each section will be rendered in a new tab. | ||||
|      * Can be overridden. | ||||
|      * | ||||
|      * @return {boolean} | ||||
|      */ | ||||
|     hasTabs() { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the section list render using the generated dataset | ||||
|      * | ||||
|      * @param dataset | ||||
|      * @return | ||||
|      */ | ||||
|     getSectionList(dataset: Array<Object>) { | ||||
|         let isEmpty = dataset[0].data.length === 0; | ||||
|         if (isEmpty) | ||||
|  | @ -201,6 +268,12 @@ export default class FetchedDataSectionList extends React.Component<Props, State | |||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Generate the tabs containing the lists | ||||
|      * | ||||
|      * @param dataset | ||||
|      * @return | ||||
|      */ | ||||
|     getTabbedView(dataset: Array<Object>) { | ||||
|         let tabbedView = []; | ||||
|         for (let i = 0; i < dataset.length; i++) { | ||||
|  | @ -214,7 +287,7 @@ export default class FetchedDataSectionList extends React.Component<Props, State | |||
|                         <Text>{dataset[i].title}</Text> | ||||
|                     </TabHeading>} | ||||
|                      key={dataset[i].title} | ||||
|                 style={{backgroundColor: ThemeManager.getCurrentThemeVariables().containerBgColor}}> | ||||
|                      style={{backgroundColor: ThemeManager.getCurrentThemeVariables().containerBgColor}}> | ||||
|                     {this.getSectionList( | ||||
|                         [ | ||||
|                             { | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| // @flow
 | ||||
| 
 | ||||
| import * as React from 'react'; | ||||
| import {Platform, Dimensions, StyleSheet, Image, FlatList, Linking} from 'react-native'; | ||||
| import {Badge, Text, Container, Content, Left, ListItem, Right} from "native-base"; | ||||
| import {Dimensions, FlatList, Image, Linking, Platform, StyleSheet} from 'react-native'; | ||||
| import {Badge, Container, Content, Left, ListItem, Right, Text} from "native-base"; | ||||
| import i18n from "i18n-js"; | ||||
| import CustomMaterialIcon from '../components/CustomMaterialIcon'; | ||||
| 
 | ||||
|  | @ -40,72 +40,56 @@ export default class SideBar extends React.Component<Props, State> { | |||
|      */ | ||||
|     constructor(props: Props) { | ||||
|         super(props); | ||||
|         // Dataset used to render the drawer
 | ||||
|         // If the link field is defined, clicking on the item will open the link
 | ||||
|         this.dataSet = [ | ||||
|             { | ||||
|                 name: i18n.t('screens.home'), | ||||
|                 route: "Home", | ||||
|                 icon: "home", | ||||
|                 bg: "#C5F442" | ||||
|                 // types: "11" // Shows the badge
 | ||||
|             }, | ||||
|             { | ||||
|                 name: i18n.t('screens.planning'), | ||||
|                 route: "Planning", | ||||
|                 icon: "calendar-range", | ||||
|                 bg: "#477EEA", | ||||
|                 // types: "11"
 | ||||
|             }, | ||||
|             { | ||||
|                 name: "Proxiwash", | ||||
|                 route: "Proxiwash", | ||||
|                 icon: "washing-machine", | ||||
|                 bg: "#477EEA", | ||||
|                 // types: "11"
 | ||||
|             }, | ||||
|             { | ||||
|                 name: "Proximo", | ||||
|                 route: "Proximo", | ||||
|                 icon: "shopping", | ||||
|                 bg: "#477EEA", | ||||
|                 // types: "11"
 | ||||
|             }, | ||||
|             { | ||||
|                 name: "Amicale", | ||||
|                 route: "amicale", | ||||
|                 icon: "web", | ||||
|                 bg: "#477EEA", | ||||
|                 link: Amicale_LINK | ||||
|                 // types: "11"
 | ||||
|             }, | ||||
|             { | ||||
|                 name: i18n.t('screens.timetable'), | ||||
|                 route: "timetable", | ||||
|                 icon: "timetable", | ||||
|                 bg: "#477EEA", | ||||
|                 link: TIMETABLE_LINK | ||||
|                 // types: "11"
 | ||||
|             }, | ||||
|             { | ||||
|                 name: "Wiketud", | ||||
|                 route: "wiketud", | ||||
|                 icon: "wikipedia", | ||||
|                 bg: "#477EEA", | ||||
|                 link: WIKETUD_LINK | ||||
|                 // types: "11"
 | ||||
|             }, | ||||
|             { | ||||
|                 name: i18n.t('screens.settings'), | ||||
|                 route: "Settings", | ||||
|                 icon: "settings", | ||||
|                 bg: "#477EEA", | ||||
|                 // types: "11"
 | ||||
|             }, | ||||
|             { | ||||
|                 name: i18n.t('screens.about'), | ||||
|                 route: "About", | ||||
|                 icon: "information", | ||||
|                 bg: "#477EEA", | ||||
|                 // types: "11"
 | ||||
|             }, | ||||
|         ]; | ||||
|     } | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| // @flow
 | ||||
| 
 | ||||
| import * as React from 'react'; | ||||
| import {Container, Text, Content, ListItem, Body} from 'native-base'; | ||||
| import {Body, Container, Content, ListItem, Text} from 'native-base'; | ||||
| import CustomHeader from "../../components/CustomHeader"; | ||||
| import {FlatList} from "react-native"; | ||||
| import i18n from "i18n-js"; | ||||
|  | @ -30,7 +30,7 @@ export default class AboutDependenciesScreen extends React.Component<Props> { | |||
|         const data = generateListFromObject(nav.getParam('data', {})); | ||||
|         return ( | ||||
|             <Container> | ||||
|                 <CustomHeader backButton={true} navigation={nav} title={i18n.t('aboutScreen.libs')} /> | ||||
|                 <CustomHeader backButton={true} navigation={nav} title={i18n.t('aboutScreen.libs')}/> | ||||
|                 <Content> | ||||
|                     <FlatList | ||||
|                         data={data} | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| // @flow
 | ||||
| 
 | ||||
| import * as React from 'react'; | ||||
| import {Platform, StyleSheet, Linking, Alert, FlatList} from 'react-native'; | ||||
| import {Container, Content, Text, Card, CardItem, Body, Left, Right, Thumbnail, H1} from 'native-base'; | ||||
| import {Alert, FlatList, Linking, Platform} from 'react-native'; | ||||
| import {Body, Card, CardItem, Container, Content, H1, Left, Right, Text, Thumbnail} from 'native-base'; | ||||
| import CustomHeader from "../../components/CustomHeader"; | ||||
| import i18n from "i18n-js"; | ||||
| import appJson from '../../app'; | ||||
|  |  | |||
|  | @ -65,7 +65,7 @@ export default class PlanningScreen extends React.Component<Props> { | |||
|                     </Text> | ||||
|                     {Platform.OS === 'android' ? | ||||
|                         <Button block style={{marginTop: 20, marginRight: 10, marginLeft: 10}} | ||||
|                         onPress={() => openWebLink('https://expo.io/@amicaleinsat/application-amicale')}> | ||||
|                                 onPress={() => openWebLink('https://expo.io/@amicaleinsat/application-amicale')}> | ||||
|                             <Text>Try the beta</Text> | ||||
|                         </Button> | ||||
|                         : <View/>} | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| // @flow
 | ||||
| 
 | ||||
| import * as React from 'react'; | ||||
| import {Container, Text, Content, ListItem, Left, Thumbnail, Right, Body} from 'native-base'; | ||||
| import {Body, Container, Content, Left, ListItem, Right, Text, Thumbnail} from 'native-base'; | ||||
| import CustomHeader from "../../components/CustomHeader"; | ||||
| import {FlatList, Platform} from "react-native"; | ||||
| import Touchable from 'react-native-platform-touchable'; | ||||
|  | @ -132,8 +132,14 @@ export default class ProximoListScreen extends React.Component<Props, State> { | |||
|         this.setSortMode(this.state.currentSortMode, this.state.isSortReversed); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * get color depending on quantity available | ||||
|      * | ||||
|      * @param availableStock | ||||
|      * @return | ||||
|      */ | ||||
|     getStockColor(availableStock: number) { | ||||
|         let color : string; | ||||
|         let color: string; | ||||
|         if (availableStock > 3) | ||||
|             color = ThemeManager.getCurrentThemeVariables().brandSuccess; | ||||
|         else if (availableStock > 0) | ||||
|  | @ -234,7 +240,8 @@ export default class ProximoListScreen extends React.Component<Props, State> { | |||
|                                     </Text> | ||||
|                                     <Text note style={{ | ||||
|                                         marginLeft: 20, | ||||
|                                     color: this.getStockColor(parseInt(item.quantity))}}> | ||||
|                                         color: this.getStockColor(parseInt(item.quantity)) | ||||
|                                     }}> | ||||
|                                         {item.quantity + ' ' + i18n.t('proximoScreen.inStock')} | ||||
|                                     </Text> | ||||
|                                 </Body> | ||||
|  |  | |||
|  | @ -78,7 +78,7 @@ export default class ProximoMainScreen extends FetchedDataSectionList { | |||
|         return finalData; | ||||
|     } | ||||
| 
 | ||||
|     getRenderItem(item: Object, section : Object, data : Object) { | ||||
|     getRenderItem(item: Object, section: Object, data: Object) { | ||||
|         if (item.data.length > 0) { | ||||
|             return ( | ||||
|                 <ListItem | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| // @flow
 | ||||
| 
 | ||||
| import * as React from 'react'; | ||||
| import {Alert, View, Platform} from 'react-native'; | ||||
| import {Alert, Platform, View} from 'react-native'; | ||||
| import {Body, Card, CardItem, Left, Right, Text} from 'native-base'; | ||||
| import ThemeManager from '../utils/ThemeManager'; | ||||
| import i18n from "i18n-js"; | ||||
|  | @ -36,7 +36,7 @@ let stateColors = {}; | |||
|  */ | ||||
| export default class ProxiwashScreen extends FetchedDataSectionList { | ||||
| 
 | ||||
|     refreshInterval : IntervalID; | ||||
|     refreshInterval: IntervalID; | ||||
| 
 | ||||
|     /** | ||||
|      * Creates machine state parameters using current theme and translations | ||||
|  | @ -77,6 +77,9 @@ export default class ProxiwashScreen extends FetchedDataSectionList { | |||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Setup notification channel for android and add listeners to detect notifications fired | ||||
|      */ | ||||
|     componentDidMount() { | ||||
|         super.componentDidMount(); | ||||
|         if (Platform.OS === 'android') { | ||||
|  | @ -104,10 +107,6 @@ export default class ProxiwashScreen extends FetchedDataSectionList { | |||
|         return [i18n.t("proxiwashScreen.listUpdated"), i18n.t("proxiwashScreen.listUpdateFail")]; | ||||
|     } | ||||
| 
 | ||||
|     getKeyExtractor(item: Object) { | ||||
|         return item !== undefined ? item.number : undefined; | ||||
|     } | ||||
| 
 | ||||
|     getDryersKeyExtractor(item: Object) { | ||||
|         return item !== undefined ? "dryer" + item.number : undefined; | ||||
|     } | ||||
|  | @ -202,13 +201,26 @@ export default class ProxiwashScreen extends FetchedDataSectionList { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     getMachineIndexInWatchList(machineId: string) { | ||||
|     /** | ||||
|      * Get the index of the given machine ID in the watchlist array | ||||
|      * | ||||
|      * @param machineId | ||||
|      * @return | ||||
|      */ | ||||
|     getMachineIndexInWatchList(machineId: string): number { | ||||
|         let elem = this.state.machinesWatched.find(function (elem) { | ||||
|             return elem.machineNumber === machineId | ||||
|         }); | ||||
|         return this.state.machinesWatched.indexOf(elem); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Add the given notifications associated to a machine ID to the watchlist, and save the array to the preferences | ||||
|      * | ||||
|      * @param machineId | ||||
|      * @param endNotificationID | ||||
|      * @param reminderNotificationID | ||||
|      */ | ||||
|     saveNotificationToPrefs(machineId: string, endNotificationID: string, reminderNotificationID: string | null) { | ||||
|         let data = this.state.machinesWatched; | ||||
|         data.push({ | ||||
|  | @ -219,13 +231,23 @@ export default class ProxiwashScreen extends FetchedDataSectionList { | |||
|         this.updateNotificationPrefs(data); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * remove the given index from the watchlist array and save it to preferences | ||||
|      * | ||||
|      * @param index | ||||
|      */ | ||||
|     removeNotificationFromPrefs(index: number) { | ||||
|         let data = this.state.machinesWatched; | ||||
|         data.splice(index, 1); | ||||
|         this.updateNotificationPrefs(data); | ||||
|     } | ||||
| 
 | ||||
|     updateNotificationPrefs(data: Object) { | ||||
|     /** | ||||
|      * Set the given data as the watchlist and save it to preferences | ||||
|      * | ||||
|      * @param data | ||||
|      */ | ||||
|     updateNotificationPrefs(data: Array<Object>) { | ||||
|         this.setState({machinesWatched: data}); | ||||
|         let prefKey = AsyncStorageManager.getInstance().preferences.proxiwashWatchedMachines.key; | ||||
|         AsyncStorageManager.getInstance().savePref(prefKey, JSON.stringify(data)); | ||||
|  | @ -266,6 +288,13 @@ export default class ProxiwashScreen extends FetchedDataSectionList { | |||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Show an alert fo a machine, allowing to enable/disable notifications if running | ||||
|      * | ||||
|      * @param title | ||||
|      * @param item | ||||
|      * @param remainingTime | ||||
|      */ | ||||
|     showAlert(title: string, item: Object, remainingTime: number) { | ||||
|         let buttons = [{text: i18n.t("proxiwashScreen.modal.ok")}]; | ||||
|         let message = modalStateStrings[MACHINE_STATES[item.state]]; | ||||
|  |  | |||
|  | @ -2,18 +2,18 @@ | |||
| 
 | ||||
| import * as React from 'react'; | ||||
| import { | ||||
|     Body, | ||||
|     Card, | ||||
|     CardItem, | ||||
|     CheckBox, | ||||
|     Container, | ||||
|     Content, | ||||
|     Left, | ||||
|     List, | ||||
|     ListItem, | ||||
|     Picker, | ||||
|     Right, | ||||
|     Text, | ||||
|     List, | ||||
|     CheckBox, | ||||
|     Body, | ||||
|     CardItem, | ||||
|     Card, | ||||
|     Picker, | ||||
| } from "native-base"; | ||||
| import CustomHeader from "../components/CustomHeader"; | ||||
| import ThemeManager from '../utils/ThemeManager'; | ||||
|  |  | |||
|  | @ -3,7 +3,9 @@ | |||
| import {AsyncStorage} from "react-native"; | ||||
| 
 | ||||
| /** | ||||
|  * Static class used to manage preferences | ||||
|  * Static class used to manage preferences. | ||||
|  * Preferences are fetched at the start of the app and saved in an instance object. | ||||
|  * This allows for a synchronous access to saved data. | ||||
|  */ | ||||
| 
 | ||||
| export default class AsyncStorageManager { | ||||
|  | @ -20,30 +22,36 @@ export default class AsyncStorageManager { | |||
|             AsyncStorageManager.instance; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // Object storing preferences keys, default and current values for use in the app
 | ||||
|     preferences = { | ||||
|         showIntro: { | ||||
|             key: 'showIntro', | ||||
|             default: '1', | ||||
|             current : '', | ||||
|             current: '', | ||||
|         }, | ||||
|         proxiwashNotifications: { | ||||
|             key: 'proxiwashNotifications', | ||||
|             default: '5', | ||||
|             current : '', | ||||
|             current: '', | ||||
|         }, | ||||
|         proxiwashWatchedMachines : { | ||||
|         proxiwashWatchedMachines: { | ||||
|             key: 'proxiwashWatchedMachines', | ||||
|             default: '[]', | ||||
|             current : '', | ||||
|             current: '', | ||||
|         }, | ||||
|         nightMode: { | ||||
|             key: 'nightMode', | ||||
|             default : '0', | ||||
|             current : '', | ||||
|             default: '0', | ||||
|             current: '', | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     /** | ||||
|      * Set preferences object current values from AsyncStorage. | ||||
|      * This function should be called at the app's start. | ||||
|      * | ||||
|      * @return {Promise<void>} | ||||
|      */ | ||||
|     async loadPreferences() { | ||||
|         let prefKeys = []; | ||||
|         // Get all available keys
 | ||||
|  | @ -51,18 +59,25 @@ export default class AsyncStorageManager { | |||
|             prefKeys.push(value.key); | ||||
|         } | ||||
|         // Get corresponding values
 | ||||
|         let resultArray : Array<Array<string>> = await AsyncStorage.multiGet(prefKeys); | ||||
|         let resultArray: Array<Array<string>> = await AsyncStorage.multiGet(prefKeys); | ||||
|         // Save those values for later use
 | ||||
|         for (let i = 0; i < resultArray.length; i++) { | ||||
|             let key : string = resultArray[i][0]; | ||||
|             let val : string | null = resultArray[i][1]; | ||||
|             let key: string = resultArray[i][0]; | ||||
|             let val: string | null = resultArray[i][1]; | ||||
|             if (val === null) | ||||
|                 val = this.preferences[key].default; | ||||
|             this.preferences[key].current = val; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     savePref(key : string, val : string) { | ||||
|     /** | ||||
|      * Save the value associated to the given key to preferences. | ||||
|      * This updates the preferences object and saves it to AsynStorage. | ||||
|      * | ||||
|      * @param key | ||||
|      * @param val | ||||
|      */ | ||||
|     savePref(key: string, val: string) { | ||||
|         this.preferences[key].current = val; | ||||
|         AsyncStorage.setItem(key, val); | ||||
|     } | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import platform from '../native-base-theme/variables/platform'; | |||
| import platformDark from '../native-base-theme/variables/platformDark'; | ||||
| import getTheme from '../native-base-theme/components'; | ||||
| import AsyncStorageManager from "./AsyncStorageManager"; | ||||
| 
 | ||||
| /** | ||||
|  * Singleton class used to manage themes | ||||
|  */ | ||||
|  |  | |||
|  | @ -1,17 +1,26 @@ | |||
| import {Toast} from "native-base"; | ||||
| 
 | ||||
| /** | ||||
|  * Class used to get json data from the web | ||||
|  */ | ||||
| export default class WebDataManager { | ||||
| 
 | ||||
|     FETCH_URL : string; | ||||
|     lastDataFetched : Object = {}; | ||||
|     FETCH_URL: string; | ||||
|     lastDataFetched: Object = {}; | ||||
| 
 | ||||
| 
 | ||||
|     constructor(url) { | ||||
|         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 = {}; | ||||
|         let fetchedData: Object = {}; | ||||
|         try { | ||||
|             let response = await fetch(this.FETCH_URL); | ||||
|             fetchedData = await response.json(); | ||||
|  | @ -23,10 +32,21 @@ export default class WebDataManager { | |||
|         return fetchedData; | ||||
|     } | ||||
| 
 | ||||
|     isDataObjectValid() { | ||||
|     /** | ||||
|      * Detects if the fetched data is not an empty object | ||||
|      * | ||||
|      * @return | ||||
|      */ | ||||
|     isDataObjectValid(): boolean { | ||||
|         return Object.keys(this.lastDataFetched).length > 0; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Show a toast message depending on the validity of the fetched data | ||||
|      * | ||||
|      * @param successString | ||||
|      * @param errorString | ||||
|      */ | ||||
|     showUpdateToast(successString, errorString) { | ||||
|         let isSuccess = this.isDataObjectValid(); | ||||
|         if (!isSuccess) { | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue