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
|
// @flow
|
||||||
|
|
||||||
import React from 'react';
|
import * as React from 'react';
|
||||||
import {Root, StyleProvider, Text} from 'native-base';
|
import {Root, StyleProvider, Text} from 'native-base';
|
||||||
import {Ionicons} from '@expo/vector-icons';
|
import {Image, StyleSheet, View} from 'react-native'
|
||||||
import {StyleSheet, View, Image} from 'react-native'
|
|
||||||
import AppNavigator from './navigation/AppNavigator';
|
import AppNavigator from './navigation/AppNavigator';
|
||||||
import ThemeManager from './utils/ThemeManager';
|
import ThemeManager from './utils/ThemeManager';
|
||||||
import LocaleManager from './utils/LocaleManager';
|
import LocaleManager from './utils/LocaleManager';
|
||||||
|
@ -43,6 +42,7 @@ const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Content to be used int the intro slides
|
||||||
const slides = [
|
const slides = [
|
||||||
{
|
{
|
||||||
key: '1',
|
key: '1',
|
||||||
|
@ -61,7 +61,7 @@ const slides = [
|
||||||
{
|
{
|
||||||
key: '3',
|
key: '3',
|
||||||
title: 'Le proximo',
|
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',
|
icon: 'shopping',
|
||||||
colors: ['#f9a967', '#da5204'],
|
colors: ['#f9a967', '#da5204'],
|
||||||
},
|
},
|
||||||
|
@ -101,12 +101,14 @@ export default class App extends React.Component<Props, State> {
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
async componentWillMount() {
|
async componentWillMount() {
|
||||||
|
// Wait for custom fonts to be loaded before showing the app
|
||||||
await Font.loadAsync({
|
await Font.loadAsync({
|
||||||
'Roboto': require('native-base/Fonts/Roboto.ttf'),
|
'Roboto': require('native-base/Fonts/Roboto.ttf'),
|
||||||
'Roboto_medium': require('native-base/Fonts/Roboto_medium.ttf'),
|
'Roboto_medium': require('native-base/Fonts/Roboto_medium.ttf'),
|
||||||
});
|
});
|
||||||
await AsyncStorageManager.getInstance().loadPreferences();
|
await AsyncStorageManager.getInstance().loadPreferences();
|
||||||
ThemeManager.getInstance().setUpdateThemeCallback(() => this.updateTheme());
|
ThemeManager.getInstance().setUpdateThemeCallback(() => this.updateTheme());
|
||||||
|
// Only show intro if this is the first time starting the app
|
||||||
this.setState({
|
this.setState({
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
currentTheme: ThemeManager.getCurrentTheme(),
|
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() {
|
updateTheme() {
|
||||||
// console.log('update theme called');
|
|
||||||
this.setState({
|
this.setState({
|
||||||
currentTheme: ThemeManager.getCurrentTheme()
|
currentTheme: ThemeManager.getCurrentTheme()
|
||||||
});
|
});
|
||||||
clearThemeCache();
|
clearThemeCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render item to be used for the intro slides
|
||||||
|
* @param item
|
||||||
|
* @param dimensions
|
||||||
|
*/
|
||||||
getIntroRenderItem(item: Object, dimensions: Object) {
|
getIntroRenderItem(item: Object, dimensions: Object) {
|
||||||
return (
|
return (
|
||||||
<LinearGradient
|
<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() {
|
onIntroDone() {
|
||||||
this.setState({showIntro: false});
|
this.setState({showIntro: false});
|
||||||
AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.showIntro.key, '0');
|
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
|
* Renders the app based on loading state
|
||||||
*
|
|
||||||
* @returns {*}
|
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
if (this.state.isLoading) {
|
if (this.state.isLoading) {
|
||||||
|
|
34
app.json
34
app.json
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"expo": {
|
"expo": {
|
||||||
"name": "Amicale INSAT",
|
"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",
|
"slug": "application-amicale",
|
||||||
"privacy": "public",
|
"privacy": "public",
|
||||||
"sdkVersion": "33.0.0",
|
"sdkVersion": "33.0.0",
|
||||||
|
@ -9,29 +10,42 @@
|
||||||
"android",
|
"android",
|
||||||
"web"
|
"web"
|
||||||
],
|
],
|
||||||
"version": "0.0.4",
|
"version": "0.0.5",
|
||||||
"orientation": "portrait",
|
"orientation": "portrait",
|
||||||
"icon": "./assets/icon.png",
|
|
||||||
"primaryColor": "#e42612",
|
"primaryColor": "#e42612",
|
||||||
|
"icon": "./assets/icon.png",
|
||||||
"splash": {
|
"splash": {
|
||||||
"image": "./assets/splash.png",
|
"backgroundColor": "#fff",
|
||||||
"resizeMode": "cover",
|
"resizeMode": "contain",
|
||||||
"backgroundColor": "#fff"
|
"image": "./assets/splash.png"
|
||||||
|
},
|
||||||
|
"notification": {
|
||||||
|
"icon": "./assets/amicale-notification.png",
|
||||||
|
"color": "#e42612",
|
||||||
|
"androidMode": "default"
|
||||||
},
|
},
|
||||||
"updates": {
|
"updates": {
|
||||||
"fallbackToCacheTimeout": 0
|
"enabled": false
|
||||||
},
|
},
|
||||||
"assetBundlePatterns": [
|
"assetBundlePatterns": [
|
||||||
"**/*"
|
"**/*"
|
||||||
],
|
],
|
||||||
"ios": {
|
"ios": {
|
||||||
"supportsTablet": true,
|
"bundleIdentifier": "amicale.insat.application",
|
||||||
"icon": "./assets/ios.icon.png",
|
"buildNumber": "0.0.5",
|
||||||
"bundleIdentifier": "com.test.applicationamicale"
|
"icon": "./assets/ios.icon.png"
|
||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
|
"package": "amicale.insat.application",
|
||||||
|
"versionCode": 1,
|
||||||
"icon": "./assets/android.icon.png",
|
"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
|
// @flow
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {Body, Header, Icon, Left, Right, Title} from "native-base";
|
import {Body, Header, Left, Right, Title} from "native-base";
|
||||||
import {StyleSheet, Platform} from "react-native";
|
import {Platform, StyleSheet} from "react-native";
|
||||||
import {getStatusBarHeight} from "react-native-status-bar-height";
|
import {getStatusBarHeight} from "react-native-status-bar-height";
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable from 'react-native-platform-touchable';
|
||||||
import ThemeManager from "../utils/ThemeManager";
|
import ThemeManager from "../utils/ThemeManager";
|
||||||
|
@ -34,6 +34,7 @@ export default class CustomHeader extends React.Component<Props> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let button;
|
let button;
|
||||||
|
// Does the app have a back button or a burger menu ?
|
||||||
if (this.props.backButton)
|
if (this.props.backButton)
|
||||||
button =
|
button =
|
||||||
<Touchable
|
<Touchable
|
||||||
|
|
|
@ -9,7 +9,7 @@ type Props = {
|
||||||
icon: string,
|
icon: string,
|
||||||
color: ?string,
|
color: ?string,
|
||||||
fontSize: number,
|
fontSize: number,
|
||||||
width: number|string,
|
width: number | string,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,16 +20,20 @@ type State = {
|
||||||
machinesWatched: Array<Object>,
|
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> {
|
export default class FetchedDataSectionList extends React.Component<Props, State> {
|
||||||
|
|
||||||
webDataManager: WebDataManager;
|
webDataManager: WebDataManager;
|
||||||
|
|
||||||
willFocusSubscription : function;
|
willFocusSubscription: function;
|
||||||
willBlurSubscription : function;
|
willBlurSubscription: function;
|
||||||
refreshInterval: IntervalID;
|
refreshInterval: IntervalID;
|
||||||
refreshTime: number;
|
refreshTime: number;
|
||||||
|
|
||||||
constructor(fetchUrl: string, refreshTime : number) {
|
constructor(fetchUrl: string, refreshTime: number) {
|
||||||
super();
|
super();
|
||||||
this.webDataManager = new WebDataManager(fetchUrl);
|
this.webDataManager = new WebDataManager(fetchUrl);
|
||||||
this.refreshTime = refreshTime;
|
this.refreshTime = refreshTime;
|
||||||
|
@ -42,16 +46,25 @@ export default class FetchedDataSectionList extends React.Component<Props, State
|
||||||
machinesWatched: [],
|
machinesWatched: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
getHeaderTranslation() {
|
/**
|
||||||
|
* Get the translation for the header in the current language
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
getHeaderTranslation() : string {
|
||||||
return "Header";
|
return "Header";
|
||||||
}
|
}
|
||||||
|
|
||||||
getUpdateToastTranslations() {
|
/**
|
||||||
|
* Get the translation for the toasts in the current language
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
getUpdateToastTranslations(): Array<string> {
|
||||||
return ["whoa", "nah"];
|
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() {
|
componentDidMount() {
|
||||||
this.willFocusSubscription = this.props.navigation.addListener(
|
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() {
|
onScreenFocus() {
|
||||||
this._onRefresh();
|
this._onRefresh();
|
||||||
if (this.refreshTime > 0)
|
if (this.refreshTime > 0)
|
||||||
this.refreshInterval = setInterval(() => this._onRefresh(), this.refreshTime)
|
this.refreshInterval = setInterval(() => this._onRefresh(), this.refreshTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove any interval on un-focus
|
||||||
|
*/
|
||||||
onScreenBlur() {
|
onScreenBlur() {
|
||||||
clearInterval(this.refreshInterval);
|
clearInterval(this.refreshInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister from event when un-mounting components
|
||||||
|
*/
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (this.willBlurSubscription !== undefined)
|
if (this.willBlurSubscription !== undefined)
|
||||||
this.willBlurSubscription.remove();
|
this.willBlurSubscription.remove();
|
||||||
if (this.willFocusSubscription !== undefined)
|
if (this.willFocusSubscription !== undefined)
|
||||||
this.willFocusSubscription.remove();
|
this.willFocusSubscription.remove();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh data and show a toast if any error occurred
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
_onRefresh = () => {
|
_onRefresh = () => {
|
||||||
console.log('refresh');
|
|
||||||
this.setState({refreshing: true});
|
this.setState({refreshing: true});
|
||||||
this.webDataManager.readData().then((fetchedData) => {
|
this.webDataManager.readData().then((fetchedData) => {
|
||||||
this.setState({
|
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) {
|
getRenderItem(item: Object, section: Object, data: Object) {
|
||||||
return <View/>;
|
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) {
|
getRenderSectionHeader(title: String) {
|
||||||
return <View/>;
|
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) {
|
getEmptyRenderItem(text: string, icon: string) {
|
||||||
return (
|
return (
|
||||||
<View>
|
<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}
|
* @param fetchedData {Object}
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
|
@ -146,6 +194,12 @@ export default class FetchedDataSectionList extends React.Component<Props, State
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the dataset when no fetched data is available.
|
||||||
|
* No need to be overridden, has good defaults.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
createEmptyDataset() {
|
createEmptyDataset() {
|
||||||
return [
|
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() {
|
hasTabs() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the section list render using the generated dataset
|
||||||
|
*
|
||||||
|
* @param dataset
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
getSectionList(dataset: Array<Object>) {
|
getSectionList(dataset: Array<Object>) {
|
||||||
let isEmpty = dataset[0].data.length === 0;
|
let isEmpty = dataset[0].data.length === 0;
|
||||||
if (isEmpty)
|
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>) {
|
getTabbedView(dataset: Array<Object>) {
|
||||||
let tabbedView = [];
|
let tabbedView = [];
|
||||||
for (let i = 0; i < dataset.length; i++) {
|
for (let i = 0; i < dataset.length; i++) {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Platform, Dimensions, StyleSheet, Image, FlatList, Linking} from 'react-native';
|
import {Dimensions, FlatList, Image, Linking, Platform, StyleSheet} from 'react-native';
|
||||||
import {Badge, Text, Container, Content, Left, ListItem, Right} from "native-base";
|
import {Badge, Container, Content, Left, ListItem, Right, Text} from "native-base";
|
||||||
import i18n from "i18n-js";
|
import i18n from "i18n-js";
|
||||||
import CustomMaterialIcon from '../components/CustomMaterialIcon';
|
import CustomMaterialIcon from '../components/CustomMaterialIcon';
|
||||||
|
|
||||||
|
@ -40,72 +40,56 @@ export default class SideBar extends React.Component<Props, State> {
|
||||||
*/
|
*/
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(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 = [
|
this.dataSet = [
|
||||||
{
|
{
|
||||||
name: i18n.t('screens.home'),
|
name: i18n.t('screens.home'),
|
||||||
route: "Home",
|
route: "Home",
|
||||||
icon: "home",
|
icon: "home",
|
||||||
bg: "#C5F442"
|
|
||||||
// types: "11" // Shows the badge
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n.t('screens.planning'),
|
name: i18n.t('screens.planning'),
|
||||||
route: "Planning",
|
route: "Planning",
|
||||||
icon: "calendar-range",
|
icon: "calendar-range",
|
||||||
bg: "#477EEA",
|
|
||||||
// types: "11"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Proxiwash",
|
name: "Proxiwash",
|
||||||
route: "Proxiwash",
|
route: "Proxiwash",
|
||||||
icon: "washing-machine",
|
icon: "washing-machine",
|
||||||
bg: "#477EEA",
|
|
||||||
// types: "11"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Proximo",
|
name: "Proximo",
|
||||||
route: "Proximo",
|
route: "Proximo",
|
||||||
icon: "shopping",
|
icon: "shopping",
|
||||||
bg: "#477EEA",
|
|
||||||
// types: "11"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Amicale",
|
name: "Amicale",
|
||||||
route: "amicale",
|
route: "amicale",
|
||||||
icon: "web",
|
icon: "web",
|
||||||
bg: "#477EEA",
|
|
||||||
link: Amicale_LINK
|
link: Amicale_LINK
|
||||||
// types: "11"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n.t('screens.timetable'),
|
name: i18n.t('screens.timetable'),
|
||||||
route: "timetable",
|
route: "timetable",
|
||||||
icon: "timetable",
|
icon: "timetable",
|
||||||
bg: "#477EEA",
|
|
||||||
link: TIMETABLE_LINK
|
link: TIMETABLE_LINK
|
||||||
// types: "11"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Wiketud",
|
name: "Wiketud",
|
||||||
route: "wiketud",
|
route: "wiketud",
|
||||||
icon: "wikipedia",
|
icon: "wikipedia",
|
||||||
bg: "#477EEA",
|
|
||||||
link: WIKETUD_LINK
|
link: WIKETUD_LINK
|
||||||
// types: "11"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n.t('screens.settings'),
|
name: i18n.t('screens.settings'),
|
||||||
route: "Settings",
|
route: "Settings",
|
||||||
icon: "settings",
|
icon: "settings",
|
||||||
bg: "#477EEA",
|
|
||||||
// types: "11"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n.t('screens.about'),
|
name: i18n.t('screens.about'),
|
||||||
route: "About",
|
route: "About",
|
||||||
icon: "information",
|
icon: "information",
|
||||||
bg: "#477EEA",
|
|
||||||
// types: "11"
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
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 CustomHeader from "../../components/CustomHeader";
|
||||||
import {FlatList} from "react-native";
|
import {FlatList} from "react-native";
|
||||||
import i18n from "i18n-js";
|
import i18n from "i18n-js";
|
||||||
|
@ -30,7 +30,7 @@ export default class AboutDependenciesScreen extends React.Component<Props> {
|
||||||
const data = generateListFromObject(nav.getParam('data', {}));
|
const data = generateListFromObject(nav.getParam('data', {}));
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<CustomHeader backButton={true} navigation={nav} title={i18n.t('aboutScreen.libs')} />
|
<CustomHeader backButton={true} navigation={nav} title={i18n.t('aboutScreen.libs')}/>
|
||||||
<Content>
|
<Content>
|
||||||
<FlatList
|
<FlatList
|
||||||
data={data}
|
data={data}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Platform, StyleSheet, Linking, Alert, FlatList} from 'react-native';
|
import {Alert, FlatList, Linking, Platform} from 'react-native';
|
||||||
import {Container, Content, Text, Card, CardItem, Body, Left, Right, Thumbnail, H1} from 'native-base';
|
import {Body, Card, CardItem, Container, Content, H1, Left, Right, Text, Thumbnail} from 'native-base';
|
||||||
import CustomHeader from "../../components/CustomHeader";
|
import CustomHeader from "../../components/CustomHeader";
|
||||||
import i18n from "i18n-js";
|
import i18n from "i18n-js";
|
||||||
import appJson from '../../app';
|
import appJson from '../../app';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
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 CustomHeader from "../../components/CustomHeader";
|
||||||
import {FlatList, Platform} from "react-native";
|
import {FlatList, Platform} from "react-native";
|
||||||
import Touchable from 'react-native-platform-touchable';
|
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);
|
this.setSortMode(this.state.currentSortMode, this.state.isSortReversed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get color depending on quantity available
|
||||||
|
*
|
||||||
|
* @param availableStock
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
getStockColor(availableStock: number) {
|
getStockColor(availableStock: number) {
|
||||||
let color : string;
|
let color: string;
|
||||||
if (availableStock > 3)
|
if (availableStock > 3)
|
||||||
color = ThemeManager.getCurrentThemeVariables().brandSuccess;
|
color = ThemeManager.getCurrentThemeVariables().brandSuccess;
|
||||||
else if (availableStock > 0)
|
else if (availableStock > 0)
|
||||||
|
@ -234,7 +240,8 @@ export default class ProximoListScreen extends React.Component<Props, State> {
|
||||||
</Text>
|
</Text>
|
||||||
<Text note style={{
|
<Text note style={{
|
||||||
marginLeft: 20,
|
marginLeft: 20,
|
||||||
color: this.getStockColor(parseInt(item.quantity))}}>
|
color: this.getStockColor(parseInt(item.quantity))
|
||||||
|
}}>
|
||||||
{item.quantity + ' ' + i18n.t('proximoScreen.inStock')}
|
{item.quantity + ' ' + i18n.t('proximoScreen.inStock')}
|
||||||
</Text>
|
</Text>
|
||||||
</Body>
|
</Body>
|
||||||
|
|
|
@ -78,7 +78,7 @@ export default class ProximoMainScreen extends FetchedDataSectionList {
|
||||||
return finalData;
|
return finalData;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRenderItem(item: Object, section : Object, data : Object) {
|
getRenderItem(item: Object, section: Object, data: Object) {
|
||||||
if (item.data.length > 0) {
|
if (item.data.length > 0) {
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
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 {Body, Card, CardItem, Left, Right, Text} from 'native-base';
|
||||||
import ThemeManager from '../utils/ThemeManager';
|
import ThemeManager from '../utils/ThemeManager';
|
||||||
import i18n from "i18n-js";
|
import i18n from "i18n-js";
|
||||||
|
@ -36,7 +36,7 @@ let stateColors = {};
|
||||||
*/
|
*/
|
||||||
export default class ProxiwashScreen extends FetchedDataSectionList {
|
export default class ProxiwashScreen extends FetchedDataSectionList {
|
||||||
|
|
||||||
refreshInterval : IntervalID;
|
refreshInterval: IntervalID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates machine state parameters using current theme and translations
|
* 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() {
|
componentDidMount() {
|
||||||
super.componentDidMount();
|
super.componentDidMount();
|
||||||
if (Platform.OS === 'android') {
|
if (Platform.OS === 'android') {
|
||||||
|
@ -104,10 +107,6 @@ export default class ProxiwashScreen extends FetchedDataSectionList {
|
||||||
return [i18n.t("proxiwashScreen.listUpdated"), i18n.t("proxiwashScreen.listUpdateFail")];
|
return [i18n.t("proxiwashScreen.listUpdated"), i18n.t("proxiwashScreen.listUpdateFail")];
|
||||||
}
|
}
|
||||||
|
|
||||||
getKeyExtractor(item: Object) {
|
|
||||||
return item !== undefined ? item.number : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
getDryersKeyExtractor(item: Object) {
|
getDryersKeyExtractor(item: Object) {
|
||||||
return item !== undefined ? "dryer" + item.number : undefined;
|
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) {
|
let elem = this.state.machinesWatched.find(function (elem) {
|
||||||
return elem.machineNumber === machineId
|
return elem.machineNumber === machineId
|
||||||
});
|
});
|
||||||
return this.state.machinesWatched.indexOf(elem);
|
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) {
|
saveNotificationToPrefs(machineId: string, endNotificationID: string, reminderNotificationID: string | null) {
|
||||||
let data = this.state.machinesWatched;
|
let data = this.state.machinesWatched;
|
||||||
data.push({
|
data.push({
|
||||||
|
@ -219,13 +231,23 @@ export default class ProxiwashScreen extends FetchedDataSectionList {
|
||||||
this.updateNotificationPrefs(data);
|
this.updateNotificationPrefs(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove the given index from the watchlist array and save it to preferences
|
||||||
|
*
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
removeNotificationFromPrefs(index: number) {
|
removeNotificationFromPrefs(index: number) {
|
||||||
let data = this.state.machinesWatched;
|
let data = this.state.machinesWatched;
|
||||||
data.splice(index, 1);
|
data.splice(index, 1);
|
||||||
this.updateNotificationPrefs(data);
|
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});
|
this.setState({machinesWatched: data});
|
||||||
let prefKey = AsyncStorageManager.getInstance().preferences.proxiwashWatchedMachines.key;
|
let prefKey = AsyncStorageManager.getInstance().preferences.proxiwashWatchedMachines.key;
|
||||||
AsyncStorageManager.getInstance().savePref(prefKey, JSON.stringify(data));
|
AsyncStorageManager.getInstance().savePref(prefKey, JSON.stringify(data));
|
||||||
|
@ -266,6 +288,13 @@ export default class ProxiwashScreen extends FetchedDataSectionList {
|
||||||
return true;
|
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) {
|
showAlert(title: string, item: Object, remainingTime: number) {
|
||||||
let buttons = [{text: i18n.t("proxiwashScreen.modal.ok")}];
|
let buttons = [{text: i18n.t("proxiwashScreen.modal.ok")}];
|
||||||
let message = modalStateStrings[MACHINE_STATES[item.state]];
|
let message = modalStateStrings[MACHINE_STATES[item.state]];
|
||||||
|
|
|
@ -2,18 +2,18 @@
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {
|
||||||
|
Body,
|
||||||
|
Card,
|
||||||
|
CardItem,
|
||||||
|
CheckBox,
|
||||||
Container,
|
Container,
|
||||||
Content,
|
Content,
|
||||||
Left,
|
Left,
|
||||||
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
|
Picker,
|
||||||
Right,
|
Right,
|
||||||
Text,
|
Text,
|
||||||
List,
|
|
||||||
CheckBox,
|
|
||||||
Body,
|
|
||||||
CardItem,
|
|
||||||
Card,
|
|
||||||
Picker,
|
|
||||||
} from "native-base";
|
} from "native-base";
|
||||||
import CustomHeader from "../components/CustomHeader";
|
import CustomHeader from "../components/CustomHeader";
|
||||||
import ThemeManager from '../utils/ThemeManager';
|
import ThemeManager from '../utils/ThemeManager';
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
import {AsyncStorage} from "react-native";
|
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 {
|
export default class AsyncStorageManager {
|
||||||
|
@ -20,30 +22,36 @@ export default class AsyncStorageManager {
|
||||||
AsyncStorageManager.instance;
|
AsyncStorageManager.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Object storing preferences keys, default and current values for use in the app
|
||||||
preferences = {
|
preferences = {
|
||||||
showIntro: {
|
showIntro: {
|
||||||
key: 'showIntro',
|
key: 'showIntro',
|
||||||
default: '1',
|
default: '1',
|
||||||
current : '',
|
current: '',
|
||||||
},
|
},
|
||||||
proxiwashNotifications: {
|
proxiwashNotifications: {
|
||||||
key: 'proxiwashNotifications',
|
key: 'proxiwashNotifications',
|
||||||
default: '5',
|
default: '5',
|
||||||
current : '',
|
current: '',
|
||||||
},
|
},
|
||||||
proxiwashWatchedMachines : {
|
proxiwashWatchedMachines: {
|
||||||
key: 'proxiwashWatchedMachines',
|
key: 'proxiwashWatchedMachines',
|
||||||
default: '[]',
|
default: '[]',
|
||||||
current : '',
|
current: '',
|
||||||
},
|
},
|
||||||
nightMode: {
|
nightMode: {
|
||||||
key: 'nightMode',
|
key: 'nightMode',
|
||||||
default : '0',
|
default: '0',
|
||||||
current : '',
|
current: '',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set preferences object current values from AsyncStorage.
|
||||||
|
* This function should be called at the app's start.
|
||||||
|
*
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
async loadPreferences() {
|
async loadPreferences() {
|
||||||
let prefKeys = [];
|
let prefKeys = [];
|
||||||
// Get all available keys
|
// Get all available keys
|
||||||
|
@ -51,18 +59,25 @@ export default class AsyncStorageManager {
|
||||||
prefKeys.push(value.key);
|
prefKeys.push(value.key);
|
||||||
}
|
}
|
||||||
// Get corresponding values
|
// 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
|
// Save those values for later use
|
||||||
for (let i = 0; i < resultArray.length; i++) {
|
for (let i = 0; i < resultArray.length; i++) {
|
||||||
let key : string = resultArray[i][0];
|
let key: string = resultArray[i][0];
|
||||||
let val : string | null = resultArray[i][1];
|
let val: string | null = resultArray[i][1];
|
||||||
if (val === null)
|
if (val === null)
|
||||||
val = this.preferences[key].default;
|
val = this.preferences[key].default;
|
||||||
this.preferences[key].current = val;
|
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;
|
this.preferences[key].current = val;
|
||||||
AsyncStorage.setItem(key, 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 platformDark from '../native-base-theme/variables/platformDark';
|
||||||
import getTheme from '../native-base-theme/components';
|
import getTheme from '../native-base-theme/components';
|
||||||
import AsyncStorageManager from "./AsyncStorageManager";
|
import AsyncStorageManager from "./AsyncStorageManager";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singleton class used to manage themes
|
* Singleton class used to manage themes
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,17 +1,26 @@
|
||||||
import {Toast} from "native-base";
|
import {Toast} from "native-base";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to get json data from the web
|
||||||
|
*/
|
||||||
export default class WebDataManager {
|
export default class WebDataManager {
|
||||||
|
|
||||||
FETCH_URL : string;
|
FETCH_URL: string;
|
||||||
lastDataFetched : Object = {};
|
lastDataFetched: Object = {};
|
||||||
|
|
||||||
|
|
||||||
constructor(url) {
|
constructor(url) {
|
||||||
this.FETCH_URL = 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() {
|
async readData() {
|
||||||
let fetchedData : Object = {};
|
let fetchedData: Object = {};
|
||||||
try {
|
try {
|
||||||
let response = await fetch(this.FETCH_URL);
|
let response = await fetch(this.FETCH_URL);
|
||||||
fetchedData = await response.json();
|
fetchedData = await response.json();
|
||||||
|
@ -23,10 +32,21 @@ export default class WebDataManager {
|
||||||
return fetchedData;
|
return fetchedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
isDataObjectValid() {
|
/**
|
||||||
|
* Detects if the fetched data is not an empty object
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
isDataObjectValid(): boolean {
|
||||||
return Object.keys(this.lastDataFetched).length > 0;
|
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) {
|
showUpdateToast(successString, errorString) {
|
||||||
let isSuccess = this.isDataObjectValid();
|
let isSuccess = this.isDataObjectValid();
|
||||||
if (!isSuccess) {
|
if (!isSuccess) {
|
||||||
|
|
Loading…
Reference in a new issue