Browse Source

Started writing documentation and ported app to use Flow

keplyx 2 years ago
parent
commit
0385bbb882

+ 11
- 0
.flowconfig View File

@@ -0,0 +1,11 @@
1
+[ignore]
2
+
3
+[include]
4
+
5
+[libs]
6
+
7
+[lints]
8
+
9
+[options]
10
+
11
+[strict]

+ 31
- 10
App.js View File

@@ -1,3 +1,5 @@
1
+// @flow
2
+
1 3
 import React from 'react';
2 4
 import {StyleProvider, Root, View} from 'native-base';
3 5
 import AppNavigator from './navigation/AppNavigator';
@@ -7,26 +9,37 @@ import * as Font from 'expo-font';
7 9
 // edited native-base-shoutem-theme according to
8 10
 // https://github.com/GeekyAnts/theme/pull/5/files/91f67c55ca6e65fe3af779586b506950c9f331be#diff-4cfc2dd4d5dae7954012899f2268a422
9 11
 // to allow for dynamic theme switching
10
-import { clearThemeCache } from 'native-base-shoutem-theme';
12
+import {clearThemeCache} from 'native-base-shoutem-theme';
13
+
14
+type Props = {};
15
+
16
+type State = {
17
+    isLoading: boolean,
18
+    currentTheme: ?Object,
19
+};
20
+
21
+export default class App extends React.Component<Props, State> {
11 22
 
12
-export default class App extends React.Component {
23
+    state = {
24
+        isLoading: true,
25
+        currentTheme: null,
26
+    };
13 27
 
14
-    constructor(props) {
28
+    constructor(props: Object) {
15 29
         super(props);
16
-        LocaleManager.getInstance().initTranslations();
17
-        this.updateTheme = this.updateTheme.bind(this);
18
-        this.state = {
19
-            isLoading: true,
20
-            currentTheme: undefined,
21
-        };
30
+        LocaleManager.initTranslations();
22 31
     }
23 32
 
33
+    /**
34
+     * Loads data before components are mounted, like fonts and themes
35
+     * @returns {Promise}
36
+     */
24 37
     async componentWillMount() {
25 38
         await Font.loadAsync({
26 39
             'Roboto': require('native-base/Fonts/Roboto.ttf'),
27 40
             'Roboto_medium': require('native-base/Fonts/Roboto_medium.ttf'),
28 41
         });
29
-        ThemeManager.getInstance().setUpdateThemeCallback(this.updateTheme);
42
+        ThemeManager.getInstance().setUpdateThemeCallback(() => this.updateTheme());
30 43
         await ThemeManager.getInstance().getDataFromPreferences();
31 44
         this.setState({
32 45
             isLoading: false,
@@ -34,6 +47,9 @@ export default class App extends React.Component {
34 47
         });
35 48
     }
36 49
 
50
+    /**
51
+     * Updates the theme and clears the cache to force reloading the app colors
52
+     */
37 53
     updateTheme() {
38 54
         // console.log('update theme called');
39 55
         this.setState({
@@ -42,6 +58,11 @@ export default class App extends React.Component {
42 58
         clearThemeCache();
43 59
     }
44 60
 
61
+    /**
62
+     * Renders the app based on loading state
63
+     *
64
+     * @returns {*}
65
+     */
45 66
     render() {
46 67
         if (this.state.isLoading) {
47 68
             return <View/>;

+ 22
- 11
components/CustomHeader.js View File

@@ -1,15 +1,31 @@
1
-import React from "react";
2
-import {Body, Button, Header, Icon, Left, Right, Title} from "native-base";
1
+// @flow
2
+
3
+import * as React from "react";
4
+import {Body, Header, Icon, Left, Right, Title} from "native-base";
3 5
 import {StyleSheet} from "react-native";
4 6
 import {getStatusBarHeight} from "react-native-status-bar-height";
5 7
 import Touchable from 'react-native-platform-touchable';
6 8
 
9
+type Props = {
10
+    backButton: boolean,
11
+    rightMenu: React.Node,
12
+    title: string,
13
+    navigation: Object,
14
+};
15
+
16
+
17
+export default class CustomHeader extends React.Component<Props> {
18
+
19
+    static defaultProps = {
20
+        backButton: false,
21
+        rightMenu: <Right/>,
22
+        fontSize: 26,
23
+        width: 30,
24
+    };
7 25
 
8
-export default class CustomHeader extends React.Component {
9 26
     render() {
10 27
         let button;
11
-        let rightMenu;
12
-        if (this.props.backButton !== undefined && this.props.backButton === true)
28
+        if (this.props.backButton)
13 29
             button =
14 30
                 <Touchable
15 31
                     style={{padding: 6}}
@@ -30,11 +46,6 @@ export default class CustomHeader extends React.Component {
30 46
                         type={'MaterialCommunityIcons'}/>
31 47
                 </Touchable>;
32 48
 
33
-        if (this.props.rightMenu)
34
-            rightMenu = this.props.rightMenu;
35
-        else
36
-            rightMenu = <Right/>;
37
-
38 49
         return (
39 50
             <Header style={styles.header}>
40 51
                 <Left>
@@ -43,7 +54,7 @@ export default class CustomHeader extends React.Component {
43 54
                 <Body>
44 55
                     <Title>{this.props.title}</Title>
45 56
                 </Body>
46
-                {rightMenu}
57
+                {this.props.rightMenu}
47 58
             </Header>);
48 59
     }
49 60
 };

+ 20
- 7
components/CustomMaterialIcon.js View File

@@ -1,12 +1,25 @@
1
-import React from 'react';
1
+// @flow
2
+
3
+import * as React from 'react';
2 4
 import {Icon} from "native-base";
3 5
 import ThemeManager from '../utils/ThemeManager';
4 6
 
5
-export default class CustomMaterialIcon extends React.Component {
7
+type Props = {
8
+    active: boolean,
9
+    icon: string,
10
+    color: ?string,
11
+    fontSize: number,
12
+    width: number,
13
+}
14
+
15
+export default class CustomMaterialIcon extends React.Component<Props> {
6 16
 
7
-    constructor(props) {
8
-        super(props);
9
-    }
17
+    static defaultProps = {
18
+        active: false,
19
+        color: undefined,
20
+        fontSize: 26,
21
+        width: 30,
22
+    };
10 23
 
11 24
     render() {
12 25
         return (
@@ -21,8 +34,8 @@ export default class CustomMaterialIcon extends React.Component {
21 34
                             this.props.active ?
22 35
                                 ThemeManager.getInstance().getCurrentThemeVariables().brandPrimary :
23 36
                                 ThemeManager.getInstance().getCurrentThemeVariables().customMaterialIconColor,
24
-                    fontSize: this.props.fontSize !== undefined ? this.props.fontSize : 26,
25
-                    width: this.props.width !== undefined ? this.props.width : 30
37
+                    fontSize: this.props.fontSize,
38
+                    width: this.props.width
26 39
                 }}
27 40
             />
28 41
         );

+ 18
- 6
components/SideMenu.js View File

@@ -1,6 +1,8 @@
1
-import React from 'react';
1
+// @flow
2
+
3
+import * as React from 'react';
2 4
 import {Platform, Dimensions, StyleSheet, Image, FlatList, Linking} from 'react-native';
3
-import {Badge, Text, Container, Content, Icon, Left, ListItem, Right} from "native-base";
5
+import {Badge, Text, Container, Content, Left, ListItem, Right} from "native-base";
4 6
 import i18n from "i18n-js";
5 7
 import CustomMaterialIcon from '../components/CustomMaterialIcon';
6 8
 
@@ -10,9 +12,19 @@ const drawerCover = require("../assets/drawer-cover.png");
10 12
 
11 13
 const WIKETUD_LINK = "https://www.etud.insa-toulouse.fr/wiketud/index.php/Accueil";
12 14
 
13
-export default class SideBar extends React.Component {
15
+type Props = {
16
+    navigation: Object,
17
+};
18
+
19
+type State = {
20
+    active: string,
21
+};
22
+
23
+export default class SideBar extends React.Component<Props, State> {
24
+
25
+    dataSet: Array<Object>;
14 26
 
15
-    constructor(props) {
27
+    constructor(props: Props) {
16 28
         super(props);
17 29
         this.state = {
18 30
             active: 'Home',
@@ -70,7 +82,7 @@ export default class SideBar extends React.Component {
70 82
         ];
71 83
     }
72 84
 
73
-    navigateToScreen(route) {
85
+    navigateToScreen(route: string) {
74 86
         this.props.navigation.navigate(route);
75 87
         this.props.navigation.closeDrawer();
76 88
         this.setState({active: route});
@@ -88,7 +100,7 @@ export default class SideBar extends React.Component {
88 100
                     <FlatList
89 101
                         data={this.dataSet}
90 102
                         extraData={this.state}
91
-                        keyExtractor={(item, index) => item.route}
103
+                        keyExtractor={(item) => item.route}
92 104
                         renderItem={({item}) =>
93 105
                             <ListItem
94 106
                                 button

+ 0
- 16
components/TabBarIcon.js View File

@@ -1,16 +0,0 @@
1
-import React from 'react';
2
-import {Ionicons} from '@expo/vector-icons/build/Icons';
3
-
4
-export default class TabBarIcon extends React.Component {
5
-    render() {
6
-        return (
7
-            <Ionicons
8
-                name={this.props.name}
9
-                size={26}
10
-                style={{marginBottom: -3}}
11
-                color={this.props.focused ? Colors.tabIconSelected : Colors.tabIconDefault}
12
-            />
13
-        );
14
-    }
15
-
16
-}

+ 2
- 0
navigation/AppNavigator.js View File

@@ -1,3 +1,5 @@
1
+// @flow
2
+
1 3
 import {createAppContainer, createStackNavigator} from 'react-navigation';
2 4
 
3 5
 import MainDrawerNavigator from './MainDrawerNavigator';

+ 3
- 1
navigation/MainDrawerNavigator.js View File

@@ -1,4 +1,6 @@
1
-import React from 'react';
1
+// @flow
2
+
3
+import * as React from 'react';
2 4
 import {createDrawerNavigator} from 'react-navigation';
3 5
 
4 6
 import HomeScreen from '../screens/HomeScreen';

+ 0
- 58
navigation/MainTabNavigator.js View File

@@ -1,58 +0,0 @@
1
-import React from 'react';
2
-import {Platform} from 'react-native';
3
-import {createStackNavigator} from 'react-navigation';
4
-import {createMaterialBottomTabNavigator} from "react-navigation-material-bottom-tabs";
5
-import TabBarIcon from '../components/TabBarIcon';
6
-
7
-import HomeScreen from '../screens/HomeScreen';
8
-import PlanningScreen from '../screens/PlanningScreen';
9
-
10
-const HomeStack = createStackNavigator({
11
-    Home: HomeScreen,
12
-});
13
-
14
-HomeStack.navigationOptions = {
15
-    tabBarLabel: 'Home',
16
-    tabBarIcon: ({focused}) => (
17
-        <TabBarIcon
18
-            focused={focused}
19
-            name={
20
-                Platform.OS === 'ios'
21
-                    ? 'ios-home'
22
-                    : 'md-home'
23
-            }
24
-        />
25
-    ),
26
-};
27
-
28
-const ProfileStack = createStackNavigator({
29
-    Profile: PlanningScreen,
30
-});
31
-
32
-ProfileStack.navigationOptions = {
33
-    tabBarLabel: 'Profile',
34
-    tabBarIcon: ({focused}) => (
35
-        <TabBarIcon
36
-            focused={focused}
37
-            name={
38
-                Platform.OS === 'ios'
39
-                    ? 'ios-people'
40
-                    : 'md-people'
41
-            }
42
-        />
43
-    ),
44
-};
45
-
46
-
47
-export default createMaterialBottomTabNavigator(
48
-    {
49
-        Home: HomeStack,
50
-        Profile: ProfileStack
51
-    }, {
52
-        initialRouteName: 'Home',
53
-        shifting: true,
54
-        activeColor: Colors.tabIconSelected,
55
-        inactiveColor: Colors.tabIconDefault,
56
-        barStyle: {backgroundColor: Colors.mainColor},
57
-    }
58
-);

+ 9
- 4
screens/About/AboutDependenciesScreen.js View File

@@ -1,5 +1,7 @@
1
-import React from 'react';
2
-import {Container, Text, Content, ListItem, Body, Left, Thumbnail, Right, Button, Icon} from 'native-base';
1
+// @flow
2
+
3
+import * as React from 'react';
4
+import {Container, Text, Content, ListItem, Body} from 'native-base';
3 5
 import CustomHeader from "../../components/CustomHeader";
4 6
 import {FlatList} from "react-native";
5 7
 import i18n from "i18n-js";
@@ -14,8 +16,11 @@ function generateListFromObject(object) {
14 16
     return list;
15 17
 }
16 18
 
19
+type Props = {
20
+    navigation: Object
21
+}
17 22
 
18
-export default class AboutDependenciesScreen extends React.Component {
23
+export default class AboutDependenciesScreen extends React.Component<Props> {
19 24
 
20 25
     render() {
21 26
         const nav = this.props.navigation;
@@ -26,7 +31,7 @@ export default class AboutDependenciesScreen extends React.Component {
26 31
                 <Content>
27 32
                     <FlatList
28 33
                         data={data}
29
-                        keyExtractor={(item, index) => item.name}
34
+                        keyExtractor={(item) => item.name}
30 35
                         style={{minHeight: 300, width: '100%'}}
31 36
                         renderItem={({item}) =>
32 37
                             <ListItem>

+ 14
- 16
screens/About/AboutScreen.js View File

@@ -1,6 +1,8 @@
1
-import React from 'react';
1
+// @flow
2
+
3
+import * as React from 'react';
2 4
 import {Platform, StyleSheet, Linking, Alert, FlatList} from 'react-native';
3
-import {Container, Content, Text, Card, CardItem, Body, Icon, Left, Right, Thumbnail, H1, ListItem} from 'native-base';
5
+import {Container, Content, Text, Card, CardItem, Body, Left, Right, Thumbnail, H1} from 'native-base';
4 6
 import CustomHeader from "../../components/CustomHeader";
5 7
 import i18n from "i18n-js";
6 8
 import appJson from '../../app';
@@ -20,13 +22,18 @@ const links = {
20 22
     react: 'https://facebook.github.io/react-native/',
21 23
 };
22 24
 
25
+type Props = {
26
+    navigation: Object,
27
+};
28
+
29
+
23 30
 function openWebLink(link) {
24 31
     Linking.openURL(link).catch((err) => console.error('Error opening link', err));
25 32
 }
26 33
 
27
-export default class AboutScreen extends React.Component {
34
+export default class AboutScreen extends React.Component<Props> {
28 35
 
29
-    appData = [
36
+    appData: Array<Object> = [
30 37
         {
31 38
             onPressCallback: () => openWebLink(Platform.OS === "ios" ? links.appstore : links.playstore),
32 39
             icon: Platform.OS === "ios" ? 'apple' : 'google-play',
@@ -59,7 +66,7 @@ export default class AboutScreen extends React.Component {
59 66
         },
60 67
     ];
61 68
 
62
-    authorData = [
69
+    authorData: Array<Object> = [
63 70
         {
64 71
             onPressCallback: () => Alert.alert('Coucou', 'Whaou'),
65 72
             icon: 'account-circle',
@@ -86,7 +93,7 @@ export default class AboutScreen extends React.Component {
86 93
         },
87 94
     ];
88 95
 
89
-    technoData = [
96
+    technoData: Array<Object> = [
90 97
         {
91 98
             onPressCallback: () => openWebLink(links.react),
92 99
             icon: 'react',
@@ -101,7 +108,7 @@ export default class AboutScreen extends React.Component {
101 108
         },
102 109
     ];
103 110
 
104
-    getCardItem(onPressCallback, icon, text, showChevron) {
111
+    getCardItem(onPressCallback: Function, icon: string, text: string, showChevron: boolean) {
105 112
         return (
106 113
             <CardItem button
107 114
                       onPress={onPressCallback}>
@@ -178,12 +185,3 @@ export default class AboutScreen extends React.Component {
178 185
         );
179 186
     }
180 187
 }
181
-
182
-const styles = StyleSheet.create({
183
-    container: {
184
-        flex: 1,
185
-        backgroundColor: '#fff',
186
-        alignItems: 'center',
187
-        justifyContent: 'center',
188
-    },
189
-});

+ 8
- 4
screens/HomeScreen.js View File

@@ -1,12 +1,16 @@
1
-import React from 'react';
1
+// @flow
2
+
3
+import * as React from 'react';
2 4
 import {Container, Content, Text, Button, Icon} from 'native-base';
3 5
 import CustomHeader from '../components/CustomHeader';
4 6
 import i18n from "i18n-js";
5 7
 import NotificationsManager from '../utils/NotificationsManager'
6
-import { Notifications } from 'expo';
7 8
 
9
+type Props = {
10
+    navigation: Object,
11
+}
8 12
 
9
-export default class HomeScreen extends React.Component {
13
+export default class HomeScreen extends React.Component<Props> {
10 14
     render() {
11 15
         const nav = this.props.navigation;
12 16
         return (
@@ -19,7 +23,7 @@ export default class HomeScreen extends React.Component {
19 23
                             name={'bell-ring'}
20 24
                             type={'MaterialCommunityIcons'}
21 25
                         />
22
-                        <Text>Notif</Text>
26
+                        <Text>Instant Notification</Text>
23 27
                     </Button>
24 28
                 </Content>
25 29
             </Container>

+ 9
- 11
screens/PlanningScreen.js View File

@@ -1,10 +1,15 @@
1
-import React from 'react';
2
-import { StyleSheet, View } from 'react-native';
1
+// @flow
2
+
3
+import * as React from 'react';
3 4
 import {Container, Text} from 'native-base';
4 5
 import CustomHeader from "../components/CustomHeader";
5 6
 import i18n from "i18n-js";
6 7
 
7
-export default class PlanningScreen extends React.Component {
8
+type Props = {
9
+    navigation: Object,
10
+}
11
+
12
+export default class PlanningScreen extends React.Component<Props> {
8 13
     render() {
9 14
         const nav = this.props.navigation;
10 15
         return (
@@ -14,11 +19,4 @@ export default class PlanningScreen extends React.Component {
14 19
         );
15 20
     }
16 21
 }
17
-const styles = StyleSheet.create({
18
-    container: {
19
-        flex: 1,
20
-        backgroundColor: '#fff',
21
-        alignItems: 'center',
22
-        justifyContent: 'center',
23
-    },
24
-});
22
+

+ 31
- 18
screens/Proximo/ProximoListScreen.js View File

@@ -1,9 +1,11 @@
1
-import React from 'react';
1
+// @flow
2
+
3
+import * as React from 'react';
2 4
 import {Container, Text, Content, ListItem, Left, Thumbnail, Right, Body, Icon} from 'native-base';
3 5
 import CustomHeader from "../../components/CustomHeader";
4
-import {AsyncStorage, FlatList, View} from "react-native";
6
+import {FlatList} from "react-native";
5 7
 import Touchable from 'react-native-platform-touchable';
6
-import Menu, {MenuItem, MenuDivider} from 'react-native-material-menu';
8
+import Menu, {MenuItem} from 'react-native-material-menu';
7 9
 import i18n from "i18n-js";
8 10
 
9 11
 const IMG_URL = "https://etud.insa-toulouse.fr/~vergnet/appli-amicale/img/";
@@ -39,24 +41,35 @@ function sortNameReverse(a, b) {
39 41
     return 0;
40 42
 }
41 43
 
44
+type Props = {
45
+    navigation: Object
46
+}
42 47
 
43
-export default class ProximoMainScreen extends React.Component {
44
-    constructor(props) {
45
-        super(props);
46
-        this.state = {
47
-            navData: this.props.navigation.getParam('data', []).sort(sortPrice),
48
-            currentSortMode: sortMode.price,
49
-            isSortReversed: false,
50
-            sortPriceIcon: '',
51
-            sortNameIcon: '',
52
-        };
53
-    }
48
+type State = {
49
+    navData: Array<Object>,
50
+    currentSortMode: string,
51
+    isSortReversed: boolean,
52
+    sortPriceIcon: React.Node,
53
+    sortNameIcon: React.Node,
54
+};
55
+
56
+export default class ProximoMainScreen extends React.Component<Props, State> {
57
+
58
+    state = {
59
+        navData: this.props.navigation.getParam('data', []).sort(sortPrice),
60
+        currentSortMode: sortMode.price,
61
+        isSortReversed: false,
62
+        sortPriceIcon: '',
63
+        sortNameIcon: '',
64
+    };
65
+
66
+    _menu: Menu;
54 67
 
55
-    setMenuRef = ref => {
68
+    setMenuRef = (ref: Menu) => {
56 69
         this._menu = ref;
57 70
     };
58 71
 
59
-    toggleSortMode(mode) {
72
+    toggleSortMode(mode: string) {
60 73
         let isReverse = this.state.isSortReversed;
61 74
         if (mode === this.state.currentSortMode) // reverse mode
62 75
             isReverse = !isReverse; // this.state not updating on this function cycle
@@ -65,7 +78,7 @@ export default class ProximoMainScreen extends React.Component {
65 78
         this.setSortMode(mode, isReverse);
66 79
     }
67 80
 
68
-    setSortMode(mode, isReverse) {
81
+    setSortMode(mode: string, isReverse: boolean) {
69 82
         this.setState({
70 83
             currentSortMode: mode,
71 84
             isSortReversed: isReverse
@@ -98,7 +111,7 @@ export default class ProximoMainScreen extends React.Component {
98 111
         this.setSortMode(this.state.currentSortMode, this.state.isSortReversed);
99 112
     }
100 113
 
101
-    setupSortIcons(mode, isReverse) {
114
+    setupSortIcons(mode: string, isReverse: boolean) {
102 115
         const downSortIcon =
103 116
             <Icon
104 117
                 active

+ 21
- 12
screens/Proximo/ProximoMainScreen.js View File

@@ -1,6 +1,8 @@
1
-import React from 'react';
1
+// @flow
2
+
3
+import * as React from 'react';
2 4
 import {RefreshControl, SectionList} from 'react-native';
3
-import {Container, Text, Content, ListItem, Left, Right, Body, Badge, Icon, Toast, H2} from 'native-base';
5
+import {Container, Text, ListItem, Left, Right, Body, Badge, Toast, H2} from 'native-base';
4 6
 import CustomHeader from "../../components/CustomHeader";
5 7
 import i18n from "i18n-js";
6 8
 import CustomMaterialIcon from "../../components/CustomMaterialIcon";
@@ -15,18 +17,25 @@ const typesIcons = {
15 17
     Default: "information-outline",
16 18
 };
17 19
 
18
-export default class ProximoMainScreen extends React.Component {
20
+type Props = {
21
+    navigation: Object
22
+}
19 23
 
20
-    constructor(props) {
21
-        super(props);
22
-        this.state = {
23
-            refreshing: false,
24
-            firstLoading: true,
25
-            data: {},
26
-        };
27
-    }
24
+type State = {
25
+    refreshing: boolean,
26
+    firstLoading: boolean,
27
+    data: Object,
28
+};
29
+
30
+export default class ProximoMainScreen extends React.Component<Props, State> {
31
+
32
+    state = {
33
+        refreshing: false,
34
+        firstLoading: true,
35
+        data: {},
36
+    };
28 37
 
29
-    static generateDataset(types, data) {
38
+    static generateDataset(types: Array<string>, data: Object) {
30 39
         let finalData = [];
31 40
         for (let i = 0; i < types.length; i++) {
32 41
             finalData.push({

+ 39
- 30
screens/ProxiwashScreen.js View File

@@ -1,4 +1,6 @@
1
-import React from 'react';
1
+// @flow
2
+
3
+import * as React from 'react';
2 4
 import {SectionList, RefreshControl, View} from 'react-native';
3 5
 import {Body, Container, Icon, Left, ListItem, Right, Text, Toast, H2, Button} from 'native-base';
4 6
 import CustomHeader from "../components/CustomHeader";
@@ -25,10 +27,27 @@ let stateStrings = {};
25 27
 
26 28
 let stateColors = {};
27 29
 
30
+type Props = {
31
+    navigation: Object,
32
+};
33
+
34
+type State = {
35
+    refreshing: boolean,
36
+    firstLoading: boolean,
37
+    data: Object,
38
+    machinesWatched: Array<Object>
39
+};
40
+
41
+export default class ProxiwashScreen extends React.Component<Props, State> {
28 42
 
29
-export default class ProxiwashScreen extends React.Component {
43
+    state = {
44
+        refreshing: false,
45
+        firstLoading: true,
46
+        data: {},
47
+        machinesWatched: [],
48
+    };
30 49
 
31
-    constructor(props) {
50
+    constructor(props: Props) {
32 51
         super(props);
33 52
         let colors = ThemeManager.getInstance().getCurrentThemeVariables();
34 53
         stateColors[MACHINE_STATES.TERMINE] = colors.proxiwashFinishedColor;
@@ -42,24 +61,12 @@ export default class ProxiwashScreen extends React.Component {
42 61
         stateStrings[MACHINE_STATES.FONCTIONNE] = i18n.t('proxiwashScreen.states.running');
43 62
         stateStrings[MACHINE_STATES.HS] = i18n.t('proxiwashScreen.states.broken');
44 63
         stateStrings[MACHINE_STATES.ERREUR] = i18n.t('proxiwashScreen.states.error');
45
-        this.state = {
46
-            refreshing: false,
47
-            firstLoading: true,
48
-            data: {},
49
-            machinesWatched: [],
50
-        };
51 64
     }
52 65
 
53 66
     async readData() {
54 67
         try {
55 68
             let response = await fetch(DATA_URL);
56 69
             let responseJson = await response.json();
57
-            // This prevents end notifications from showing
58
-            // let watchList = this.state.machinesWatched;
59
-            // for (let i = 0; i < watchList.length; i++) {
60
-            //     if (responseJson[MACHINE_STATES[watchList[i].machineNumber.state]] !== MACHINE_STATES.FONCTIONNE)
61
-            //         this.disableNotification(watchList[i].machineNumber);
62
-            // }
63 70
             this.setState({
64 71
                 data: responseJson
65 72
             });
@@ -98,21 +105,23 @@ export default class ProxiwashScreen extends React.Component {
98 105
         });
99 106
     };
100 107
 
101
-    static getRemainingTime(startString, endString, percentDone) {
108
+    static getRemainingTime(startString: string, endString: string, percentDone: string): number {
102 109
         let startArray = startString.split(':');
103 110
         let endArray = endString.split(':');
104 111
         let startDate = new Date();
105 112
         startDate.setHours(parseInt(startArray[0]), parseInt(startArray[1]), 0, 0);
106 113
         let endDate = new Date();
107 114
         endDate.setHours(parseInt(endArray[0]), parseInt(endArray[1]), 0, 0);
108
-        return (((100 - percentDone) / 100) * (endDate - startDate) / (60 * 1000)).toFixed(0); // Convert milliseconds into minutes
115
+        // Convert milliseconds into minutes
116
+        let time: string = (((100 - parseFloat(percentDone)) / 100) * (endDate - startDate) / (60 * 1000)).toFixed(0);
117
+        return parseInt(time);
109 118
     }
110 119
 
111
-    async setupNotifications(number, remainingTime) {
112
-        if (!this.isMachineWatched(number)) {
120
+    async setupNotifications(machineId: string, remainingTime: number) {
121
+        if (!this.isMachineWatched(machineId)) {
113 122
             let endNotifID = await NotificationsManager.scheduleNotification(
114 123
                 i18n.t('proxiwashScreen.notifications.machineFinishedTitle'),
115
-                i18n.t('proxiwashScreen.notifications.machineFinishedBody', {number: number}),
124
+                i18n.t('proxiwashScreen.notifications.machineFinishedBody', {number: machineId}),
116 125
                 new Date().getTime() + remainingTime * (60 * 1000) // Convert back to milliseconds
117 126
             );
118 127
             let reminderNotifID = undefined;
@@ -127,41 +136,41 @@ export default class ProxiwashScreen extends React.Component {
127 136
             if (remainingTime > reminderNotifTime && reminderNotifTime > 0) {
128 137
                 reminderNotifID = await NotificationsManager.scheduleNotification(
129 138
                     i18n.t('proxiwashScreen.notifications.machineRunningTitle', {time: reminderNotifTime}),
130
-                    i18n.t('proxiwashScreen.notifications.machineRunningBody', {number: number}),
139
+                    i18n.t('proxiwashScreen.notifications.machineRunningBody', {number: machineId}),
131 140
                     new Date().getTime() + (remainingTime - reminderNotifTime) * (60 * 1000) // Convert back to milliseconds
132 141
                 );
133 142
             }
134 143
             let data = this.state.machinesWatched;
135
-            data.push({machineNumber: number, endNotifID: endNotifID, reminderNotifID: reminderNotifID});
144
+            data.push({machineNumber: machineId, endNotifID: endNotifID, reminderNotifID: reminderNotifID});
136 145
             this.setState({machinesWatched: data});
137 146
             AsyncStorage.setItem(WATCHED_MACHINES_PREFKEY, JSON.stringify(data));
138 147
         } else
139
-            this.disableNotification(number);
148
+            this.disableNotification(machineId);
140 149
     }
141 150
 
142
-    disableNotification(number) {
143
-        let data = this.state.machinesWatched;
151
+    disableNotification(machineId: string) {
152
+        let data: Object = this.state.machinesWatched;
144 153
         if (data.length > 0) {
145 154
             let elem = this.state.machinesWatched.find(function (elem) {
146
-                return elem.machineNumber === number
155
+                return elem.machineNumber === machineId
147 156
             });
148 157
             let arrayIndex = data.indexOf(elem);
149
-            NotificationsManager.cancelScheduledNoification(data[arrayIndex].endNotifID);
158
+            NotificationsManager.cancelScheduledNotification(data[arrayIndex].endNotifID);
150 159
             if (data[arrayIndex].reminderNotifID !== undefined)
151
-                NotificationsManager.cancelScheduledNoification(data[arrayIndex].reminderNotifID);
160
+                NotificationsManager.cancelScheduledNotification(data[arrayIndex].reminderNotifID);
152 161
             data.splice(arrayIndex, 1);
153 162
             this.setState({machinesWatched: data});
154 163
             AsyncStorage.setItem(WATCHED_MACHINES_PREFKEY, JSON.stringify(data));
155 164
         }
156 165
     }
157 166
 
158
-    isMachineWatched(number) {
167
+    isMachineWatched(number: string) {
159 168
         return this.state.machinesWatched.find(function (elem) {
160 169
             return elem.machineNumber === number
161 170
         }) !== undefined;
162 171
     }
163 172
 
164
-    renderItem(item, section, data) {
173
+    renderItem(item: Object, section: Object, data: Object) {
165 174
         return (
166 175
             <ListItem
167 176
                 thumbnail

+ 20
- 9
screens/SettingsScreen.js View File

@@ -1,4 +1,6 @@
1
-import React from 'react';
1
+// @flow
2
+
3
+import * as React from 'react';
2 4
 import {
3 5
     Container,
4 6
     Content,
@@ -22,7 +24,16 @@ import {AsyncStorage} from 'react-native'
22 24
 
23 25
 const proxiwashNotifKey = "proxiwashNotifKey";
24 26
 
25
-export default class SettingsScreen extends React.Component {
27
+type Props = {
28
+    navigation: Object,
29
+};
30
+
31
+type State = {
32
+    nightMode: boolean,
33
+    proxiwashNotifPickerSelected: string,
34
+};
35
+
36
+export default class SettingsScreen extends React.Component<Props, State> {
26 37
     state = {
27 38
         nightMode: ThemeManager.getInstance().getNightMode(),
28 39
         proxiwashNotifPickerSelected: "5"
@@ -38,21 +49,21 @@ export default class SettingsScreen extends React.Component {
38 49
     }
39 50
 
40 51
 
41
-    onProxiwashNotidPickerValueChange(value) {
52
+    onProxiwashNotifPickerValueChange(value: string) {
42 53
         AsyncStorage.setItem(proxiwashNotifKey, value);
43 54
         this.setState({
44 55
             proxiwashNotifPickerSelected: value
45 56
         });
46 57
     }
47 58
 
48
-    getproxiwashNotifPicker() {
59
+    getProxiwashNotifPicker() {
49 60
         return (
50 61
             <Picker
51 62
                 note
52 63
                 mode="dropdown"
53 64
                 style={{width: 120}}
54 65
                 selectedValue={this.state.proxiwashNotifPickerSelected}
55
-                onValueChange={(value) => this.onProxiwashNotidPickerValueChange(value)}
66
+                onValueChange={(value) => this.onProxiwashNotifPickerValueChange(value)}
56 67
             >
57 68
                 <Picker.Item label={i18n.t('settingsScreen.proxiwashNotifReminderPicker.never')} value="never"/>
58 69
                 <Picker.Item label={i18n.t('settingsScreen.proxiwashNotifReminderPicker.1')} value="1"/>
@@ -67,7 +78,7 @@ export default class SettingsScreen extends React.Component {
67 78
     }
68 79
 
69 80
     toggleNightMode() {
70
-        ThemeManager.getInstance().setNightmode(!this.state.nightMode);
81
+        ThemeManager.getInstance().setNightMode(!this.state.nightMode);
71 82
         this.setState({nightMode: !this.state.nightMode});
72 83
         // Alert.alert(i18n.t('settingsScreen.nightMode'), i18n.t('settingsScreen.restart'));
73 84
         this.resetStack();
@@ -83,7 +94,7 @@ export default class SettingsScreen extends React.Component {
83 94
         this.props.navigation.navigate('Settings');
84 95
     }
85 96
 
86
-    getToggleItem(onPressCallback, icon, text, subtitle) {
97
+    getToggleItem(onPressCallback: Function, icon: string, text: string, subtitle: string) {
87 98
         return (
88 99
             <ListItem
89 100
                 button
@@ -109,7 +120,7 @@ export default class SettingsScreen extends React.Component {
109 120
         );
110 121
     }
111 122
 
112
-    getGeneralItem(control, icon, text, subtitle) {
123
+    static getGeneralItem(control: React.Node, icon: string, text: string, subtitle: string) {
113 124
         return (
114 125
             <ListItem
115 126
                 thumbnail
@@ -152,7 +163,7 @@ export default class SettingsScreen extends React.Component {
152 163
                             <Text>Proxiwash</Text>
153 164
                         </CardItem>
154 165
                         <List>
155
-                            {this.getGeneralItem(this.getproxiwashNotifPicker(), 'washing-machine', i18n.t('settingsScreen.proxiwashNotifReminder'), i18n.t('settingsScreen.proxiwashNotifReminderSub'))}
166
+                            {SettingsScreen.getGeneralItem(this.getProxiwashNotifPicker(), 'washing-machine', i18n.t('settingsScreen.proxiwashNotifReminder'), i18n.t('settingsScreen.proxiwashNotifReminderSub'))}
156 167
                         </List>
157 168
                     </Card>
158 169
                 </Content>

+ 9
- 10
utils/LocaleManager.js View File

@@ -1,21 +1,20 @@
1
+// @flow
2
+
1 3
 import i18n from 'i18n-js';
2 4
 import * as Localization from 'expo-localization';
3 5
 
4 6
 import en from '../translations/en';
5 7
 import fr from '../translations/fr';
6 8
 
9
+/**
10
+ * Static class used to manage locales
11
+ */
7 12
 export default class LocaleManager {
8 13
 
9
-    static instance = null;
10
-
11
-    static getInstance() {
12
-        if (LocaleManager.instance == null) {
13
-            LocaleManager.instance = new LocaleManager();
14
-        }
15
-        return this.instance;
16
-    }
17
-
18
-    initTranslations() {
14
+    /**
15
+     * Initialize translations using language files
16
+     */
17
+    static initTranslations() {
19 18
         i18n.fallbacks = true;
20 19
         i18n.translations = {fr, en};
21 20
         i18n.locale = Localization.locale;

+ 34
- 4
utils/NotificationsManager.js View File

@@ -1,8 +1,18 @@
1
+// @flow
2
+
1 3
 import * as Permissions from 'expo-permissions';
2 4
 import { Notifications } from 'expo';
3 5
 
6
+/**
7
+ * Static class used to manage notifications sent to the user
8
+ */
4 9
 export default class NotificationsManager {
5 10
 
11
+    /**
12
+     * Async function asking permission to send notifications to the user
13
+     *
14
+     * @returns {Promise}
15
+     */
6 16
     static async askPermissions() {
7 17
         const {status: existingStatus} = await Permissions.getAsync(Permissions.NOTIFICATIONS);
8 18
         let finalStatus = existingStatus;
@@ -13,7 +23,14 @@ export default class NotificationsManager {
13 23
         return finalStatus === 'granted';
14 24
     }
15 25
 
16
-    static async sendNotificationImmediately (title, body) {
26
+    /**
27
+     * Async function sending a notification without delay to the user
28
+     *
29
+     * @param title {String} Notification title
30
+     * @param body {String} Notification body text
31
+     * @returns {Promise<import("react").ReactText>} Notification Id
32
+     */
33
+    static async sendNotificationImmediately (title: string, body: string) {
17 34
         await NotificationsManager.askPermissions();
18 35
         return await Notifications.presentLocalNotificationAsync({
19 36
             title: title,
@@ -21,7 +38,15 @@ export default class NotificationsManager {
21 38
         });
22 39
     };
23 40
 
24
-    static async scheduleNotification(title, body, time) {
41
+    /**
42
+     * Async function sending notification at the specified time
43
+     *
44
+     * @param title Notification title
45
+     * @param body Notification body text
46
+     * @param time Time at which we should send the notification
47
+     * @returns {Promise<import("react").ReactText>} Notification Id
48
+     */
49
+    static async scheduleNotification(title: string, body: string, time: number): Promise<void> {
25 50
         await NotificationsManager.askPermissions();
26 51
         return Notifications.scheduleLocalNotificationAsync(
27 52
             {
@@ -34,7 +59,12 @@ export default class NotificationsManager {
34 59
         );
35 60
     };
36 61
 
37
-    static async cancelScheduledNoification(notifID) {
38
-        await Notifications.cancelScheduledNotificationAsync(notifID);
62
+    /**
63
+     * Async function used to cancel the notification of a specific ID
64
+     * @param notificationID {Number} The notification ID
65
+     * @returns {Promise}
66
+     */
67
+    static async cancelScheduledNotification(notificationID: number) {
68
+        await Notifications.cancelScheduledNotificationAsync(notificationID);
39 69
     }
40 70
 }

+ 50
- 16
utils/ThemeManager.js View File

@@ -1,3 +1,5 @@
1
+// @flow
2
+
1 3
 import {AsyncStorage} from 'react-native'
2 4
 import platform from '../native-base-theme/variables/platform';
3 5
 import platformDark from '../native-base-theme/variables/platformDark';
@@ -5,53 +7,85 @@ import getTheme from '../native-base-theme/components';
5 7
 
6 8
 const nightModeKey = 'nightMode';
7 9
 
10
+/**
11
+ * Singleton class used to manage themes
12
+ */
8 13
 export default class ThemeManager {
9 14
 
10
-    static instance = null;
15
+    static instance: ThemeManager | null = null;
16
+    nightMode: boolean;
17
+    updateThemeCallback: Function;
11 18
 
12 19
     constructor() {
13 20
         this.nightMode = false;
14
-        this.updateThemeCallback = undefined;
21
+        this.updateThemeCallback = null;
15 22
     }
16 23
 
17
-    static getInstance() {
18
-        if (ThemeManager.instance == null) {
19
-            ThemeManager.instance = new ThemeManager();
20
-        }
21
-        return this.instance;
24
+    /**
25
+     * Get this class instance or create one if none is found
26
+     * @returns {ThemeManager}
27
+     */
28
+    static getInstance(): ThemeManager {
29
+        return ThemeManager.instance === null ?
30
+            ThemeManager.instance = new ThemeManager() :
31
+            ThemeManager.instance;
22 32
     }
23 33
 
24
-    setUpdateThemeCallback(callback) {
34
+    /**
35
+     * Set the function to be called when the theme is changed (allows for general reload of the app)
36
+     * @param callback Function to call after theme change
37
+     */
38
+    setUpdateThemeCallback(callback: ?Function) {
25 39
         this.updateThemeCallback = callback;
26 40
     }
27 41
 
28
-    async getDataFromPreferences() {
29
-        let result = await AsyncStorage.getItem(nightModeKey);
42
+    /**
43
+     * Read async storage to get preferences
44
+     * @returns {Promise<void>}
45
+     */
46
+    async getDataFromPreferences(): Promise<void> {
47
+        let result: string = await AsyncStorage.getItem(nightModeKey);
30 48
 
31 49
         if (result === '1')
32 50
             this.nightMode = true;
33
-        console.log('nightmode: ' + this.nightMode);
51
+        // console.log('nightmode: ' + this.nightMode);
34 52
     }
35 53
 
36
-    setNightmode(isNightMode) {
54
+    /**
55
+     * Set night mode and save it to preferences
56
+     *
57
+     * @param isNightMode Whether to enable night mode
58
+     */
59
+    setNightMode(isNightMode: boolean) {
37 60
         this.nightMode = isNightMode;
38 61
         AsyncStorage.setItem(nightModeKey, isNightMode ? '1' : '0');
39
-        if (this.updateThemeCallback !== undefined)
62
+        if (this.updateThemeCallback !== null)
40 63
             this.updateThemeCallback();
41 64
     }
42 65
 
43
-    getNightMode() {
66
+    /**
67
+     * @returns {boolean} Night mode state
68
+     */
69
+    getNightMode(): boolean {
44 70
         return this.nightMode;
45 71
     }
46 72
 
47
-    getCurrentTheme() {
73
+    /**
74
+     * Get the current theme based on night mode
75
+     * @returns {Object}
76
+     */
77
+    getCurrentTheme(): Object {
48 78
         if (this.nightMode)
49 79
             return getTheme(platformDark);
50 80
         else
51 81
             return getTheme(platform);
52 82
     }
53 83
 
54
-    getCurrentThemeVariables() {
84
+    /**
85
+     * Get the variables contained in the current theme
86
+     * @returns {Object}
87
+     */
88
+    getCurrentThemeVariables(): Object {
55 89
         return this.getCurrentTheme().variables;
56 90
     }
57 91
 

Loading…
Cancel
Save