Browse Source

Use common class to handle section list display

keplyx 4 years ago
parent
commit
861b655fe5

+ 1
- 1
App.js View File

@@ -31,7 +31,7 @@ export default class App extends React.Component<Props, State> {
31 31
     }
32 32
 
33 33
     /**
34
-     * Loads data before components are mounted, like fonts and themes
34
+     * Loads FetchedData before components are mounted, like fonts and themes
35 35
      * @returns {Promise}
36 36
      */
37 37
     async componentWillMount() {

+ 122
- 0
components/FetchedDataSectionList.js View File

@@ -0,0 +1,122 @@
1
+// @flow
2
+
3
+import * as React from 'react';
4
+import WebDataManager from "../utils/WebDataManager";
5
+import {Container, Content, H2} from "native-base";
6
+import CustomHeader from "./CustomHeader";
7
+import {SectionList, RefreshControl, View} from "react-native";
8
+
9
+type Props = {
10
+    navigation: Object,
11
+}
12
+
13
+type State = {
14
+    refreshing: boolean,
15
+    firstLoading: boolean,
16
+    fetchedData: Object,
17
+    machinesWatched : Array<Object>
18
+};
19
+
20
+export default class FetchedDataSectionList extends React.Component<Props, State> {
21
+
22
+    webDataManager : WebDataManager;
23
+
24
+    constructor() {
25
+        super();
26
+        this.webDataManager = new WebDataManager(this.getFetchUrl());
27
+    }
28
+
29
+    state = {
30
+        refreshing: false,
31
+        firstLoading: true,
32
+        fetchedData: {},
33
+        machinesWatched : [],
34
+    };
35
+
36
+    getFetchUrl() {
37
+        return "";
38
+    }
39
+
40
+    getHeaderTranslation() {
41
+        return "Header";
42
+    }
43
+
44
+    getUpdateToastTranslations () {
45
+        return ["whoa", "nah"];
46
+    }
47
+
48
+    /**
49
+     * Refresh the FetchedData on first screen load
50
+     */
51
+    componentDidMount() {
52
+        this._onRefresh();
53
+    }
54
+
55
+    _onRefresh = () => {
56
+        this.setState({refreshing: true});
57
+        this.webDataManager.readData().then((fetchedData) => {
58
+            this.setState({
59
+                fetchedData: fetchedData,
60
+                refreshing: false,
61
+                firstLoading: false
62
+            });
63
+            this.webDataManager.showUpdateToast(this.getUpdateToastTranslations()[0], this.getUpdateToastTranslations()[1]);
64
+        });
65
+    };
66
+
67
+    getRenderItem(item: Object, section : Object, data : Object) {
68
+        return <View />;
69
+    }
70
+
71
+    getRenderSectionHeader(title: String) {
72
+        return <View />;
73
+    }
74
+
75
+    /**
76
+     * Create the dataset to be used in the list from the data fetched
77
+     * @param fetchedData {Object}
78
+     * @return {Array}
79
+     */
80
+    createDataset(fetchedData : Object) : Array<Object> {
81
+        return [];
82
+    }
83
+
84
+    /**
85
+     * What item field should be used as a key in the list
86
+     * @param item {Object}
87
+     * @return {*}
88
+     */
89
+    getKeyExtractor(item : Object) {
90
+        return item.id;
91
+    }
92
+
93
+    render() {
94
+        const nav = this.props.navigation;
95
+        const dataset = this.createDataset(this.state.fetchedData);
96
+        return (
97
+            <Container>
98
+                <CustomHeader navigation={nav} title={this.getHeaderTranslation()}/>
99
+                <Content padder>
100
+                    <SectionList
101
+                        sections={dataset}
102
+                        keyExtractor={(item) => this.getKeyExtractor(item)}
103
+                        refreshControl={
104
+                            <RefreshControl
105
+                                refreshing={this.state.refreshing}
106
+                                onRefresh={this._onRefresh}
107
+                            />
108
+                        }
109
+                        renderSectionHeader={({section: {title}}) =>
110
+                            this.getRenderSectionHeader(title)
111
+                        }
112
+                        renderItem={({item, section}) =>
113
+                            this.getRenderItem(item, section, dataset)
114
+                        }
115
+                        style={{minHeight: 300, width: '100%'}}
116
+                    />
117
+                </Content>
118
+            </Container>
119
+        );
120
+    }
121
+
122
+}

+ 42
- 117
screens/HomeScreen.js View File

@@ -1,44 +1,15 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import {Image, View, Linking, RefreshControl, FlatList} from 'react-native';
5
-import {Container, Content, Text, Button, Card, CardItem, Left, Body, Thumbnail, H2, Toast} from 'native-base';
6
-import CustomHeader from '../components/CustomHeader';
4
+import {Image, Linking} from 'react-native';
5
+import {Text, Button, Card, CardItem, Left, Body, Thumbnail} from 'native-base';
7 6
 import i18n from "i18n-js";
8 7
 import CustomMaterialIcon from '../components/CustomMaterialIcon';
8
+import FetchedDataSectionList from "../components/FetchedDataSectionList";
9 9
 
10 10
 const ICON_AMICALE = require('../assets/amicale.png');
11
-
12
-type Props = {
13
-    navigation: Object,
14
-}
15
-
16
-type State = {
17
-    refreshing: boolean,
18
-    firstLoading: boolean,
19
-    data: Object,
20
-};
21
-
22
-const FB_URL = "https://graph.facebook.com/v3.3/amicale.deseleves/posts?fields=message%2Cfull_picture%2Ccreated_time%2Cpermalink_url&access_token=EAAGliUs4Ei8BAGwHmg7SNnosoEDMuDhP3i5lYOGrIGzZBNeMeGzGhpUigJt167cKXEIM0GiurSgaC0PS4Xg2GBzOVNiZCfr8u48VVB15a9YbOsuhjBqhHAMb2sz6ibwOuDhHSvwRZCUpBZCjmAW12e7RjWJp0jvyNoYYvIQbfaLWi3Nk2mBc";
23
-
24
-let test_data = [
25
-    {
26
-        title: "News de l'Amicale",
27
-        date: "June 15, 2019",
28
-        thumbnail: require("../assets/amicale.png"),
29
-        image: require("../assets/drawer-cover.png"),
30
-        text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus congue sapien leo, ac dignissim odio dignissim sit amet. Quisque tempor, turpis sed scelerisque faucibus, dolor tortor porta sapien, eget tincidunt ante elit et ex. Sed sagittis dui non nisl aliquet viverra. Integer quis convallis enim, sit amet auctor ante. Praesent quis lacinia magna. Sed augue lacus, congue eu turpis vel, consectetur pellentesque nulla. Maecenas blandit diam odio, et finibus urna egestas non. Quisque congue finibus efficitur. Sed pretium mauris nec neque mattis, eu condimentum velit ultrices. Fusce eleifend porttitor nunc non suscipit. Aenean porttitor feugiat ipsum sit amet interdum. Maecenas tempor felis non tempus vehicula. Suspendisse sit amet eros neque. ",
31
-        link: "https://en.wikipedia.org/wiki/Main_Page"
32
-    },
33
-    {
34
-        title: "Lancement de la super appli de la mort avec un titre super long",
35
-        date: "June 14, 2019",
36
-        thumbnail: require("../assets/amicale.png"),
37
-        image: require("../assets/image-missing.png"),
38
-        text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus congue sapien leo, eget tincidunt ante elit et ex. Sed sagittis dui non nisl aliquet viverra. Integer quis convallis enim, sit amet auctor ante. Praesent quis lacinia magna. Sed augue lacus, congue eu turpis vel, consectetur pellentesque nulla. Maecenas blandit diam odio, et finibus urna egestas non. Quisque congue finibus efficitur. Sed pretium mauris nec neque mattis, eu condimentum velit ultrices. Fusce eleifend porttitor nunc non suscipit.",
39
-        link: "https://en.wikipedia.org/wiki/Central_Link"
40
-    }
41
-];
11
+const NAME_AMICALE = 'Amicale INSA Toulouse';
12
+const FB_URL = "https://graph.facebook.com/v3.3/amicale.deseleves/posts?fields=message%2Cfull_picture%2Ccreated_time%2Cpermalink_url&&date_format=U&access_token=EAAGliUs4Ei8BAGwHmg7SNnosoEDMuDhP3i5lYOGrIGzZBNeMeGzGhpUigJt167cKXEIM0GiurSgaC0PS4Xg2GBzOVNiZCfr8u48VVB15a9YbOsuhjBqhHAMb2sz6ibwOuDhHSvwRZCUpBZCjmAW12e7RjWJp0jvyNoYYvIQbfaLWi3Nk2mBc";
42 13
 
43 14
 /**
44 15
  * Opens a link in the device's browser
@@ -51,68 +22,56 @@ function openWebLink(link) {
51 22
 /**
52 23
  * Class defining the app's home screen
53 24
  */
54
-export default class HomeScreen extends React.Component<Props, State> {
25
+export default class HomeScreen extends FetchedDataSectionList {
55 26
 
56
-    state = {
57
-        refreshing: false,
58
-        firstLoading: true,
59
-        data: {},
60
-    };
27
+    getHeaderTranslation() {
28
+        return i18n.t("screens.home");
29
+    }
61 30
 
62
-    async readData() {
63
-        try {
64
-            let response = await fetch(FB_URL);
65
-            let responseJson = await response.json();
66
-            this.setState({
67
-                data: responseJson
68
-            });
69
-        } catch (error) {
70
-            console.log('Could not read data from server');
71
-            console.log(error);
72
-            this.setState({
73
-                data: {}
74
-            });
75
-        }
31
+    getUpdateToastTranslations () {
32
+        return [i18n.t("homeScreen.listUpdated"),i18n.t("homeScreen.listUpdateFail")];
76 33
     }
77 34
 
78
-    isDataObjectValid() {
79
-        return Object.keys(this.state.data).length > 0;
35
+    getKeyExtractor(item : Object) {
36
+        return item.id;
80 37
     }
81 38
 
82
-    _onRefresh = () => {
83
-        this.setState({refreshing: true});
84
-        this.readData().then(() => {
85
-            this.setState({
86
-                refreshing: false,
87
-                firstLoading: false
88
-            });
89
-            if (this.isDataObjectValid()) {
90
-                Toast.show({
91
-                    text: i18n.t('proxiwashScreen.listUpdated'),
92
-                    buttonText: 'OK',
93
-                    type: "success",
94
-                    duration: 2000
95
-                })
96
-            } else {
97
-                Toast.show({
98
-                    text: i18n.t('proxiwashScreen.listUpdateFail'),
99
-                    buttonText: 'OK',
100
-                    type: "danger",
101
-                    duration: 4000
102
-                })
39
+    createDataset(fetchedData : Object) {
40
+        let data = [];
41
+        if (fetchedData.data !== undefined)
42
+            data = fetchedData.data;
43
+        return [
44
+            {
45
+                title: '',
46
+                data: data,
47
+                extraData: super.state
103 48
             }
104
-        });
105
-    };
49
+        ];
50
+    }
51
+
52
+    getFetchUrl() {
53
+        return FB_URL;
54
+    }
55
+
56
+    /**
57
+     * Converts a dateString using Unix Timestamp to a formatted date
58
+     * @param dateString {string} The Unix Timestamp representation of a date
59
+     * @return {string} The formatted output date
60
+     */
61
+    static getFormattedDate(dateString: string) {
62
+        let date = new Date(Number.parseInt(dateString) * 1000);
63
+        return date.toLocaleString();
64
+    }
106 65
 
107
-    getRenderItem(item: Object) {
66
+    getRenderItem(item: Object, section : Object, data : Object) {
108 67
         return (
109 68
             <Card style={{flex: 0}}>
110 69
                 <CardItem>
111 70
                     <Left>
112 71
                         <Thumbnail source={ICON_AMICALE}/>
113 72
                         <Body>
114
-                            <Text>Amicale</Text>
115
-                            <Text note>{item.created_time}</Text>
73
+                            <Text>{NAME_AMICALE}</Text>
74
+                            <Text note>{HomeScreen.getFormattedDate(item.created_time)}</Text>
116 75
                         </Body>
117 76
                     </Left>
118 77
                 </CardItem>
@@ -126,7 +85,7 @@ export default class HomeScreen extends React.Component<Props, State> {
126 85
                 <CardItem>
127 86
                     <Left>
128 87
                         <Button transparent
129
-                        onPress={() => openWebLink(item.permalink_url)}>
88
+                                onPress={() => openWebLink(item.permalink_url)}>
130 89
                             <CustomMaterialIcon
131 90
                                 icon="facebook"
132 91
                                 color="#57aeff"
@@ -139,38 +98,4 @@ export default class HomeScreen extends React.Component<Props, State> {
139 98
         );
140 99
     }
141 100
 
142
-    /**
143
-     * Refresh the data on first screen load
144
-     */
145
-    componentDidMount() {
146
-        this._onRefresh();
147
-    }
148
-
149
-
150
-    render() {
151
-        const nav = this.props.navigation;
152
-        let displayData = this.state.data.data;
153
-        return (
154
-            <Container>
155
-                <CustomHeader navigation={nav} title={i18n.t('screens.home')}/>
156
-                <Content padder>
157
-                    <FlatList
158
-                        data={displayData}
159
-                        extraData={this.state}
160
-                        keyExtractor={(item) => item.id}
161
-                        refreshControl={
162
-                            <RefreshControl
163
-                                refreshing={this.state.refreshing}
164
-                                onRefresh={this._onRefresh}
165
-                            />
166
-                        }
167
-                        renderItem={({item}) =>
168
-                            this.getRenderItem(item)
169
-                        }
170
-                        style={{minHeight: 300, width: '100%'}}
171
-                    />
172
-                </Content>
173
-            </Container>
174
-        );
175
-    }
176 101
 }

+ 73
- 144
screens/Proximo/ProximoMainScreen.js View File

@@ -1,11 +1,10 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import {RefreshControl, SectionList} from 'react-native';
5
-import {Container, Text, ListItem, Left, Right, Body, Badge, Toast, H2} from 'native-base';
6
-import CustomHeader from "../../components/CustomHeader";
4
+import {Badge, Body, H2, Left, ListItem, Right, Text} from 'native-base';
7 5
 import i18n from "i18n-js";
8 6
 import CustomMaterialIcon from "../../components/CustomMaterialIcon";
7
+import FetchedDataSectionList from "../../components/FetchedDataSectionList";
9 8
 
10 9
 const DATA_URL = "https://etud.insa-toulouse.fr/~vergnet/appli-amicale/dataProximo.json";
11 10
 
@@ -17,168 +16,98 @@ const typesIcons = {
17 16
     Default: "information-outline",
18 17
 };
19 18
 
20
-type Props = {
21
-    navigation: Object
22
-}
23
-
24
-type State = {
25
-    refreshing: boolean,
26
-    firstLoading: boolean,
27
-    data: Object,
28
-};
29 19
 
30 20
 /**
31 21
  * Class defining the main proximo screen. This screen shows the different categories of articles
32 22
  * offered by proximo.
33 23
  */
34
-export default class ProximoMainScreen extends React.Component<Props, State> {
24
+export default class ProximoMainScreen extends FetchedDataSectionList {
25
+
26
+    getFetchUrl() {
27
+        return DATA_URL;
28
+    }
29
+
30
+    getHeaderTranslation() {
31
+        return i18n.t("screens.proximo");
32
+    }
33
+
34
+    getUpdateToastTranslations() {
35
+        return [i18n.t("proximoScreen.listUpdated"), i18n.t("proximoScreen.listUpdateFail")];
36
+    }
35 37
 
36
-    state = {
37
-        refreshing: false,
38
-        firstLoading: true,
39
-        data: {},
40
-    };
38
+    getKeyExtractor(item: Object) {
39
+        return item.type;
40
+    }
41
+
42
+    createDataset(fetchedData: Object) {
43
+        return [
44
+            {
45
+                title: i18n.t('proximoScreen.listTitle'),
46
+                data: ProximoMainScreen.generateData(fetchedData),
47
+                extraData: super.state,
48
+            }
49
+        ];
50
+    }
41 51
 
42 52
     /**
43
-     * Generate the dataset using types and data.
53
+     * Generate the data using types and FetchedData.
44 54
      * This will group items under the same type.
45 55
      *
46
-     * @param types An array containing the types available (categories)
47
-     * @param data The array of articles represented by objects
56
+     * @param fetchedData The array of articles represented by objects
48 57
      * @returns {Array} The formatted dataset
49 58
      */
50
-    static generateDataset(types: Array<string>, data: Array<Object>) {
59
+    static generateData(fetchedData: Object) {
51 60
         let finalData = [];
52
-        for (let i = 0; i < types.length; i++) {
53
-            finalData.push({
54
-                type: types[i],
55
-                data: []
56
-            });
57
-            for (let k = 0; k < data.length; k++) {
58
-                if (data[k]['type'].includes(types[i])) {
59
-                    finalData[i].data.push(data[k]);
61
+        if (fetchedData.types !== undefined && fetchedData.articles !== undefined) {
62
+            let types = fetchedData.types;
63
+            let articles = fetchedData.articles;
64
+            for (let i = 0; i < types.length; i++) {
65
+                finalData.push({
66
+                    type: types[i],
67
+                    data: []
68
+                });
69
+                for (let k = 0; k < articles.length; k++) {
70
+                    if (articles[k]['type'].includes(types[i])) {
71
+                        finalData[i].data.push(articles[k]);
72
+                    }
60 73
                 }
61 74
             }
62 75
         }
63 76
         return finalData;
64 77
     }
65 78
 
66
-    /**
67
-     * Async function reading data from the proximo website and setting the state to rerender the list
68
-     *
69
-     * @returns {Promise<void>}
70
-     */
71
-    async readData() {
72
-        try {
73
-            let response = await fetch(DATA_URL);
74
-            let responseJson = await response.json();
75
-
76
-            if (responseJson['articles'].length !== 0 && responseJson['types'].length !== 0) {
77
-                let data = ProximoMainScreen.generateDataset(responseJson['types'], responseJson['articles']);
78
-                this.setState({
79
-                    data: data
80
-                });
81
-            } else
82
-                this.setState({data: undefined});
83
-        } catch (error) {
84
-            console.error(error);
85
-        }
86
-    }
87
-
88
-    /**
89
-     * Refresh the list on first screen load
90
-     */
91
-    componentDidMount() {
92
-        this._onRefresh();
93
-    }
94
-
95
-    /**
96
-     * Display a loading indicator and fetch data from the internet
97
-     *
98
-     * @private
99
-     */
100
-    _onRefresh = () => {
101
-        this.setState({refreshing: true});
102
-        this.readData().then(() => {
103
-            this.setState({
104
-                refreshing: false,
105
-                firstLoading: false
106
-            });
107
-            Toast.show({
108
-                text: i18n.t('proximoScreen.listUpdated'),
109
-                buttonText: 'OK',
110
-                type: "success",
111
-                duration: 2000
112
-            })
113
-        });
114
-    };
115
-
116
-    /**
117
-     * Renders the proximo categories list.
118
-     * If we are loading for the first time, change the data for the SectionList to display a loading message.
119
-     *
120
-     * @returns {react.Node}
121
-     */
122
-    render() {
123
-        const nav = this.props.navigation;
124
-        const data = [
125
-            {
126
-                title: i18n.t('proximoScreen.listTitle'),
127
-                data: this.state.data,
128
-                extraData: this.state,
129
-            }
130
-        ];
131
-        const loadingData = [
132
-            {
133
-                title: i18n.t('proximoScreen.loading'),
134
-                data: []
135
-            }
136
-        ];
79
+    getRenderItem(item: Object, section : Object, data : Object) {
137 80
         return (
138
-            <Container>
139
-                <CustomHeader navigation={nav} title={'Proximo'}/>
140
-                <SectionList
141
-                    sections={this.state.firstLoading ? loadingData : data}
142
-                    keyExtractor={(item, index) => item.type}
143
-                    refreshControl={
144
-                        <RefreshControl
145
-                            refreshing={this.state.refreshing}
146
-                            onRefresh={this._onRefresh}
147
-                        />
148
-                    }
149
-                    style={{minHeight: 300, width: '100%'}}
150
-                    renderSectionHeader={({section: {title}}) => (
151
-                        <H2 style={{textAlign: 'center', paddingVertical: 10}}>{title}</H2>
152
-                    )}
153
-                    renderItem={({item}) =>
154
-                        <ListItem
155
-                            button
156
-                            thumbnail
157
-                            onPress={() => {
158
-                                nav.navigate('ProximoListScreen', item);
159
-                            }}
160
-                        >
161
-                            <Left>
162
-                                <CustomMaterialIcon
163
-                                    icon={typesIcons[item.type] ? typesIcons[item.type] : typesIcons.Default}
164
-                                    fontSize={30}
165
-                                />
166
-                            </Left>
167
-                            <Body>
168
-                                <Text>
169
-                                    {item.type}
170
-                                </Text>
171
-                                <Badge><Text>
172
-                                    {item.data.length} {item.data.length > 1 ? i18n.t('proximoScreen.articles') : i18n.t('proximoScreen.article')}
173
-                                </Text></Badge>
174
-                            </Body>
175
-                            <Right>
176
-                                <CustomMaterialIcon icon="chevron-right"/>
177
-                            </Right>
178
-                        </ListItem>}
179
-                />
180
-            </Container>
81
+            <ListItem
82
+                button
83
+                thumbnail
84
+                onPress={() => {
85
+                    this.props.navigation.navigate('ProximoListScreen', item);
86
+                }}
87
+            >
88
+                <Left>
89
+                    <CustomMaterialIcon
90
+                        icon={typesIcons[item.type] ? typesIcons[item.type] : typesIcons.Default}
91
+                        fontSize={30}
92
+                    />
93
+                </Left>
94
+                <Body>
95
+                    <Text>
96
+                        {item.type}
97
+                    </Text>
98
+                    <Badge><Text>
99
+                        {item.data.length} {item.data.length > 1 ? i18n.t('proximoScreen.articles') : i18n.t('proximoScreen.article')}
100
+                    </Text></Badge>
101
+                </Body>
102
+                <Right>
103
+                    <CustomMaterialIcon icon="chevron-right"/>
104
+                </Right>
105
+            </ListItem>
181 106
         );
182 107
     }
108
+
109
+    getRenderSectionHeader(title: String) {
110
+        return <H2 style={{textAlign: 'center', paddingVertical: 10}}>{title}</H2>;
111
+    }
183 112
 }
184 113
 

+ 47
- 150
screens/ProxiwashScreen.js View File

@@ -1,14 +1,13 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import {SectionList, RefreshControl, View} from 'react-native';
5
-import {Body, Container, Icon, Left, ListItem, Right, Text, Toast, H2, Button} from 'native-base';
6
-import CustomHeader from "../components/CustomHeader";
4
+import {AsyncStorage, View} from 'react-native';
5
+import {Body, Button, H2, Icon, Left, ListItem, Right, Text} from 'native-base';
7 6
 import ThemeManager from '../utils/ThemeManager';
8
-import NotificationsManager from '../utils/NotificationsManager';
9 7
 import i18n from "i18n-js";
10
-import {AsyncStorage} from 'react-native'
11 8
 import CustomMaterialIcon from "../components/CustomMaterialIcon";
9
+import FetchedDataSectionList from "../components/FetchedDataSectionList";
10
+import NotificationsManager from "../utils/NotificationsManager";
12 11
 
13 12
 const DATA_URL = "https://etud.insa-toulouse.fr/~vergnet/appli-amicale/dataProxiwash.json";
14 13
 const WATCHED_MACHINES_PREFKEY = "proxiwash.watchedMachines";
@@ -27,37 +26,25 @@ let stateStrings = {};
27 26
 
28 27
 let stateColors = {};
29 28
 
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 29
 
41 30
 /**
42 31
  * Class defining the app's proxiwash screen. This screen shows information about washing machines and
43 32
  * dryers, taken from a scrapper reading proxiwash website
44 33
  */
45
-export default class ProxiwashScreen extends React.Component<Props, State> {
34
+export default class ProxiwashScreen extends FetchedDataSectionList {
46 35
 
47 36
     state = {
48 37
         refreshing: false,
49 38
         firstLoading: true,
50
-        data: {},
51
-        machinesWatched: [],
39
+        fetchedData: {},
40
+        machinesWatched : [],
52 41
     };
53 42
 
54 43
     /**
55 44
      * Creates machine state parameters using current theme and translations
56
-     *
57
-     * @param props
58 45
      */
59
-    constructor(props: Props) {
60
-        super(props);
46
+    constructor() {
47
+        super();
61 48
         let colors = ThemeManager.getInstance().getCurrentThemeVariables();
62 49
         stateColors[MACHINE_STATES.TERMINE] = colors.proxiwashFinishedColor;
63 50
         stateColors[MACHINE_STATES.DISPONIBLE] = colors.proxiwashReadyColor;
@@ -72,34 +59,20 @@ export default class ProxiwashScreen extends React.Component<Props, State> {
72 59
         stateStrings[MACHINE_STATES.ERREUR] = i18n.t('proxiwashScreen.states.error');
73 60
     }
74 61
 
75
-    /**
76
-     * Check if the data object contains valid entries
77
-     *
78
-     * @returns {boolean}
79
-     */
80
-    isDataObjectValid() {
81
-        return Object.keys(this.state.data).length > 0;
62
+    getFetchUrl() {
63
+        return DATA_URL;
82 64
     }
83 65
 
84
-    /**
85
-     * Read the data from the proxiwash scrapper and set it to current state to reload the screen
86
-     *
87
-     * @returns {Promise<void>}
88
-     */
89
-    async readData() {
90
-        try {
91
-            let response = await fetch(DATA_URL);
92
-            let responseJson = await response.json();
93
-            this.setState({
94
-                data: responseJson
95
-            });
96
-        } catch (error) {
97
-            console.log('Could not read data from server');
98
-            console.log(error);
99
-            this.setState({
100
-                data: {}
101
-            });
102
-        }
66
+    getHeaderTranslation() {
67
+        return i18n.t("screens.proxiwash");
68
+    }
69
+
70
+    getUpdateToastTranslations() {
71
+        return [i18n.t("proxiwashScreen.listUpdated"), i18n.t("proxiwashScreen.listUpdateFail")];
72
+    }
73
+
74
+    getKeyExtractor(item: Object) {
75
+        return item.number;
103 76
     }
104 77
 
105 78
     /**
@@ -117,43 +90,6 @@ export default class ProxiwashScreen extends React.Component<Props, State> {
117 90
     }
118 91
 
119 92
     /**
120
-     * Refresh the data on first screen load
121
-     */
122
-    componentDidMount() {
123
-        this._onRefresh();
124
-    }
125
-
126
-    /**
127
-     * Show the refresh indicator and wait for data to be fetched from the scrapper
128
-     *
129
-     * @private
130
-     */
131
-    _onRefresh = () => {
132
-        this.setState({refreshing: true});
133
-        this.readData().then(() => {
134
-            this.setState({
135
-                refreshing: false,
136
-                firstLoading: false
137
-            });
138
-            if (this.isDataObjectValid()) {
139
-                Toast.show({
140
-                    text: i18n.t('proxiwashScreen.listUpdated'),
141
-                    buttonText: 'OK',
142
-                    type: "success",
143
-                    duration: 2000
144
-                })
145
-            } else {
146
-                Toast.show({
147
-                    text: i18n.t('proxiwashScreen.listUpdateFail'),
148
-                    buttonText: 'OK',
149
-                    type: "danger",
150
-                    duration: 4000
151
-                })
152
-            }
153
-        });
154
-    };
155
-
156
-    /**
157 93
      * Get the time remaining based on start/end time and done percent
158 94
      *
159 95
      * @param startString The string representing the start time. Format: hh:mm
@@ -247,15 +183,30 @@ export default class ProxiwashScreen extends React.Component<Props, State> {
247 183
         }) !== undefined;
248 184
     }
249 185
 
186
+    createDataset(fetchedData: Object) {
187
+        return [
188
+            {
189
+                title: i18n.t('proxiwashScreen.dryers'),
190
+                data: fetchedData.dryers === undefined ? [] : fetchedData.dryers,
191
+                extraData: super.state
192
+            },
193
+            {
194
+                title: i18n.t('proxiwashScreen.washers'),
195
+                data: fetchedData.washers === undefined ? [] : fetchedData.washers,
196
+                extraData: super.state
197
+            },
198
+        ];
199
+    }
200
+
250 201
     /**
251 202
      * Get list item to be rendered
252 203
      *
253
-     * @param item The object containing the item's data
204
+     * @param item The object containing the item's FetchedData
254 205
      * @param section The object describing the current SectionList section
255
-     * @param data The full data used by the SectionList
206
+     * @param data The full FetchedData used by the SectionList
256 207
      * @returns {React.Node}
257 208
      */
258
-    renderItem(item: Object, section: Object, data: Object) {
209
+    getRenderItem(item: Object, section: Object, data: Object) {
259 210
         return (
260 211
             <ListItem
261 212
                 thumbnail
@@ -293,13 +244,15 @@ export default class ProxiwashScreen extends React.Component<Props, State> {
293 244
                                 {backgroundColor: '#ba7c1f'} : {}}
294 245
                             onPress={() => {
295 246
                                 this.setupNotifications(item.number, ProxiwashScreen.getRemainingTime(item.startTime, item.endTime, item.donePercent))
296
-                            }}>
247
+                            }}
248
+                        >
297 249
                             <Text>
298 250
                                 {ProxiwashScreen.getRemainingTime(item.startTime, item.endTime, item.donePercent) + ' ' + i18n.t('proxiwashScreen.min')}
299 251
                             </Text>
300
-                            <Icon name={this.isMachineWatched(item.number) ? 'bell-ring' : 'bell'}
301
-                                  type={'MaterialCommunityIcons'}
302
-                                  style={{fontSize: 30, width: 30}}
252
+                            <Icon
253
+                                name={this.isMachineWatched(item.number) ? 'bell-ring' : 'bell'}
254
+                                type={'MaterialCommunityIcons'}
255
+                                style={{fontSize: 30, width: 30}}
303 256
                             />
304 257
                         </Button>
305 258
                         : <Text style={MACHINE_STATES[item.state] === MACHINE_STATES.TERMINE ?
@@ -311,63 +264,7 @@ export default class ProxiwashScreen extends React.Component<Props, State> {
311 264
             </ListItem>);
312 265
     }
313 266
 
314
-    /**
315
-     * Renders the machines list.
316
-     * If we are loading for the first time, change the data for the SectionList to display a loading message.
317
-     *
318
-     * @returns {react.Node}
319
-     */
320
-    render() {
321
-        const nav = this.props.navigation;
322
-        let data = [];
323
-        if (!this.isDataObjectValid()) {
324
-            data = [
325
-                {
326
-                    title: i18n.t('proxiwashScreen.error'),
327
-                    data: []
328
-                }
329
-            ];
330
-        } else {
331
-            data = [
332
-                {
333
-                    title: i18n.t('proxiwashScreen.dryers'),
334
-                    data: this.state.data.dryers === undefined ? [] : this.state.data.dryers,
335
-                    extraData: this.state
336
-                },
337
-                {
338
-                    title: i18n.t('proxiwashScreen.washers'),
339
-                    data: this.state.data.washers === undefined ? [] : this.state.data.washers,
340
-                    extraData: this.state
341
-                },
342
-            ];
343
-        }
344
-
345
-        const loadingData = [
346
-            {
347
-                title: i18n.t('proxiwashScreen.loading'),
348
-                data: []
349
-            }
350
-        ];
351
-        return (
352
-            <Container>
353
-                <CustomHeader navigation={nav} title={'Proxiwash'}/>
354
-                <SectionList
355
-                    sections={this.state.firstLoading ? loadingData : data}
356
-                    keyExtractor={(item) => item.number}
357
-                    refreshControl={
358
-                        <RefreshControl
359
-                            refreshing={this.state.refreshing}
360
-                            onRefresh={this._onRefresh}
361
-                        />
362
-                    }
363
-                    renderSectionHeader={({section: {title}}) => (
364
-                        <H2 style={{textAlign: 'center', paddingVertical: 10}}>{title}</H2>
365
-                    )}
366
-                    renderItem={({item, section}) =>
367
-                        this.renderItem(item, section, data)
368
-                    }
369
-                />
370
-            </Container>
371
-        );
267
+    getRenderSectionHeader(title: String) {
268
+        return <H2 style={{textAlign: 'center', paddingVertical: 10}}>{title}</H2>;
372 269
     }
373 270
 }

+ 1
- 1
screens/SettingsScreen.js View File

@@ -43,7 +43,7 @@ export default class SettingsScreen extends React.Component<Props, State> {
43 43
     };
44 44
 
45 45
     /**
46
-     * Gets data from preferences before rendering components
46
+     * Gets FetchedData from preferences before rendering components
47 47
      *
48 48
      * @returns {Promise<void>}
49 49
      */

+ 6
- 0
translations/en.json View File

@@ -2,6 +2,8 @@
2 2
   "screens": {
3 3
     "home": "Home",
4 4
     "planning": "Planning",
5
+    "proxiwash": "Proxiwash",
6
+    "proximo": "Proximo",
5 7
     "settings": "Settings",
6 8
     "about": "About"
7 9
   },
@@ -22,6 +24,10 @@
22 24
       "30": "30 min"
23 25
     }
24 26
   },
27
+  "homeScreen": {
28
+    "listUpdated": "List updated!",
29
+    "listUpdateFail": "Error while updating list"
30
+  },
25 31
   "aboutScreen": {
26 32
     "appstore": "See on the Appstore",
27 33
     "playstore": "See on the Playstore",

+ 6
- 0
translations/fr.json View File

@@ -2,6 +2,8 @@
2 2
   "screens": {
3 3
     "home": "Accueil",
4 4
     "planning": "Planning",
5
+    "proxiwash": "Proxiwash",
6
+    "proximo": "Proximo",
5 7
     "settings": "Paramètres",
6 8
     "about": "À Propos"
7 9
   },
@@ -22,6 +24,10 @@
22 24
       "30": "30 min"
23 25
     }
24 26
   },
27
+  "homeScreen": {
28
+    "listUpdated": "List mise à jour!",
29
+    "listUpdateFail": "Erreur lors de la mise à jour de la liste"
30
+  },
25 31
   "aboutScreen": {
26 32
     "appstore": "Voir sur l'Appstore",
27 33
     "playstore": "Voir sur le Playstore",

+ 40
- 0
utils/WebDataManager.js View File

@@ -0,0 +1,40 @@
1
+import {Toast} from "native-base";
2
+
3
+export default class WebDataManager {
4
+
5
+    FETCH_URL : string;
6
+    lastDataFetched : Object = {};
7
+
8
+
9
+    constructor(url) {
10
+        this.FETCH_URL = url;
11
+    }
12
+
13
+    async readData() {
14
+        let fetchedData : Object = {};
15
+        try {
16
+            let response = await fetch(this.FETCH_URL);
17
+            fetchedData = await response.json();
18
+        } catch (error) {
19
+            console.log('Could not read FetchedData from server');
20
+            console.log(error);
21
+        }
22
+        this.lastDataFetched = fetchedData;
23
+        return fetchedData;
24
+    }
25
+
26
+    isDataObjectValid() {
27
+        return Object.keys(this.lastDataFetched).length > 0;
28
+    }
29
+
30
+    showUpdateToast(successString, errorString) {
31
+        let isSuccess = this.isDataObjectValid();
32
+        Toast.show({
33
+            text: isSuccess ? successString : errorString,
34
+            buttonText: 'OK',
35
+            type: isSuccess ? "success" : "danger",
36
+            duration: 2000
37
+        })
38
+    }
39
+
40
+}

Loading…
Cancel
Save