Browse Source

Reworked section list to use react native design

keplyx 4 years ago
parent
commit
b562357a95

+ 2
- 0
App.js View File

@@ -14,6 +14,7 @@ import ThemeManager from './utils/ThemeManager';
14 14
 import { NavigationContainer } from '@react-navigation/native';
15 15
 import { createStackNavigator } from '@react-navigation/stack';
16 16
 import DrawerNavigator from './navigation/DrawerNavigator';
17
+import { enableScreens } from 'react-native-screens';
17 18
 
18 19
 type Props = {};
19 20
 
@@ -38,6 +39,7 @@ export default class App extends React.Component<Props, State> {
38 39
     constructor(props: Object) {
39 40
         super(props);
40 41
         LocaleManager.initTranslations();
42
+        enableScreens();
41 43
     }
42 44
 
43 45
     /**

+ 1
- 3
components/CustomHeader.js View File

@@ -8,7 +8,6 @@ import Touchable from 'react-native-platform-touchable';
8 8
 import ThemeManager from "../utils/ThemeManager";
9 9
 import CustomMaterialIcon from "./CustomMaterialIcon";
10 10
 import i18n from "i18n-js";
11
-import {NavigationActions} from 'react-navigation';
12 11
 
13 12
 type Props = {
14 13
     hasBackButton: boolean,
@@ -105,8 +104,7 @@ export default class CustomHeader extends React.Component<Props> {
105 104
 
106 105
 
107 106
     onPressBack() {
108
-        const backAction = NavigationActions.back();
109
-        this.props.navigation.dispatch(backAction);
107
+        this.props.navigation.goBack();
110 108
     }
111 109
 
112 110
     render() {

+ 0
- 399
components/FetchedDataSectionList.js View File

@@ -1,399 +0,0 @@
1
-// @flow
2
-
3
-import * as React from 'react';
4
-import WebDataManager from "../utils/WebDataManager";
5
-import {H3, Spinner, Tab, TabHeading, Tabs, Text} from "native-base";
6
-import {RefreshControl, SectionList, View} from "react-native";
7
-import CustomMaterialIcon from "./CustomMaterialIcon";
8
-import i18n from 'i18n-js';
9
-import ThemeManager from "../utils/ThemeManager";
10
-import BaseContainer from "./BaseContainer";
11
-
12
-type Props = {
13
-    navigation: Object,
14
-}
15
-
16
-type State = {
17
-    refreshing: boolean,
18
-    firstLoading: boolean,
19
-    fetchedData: Object,
20
-    machinesWatched: Array<string>,
21
-};
22
-
23
-/**
24
- * Class used to create a basic list view using online json data.
25
- * Used by inheriting from it and redefining getters.
26
- */
27
-export default class FetchedDataSectionList extends React.Component<Props, State> {
28
-    webDataManager: WebDataManager;
29
-
30
-    willFocusSubscription: function;
31
-    willBlurSubscription: function;
32
-    refreshInterval: IntervalID;
33
-    refreshTime: number;
34
-    lastRefresh: Date;
35
-
36
-    minTimeBetweenRefresh = 60;
37
-    state = {
38
-        refreshing: false,
39
-        firstLoading: true,
40
-        fetchedData: {},
41
-        machinesWatched: [],
42
-    };
43
-
44
-    onRefresh: Function;
45
-    onFetchSuccess: Function;
46
-    onFetchError: Function;
47
-    renderSectionHeaderEmpty: Function;
48
-    renderSectionHeaderNotEmpty: Function;
49
-    renderItemEmpty: Function;
50
-    renderItemNotEmpty: Function;
51
-
52
-    constructor(fetchUrl: string, refreshTime: number) {
53
-        super();
54
-        this.webDataManager = new WebDataManager(fetchUrl);
55
-        this.refreshTime = refreshTime;
56
-        // creating references to functions used in render()
57
-        this.onRefresh = this.onRefresh.bind(this);
58
-        this.onFetchSuccess = this.onFetchSuccess.bind(this);
59
-        this.onFetchError = this.onFetchError.bind(this);
60
-        this.renderSectionHeaderEmpty = this.renderSectionHeader.bind(this, true);
61
-        this.renderSectionHeaderNotEmpty = this.renderSectionHeader.bind(this, false);
62
-        this.renderItemEmpty = this.renderItem.bind(this, true);
63
-        this.renderItemNotEmpty = this.renderItem.bind(this, false);
64
-    }
65
-
66
-
67
-    /**
68
-     * Get the translation for the header in the current language
69
-     * @return {string}
70
-     */
71
-    getHeaderTranslation(): string {
72
-        return "Header";
73
-    }
74
-
75
-    /**
76
-     * Get the translation for the toasts in the current language
77
-     * @return {string}
78
-     */
79
-    getUpdateToastTranslations(): Array<string> {
80
-        return ["whoa", "nah"];
81
-    }
82
-
83
-    setMinTimeRefresh(value: number) {
84
-        this.minTimeBetweenRefresh = value;
85
-    }
86
-
87
-    /**
88
-     * Register react navigation events on first screen load.
89
-     * Allows to detect when the screen is focused
90
-     */
91
-    componentDidMount() {
92
-        this.willFocusSubscription = this.props.navigation.addListener(
93
-            'willFocus', this.onScreenFocus.bind(this));
94
-        this.willBlurSubscription = this.props.navigation.addListener(
95
-            'willBlur', this.onScreenBlur.bind(this));
96
-    }
97
-
98
-    /**
99
-     * Refresh data when focusing the screen and setup a refresh interval if asked to
100
-     */
101
-    onScreenFocus() {
102
-        this.onRefresh();
103
-        if (this.refreshTime > 0)
104
-            this.refreshInterval = setInterval(this.onRefresh.bind(this), this.refreshTime)
105
-    }
106
-
107
-    /**
108
-     * Remove any interval on un-focus
109
-     */
110
-    onScreenBlur() {
111
-        clearInterval(this.refreshInterval);
112
-    }
113
-
114
-    /**
115
-     * Unregister from event when un-mounting components
116
-     */
117
-    componentWillUnmount() {
118
-        if (this.willBlurSubscription !== undefined)
119
-            this.willBlurSubscription.remove();
120
-        if (this.willFocusSubscription !== undefined)
121
-            this.willFocusSubscription.remove();
122
-    }
123
-
124
-    onFetchSuccess(fetchedData: Object) {
125
-        this.setState({
126
-            fetchedData: fetchedData,
127
-            refreshing: false,
128
-            firstLoading: false
129
-        });
130
-        this.lastRefresh = new Date();
131
-    }
132
-
133
-    onFetchError() {
134
-        this.setState({
135
-            fetchedData: {},
136
-            refreshing: false,
137
-            firstLoading: false
138
-        });
139
-        this.webDataManager.showUpdateToast(this.getUpdateToastTranslations()[0], this.getUpdateToastTranslations()[1]);
140
-    }
141
-
142
-    /**
143
-     * Refresh data and show a toast if any error occurred
144
-     * @private
145
-     */
146
-    onRefresh() {
147
-        let canRefresh;
148
-        if (this.lastRefresh !== undefined)
149
-            canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) / 1000 > this.minTimeBetweenRefresh;
150
-        else
151
-            canRefresh = true;
152
-
153
-        if (canRefresh) {
154
-            this.setState({refreshing: true});
155
-            this.webDataManager.readData()
156
-                .then(this.onFetchSuccess)
157
-                .catch(this.onFetchError);
158
-        }
159
-    }
160
-
161
-    /**
162
-     * Get the render item to be used for display in the list.
163
-     * Must be overridden by inheriting class.
164
-     *
165
-     * @param item
166
-     * @param section
167
-     * @return {*}
168
-     */
169
-    getRenderItem(item: Object, section: Object) {
170
-        return <View/>;
171
-    }
172
-
173
-    /**
174
-     * Get the render item to be used for the section title in the list.
175
-     * Must be overridden by inheriting class.
176
-     *
177
-     * @param title
178
-     * @return {*}
179
-     */
180
-    getRenderSectionHeader(title: string) {
181
-        return <View/>;
182
-    }
183
-
184
-    /**
185
-     * Get the render item to be used when the list is empty.
186
-     * No need to be overridden, has good defaults.
187
-     *
188
-     * @param text
189
-     * @param isSpinner
190
-     * @param icon
191
-     * @return {*}
192
-     */
193
-    getEmptyRenderItem(text: string, isSpinner: boolean, icon: string) {
194
-        return (
195
-            <View>
196
-                <View style={{
197
-                    justifyContent: 'center',
198
-                    alignItems: 'center',
199
-                    width: '100%',
200
-                    height: 100,
201
-                    marginBottom: 20
202
-                }}>
203
-                    {isSpinner ?
204
-                        <Spinner/>
205
-                        :
206
-                        <CustomMaterialIcon
207
-                            icon={icon}
208
-                            fontSize={100}
209
-                            width={100}
210
-                            color={ThemeManager.getCurrentThemeVariables().fetchedDataSectionListErrorText}/>}
211
-                </View>
212
-
213
-                <H3 style={{
214
-                    textAlign: 'center',
215
-                    marginRight: 20,
216
-                    marginLeft: 20,
217
-                    color: ThemeManager.getCurrentThemeVariables().fetchedDataSectionListErrorText
218
-                }}>
219
-                    {text}
220
-                </H3>
221
-            </View>);
222
-    }
223
-
224
-    /**
225
-     * Create the dataset to be used in the list from the data fetched.
226
-     * Must be overridden.
227
-     *
228
-     * @param fetchedData {Object}
229
-     * @return {Array}
230
-     */
231
-    createDataset(fetchedData: Object): Array<Object> {
232
-        return [];
233
-    }
234
-
235
-
236
-    datasetKeyExtractor(item: Object) {
237
-        return item.text
238
-    }
239
-
240
-    /**
241
-     * Create the dataset when no fetched data is available.
242
-     * No need to be overridden, has good defaults.
243
-     *
244
-     * @return
245
-     */
246
-    createEmptyDataset() {
247
-        return [
248
-            {
249
-                title: '',
250
-                data: [
251
-                    {
252
-                        text: this.state.refreshing ?
253
-                            i18n.t('general.loading') :
254
-                            i18n.t('general.networkError'),
255
-                        isSpinner: this.state.refreshing,
256
-                        icon: this.state.refreshing ?
257
-                            'refresh' :
258
-                            'access-point-network-off'
259
-                    }
260
-                ],
261
-                keyExtractor: this.datasetKeyExtractor,
262
-            }
263
-        ];
264
-    }
265
-
266
-    /**
267
-     * Should the app use a tab layout instead of a section list ?
268
-     * If yes, each section will be rendered in a new tab.
269
-     * Can be overridden.
270
-     *
271
-     * @return {boolean}
272
-     */
273
-    hasTabs() {
274
-        return false;
275
-    }
276
-
277
-    hasBackButton() {
278
-        return false;
279
-    }
280
-
281
-    getRightButton() {
282
-        return <View/>
283
-    }
284
-
285
-    hasStickyHeader() {
286
-        return false;
287
-    }
288
-
289
-    hasSideMenu() {
290
-        return true;
291
-    }
292
-
293
-
294
-    renderSectionHeader(isEmpty: boolean, {section: {title}} : Object) {
295
-        return isEmpty ?
296
-            <View/> :
297
-            this.getRenderSectionHeader(title)
298
-    }
299
-
300
-    renderItem(isEmpty: boolean, {item, section}: Object) {
301
-        return isEmpty ?
302
-            this.getEmptyRenderItem(item.text, item.isSpinner, item.icon) :
303
-            this.getRenderItem(item, section)
304
-    }
305
-
306
-    /**
307
-     * Get the section list render using the generated dataset
308
-     *
309
-     * @param dataset
310
-     * @return
311
-     */
312
-    getSectionList(dataset: Array<Object>) {
313
-        let isEmpty = dataset[0].data.length === 0;
314
-        if (isEmpty)
315
-            dataset = this.createEmptyDataset();
316
-        return (
317
-            <SectionList
318
-                sections={dataset}
319
-                stickySectionHeadersEnabled={this.hasStickyHeader()}
320
-                refreshControl={
321
-                    <RefreshControl
322
-                        refreshing={this.state.refreshing}
323
-                        onRefresh={this.onRefresh}
324
-                    />
325
-                }
326
-                renderSectionHeader={isEmpty ? this.renderSectionHeaderEmpty : this.renderSectionHeaderNotEmpty}
327
-                renderItem={isEmpty ? this.renderItemEmpty : this.renderItemNotEmpty}
328
-                style={{minHeight: 300, width: '100%'}}
329
-                contentContainerStyle={
330
-                    isEmpty ?
331
-                        {flexGrow: 1, justifyContent: 'center', alignItems: 'center'} : {}
332
-                }
333
-            />
334
-        );
335
-    }
336
-
337
-    /**
338
-     * Generate the tabs containing the lists
339
-     *
340
-     * @param dataset
341
-     * @return
342
-     */
343
-    getTabbedView(dataset: Array<Object>) {
344
-        let tabbedView = [];
345
-        for (let i = 0; i < dataset.length; i++) {
346
-            tabbedView.push(
347
-                <Tab heading={
348
-                    <TabHeading>
349
-                        <CustomMaterialIcon
350
-                            icon={dataset[i].icon}
351
-                            color={ThemeManager.getCurrentThemeVariables().tabIconColor}
352
-                            fontSize={20}
353
-                        />
354
-                        <Text>{dataset[i].title}</Text>
355
-                    </TabHeading>}
356
-                     key={dataset[i].title}
357
-                     style={{backgroundColor: ThemeManager.getCurrentThemeVariables().containerBgColor}}>
358
-                    {this.getSectionList(
359
-                        [
360
-                            {
361
-                                title: dataset[i].title,
362
-                                data: dataset[i].data,
363
-                                extraData: dataset[i].extraData,
364
-                                keyExtractor: dataset[i].keyExtractor
365
-                            }
366
-                        ]
367
-                    )}
368
-                </Tab>);
369
-        }
370
-        return tabbedView;
371
-    }
372
-
373
-    render() {
374
-        // console.log("rendering FetchedDataSectionList");
375
-        const dataset = this.createDataset(this.state.fetchedData);
376
-        return (
377
-            <BaseContainer
378
-                navigation={this.props.navigation}
379
-                headerTitle={this.getHeaderTranslation()}
380
-                headerRightButton={this.getRightButton()}
381
-                hasTabs={this.hasTabs()}
382
-                hasBackButton={this.hasBackButton()}
383
-                hasSideMenu={this.hasSideMenu()}
384
-            >
385
-                {this.hasTabs() ?
386
-                    <Tabs
387
-                        tabContainerStyle={{
388
-                            elevation: 0, // Fix for android shadow
389
-                        }}
390
-                    >
391
-                        {this.getTabbedView(dataset)}
392
-                    </Tabs>
393
-                    :
394
-                    this.getSectionList(dataset)
395
-                }
396
-            </BaseContainer>
397
-        );
398
-    }
399
-}

+ 221
- 0
components/WebSectionList.js View File

@@ -0,0 +1,221 @@
1
+// @flow
2
+
3
+import * as React from 'react';
4
+import {H3, Spinner, View} from "native-base";
5
+import ThemeManager from '../utils/ThemeManager';
6
+import WebDataManager from "../utils/WebDataManager";
7
+import CustomMaterialIcon from "./CustomMaterialIcon";
8
+import i18n from "i18n-js";
9
+import {RefreshControl, SectionList} from "react-native";
10
+
11
+type Props = {
12
+    navigation: Object,
13
+    fetchUrl: string,
14
+    refreshTime: number,
15
+    renderItem: React.Node,
16
+    renderSectionHeader: React.Node,
17
+    stickyHeader: boolean,
18
+    createDataset: Function,
19
+    updateErrorText: string,
20
+}
21
+
22
+type State = {
23
+    refreshing: boolean,
24
+    firstLoading: boolean,
25
+    fetchedData: Object,
26
+};
27
+
28
+/**
29
+ * Custom component defining a material icon using native base
30
+ *
31
+ * @prop active {boolean} Whether to set the icon color to active
32
+ * @prop icon {string} The icon string to use from MaterialCommunityIcons
33
+ * @prop color {string} The icon color. Use default theme color if unspecified
34
+ * @prop fontSize {number} The icon size. Use 26 if unspecified
35
+ * @prop width {number} The icon width. Use 30 if unspecified
36
+ */
37
+export default class WebSectionList extends React.Component<Props, State> {
38
+
39
+    static defaultProps = {
40
+        renderSectionHeader: undefined,
41
+        stickyHeader: false,
42
+    };
43
+
44
+    webDataManager: WebDataManager;
45
+
46
+    refreshInterval: IntervalID;
47
+    lastRefresh: Date;
48
+
49
+    state = {
50
+        refreshing: false,
51
+        firstLoading: true,
52
+        fetchedData: {},
53
+    };
54
+
55
+    onRefresh: Function;
56
+    onFetchSuccess: Function;
57
+    onFetchError: Function;
58
+    getEmptyRenderItem: Function;
59
+    getEmptySectionHeader: Function;
60
+
61
+    constructor() {
62
+        super();
63
+        // creating references to functions used in render()
64
+        this.onRefresh = this.onRefresh.bind(this);
65
+        this.onFetchSuccess = this.onFetchSuccess.bind(this);
66
+        this.onFetchError = this.onFetchError.bind(this);
67
+        this.getEmptyRenderItem = this.getEmptyRenderItem.bind(this);
68
+        this.getEmptySectionHeader = this.getEmptySectionHeader.bind(this);
69
+    }
70
+
71
+    /**
72
+     * Register react navigation events on first screen load.
73
+     * Allows to detect when the screen is focused
74
+     */
75
+    componentDidMount() {
76
+        this.webDataManager = new WebDataManager(this.props.fetchUrl);
77
+        const onScreenFocus = this.onScreenFocus.bind(this);
78
+        const onScreenBlur = this.onScreenBlur.bind(this);
79
+        this.props.navigation.addListener('focus', onScreenFocus);
80
+        this.props.navigation.addListener('blur', onScreenBlur);
81
+    }
82
+
83
+    /**
84
+     * Refresh data when focusing the screen and setup a refresh interval if asked to
85
+     */
86
+    onScreenFocus() {
87
+        this.onRefresh();
88
+        if (this.props.refreshTime > 0)
89
+            this.refreshInterval = setInterval(this.onRefresh, this.props.refreshTime)
90
+    }
91
+
92
+    /**
93
+     * Remove any interval on un-focus
94
+     */
95
+    onScreenBlur() {
96
+        clearInterval(this.refreshInterval);
97
+    }
98
+
99
+
100
+    onFetchSuccess(fetchedData: Object) {
101
+        this.setState({
102
+            fetchedData: fetchedData,
103
+            refreshing: false,
104
+            firstLoading: false
105
+        });
106
+        this.lastRefresh = new Date();
107
+    }
108
+
109
+    onFetchError() {
110
+        this.setState({
111
+            fetchedData: {},
112
+            refreshing: false,
113
+            firstLoading: false
114
+        });
115
+        this.webDataManager.showUpdateToast(this.props.updateErrorText);
116
+    }
117
+
118
+    /**
119
+     * Refresh data and show a toast if any error occurred
120
+     * @private
121
+     */
122
+    onRefresh() {
123
+        let canRefresh;
124
+        if (this.lastRefresh !== undefined)
125
+            canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) > this.props.refreshTime;
126
+        else
127
+            canRefresh = true;
128
+        if (canRefresh) {
129
+            this.setState({refreshing: true});
130
+            this.webDataManager.readData()
131
+                .then(this.onFetchSuccess)
132
+                .catch(this.onFetchError);
133
+        }
134
+    }
135
+
136
+    getEmptySectionHeader({section}: Object) {
137
+        return <View/>;
138
+    }
139
+
140
+    getEmptyRenderItem({item}: Object) {
141
+        return (
142
+            <View>
143
+                <View style={{
144
+                    justifyContent: 'center',
145
+                    alignItems: 'center',
146
+                    width: '100%',
147
+                    height: 100,
148
+                    marginBottom: 20
149
+                }}>
150
+                    {this.state.refreshing ?
151
+                        <Spinner/>
152
+                        :
153
+                        <CustomMaterialIcon
154
+                            icon={item.icon}
155
+                            fontSize={100}
156
+                            width={100}
157
+                            color={ThemeManager.getCurrentThemeVariables().fetchedDataSectionListErrorText}/>}
158
+                </View>
159
+
160
+                <H3 style={{
161
+                    textAlign: 'center',
162
+                    marginRight: 20,
163
+                    marginLeft: 20,
164
+                    color: ThemeManager.getCurrentThemeVariables().fetchedDataSectionListErrorText
165
+                }}>
166
+                    {item.text}
167
+                </H3>
168
+            </View>);
169
+    }
170
+
171
+    createEmptyDataset() {
172
+        return [
173
+            {
174
+                title: '',
175
+                data: [
176
+                    {
177
+                        text: this.state.refreshing ?
178
+                            i18n.t('general.loading') :
179
+                            i18n.t('general.networkError'),
180
+                        isSpinner: this.state.refreshing,
181
+                        icon: this.state.refreshing ?
182
+                            'refresh' :
183
+                            'access-point-network-off'
184
+                    }
185
+                ],
186
+                keyExtractor: this.datasetKeyExtractor,
187
+            }
188
+        ];
189
+    }
190
+
191
+    datasetKeyExtractor(item: Object) {
192
+        return item.text
193
+    }
194
+
195
+    render() {
196
+        let dataset = this.props.createDataset(this.state.fetchedData);
197
+        const isEmpty = dataset[0].data.length === 0;
198
+        const shouldRenderHeader = isEmpty || (this.props.renderSectionHeader !== undefined);
199
+        if (isEmpty)
200
+            dataset = this.createEmptyDataset();
201
+        return (
202
+            <SectionList
203
+                sections={dataset}
204
+                refreshControl={
205
+                    <RefreshControl
206
+                        refreshing={this.state.refreshing}
207
+                        onRefresh={this.onRefresh}
208
+                    />
209
+                }
210
+                renderSectionHeader={shouldRenderHeader ? this.getEmptySectionHeader : this.props.renderSectionHeader}
211
+                renderItem={isEmpty ? this.getEmptyRenderItem : this.props.renderItem}
212
+                style={{minHeight: 300, width: '100%'}}
213
+                stickySectionHeadersEnabled={this.props.stickyHeader}
214
+                contentContainerStyle={
215
+                    isEmpty ?
216
+                        {flexGrow: 1, justifyContent: 'center', alignItems: 'center'} : {}
217
+                }
218
+            />
219
+        );
220
+    }
221
+}

+ 106
- 105
screens/HomeScreen.js View File

@@ -1,14 +1,16 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import {Image, Linking, TouchableOpacity, View} from 'react-native';
5
-import {Body, Button, Card, CardItem, H1, Left, Text, Thumbnail} from 'native-base';
4
+import {Image, TouchableOpacity, View} from 'react-native';
5
+import {Body, Button, Card, CardItem, Left, Text, Thumbnail} from 'native-base';
6 6
 import i18n from "i18n-js";
7 7
 import CustomMaterialIcon from '../components/CustomMaterialIcon';
8
-import FetchedDataSectionList from "../components/FetchedDataSectionList";
9 8
 import Autolink from 'react-native-autolink';
10 9
 import ThemeManager from "../utils/ThemeManager";
11 10
 import DashboardItem from "../components/DashboardItem";
11
+import * as WebBrowser from 'expo-web-browser';
12
+import BaseContainer from "../components/BaseContainer";
13
+import WebSectionList from "../components/WebSectionList";
12 14
 // import DATA from "../dashboard_data.json";
13 15
 
14 16
 
@@ -25,30 +27,40 @@ const REFRESH_TIME = 1000 * 20; // Refresh every 20 seconds
25 27
 
26 28
 const CARD_BORDER_RADIUS = 10;
27 29
 
28
-/**
29
- * Opens a link in the device's browser
30
- * @param link The link to open
31
- */
32
-function openWebLink(link) {
33
-    Linking.openURL(link).catch((err) => console.error('Error opening link', err));
30
+type Props = {
31
+    navigation: Object,
34 32
 }
35 33
 
36 34
 /**
37 35
  * Class defining the app's home screen
38 36
  */
39
-export default class HomeScreen extends FetchedDataSectionList {
37
+export default class HomeScreen extends React.Component<Props> {
40 38
 
41 39
     onProxiwashClick: Function;
42 40
     onTutorInsaClick: Function;
43 41
     onMenuClick: Function;
44 42
     onProximoClick: Function;
43
+    getRenderItem: Function;
44
+    createDataset: Function;
45 45
 
46 46
     constructor() {
47
-        super(DATA_URL, REFRESH_TIME);
47
+        super();
48 48
         this.onProxiwashClick = this.onProxiwashClick.bind(this);
49 49
         this.onTutorInsaClick = this.onTutorInsaClick.bind(this);
50 50
         this.onMenuClick = this.onMenuClick.bind(this);
51 51
         this.onProximoClick = this.onProximoClick.bind(this);
52
+        this.getRenderItem = this.getRenderItem.bind(this);
53
+        this.createDataset = this.createDataset.bind(this);
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();
52 64
     }
53 65
 
54 66
     onProxiwashClick() {
@@ -56,7 +68,7 @@ export default class HomeScreen extends FetchedDataSectionList {
56 68
     }
57 69
 
58 70
     onTutorInsaClick() {
59
-        this.props.navigation.navigate('TutorInsaScreen');
71
+        WebBrowser.openBrowserAsync("https://www.etud.insa-toulouse.fr/~tutorinsa/");
60 72
     }
61 73
 
62 74
     onProximoClick() {
@@ -67,24 +79,6 @@ export default class HomeScreen extends FetchedDataSectionList {
67 79
         this.props.navigation.navigate('SelfMenuScreen');
68 80
     }
69 81
 
70
-    /**
71
-     * Converts a dateString using Unix Timestamp to a formatted date
72
-     * @param dateString {string} The Unix Timestamp representation of a date
73
-     * @return {string} The formatted output date
74
-     */
75
-    static getFormattedDate(dateString: string) {
76
-        let date = new Date(Number.parseInt(dateString) * 1000);
77
-        return date.toLocaleString();
78
-    }
79
-
80
-    getHeaderTranslation() {
81
-        return i18n.t("screens.home");
82
-    }
83
-
84
-    getUpdateToastTranslations() {
85
-        return [i18n.t("homeScreen.listUpdated"), i18n.t("homeScreen.listUpdateFail")];
86
-    }
87
-
88 82
     getKeyExtractor(item: Object) {
89 83
         return item !== undefined ? item.id : undefined;
90 84
     }
@@ -154,25 +148,6 @@ export default class HomeScreen extends FetchedDataSectionList {
154 148
         return dataset
155 149
     }
156 150
 
157
-    getRenderSectionHeader(title: string) {
158
-        if (title === '') {
159
-            return <View/>;
160
-        } else {
161
-            return (
162
-                <View style={{
163
-                    backgroundColor: ThemeManager.getCurrentThemeVariables().containerBgColor
164
-                }}>
165
-                    <H1 style={{
166
-                        marginLeft: 'auto',
167
-                        marginRight: 'auto',
168
-                        marginTop: 10,
169
-                        marginBottom: 10
170
-                    }}>{title}</H1>
171
-                </View>
172
-            );
173
-        }
174
-    }
175
-
176 151
     getDashboardItem(item: Object) {
177 152
         let content = item['content'];
178 153
         if (item['id'] === 'event')
@@ -318,7 +293,7 @@ export default class HomeScreen extends FetchedDataSectionList {
318 293
         if (isAvailable)
319 294
             this.props.navigation.navigate('PlanningDisplayScreen', {data: displayEvent});
320 295
         else
321
-            this.props.navigation.navigate('PlanningScreen');
296
+            this.props.navigation.navigate('Planning');
322 297
     };
323 298
 
324 299
 
@@ -345,13 +320,13 @@ export default class HomeScreen extends FetchedDataSectionList {
345 320
             subtitle = i18n.t('homeScreen.dashboard.todayEventsSubtitleNA');
346 321
 
347 322
         let displayEvent = this.getDisplayEvent(futureEvents);
348
-
323
+        const clickAction = this.clickAction.bind(this, isAvailable, displayEvent);
349 324
         return (
350 325
             <DashboardItem
351 326
                 subtitle={subtitle}
352 327
                 color={color}
353 328
                 icon={icon}
354
-                clickAction={this.clickAction.bind(this, isAvailable, displayEvent)}
329
+                clickAction={clickAction}
355 330
                 title={title}
356 331
                 isAvailable={isAvailable}
357 332
                 displayEvent={displayEvent}
@@ -523,64 +498,90 @@ export default class HomeScreen extends FetchedDataSectionList {
523 498
         );
524 499
     }
525 500
 
501
+    openLink(link: string) {
502
+        WebBrowser.openBrowserAsync(link);
503
+    }
526 504
 
527
-    getRenderItem(item: Object, section: Object) {
505
+    getFeedItem(item: Object) {
506
+        const onImagePress = this.openLink.bind(this, item.full_picture);
507
+        const onOutLinkPress = this.openLink.bind(this, item.permalink_url);
528 508
         return (
529
-            section['id'] === SECTIONS_ID[0] ? this.getDashboardItem(item) :
530
-                <Card style={{
531
-                    flex: 0,
532
-                    marginLeft: 10,
533
-                    marginRight: 10,
534
-                    borderRadius: CARD_BORDER_RADIUS,
509
+            <Card style={{
510
+                flex: 0,
511
+                marginLeft: 10,
512
+                marginRight: 10,
513
+                borderRadius: CARD_BORDER_RADIUS,
514
+            }}>
515
+                <CardItem style={{
516
+                    backgroundColor: 'transparent'
535 517
                 }}>
536
-                    <CardItem style={{
537
-                        backgroundColor: 'transparent'
538
-                    }}>
539
-                        <Left>
540
-                            <Thumbnail source={ICON_AMICALE} square/>
541
-                            <Body>
542
-                                <Text>{NAME_AMICALE}</Text>
543
-                                <Text note>{HomeScreen.getFormattedDate(item.created_time)}</Text>
544
-                            </Body>
545
-                        </Left>
546
-                    </CardItem>
547
-                    <CardItem style={{
548
-                        backgroundColor: 'transparent'
549
-                    }}>
518
+                    <Left>
519
+                        <Thumbnail source={ICON_AMICALE} square/>
550 520
                         <Body>
551
-                            {item.full_picture !== '' && item.full_picture !== undefined ?
552
-                                <TouchableOpacity onPress={openWebLink.bind(null, item.full_picture)}
553
-                                                  style={{width: '100%', height: 250, marginBottom: 5}}>
554
-                                    <Image source={{uri: item.full_picture}}
555
-                                           style={{flex: 1, resizeMode: "contain"}}
556
-                                           resizeMode="contain"
557
-                                    />
558
-                                </TouchableOpacity>
559
-                                : <View/>}
560
-                            {item.message !== undefined ?
561
-                                <Autolink
562
-                                    text={item.message}
563
-                                    hashtag="facebook"
564
-                                    style={{color: ThemeManager.getCurrentThemeVariables().textColor}}
565
-                                /> : <View/>
566
-                            }
521
+                            <Text>{NAME_AMICALE}</Text>
522
+                            <Text note>{HomeScreen.getFormattedDate(item.created_time)}</Text>
567 523
                         </Body>
568
-                    </CardItem>
569
-                    <CardItem style={{
570
-                        backgroundColor: 'transparent'
571
-                    }}>
572
-                        <Left>
573
-                            <Button transparent
574
-                                    onPress={openWebLink.bind(null, item.permalink_url)}>
575
-                                <CustomMaterialIcon
576
-                                    icon="facebook"
577
-                                    color="#57aeff"
578
-                                    width={20}/>
579
-                                <Text>En savoir plus</Text>
580
-                            </Button>
581
-                        </Left>
582
-                    </CardItem>
583
-                </Card>
524
+                    </Left>
525
+                </CardItem>
526
+                <CardItem style={{
527
+                    backgroundColor: 'transparent'
528
+                }}>
529
+                    <Body>
530
+                        {item.full_picture !== '' && item.full_picture !== undefined ?
531
+                            <TouchableOpacity onPress={onImagePress}
532
+                                              style={{width: '100%', height: 250, marginBottom: 5}}>
533
+                                <Image source={{uri: item.full_picture}}
534
+                                       style={{flex: 1, resizeMode: "contain"}}
535
+                                       resizeMode="contain"
536
+                                />
537
+                            </TouchableOpacity>
538
+                            : <View/>}
539
+                        {item.message !== undefined ?
540
+                            <Autolink
541
+                                text={item.message}
542
+                                hashtag="facebook"
543
+                                style={{color: ThemeManager.getCurrentThemeVariables().textColor}}
544
+                            /> : <View/>
545
+                        }
546
+                    </Body>
547
+                </CardItem>
548
+                <CardItem style={{
549
+                    backgroundColor: 'transparent'
550
+                }}>
551
+                    <Left>
552
+                        <Button transparent
553
+                                onPress={onOutLinkPress}>
554
+                            <CustomMaterialIcon
555
+                                icon="facebook"
556
+                                color="#57aeff"
557
+                                width={20}/>
558
+                            <Text>En savoir plus</Text>
559
+                        </Button>
560
+                    </Left>
561
+                </CardItem>
562
+            </Card>
563
+        );
564
+    }
565
+
566
+    getRenderItem({item, section}: Object) {
567
+        return (section['id'] === SECTIONS_ID[0] ?
568
+            this.getDashboardItem(item) : this.getFeedItem(item));
569
+    }
570
+
571
+    render() {
572
+        const nav = this.props.navigation;
573
+        return (
574
+            <BaseContainer
575
+                navigation={nav}
576
+                headerTitle={i18n.t('screens.home')}>
577
+                <WebSectionList
578
+                    createDataset={this.createDataset}
579
+                    navigation={nav}
580
+                    refreshTime={REFRESH_TIME}
581
+                    fetchUrl={DATA_URL}
582
+                    renderItem={this.getRenderItem}
583
+                    updateErrorText={i18n.t("homeScreen.listUpdateFail")}/>
584
+            </BaseContainer>
584 585
         );
585 586
     }
586 587
 }

+ 14
- 13
screens/Proximo/ProximoListScreen.js View File

@@ -41,7 +41,8 @@ function sortNameReverse(a, b) {
41 41
 }
42 42
 
43 43
 type Props = {
44
-    navigation: Object
44
+    navigation: Object,
45
+    route: Object,
45 46
 }
46 47
 
47 48
 type State = {
@@ -60,16 +61,8 @@ export default class ProximoListScreen extends React.Component<Props, State> {
60 61
 
61 62
     modalRef: { current: null | Modalize };
62 63
     originalData: Array<Object>;
63
-    navData = this.props.navigation.getParam('data', []);
64
-    shouldFocusSearchBar = this.props.navigation.getParam('shouldFocusSearchBar', false);
65
-    state = {
66
-        currentlyDisplayedData: this.navData['data'].sort(sortPrice),
67
-        currentSortMode: sortMode.price,
68
-        isSortReversed: false,
69
-        sortPriceIcon: '',
70
-        sortNameIcon: '',
71
-        modalCurrentDisplayItem: {},
72
-    };
64
+    shouldFocusSearchBar: boolean;
65
+
73 66
     sortMenuRef: Menu;
74 67
 
75 68
     onMenuRef: Function;
@@ -82,7 +75,16 @@ export default class ProximoListScreen extends React.Component<Props, State> {
82 75
     constructor(props: any) {
83 76
         super(props);
84 77
         this.modalRef = React.createRef();
85
-        this.originalData = this.navData['data'];
78
+        this.originalData = this.props.route.params['data']['data'];
79
+        this.shouldFocusSearchBar = this.props.route.params['shouldFocusSearchBar'];
80
+        this.state = {
81
+            currentlyDisplayedData: this.originalData,
82
+            currentSortMode: sortMode.price,
83
+            isSortReversed: false,
84
+            sortPriceIcon: '',
85
+            sortNameIcon: '',
86
+            modalCurrentDisplayItem: {},
87
+        };
86 88
 
87 89
         this.onMenuRef = this.onMenuRef.bind(this);
88 90
         this.onSearchStringChange = this.onSearchStringChange.bind(this);
@@ -365,7 +367,6 @@ export default class ProximoListScreen extends React.Component<Props, State> {
365 367
                     shouldFocusSearchBar={this.shouldFocusSearchBar}
366 368
                     rightButton={this.getSortMenu()}
367 369
                 />
368
-
369 370
                 <FlatList
370 371
                     data={this.state.currentlyDisplayedData}
371 372
                     extraData={this.state.currentlyDisplayedData}

+ 43
- 20
screens/Proximo/ProximoMainScreen.js View File

@@ -5,26 +5,40 @@ import {Platform, View} from 'react-native'
5 5
 import {Body, Left, ListItem, Right, Text} from 'native-base';
6 6
 import i18n from "i18n-js";
7 7
 import CustomMaterialIcon from "../../components/CustomMaterialIcon";
8
-import FetchedDataSectionList from "../../components/FetchedDataSectionList";
9 8
 import ThemeManager from "../../utils/ThemeManager";
10 9
 import Touchable from "react-native-platform-touchable";
10
+import BaseContainer from "../../components/BaseContainer";
11
+import WebSectionList from "../../components/WebSectionList";
11 12
 
12 13
 const DATA_URL = "https://etud.insa-toulouse.fr/~proximo/data/stock-v2.json";
13 14
 
15
+type Props = {
16
+    navigation: Object,
17
+}
18
+
19
+type State = {
20
+    fetchedData: Object,
21
+}
14 22
 
15 23
 /**
16 24
  * Class defining the main proximo screen. This screen shows the different categories of articles
17 25
  * offered by proximo.
18 26
  */
19
-export default class ProximoMainScreen extends FetchedDataSectionList {
27
+export default class ProximoMainScreen extends React.Component<Props, State> {
28
+
29
+    articles: Object;
20 30
 
21 31
     onPressSearchBtn: Function;
22 32
     onPressAboutBtn: Function;
33
+    getRenderItem: Function;
34
+    createDataset: Function;
23 35
 
24 36
     constructor() {
25
-        super(DATA_URL, 0);
37
+        super();
26 38
         this.onPressSearchBtn = this.onPressSearchBtn.bind(this);
27 39
         this.onPressAboutBtn = this.onPressAboutBtn.bind(this);
40
+        this.getRenderItem = this.getRenderItem.bind(this);
41
+        this.createDataset = this.createDataset.bind(this);
28 42
     }
29 43
 
30 44
     static sortFinalData(a: Object, b: Object) {
@@ -45,14 +59,6 @@ export default class ProximoMainScreen extends FetchedDataSectionList {
45 59
         return 0;
46 60
     }
47 61
 
48
-    getHeaderTranslation() {
49
-        return i18n.t("screens.proximo");
50
-    }
51
-
52
-    getUpdateToastTranslations() {
53
-        return [i18n.t("proximoScreen.listUpdated"), i18n.t("proximoScreen.listUpdateFail")];
54
-    }
55
-
56 62
     getKeyExtractor(item: Object) {
57 63
         return item !== undefined ? item.type['id'] : undefined;
58 64
     }
@@ -62,7 +68,7 @@ export default class ProximoMainScreen extends FetchedDataSectionList {
62 68
             {
63 69
                 title: '',
64 70
                 data: this.generateData(fetchedData),
65
-                extraData: super.state,
71
+                extraData: this.state,
66 72
                 keyExtractor: this.getKeyExtractor
67 73
             }
68 74
         ];
@@ -77,21 +83,22 @@ export default class ProximoMainScreen extends FetchedDataSectionList {
77 83
      */
78 84
     generateData(fetchedData: Object) {
79 85
         let finalData = [];
86
+        this.articles = undefined;
80 87
         if (fetchedData.types !== undefined && fetchedData.articles !== undefined) {
81 88
             let types = fetchedData.types;
82
-            let articles = fetchedData.articles;
89
+            this.articles = fetchedData.articles;
83 90
             finalData.push({
84 91
                 type: {
85 92
                     id: -1,
86 93
                     name: i18n.t('proximoScreen.all'),
87 94
                     icon: 'star'
88 95
                 },
89
-                data: this.getAvailableArticles(articles, undefined)
96
+                data: this.getAvailableArticles(this.articles, undefined)
90 97
             });
91 98
             for (let i = 0; i < types.length; i++) {
92 99
                 finalData.push({
93 100
                     type: types[i],
94
-                    data: this.getAvailableArticles(articles, types[i])
101
+                    data: this.getAvailableArticles(this.articles, types[i])
95 102
                 });
96 103
 
97 104
             }
@@ -128,8 +135,8 @@ export default class ProximoMainScreen extends FetchedDataSectionList {
128 135
                     name: i18n.t('proximoScreen.all'),
129 136
                     icon: 'star'
130 137
                 },
131
-                data: this.state.fetchedData.articles !== undefined ?
132
-                    this.getAvailableArticles(this.state.fetchedData.articles, undefined) : []
138
+                data: this.articles !== undefined ?
139
+                    this.getAvailableArticles(this.articles, undefined) : []
133 140
             },
134 141
         };
135 142
         this.props.navigation.navigate('ProximoListScreen', searchScreenData);
@@ -163,7 +170,7 @@ export default class ProximoMainScreen extends FetchedDataSectionList {
163 170
         );
164 171
     }
165 172
 
166
-    getRenderItem(item: Object, section: Object) {
173
+    getRenderItem({item}: Object) {
167 174
         let dataToSend = {
168 175
             shouldFocusSearchBar: false,
169 176
             data: item,
@@ -196,10 +203,26 @@ export default class ProximoMainScreen extends FetchedDataSectionList {
196 203
                     </Right>
197 204
                 </ListItem>
198 205
             );
199
-        } else {
206
+        } else
200 207
             return <View/>;
201
-        }
208
+    }
202 209
 
210
+    render() {
211
+        const nav = this.props.navigation;
212
+        return (
213
+            <BaseContainer
214
+                navigation={nav}
215
+                headerTitle={i18n.t('screens.proximo')}
216
+                headerRightButton={this.getRightButton()}>
217
+                <WebSectionList
218
+                    createDataset={this.createDataset}
219
+                    navigation={nav}
220
+                    refreshTime={0}
221
+                    fetchUrl={DATA_URL}
222
+                    renderItem={this.getRenderItem}
223
+                    updateErrorText={i18n.t("homeScreen.listUpdateFail")}/>
224
+            </BaseContainer>
225
+        );
203 226
     }
204 227
 }
205 228
 

+ 56
- 35
screens/Proxiwash/ProxiwashScreen.js View File

@@ -6,12 +6,13 @@ import {Body, Card, CardItem, Left, Right, Text} from 'native-base';
6 6
 import ThemeManager from '../../utils/ThemeManager';
7 7
 import i18n from "i18n-js";
8 8
 import CustomMaterialIcon from "../../components/CustomMaterialIcon";
9
-import FetchedDataSectionList from "../../components/FetchedDataSectionList";
9
+import WebSectionList from "../../components/WebSectionList";
10 10
 import NotificationsManager from "../../utils/NotificationsManager";
11 11
 import PlatformTouchable from "react-native-platform-touchable";
12 12
 import Touchable from "react-native-platform-touchable";
13 13
 import AsyncStorageManager from "../../utils/AsyncStorageManager";
14 14
 import * as Expo from "expo";
15
+import BaseContainer from "../../components/BaseContainer";
15 16
 
16 17
 const DATA_URL = "https://etud.insa-toulouse.fr/~amicale_app/washinsa/washinsa.json";
17 18
 
@@ -30,19 +31,41 @@ let stateColors = {};
30 31
 
31 32
 const REFRESH_TIME = 1000 * 10; // Refresh every 10 seconds
32 33
 
34
+type Props = {
35
+    navigation: Object,
36
+}
37
+
38
+type State = {
39
+    refreshing: boolean,
40
+    firstLoading: boolean,
41
+    fetchedData: Object,
42
+    machinesWatched: Array<string>,
43
+};
44
+
45
+
33 46
 /**
34 47
  * Class defining the app's proxiwash screen. This screen shows information about washing machines and
35 48
  * dryers, taken from a scrapper reading proxiwash website
36 49
  */
37
-export default class ProxiwashScreen extends FetchedDataSectionList {
50
+export default class ProxiwashScreen extends React.Component<Props, State> {
38 51
 
39 52
     onAboutPress: Function;
53
+    getRenderItem: Function;
54
+    createDataset: Function;
55
+
56
+    state = {
57
+        refreshing: false,
58
+        firstLoading: true,
59
+        fetchedData: {},
60
+        // machinesWatched: JSON.parse(dataString),
61
+        machinesWatched: [],
62
+    };
40 63
 
41 64
     /**
42 65
      * Creates machine state parameters using current theme and translations
43 66
      */
44 67
     constructor() {
45
-        super(DATA_URL, REFRESH_TIME);
68
+        super();
46 69
         let colors = ThemeManager.getCurrentThemeVariables();
47 70
         stateColors[MACHINE_STATES.TERMINE] = colors.proxiwashFinishedColor;
48 71
         stateColors[MACHINE_STATES.DISPONIBLE] = colors.proxiwashReadyColor;
@@ -69,23 +92,16 @@ export default class ProxiwashScreen extends FetchedDataSectionList {
69 92
         stateIcons[MACHINE_STATES.ERREUR] = 'alert';
70 93
 
71 94
         // let dataString = AsyncStorageManager.getInstance().preferences.proxiwashWatchedMachines.current;
72
-        this.state = {
73
-            refreshing: false,
74
-            firstLoading: true,
75
-            fetchedData: {},
76
-            // machinesWatched: JSON.parse(dataString),
77
-            machinesWatched: [],
78
-        };
79
-        this.setMinTimeRefresh(30);
80
-
95
+        // this.setMinTimeRefresh(30);
81 96
         this.onAboutPress = this.onAboutPress.bind(this);
97
+        this.getRenderItem = this.getRenderItem.bind(this);
98
+        this.createDataset = this.createDataset.bind(this);
82 99
     }
83 100
 
84 101
     /**
85 102
      * Setup notification channel for android and add listeners to detect notifications fired
86 103
      */
87 104
     componentDidMount() {
88
-        super.componentDidMount();
89 105
         if (AsyncStorageManager.getInstance().preferences.expoToken.current !== '') {
90 106
             // Get latest watchlist from server
91 107
             NotificationsManager.getMachineNotificationWatchlist((fetchedList) => {
@@ -107,14 +123,6 @@ export default class ProxiwashScreen extends FetchedDataSectionList {
107 123
         }
108 124
     }
109 125
 
110
-    getHeaderTranslation() {
111
-        return i18n.t("screens.proxiwash");
112
-    }
113
-
114
-    getUpdateToastTranslations() {
115
-        return [i18n.t("proxiwashScreen.listUpdated"), i18n.t("proxiwashScreen.listUpdateFail")];
116
-    }
117
-
118 126
     getDryersKeyExtractor(item: Object) {
119 127
         return item !== undefined ? "dryer" + item.number : undefined;
120 128
     }
@@ -213,27 +221,22 @@ export default class ProxiwashScreen extends FetchedDataSectionList {
213 221
     createDataset(fetchedData: Object) {
214 222
         return [
215 223
             {
216
-                title: i18n.t('proxiwashScreen.washers'),
217
-                icon: 'washing-machine',
218
-                data: fetchedData.washers === undefined ? [] : fetchedData.washers,
219
-                extraData: super.state,
220
-                keyExtractor: this.getWashersKeyExtractor
221
-            },
222
-            {
223 224
                 title: i18n.t('proxiwashScreen.dryers'),
224 225
                 icon: 'tumble-dryer',
225 226
                 data: fetchedData.dryers === undefined ? [] : fetchedData.dryers,
226
-                extraData: super.state,
227
+                extraData: this.state,
227 228
                 keyExtractor: this.getDryersKeyExtractor
228 229
             },
229
-
230
+            {
231
+                title: i18n.t('proxiwashScreen.washers'),
232
+                icon: 'washing-machine',
233
+                data: fetchedData.washers === undefined ? [] : fetchedData.washers,
234
+                extraData: this.state,
235
+                keyExtractor: this.getWashersKeyExtractor
236
+            },
230 237
         ];
231 238
     }
232 239
 
233
-    hasTabs(): boolean {
234
-        return true;
235
-    }
236
-
237 240
     /**
238 241
      * Show an alert fo a machine, allowing to enable/disable notifications if running
239 242
      *
@@ -292,6 +295,24 @@ export default class ProxiwashScreen extends FetchedDataSectionList {
292 295
         );
293 296
     }
294 297
 
298
+    render() {
299
+        const nav = this.props.navigation;
300
+        return (
301
+            <BaseContainer
302
+                navigation={nav}
303
+                headerTitle={i18n.t('screens.proxiwash')}
304
+                headerRightButton={this.getRightButton()}>
305
+                <WebSectionList
306
+                    createDataset={this.createDataset}
307
+                    navigation={nav}
308
+                    refreshTime={REFRESH_TIME}
309
+                    fetchUrl={DATA_URL}
310
+                    renderItem={this.getRenderItem}
311
+                    updateErrorText={i18n.t("proxiwashScreen.listUpdateFail")}/>
312
+            </BaseContainer>
313
+        );
314
+    }
315
+
295 316
     /**
296 317
      * Get list item to be rendered
297 318
      *
@@ -299,7 +320,7 @@ export default class ProxiwashScreen extends FetchedDataSectionList {
299 320
      * @param section The object describing the current SectionList section
300 321
      * @returns {React.Node}
301 322
      */
302
-    getRenderItem(item: Object, section: Object) {
323
+    getRenderItem({item, section} : Object) {
303 324
         let isMachineRunning = MACHINE_STATES[item.state] === MACHINE_STATES["EN COURS"];
304 325
         let machineName = (section.title === i18n.t('proxiwashScreen.dryers') ? i18n.t('proxiwashScreen.dryer') : i18n.t('proxiwashScreen.washer')) + ' n°' + item.number;
305 326
         let isDryer = section.title === i18n.t('proxiwashScreen.dryers');

+ 38
- 25
screens/SelfMenuScreen.js View File

@@ -5,22 +5,31 @@ import {View} from 'react-native';
5 5
 import {Card, CardItem, H2, H3, Text} from 'native-base';
6 6
 import ThemeManager from "../utils/ThemeManager";
7 7
 import i18n from "i18n-js";
8
-import FetchedDataSectionList from "../components/FetchedDataSectionList";
8
+import BaseContainer from "../components/BaseContainer";
9
+import WebSectionList from "../components/WebSectionList";
9 10
 
10 11
 const DATA_URL = "https://etud.insa-toulouse.fr/~amicale_app/menu/menu_data.json";
11 12
 
13
+type Props = {
14
+    navigation: Object,
15
+}
16
+
12 17
 /**
13 18
  * Class defining the app's menu screen.
14 19
  * This screen fetches data from etud to render the RU menu
15 20
  */
16
-export default class SelfMenuScreen extends FetchedDataSectionList {
21
+export default class SelfMenuScreen extends React.Component<Props> {
17 22
 
18 23
     // Hard code strings as toLocaleDateString does not work on current android JS engine
19 24
     daysOfWeek = [];
20 25
     monthsOfYear = [];
21 26
 
27
+    getRenderItem: Function;
28
+    getRenderSectionHeader: Function;
29
+    createDataset: Function;
30
+
22 31
     constructor() {
23
-        super(DATA_URL, 0);
32
+        super();
24 33
         this.daysOfWeek.push(i18n.t("date.daysOfWeek.monday"));
25 34
         this.daysOfWeek.push(i18n.t("date.daysOfWeek.tuesday"));
26 35
         this.daysOfWeek.push(i18n.t("date.daysOfWeek.wednesday"));
@@ -41,32 +50,16 @@ export default class SelfMenuScreen extends FetchedDataSectionList {
41 50
         this.monthsOfYear.push(i18n.t("date.monthsOfYear.october"));
42 51
         this.monthsOfYear.push(i18n.t("date.monthsOfYear.november"));
43 52
         this.monthsOfYear.push(i18n.t("date.monthsOfYear.december"));
44
-    }
45 53
 
46
-    getHeaderTranslation() {
47
-        return i18n.t("screens.menuSelf");
48
-    }
49
-
50
-    getUpdateToastTranslations() {
51
-        return [i18n.t("homeScreen.listUpdated"), i18n.t("homeScreen.listUpdateFail")];
54
+        this.getRenderItem = this.getRenderItem.bind(this);
55
+        this.getRenderSectionHeader = this.getRenderSectionHeader.bind(this);
56
+        this.createDataset = this.createDataset.bind(this);
52 57
     }
53 58
 
54 59
     getKeyExtractor(item: Object) {
55 60
         return item !== undefined ? item['name'] : undefined;
56 61
     }
57 62
 
58
-    hasBackButton() {
59
-        return true;
60
-    }
61
-
62
-    hasStickyHeader(): boolean {
63
-        return true;
64
-    }
65
-
66
-    hasSideMenu(): boolean {
67
-        return false;
68
-    }
69
-
70 63
     createDataset(fetchedData: Object) {
71 64
         let result = [];
72 65
         // Prevent crash by giving a default value when fetchedData is empty (not yet available)
@@ -101,7 +94,8 @@ export default class SelfMenuScreen extends FetchedDataSectionList {
101 94
         return this.daysOfWeek[date.getDay() - 1] + " " + date.getDate() + " " + this.monthsOfYear[date.getMonth()] + " " + date.getFullYear();
102 95
     }
103 96
 
104
-    getRenderSectionHeader(title: string) {
97
+    getRenderSectionHeader({section}: Object) {
98
+        let title = "";
105 99
         return (
106 100
             <Card style={{
107 101
                 marginLeft: 10,
@@ -114,12 +108,12 @@ export default class SelfMenuScreen extends FetchedDataSectionList {
114 108
                     textAlign: 'center',
115 109
                     marginTop: 10,
116 110
                     marginBottom: 10
117
-                }}>{title}</H2>
111
+                }}>{section.title}</H2>
118 112
             </Card>
119 113
         );
120 114
     }
121 115
 
122
-    getRenderItem(item: Object, section: Object) {
116
+    getRenderItem({item}: Object) {
123 117
         return (
124 118
             <Card style={{
125 119
                 flex: 0,
@@ -167,5 +161,24 @@ export default class SelfMenuScreen extends FetchedDataSectionList {
167 161
         return name.charAt(0) + name.substr(1).toLowerCase();
168 162
     }
169 163
 
164
+    render() {
165
+        const nav = this.props.navigation;
166
+        return (
167
+            <BaseContainer
168
+                navigation={nav}
169
+                headerTitle={i18n.t('screens.menuSelf')}
170
+                hasBackButton={true}>
171
+                <WebSectionList
172
+                    createDataset={this.createDataset}
173
+                    navigation={nav}
174
+                    refreshTime={0}
175
+                    fetchUrl={DATA_URL}
176
+                    renderItem={this.getRenderItem}
177
+                    renderSectionHeader={this.getRenderSectionHeader}
178
+                    updateErrorText={i18n.t("homeScreen.listUpdateFail")}
179
+                    stickyHeader={true}/>
180
+            </BaseContainer>
181
+        );
182
+    }
170 183
 }
171 184
 

+ 2
- 3
utils/WebDataManager.js View File

@@ -45,14 +45,13 @@ export default class WebDataManager {
45 45
     /**
46 46
      * Show a toast message depending on the validity of the fetched data
47 47
      *
48
-     * @param successString
49 48
      * @param errorString
50 49
      */
51
-    showUpdateToast(successString, errorString) {
50
+    showUpdateToast(errorString) {
52 51
         let isSuccess = this.isDataObjectValid();
53 52
         if (!isSuccess) {
54 53
             Toast.show({
55
-                text: isSuccess ? successString : errorString,
54
+                text: errorString,
56 55
                 buttonText: 'OK',
57 56
                 type: isSuccess ? "success" : "danger",
58 57
                 duration: 2000

Loading…
Cancel
Save