Browse Source

Improve Proxiwash components to match linter

Arnaud Vergnet 1 year ago
parent
commit
1cc0802c12

+ 223
- 181
src/components/Lists/Proxiwash/ProxiwashListItem.js View File

@@ -1,194 +1,236 @@
1
+// @flow
2
+
1 3
 import * as React from 'react';
2
-import {Avatar, Caption, List, ProgressBar, Surface, Text, withTheme} from 'react-native-paper';
3
-import {StyleSheet, View} from "react-native";
4
-import ProxiwashConstants from "../../../constants/ProxiwashConstants";
5
-import i18n from "i18n-js";
6
-import AprilFoolsManager from "../../../managers/AprilFoolsManager";
7
-import * as Animatable from "react-native-animatable";
8
-import type {CustomTheme} from "../../../managers/ThemeManager";
9
-import type {Machine} from "../../../screens/Proxiwash/ProxiwashScreen";
10
-
11
-type Props = {
12
-    item: Machine,
13
-    theme: CustomTheme,
14
-    onPress: Function,
15
-    isWatched: boolean,
4
+import {
5
+  Avatar,
6
+  Caption,
7
+  List,
8
+  ProgressBar,
9
+  Surface,
10
+  Text,
11
+  withTheme,
12
+} from 'react-native-paper';
13
+import {StyleSheet, View} from 'react-native';
14
+import i18n from 'i18n-js';
15
+import * as Animatable from 'react-native-animatable';
16
+import ProxiwashConstants from '../../../constants/ProxiwashConstants';
17
+import AprilFoolsManager from '../../../managers/AprilFoolsManager';
18
+import type {CustomTheme} from '../../../managers/ThemeManager';
19
+import type {ProxiwashMachineType} from '../../../screens/Proxiwash/ProxiwashScreen';
20
+
21
+type PropsType = {
22
+  item: ProxiwashMachineType,
23
+  theme: CustomTheme,
24
+  onPress: (
25
+    title: string,
26
+    item: ProxiwashMachineType,
16 27
     isDryer: boolean,
17
-    height: number,
18
-}
28
+  ) => void,
29
+  isWatched: boolean,
30
+  isDryer: boolean,
31
+  height: number,
32
+};
19 33
 
20 34
 const AnimatedIcon = Animatable.createAnimatableComponent(Avatar.Icon);
21 35
 
36
+const styles = StyleSheet.create({
37
+  container: {
38
+    margin: 5,
39
+    justifyContent: 'center',
40
+    elevation: 1,
41
+  },
42
+  icon: {
43
+    backgroundColor: 'transparent',
44
+  },
45
+  progressBar: {
46
+    position: 'absolute',
47
+    left: 0,
48
+    borderRadius: 4,
49
+  },
50
+});
22 51
 
23 52
 /**
24 53
  * Component used to display a proxiwash item, showing machine progression and state
25 54
  */
26
-class ProxiwashListItem extends React.Component<Props> {
27
-
28
-    stateColors: Object;
29
-    stateStrings: Object;
30
-
31
-    title: string;
32
-
33
-    constructor(props) {
34
-        super(props);
35
-        this.stateColors = {};
36
-        this.stateStrings = {};
37
-
38
-        this.updateStateStrings();
39
-
40
-        let displayNumber = props.item.number;
41
-        if (AprilFoolsManager.getInstance().isAprilFoolsEnabled())
42
-            displayNumber = AprilFoolsManager.getProxiwashMachineDisplayNumber(parseInt(props.item.number));
43
-
44
-        this.title = props.isDryer
45
-            ? i18n.t('screens.proxiwash.dryer')
46
-            : i18n.t('screens.proxiwash.washer');
47
-        this.title += ' n°' + displayNumber;
48
-    }
49
-
50
-    shouldComponentUpdate(nextProps: Props): boolean {
51
-        const props = this.props;
52
-        return (nextProps.theme.dark !== props.theme.dark)
53
-            || (nextProps.item.state !== props.item.state)
54
-            || (nextProps.item.donePercent !== props.item.donePercent)
55
-            || (nextProps.isWatched !== props.isWatched);
56
-    }
57
-
58
-    updateStateStrings() {
59
-        this.stateStrings[ProxiwashConstants.machineStates.AVAILABLE] = i18n.t('screens.proxiwash.states.ready');
60
-        this.stateStrings[ProxiwashConstants.machineStates.RUNNING] = i18n.t('screens.proxiwash.states.running');
61
-        this.stateStrings[ProxiwashConstants.machineStates.RUNNING_NOT_STARTED] = i18n.t('screens.proxiwash.states.runningNotStarted');
62
-        this.stateStrings[ProxiwashConstants.machineStates.FINISHED] = i18n.t('screens.proxiwash.states.finished');
63
-        this.stateStrings[ProxiwashConstants.machineStates.UNAVAILABLE] = i18n.t('screens.proxiwash.states.broken');
64
-        this.stateStrings[ProxiwashConstants.machineStates.ERROR] = i18n.t('screens.proxiwash.states.error');
65
-        this.stateStrings[ProxiwashConstants.machineStates.UNKNOWN] = i18n.t('screens.proxiwash.states.unknown');
66
-    }
67
-
68
-    updateStateColors() {
69
-        const colors = this.props.theme.colors;
70
-        this.stateColors[ProxiwashConstants.machineStates.AVAILABLE] = colors.proxiwashReadyColor;
71
-        this.stateColors[ProxiwashConstants.machineStates.RUNNING] = colors.proxiwashRunningColor;
72
-        this.stateColors[ProxiwashConstants.machineStates.RUNNING_NOT_STARTED] = colors.proxiwashRunningNotStartedColor;
73
-        this.stateColors[ProxiwashConstants.machineStates.FINISHED] = colors.proxiwashFinishedColor;
74
-        this.stateColors[ProxiwashConstants.machineStates.UNAVAILABLE] = colors.proxiwashBrokenColor;
75
-        this.stateColors[ProxiwashConstants.machineStates.ERROR] = colors.proxiwashErrorColor;
76
-        this.stateColors[ProxiwashConstants.machineStates.UNKNOWN] = colors.proxiwashUnknownColor;
77
-    }
78
-
79
-    onListItemPress = () => this.props.onPress(this.title, this.props.item, this.props.isDryer);
80
-
81
-    render() {
82
-        const props = this.props;
83
-        const colors = props.theme.colors;
84
-        const machineState = props.item.state;
85
-        const isRunning = machineState === ProxiwashConstants.machineStates.RUNNING;
86
-        const isReady = machineState === ProxiwashConstants.machineStates.AVAILABLE;
87
-        const description = isRunning ? props.item.startTime + '/' + props.item.endTime : '';
88
-        const stateIcon = ProxiwashConstants.stateIcons[machineState];
89
-        const stateString = this.stateStrings[machineState];
90
-        const progress = isRunning
91
-            ? props.item.donePercent !== ''
92
-                ? parseFloat(props.item.donePercent) / 100
93
-                : 0
94
-            : 1;
95
-
96
-        const icon = props.isWatched
97
-            ? <AnimatedIcon
98
-                icon={'bell-ring'}
99
-                animation={"rubberBand"}
100
-                useNativeDriver
101
-                size={50}
102
-                color={colors.primary}
103
-                style={styles.icon}
104
-            />
105
-            : <AnimatedIcon
106
-                icon={props.isDryer ? 'tumble-dryer' : 'washing-machine'}
107
-                animation={isRunning ? "pulse" : undefined}
108
-                iterationCount={"infinite"}
109
-                easing={"linear"}
110
-                duration={1000}
111
-                useNativeDriver
112
-                size={40}
113
-                color={colors.text}
114
-                style={styles.icon}
115
-            />;
116
-        this.updateStateColors();
117
-        return (
118
-            <Surface
119
-                style={{
120
-                    ...styles.container,
121
-                    height: props.height,
122
-                    borderRadius: 4,
123
-                }}
124
-            >
125
-                {
126
-                    !isReady
127
-                        ? <ProgressBar
128
-                            style={{
129
-                                ...styles.progressBar,
130
-                                height: props.height
131
-                            }}
132
-                            progress={progress}
133
-                            color={this.stateColors[machineState]}
134
-                        />
135
-                        : null
136
-                }
137
-                <List.Item
138
-                    title={this.title}
139
-                    description={description}
140
-                    style={{
141
-                        height: props.height,
142
-                        justifyContent: 'center',
143
-                    }}
144
-                    onPress={this.onListItemPress}
145
-                    left={() => icon}
146
-                    right={() => (
147
-                        <View style={{flexDirection: 'row',}}>
148
-                            <View style={{justifyContent: 'center',}}>
149
-                                <Text style={
150
-                                    machineState === ProxiwashConstants.machineStates.FINISHED ?
151
-                                        {fontWeight: 'bold',} : {}
152
-                                }
153
-                                >
154
-                                    {stateString}
155
-                                </Text>
156
-                                {
157
-                                    machineState === ProxiwashConstants.machineStates.RUNNING
158
-                                        ? <Caption>{props.item.remainingTime} min</Caption>
159
-                                        : null
160
-                                }
161
-
162
-                            </View>
163
-                            <View style={{justifyContent: 'center',}}>
164
-                                <Avatar.Icon
165
-                                    icon={stateIcon}
166
-                                    color={colors.text}
167
-                                    size={30}
168
-                                    style={styles.icon}
169
-                                />
170
-                            </View>
171
-                        </View>)}
55
+class ProxiwashListItem extends React.Component<PropsType> {
56
+  stateColors: {[key: string]: string};
57
+
58
+  stateStrings: {[key: string]: string};
59
+
60
+  title: string;
61
+
62
+  constructor(props: PropsType) {
63
+    super(props);
64
+    this.stateColors = {};
65
+    this.stateStrings = {};
66
+
67
+    this.updateStateStrings();
68
+
69
+    let displayNumber = props.item.number;
70
+    if (AprilFoolsManager.getInstance().isAprilFoolsEnabled())
71
+      displayNumber = AprilFoolsManager.getProxiwashMachineDisplayNumber(
72
+        parseInt(props.item.number, 10),
73
+      );
74
+
75
+    this.title = props.isDryer
76
+      ? i18n.t('screens.proxiwash.dryer')
77
+      : i18n.t('screens.proxiwash.washer');
78
+    this.title += ` n°${displayNumber}`;
79
+  }
80
+
81
+  shouldComponentUpdate(nextProps: PropsType): boolean {
82
+    const {props} = this;
83
+    return (
84
+      nextProps.theme.dark !== props.theme.dark ||
85
+      nextProps.item.state !== props.item.state ||
86
+      nextProps.item.donePercent !== props.item.donePercent ||
87
+      nextProps.isWatched !== props.isWatched
88
+    );
89
+  }
90
+
91
+  onListItemPress = () => {
92
+    const {props} = this;
93
+    props.onPress(this.title, props.item, props.isDryer);
94
+  };
95
+
96
+  updateStateStrings() {
97
+    this.stateStrings[ProxiwashConstants.machineStates.AVAILABLE] = i18n.t(
98
+      'screens.proxiwash.states.ready',
99
+    );
100
+    this.stateStrings[ProxiwashConstants.machineStates.RUNNING] = i18n.t(
101
+      'screens.proxiwash.states.running',
102
+    );
103
+    this.stateStrings[
104
+      ProxiwashConstants.machineStates.RUNNING_NOT_STARTED
105
+    ] = i18n.t('screens.proxiwash.states.runningNotStarted');
106
+    this.stateStrings[ProxiwashConstants.machineStates.FINISHED] = i18n.t(
107
+      'screens.proxiwash.states.finished',
108
+    );
109
+    this.stateStrings[ProxiwashConstants.machineStates.UNAVAILABLE] = i18n.t(
110
+      'screens.proxiwash.states.broken',
111
+    );
112
+    this.stateStrings[ProxiwashConstants.machineStates.ERROR] = i18n.t(
113
+      'screens.proxiwash.states.error',
114
+    );
115
+    this.stateStrings[ProxiwashConstants.machineStates.UNKNOWN] = i18n.t(
116
+      'screens.proxiwash.states.unknown',
117
+    );
118
+  }
119
+
120
+  updateStateColors() {
121
+    const {props} = this;
122
+    const {colors} = props.theme;
123
+    this.stateColors[ProxiwashConstants.machineStates.AVAILABLE] =
124
+      colors.proxiwashReadyColor;
125
+    this.stateColors[ProxiwashConstants.machineStates.RUNNING] =
126
+      colors.proxiwashRunningColor;
127
+    this.stateColors[ProxiwashConstants.machineStates.RUNNING_NOT_STARTED] =
128
+      colors.proxiwashRunningNotStartedColor;
129
+    this.stateColors[ProxiwashConstants.machineStates.FINISHED] =
130
+      colors.proxiwashFinishedColor;
131
+    this.stateColors[ProxiwashConstants.machineStates.UNAVAILABLE] =
132
+      colors.proxiwashBrokenColor;
133
+    this.stateColors[ProxiwashConstants.machineStates.ERROR] =
134
+      colors.proxiwashErrorColor;
135
+    this.stateColors[ProxiwashConstants.machineStates.UNKNOWN] =
136
+      colors.proxiwashUnknownColor;
137
+  }
138
+
139
+  render(): React.Node {
140
+    const {props} = this;
141
+    const {colors} = props.theme;
142
+    const machineState = props.item.state;
143
+    const isRunning = machineState === ProxiwashConstants.machineStates.RUNNING;
144
+    const isReady = machineState === ProxiwashConstants.machineStates.AVAILABLE;
145
+    const description = isRunning
146
+      ? `${props.item.startTime}/${props.item.endTime}`
147
+      : '';
148
+    const stateIcon = ProxiwashConstants.stateIcons[machineState];
149
+    const stateString = this.stateStrings[machineState];
150
+    let progress;
151
+    if (isRunning && props.item.donePercent !== '')
152
+      progress = parseFloat(props.item.donePercent) / 100;
153
+    else if (isRunning) progress = 0;
154
+    else progress = 1;
155
+
156
+    const icon = props.isWatched ? (
157
+      <AnimatedIcon
158
+        icon="bell-ring"
159
+        animation="rubberBand"
160
+        useNativeDriver
161
+        size={50}
162
+        color={colors.primary}
163
+        style={styles.icon}
164
+      />
165
+    ) : (
166
+      <AnimatedIcon
167
+        icon={props.isDryer ? 'tumble-dryer' : 'washing-machine'}
168
+        animation={isRunning ? 'pulse' : undefined}
169
+        iterationCount="infinite"
170
+        easing="linear"
171
+        duration={1000}
172
+        useNativeDriver
173
+        size={40}
174
+        color={colors.text}
175
+        style={styles.icon}
176
+      />
177
+    );
178
+    this.updateStateColors();
179
+    return (
180
+      <Surface
181
+        style={{
182
+          ...styles.container,
183
+          height: props.height,
184
+          borderRadius: 4,
185
+        }}>
186
+        {!isReady ? (
187
+          <ProgressBar
188
+            style={{
189
+              ...styles.progressBar,
190
+              height: props.height,
191
+            }}
192
+            progress={progress}
193
+            color={this.stateColors[machineState]}
194
+          />
195
+        ) : null}
196
+        <List.Item
197
+          title={this.title}
198
+          description={description}
199
+          style={{
200
+            height: props.height,
201
+            justifyContent: 'center',
202
+          }}
203
+          onPress={this.onListItemPress}
204
+          left={(): React.Node => icon}
205
+          right={(): React.Node => (
206
+            <View style={{flexDirection: 'row'}}>
207
+              <View style={{justifyContent: 'center'}}>
208
+                <Text
209
+                  style={
210
+                    machineState === ProxiwashConstants.machineStates.FINISHED
211
+                      ? {fontWeight: 'bold'}
212
+                      : {}
213
+                  }>
214
+                  {stateString}
215
+                </Text>
216
+                {machineState === ProxiwashConstants.machineStates.RUNNING ? (
217
+                  <Caption>{props.item.remainingTime} min</Caption>
218
+                ) : null}
219
+              </View>
220
+              <View style={{justifyContent: 'center'}}>
221
+                <Avatar.Icon
222
+                  icon={stateIcon}
223
+                  color={colors.text}
224
+                  size={30}
225
+                  style={styles.icon}
172 226
                 />
173
-            </Surface>
174
-        );
175
-    }
227
+              </View>
228
+            </View>
229
+          )}
230
+        />
231
+      </Surface>
232
+    );
233
+  }
176 234
 }
177 235
 
178
-const styles = StyleSheet.create({
179
-    container: {
180
-        margin: 5,
181
-        justifyContent: 'center',
182
-        elevation: 1
183
-    },
184
-    icon: {
185
-        backgroundColor: 'transparent'
186
-    },
187
-    progressBar: {
188
-        position: 'absolute',
189
-        left: 0,
190
-        borderRadius: 4,
191
-    },
192
-});
193
-
194 236
 export default withTheme(ProxiwashListItem);

+ 61
- 61
src/components/Lists/Proxiwash/ProxiwashSectionHeader.js View File

@@ -1,72 +1,72 @@
1
+// @flow
2
+
1 3
 import * as React from 'react';
2 4
 import {Avatar, Text, withTheme} from 'react-native-paper';
3
-import {StyleSheet, View} from "react-native";
4
-import i18n from "i18n-js";
5
+import {StyleSheet, View} from 'react-native';
6
+import i18n from 'i18n-js';
7
+import type {CustomTheme} from '../../../managers/ThemeManager';
5 8
 
6
-type Props = {
7
-    title: string,
8
-    isDryer: boolean,
9
-    nbAvailable: number,
10
-}
9
+type PropsType = {
10
+  theme: CustomTheme,
11
+  title: string,
12
+  isDryer: boolean,
13
+  nbAvailable: number,
14
+};
15
+
16
+const styles = StyleSheet.create({
17
+  container: {
18
+    flexDirection: 'row',
19
+    marginLeft: 5,
20
+    marginRight: 5,
21
+    marginBottom: 10,
22
+    marginTop: 20,
23
+  },
24
+  icon: {
25
+    backgroundColor: 'transparent',
26
+  },
27
+  text: {
28
+    fontSize: 20,
29
+    fontWeight: 'bold',
30
+  },
31
+});
11 32
 
12 33
 /**
13 34
  * Component used to display a proxiwash item, showing machine progression and state
14 35
  */
15
-class ProxiwashListItem extends React.Component<Props> {
36
+class ProxiwashListItem extends React.Component<PropsType> {
37
+  shouldComponentUpdate(nextProps: PropsType): boolean {
38
+    const {props} = this;
39
+    return (
40
+      nextProps.theme.dark !== props.theme.dark ||
41
+      nextProps.nbAvailable !== props.nbAvailable
42
+    );
43
+  }
16 44
 
17
-    constructor(props) {
18
-        super(props);
19
-    }
20
-
21
-    shouldComponentUpdate(nextProps: Props) {
22
-        return (nextProps.theme.dark !== this.props.theme.dark)
23
-            || (nextProps.nbAvailable !== this.props.nbAvailable)
24
-    }
25
-
26
-    render() {
27
-        const props = this.props;
28
-        const subtitle = props.nbAvailable + ' ' + (
29
-            (props.nbAvailable <= 1)
30
-                ? i18n.t('screens.proxiwash.numAvailable')
31
-                : i18n.t('screens.proxiwash.numAvailablePlural'));
32
-        const iconColor = props.nbAvailable > 0
33
-        ? this.props.theme.colors.success
34
-        : this.props.theme.colors.primary;
35
-        return (
36
-            <View style={styles.container}>
37
-                <Avatar.Icon
38
-                    icon={props.isDryer ? 'tumble-dryer' : 'washing-machine'}
39
-                    color={iconColor}
40
-                    style={styles.icon}
41
-                />
42
-                <View style={{justifyContent: 'center'}}>
43
-                    <Text style={styles.text}>
44
-                        {props.title}
45
-                    </Text>
46
-                    <Text style={{color: this.props.theme.colors.subtitle}}>
47
-                        {subtitle}
48
-                    </Text>
49
-                </View>
50
-            </View>
51
-        );
52
-    }
45
+  render(): React.Node {
46
+    const {props} = this;
47
+    const subtitle = `${props.nbAvailable} ${
48
+      props.nbAvailable <= 1
49
+        ? i18n.t('screens.proxiwash.numAvailable')
50
+        : i18n.t('screens.proxiwash.numAvailablePlural')
51
+    }`;
52
+    const iconColor =
53
+      props.nbAvailable > 0
54
+        ? props.theme.colors.success
55
+        : props.theme.colors.primary;
56
+    return (
57
+      <View style={styles.container}>
58
+        <Avatar.Icon
59
+          icon={props.isDryer ? 'tumble-dryer' : 'washing-machine'}
60
+          color={iconColor}
61
+          style={styles.icon}
62
+        />
63
+        <View style={{justifyContent: 'center'}}>
64
+          <Text style={styles.text}>{props.title}</Text>
65
+          <Text style={{color: props.theme.colors.subtitle}}>{subtitle}</Text>
66
+        </View>
67
+      </View>
68
+    );
69
+  }
53 70
 }
54 71
 
55
-const styles = StyleSheet.create({
56
-    container: {
57
-        flexDirection: 'row',
58
-        marginLeft: 5,
59
-        marginRight: 5,
60
-        marginBottom: 10,
61
-        marginTop: 20,
62
-    },
63
-    icon: {
64
-        backgroundColor: 'transparent'
65
-    },
66
-    text: {
67
-        fontSize: 20,
68
-        fontWeight: 'bold',
69
-    }
70
-});
71
-
72 72
 export default withTheme(ProxiwashListItem);

+ 105
- 73
src/screens/Proxiwash/ProxiwashAboutScreen.js View File

@@ -2,85 +2,117 @@
2 2
 
3 3
 import * as React from 'react';
4 4
 import {Image, View} from 'react-native';
5
-import i18n from "i18n-js";
5
+import i18n from 'i18n-js';
6 6
 import {Card, List, Paragraph, Text, Title} from 'react-native-paper';
7
-import CustomTabBar from "../../components/Tabbar/CustomTabBar";
8
-import CollapsibleScrollView from "../../components/Collapsible/CollapsibleScrollView";
7
+import CustomTabBar from '../../components/Tabbar/CustomTabBar';
8
+import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrollView';
9 9
 
10
-type Props = {};
11
-
12
-const LOGO = "https://etud.insa-toulouse.fr/~amicale_app/images/Proxiwash.png";
10
+const LOGO = 'https://etud.insa-toulouse.fr/~amicale_app/images/Proxiwash.png';
13 11
 
14 12
 /**
15 13
  * Class defining the proxiwash about screen.
16 14
  */
17
-export default class ProxiwashAboutScreen extends React.Component<Props> {
18
-
19
-    render() {
20
-        return (
21
-            <CollapsibleScrollView
22
-                style={{padding: 5}}
23
-                hasTab={true}
24
-            >
25
-                <View style={{
26
-                    width: '100%',
27
-                    height: 100,
28
-                    marginTop: 20,
29
-                    marginBottom: 20,
30
-                    justifyContent: 'center',
31
-                    alignItems: 'center'
32
-                }}>
33
-                    <Image
34
-                        source={{uri: LOGO}}
35
-                        style={{height: '100%', width: '100%', resizeMode: "contain"}}/>
36
-                </View>
37
-                <Text>{i18n.t('screens.proxiwash.description')}</Text>
38
-                <Card style={{margin: 5}}>
39
-                    <Card.Title
40
-                        title={i18n.t('screens.proxiwash.dryer')}
41
-                        left={props => <List.Icon {...props} icon={'tumble-dryer'}/>}
42
-                    />
43
-                    <Card.Content>
44
-                        <Title>{i18n.t('screens.proxiwash.procedure')}</Title>
45
-                        <Paragraph>{i18n.t('screens.proxiwash.dryerProcedure')}</Paragraph>
46
-                        <Title>{i18n.t('screens.proxiwash.tips')}</Title>
47
-                        <Paragraph>{i18n.t('screens.proxiwash.dryerTips')}</Paragraph>
48
-                    </Card.Content>
49
-                </Card>
15
+// eslint-disable-next-line react/prefer-stateless-function
16
+export default class ProxiwashAboutScreen extends React.Component<null> {
17
+  render(): React.Node {
18
+    return (
19
+      <CollapsibleScrollView style={{padding: 5}} hasTab>
20
+        <View
21
+          style={{
22
+            width: '100%',
23
+            height: 100,
24
+            marginTop: 20,
25
+            marginBottom: 20,
26
+            justifyContent: 'center',
27
+            alignItems: 'center',
28
+          }}>
29
+          <Image
30
+            source={{uri: LOGO}}
31
+            style={{height: '100%', width: '100%', resizeMode: 'contain'}}
32
+          />
33
+        </View>
34
+        <Text>{i18n.t('screens.proxiwash.description')}</Text>
35
+        <Card style={{margin: 5}}>
36
+          <Card.Title
37
+            title={i18n.t('screens.proxiwash.dryer')}
38
+            left={({
39
+              size,
40
+              color,
41
+            }: {
42
+              size: number,
43
+              color: string,
44
+            }): React.Node => (
45
+              <List.Icon size={size} color={color} icon="tumble-dryer" />
46
+            )}
47
+          />
48
+          <Card.Content>
49
+            <Title>{i18n.t('screens.proxiwash.procedure')}</Title>
50
+            <Paragraph>{i18n.t('screens.proxiwash.dryerProcedure')}</Paragraph>
51
+            <Title>{i18n.t('screens.proxiwash.tips')}</Title>
52
+            <Paragraph>{i18n.t('screens.proxiwash.dryerTips')}</Paragraph>
53
+          </Card.Content>
54
+        </Card>
50 55
 
51
-                <Card style={{margin: 5}}>
52
-                    <Card.Title
53
-                        title={i18n.t('screens.proxiwash.washer')}
54
-                        left={props => <List.Icon {...props} icon={'washing-machine'}/>}
55
-                    />
56
-                    <Card.Content>
57
-                        <Title>{i18n.t('screens.proxiwash.procedure')}</Title>
58
-                        <Paragraph>{i18n.t('screens.proxiwash.washerProcedure')}</Paragraph>
59
-                        <Title>{i18n.t('screens.proxiwash.tips')}</Title>
60
-                        <Paragraph>{i18n.t('screens.proxiwash.washerTips')}</Paragraph>
61
-                    </Card.Content>
62
-                </Card>
56
+        <Card style={{margin: 5}}>
57
+          <Card.Title
58
+            title={i18n.t('screens.proxiwash.washer')}
59
+            left={({
60
+              size,
61
+              color,
62
+            }: {
63
+              size: number,
64
+              color: string,
65
+            }): React.Node => (
66
+              <List.Icon size={size} color={color} icon="washing-machine" />
67
+            )}
68
+          />
69
+          <Card.Content>
70
+            <Title>{i18n.t('screens.proxiwash.procedure')}</Title>
71
+            <Paragraph>{i18n.t('screens.proxiwash.washerProcedure')}</Paragraph>
72
+            <Title>{i18n.t('screens.proxiwash.tips')}</Title>
73
+            <Paragraph>{i18n.t('screens.proxiwash.washerTips')}</Paragraph>
74
+          </Card.Content>
75
+        </Card>
63 76
 
64
-                <Card style={{margin: 5}}>
65
-                    <Card.Title
66
-                        title={i18n.t('screens.proxiwash.tariffs')}
67
-                        left={props => <List.Icon {...props} icon={'circle-multiple'}/>}
68
-                    />
69
-                    <Card.Content>
70
-                        <Paragraph>{i18n.t('screens.proxiwash.washersTariff')}</Paragraph>
71
-                        <Paragraph>{i18n.t('screens.proxiwash.dryersTariff')}</Paragraph>
72
-                    </Card.Content>
73
-                </Card>
74
-                <Card style={{margin: 5, marginBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}>
75
-                    <Card.Title
76
-                        title={i18n.t('screens.proxiwash.paymentMethods')}
77
-                        left={props => <List.Icon {...props} icon={'cash'}/>}
78
-                    />
79
-                    <Card.Content>
80
-                        <Paragraph>{i18n.t('screens.proxiwash.paymentMethodsDescription')}</Paragraph>
81
-                    </Card.Content>
82
-                </Card>
83
-            </CollapsibleScrollView>
84
-        );
85
-    }
77
+        <Card style={{margin: 5}}>
78
+          <Card.Title
79
+            title={i18n.t('screens.proxiwash.tariffs')}
80
+            left={({
81
+              size,
82
+              color,
83
+            }: {
84
+              size: number,
85
+              color: string,
86
+            }): React.Node => (
87
+              <List.Icon size={size} color={color} icon="circle-multiple" />
88
+            )}
89
+          />
90
+          <Card.Content>
91
+            <Paragraph>{i18n.t('screens.proxiwash.washersTariff')}</Paragraph>
92
+            <Paragraph>{i18n.t('screens.proxiwash.dryersTariff')}</Paragraph>
93
+          </Card.Content>
94
+        </Card>
95
+        <Card
96
+          style={{margin: 5, marginBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}>
97
+          <Card.Title
98
+            title={i18n.t('screens.proxiwash.paymentMethods')}
99
+            left={({
100
+              size,
101
+              color,
102
+            }: {
103
+              size: number,
104
+              color: string,
105
+            }): React.Node => (
106
+              <List.Icon size={size} color={color} icon="cash" />
107
+            )}
108
+          />
109
+          <Card.Content>
110
+            <Paragraph>
111
+              {i18n.t('screens.proxiwash.paymentMethodsDescription')}
112
+            </Paragraph>
113
+          </Card.Content>
114
+        </Card>
115
+      </CollapsibleScrollView>
116
+    );
117
+  }
86 118
 }

+ 453
- 394
src/screens/Proxiwash/ProxiwashScreen.js View File

@@ -2,421 +2,480 @@
2 2
 
3 3
 import * as React from 'react';
4 4
 import {Alert, View} from 'react-native';
5
-import i18n from "i18n-js";
6
-import WebSectionList from "../../components/Screens/WebSectionList";
7
-import * as Notifications from "../../utils/Notifications";
8
-import AsyncStorageManager from "../../managers/AsyncStorageManager";
5
+import i18n from 'i18n-js';
9 6
 import {Avatar, Button, Card, Text, withTheme} from 'react-native-paper';
10
-import ProxiwashListItem from "../../components/Lists/Proxiwash/ProxiwashListItem";
11
-import ProxiwashConstants from "../../constants/ProxiwashConstants";
12
-import CustomModal from "../../components/Overrides/CustomModal";
13
-import AprilFoolsManager from "../../managers/AprilFoolsManager";
14
-import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton";
15
-import ProxiwashSectionHeader from "../../components/Lists/Proxiwash/ProxiwashSectionHeader";
16
-import type {CustomTheme} from "../../managers/ThemeManager";
17
-import {StackNavigationProp} from "@react-navigation/stack";
18
-import {getCleanedMachineWatched, getMachineEndDate, isMachineWatched} from "../../utils/Proxiwash";
19
-import {Modalize} from "react-native-modalize";
20
-import {MASCOT_STYLE} from "../../components/Mascot/Mascot";
21
-import MascotPopup from "../../components/Mascot/MascotPopup";
22
-
23
-const DATA_URL = "https://etud.insa-toulouse.fr/~amicale_app/v2/washinsa/washinsa_data.json";
24
-
25
-let modalStateStrings = {};
7
+import {StackNavigationProp} from '@react-navigation/stack';
8
+import {Modalize} from 'react-native-modalize';
9
+import WebSectionList from '../../components/Screens/WebSectionList';
10
+import * as Notifications from '../../utils/Notifications';
11
+import AsyncStorageManager from '../../managers/AsyncStorageManager';
12
+import ProxiwashListItem from '../../components/Lists/Proxiwash/ProxiwashListItem';
13
+import ProxiwashConstants from '../../constants/ProxiwashConstants';
14
+import CustomModal from '../../components/Overrides/CustomModal';
15
+import AprilFoolsManager from '../../managers/AprilFoolsManager';
16
+import MaterialHeaderButtons, {
17
+  Item,
18
+} from '../../components/Overrides/CustomHeaderButton';
19
+import ProxiwashSectionHeader from '../../components/Lists/Proxiwash/ProxiwashSectionHeader';
20
+import type {CustomTheme} from '../../managers/ThemeManager';
21
+import {
22
+  getCleanedMachineWatched,
23
+  getMachineEndDate,
24
+  isMachineWatched,
25
+} from '../../utils/Proxiwash';
26
+import {MASCOT_STYLE} from '../../components/Mascot/Mascot';
27
+import MascotPopup from '../../components/Mascot/MascotPopup';
28
+import type {SectionListDataType} from '../../components/Screens/WebSectionList';
29
+
30
+const DATA_URL =
31
+  'https://etud.insa-toulouse.fr/~amicale_app/v2/washinsa/washinsa_data.json';
32
+
33
+const modalStateStrings = {};
26 34
 
27 35
 const REFRESH_TIME = 1000 * 10; // Refresh every 10 seconds
28 36
 const LIST_ITEM_HEIGHT = 64;
29 37
 
30
-export type Machine = {
31
-    number: string,
32
-    state: string,
33
-    startTime: string,
34
-    endTime: string,
35
-    donePercent: string,
36
-    remainingTime: string,
37
-    program: string,
38
-}
39
-
40
-type Props = {
41
-    navigation: StackNavigationProp,
42
-    theme: CustomTheme,
43
-}
38
+export type ProxiwashMachineType = {
39
+  number: string,
40
+  state: string,
41
+  startTime: string,
42
+  endTime: string,
43
+  donePercent: string,
44
+  remainingTime: string,
45
+  program: string,
46
+};
44 47
 
45
-type State = {
46
-    refreshing: boolean,
47
-    modalCurrentDisplayItem: React.Node,
48
-    machinesWatched: Array<Machine>,
48
+type PropsType = {
49
+  navigation: StackNavigationProp,
50
+  theme: CustomTheme,
49 51
 };
50 52
 
53
+type StateType = {
54
+  modalCurrentDisplayItem: React.Node,
55
+  machinesWatched: Array<ProxiwashMachineType>,
56
+};
51 57
 
52 58
 /**
53 59
  * Class defining the app's proxiwash screen. This screen shows information about washing machines and
54 60
  * dryers, taken from a scrapper reading proxiwash website
55 61
  */
56
-class ProxiwashScreen extends React.Component<Props, State> {
57
-
58
-    modalRef: null | Modalize;
59
-
60
-    fetchedData: {
61
-        dryers: Array<Machine>,
62
-        washers: Array<Machine>,
62
+class ProxiwashScreen extends React.Component<PropsType, StateType> {
63
+  /**
64
+   * Shows a warning telling the user notifications are disabled for the app
65
+   */
66
+  static showNotificationsDisabledWarning() {
67
+    Alert.alert(
68
+      i18n.t('screens.proxiwash.modal.notificationErrorTitle'),
69
+      i18n.t('screens.proxiwash.modal.notificationErrorDescription'),
70
+    );
71
+  }
72
+
73
+  modalRef: null | Modalize;
74
+
75
+  fetchedData: {
76
+    dryers: Array<ProxiwashMachineType>,
77
+    washers: Array<ProxiwashMachineType>,
78
+  };
79
+
80
+  /**
81
+   * Creates machine state parameters using current theme and translations
82
+   */
83
+  constructor() {
84
+    super();
85
+    this.state = {
86
+      modalCurrentDisplayItem: null,
87
+      machinesWatched: AsyncStorageManager.getObject(
88
+        AsyncStorageManager.PREFERENCES.proxiwashWatchedMachines.key,
89
+      ),
63 90
     };
64
-
65
-    state = {
66
-        refreshing: false,
67
-        modalCurrentDisplayItem: null,
68
-        machinesWatched: AsyncStorageManager.getObject(AsyncStorageManager.PREFERENCES.proxiwashWatchedMachines.key),
91
+    modalStateStrings[ProxiwashConstants.machineStates.AVAILABLE] = i18n.t(
92
+      'screens.proxiwash.modal.ready',
93
+    );
94
+    modalStateStrings[ProxiwashConstants.machineStates.RUNNING] = i18n.t(
95
+      'screens.proxiwash.modal.running',
96
+    );
97
+    modalStateStrings[
98
+      ProxiwashConstants.machineStates.RUNNING_NOT_STARTED
99
+    ] = i18n.t('screens.proxiwash.modal.runningNotStarted');
100
+    modalStateStrings[ProxiwashConstants.machineStates.FINISHED] = i18n.t(
101
+      'screens.proxiwash.modal.finished',
102
+    );
103
+    modalStateStrings[ProxiwashConstants.machineStates.UNAVAILABLE] = i18n.t(
104
+      'screens.proxiwash.modal.broken',
105
+    );
106
+    modalStateStrings[ProxiwashConstants.machineStates.ERROR] = i18n.t(
107
+      'screens.proxiwash.modal.error',
108
+    );
109
+    modalStateStrings[ProxiwashConstants.machineStates.UNKNOWN] = i18n.t(
110
+      'screens.proxiwash.modal.unknown',
111
+    );
112
+  }
113
+
114
+  /**
115
+   * Setup notification channel for android and add listeners to detect notifications fired
116
+   */
117
+  componentDidMount() {
118
+    const {navigation} = this.props;
119
+    navigation.setOptions({
120
+      headerRight: (): React.Node => (
121
+        <MaterialHeaderButtons>
122
+          <Item
123
+            title="information"
124
+            iconName="information"
125
+            onPress={this.onAboutPress}
126
+          />
127
+        </MaterialHeaderButtons>
128
+      ),
129
+    });
130
+  }
131
+
132
+  /**
133
+   * Callback used when pressing the about button.
134
+   * This will open the ProxiwashAboutScreen.
135
+   */
136
+  onAboutPress = () => {
137
+    const {navigation} = this.props;
138
+    navigation.navigate('proxiwash-about');
139
+  };
140
+
141
+  /**
142
+   * Callback used when the user clicks on enable notifications for a machine
143
+   *
144
+   * @param machine The machine to set notifications for
145
+   */
146
+  onSetupNotificationsPress(machine: ProxiwashMachineType) {
147
+    if (this.modalRef) {
148
+      this.modalRef.close();
149
+    }
150
+    this.setupNotifications(machine);
151
+  }
152
+
153
+  /**
154
+   * Callback used when receiving modal ref
155
+   *
156
+   * @param ref
157
+   */
158
+  onModalRef = (ref: Modalize) => {
159
+    this.modalRef = ref;
160
+  };
161
+
162
+  /**
163
+   * Generates the modal content.
164
+   * This shows information for the given machine.
165
+   *
166
+   * @param title The title to use
167
+   * @param item The item to display information for in the modal
168
+   * @param isDryer True if the given item is a dryer
169
+   * @return {*}
170
+   */
171
+  getModalContent(
172
+    title: string,
173
+    item: ProxiwashMachineType,
174
+    isDryer: boolean,
175
+  ): React.Node {
176
+    const {props, state} = this;
177
+    let button = {
178
+      text: i18n.t('screens.proxiwash.modal.ok'),
179
+      icon: '',
180
+      onPress: undefined,
69 181
     };
70
-
71
-    /**
72
-     * Creates machine state parameters using current theme and translations
73
-     */
74
-    constructor(props) {
75
-        super(props);
76
-        modalStateStrings[ProxiwashConstants.machineStates.AVAILABLE] = i18n.t('screens.proxiwash.modal.ready');
77
-        modalStateStrings[ProxiwashConstants.machineStates.RUNNING] = i18n.t('screens.proxiwash.modal.running');
78
-        modalStateStrings[ProxiwashConstants.machineStates.RUNNING_NOT_STARTED] = i18n.t('screens.proxiwash.modal.runningNotStarted');
79
-        modalStateStrings[ProxiwashConstants.machineStates.FINISHED] = i18n.t('screens.proxiwash.modal.finished');
80
-        modalStateStrings[ProxiwashConstants.machineStates.UNAVAILABLE] = i18n.t('screens.proxiwash.modal.broken');
81
-        modalStateStrings[ProxiwashConstants.machineStates.ERROR] = i18n.t('screens.proxiwash.modal.error');
82
-        modalStateStrings[ProxiwashConstants.machineStates.UNKNOWN] = i18n.t('screens.proxiwash.modal.unknown');
182
+    let message = modalStateStrings[item.state];
183
+    const onPress = this.onSetupNotificationsPress.bind(this, item);
184
+    if (item.state === ProxiwashConstants.machineStates.RUNNING) {
185
+      let remainingTime = parseInt(item.remainingTime, 10);
186
+      if (remainingTime < 0) remainingTime = 0;
187
+
188
+      button = {
189
+        text: isMachineWatched(item, state.machinesWatched)
190
+          ? i18n.t('screens.proxiwash.modal.disableNotifications')
191
+          : i18n.t('screens.proxiwash.modal.enableNotifications'),
192
+        icon: '',
193
+        onPress,
194
+      };
195
+      message = i18n.t('screens.proxiwash.modal.running', {
196
+        start: item.startTime,
197
+        end: item.endTime,
198
+        remaining: remainingTime,
199
+        program: item.program,
200
+      });
201
+    } else if (item.state === ProxiwashConstants.machineStates.AVAILABLE) {
202
+      if (isDryer) message += `\n${i18n.t('screens.proxiwash.dryersTariff')}`;
203
+      else message += `\n${i18n.t('screens.proxiwash.washersTariff')}`;
83 204
     }
84
-
85
-    /**
86
-     * Setup notification channel for android and add listeners to detect notifications fired
87
-     */
88
-    componentDidMount() {
89
-        this.props.navigation.setOptions({
90
-            headerRight: () =>
91
-                <MaterialHeaderButtons>
92
-                    <Item title="information" iconName="information" onPress={this.onAboutPress}/>
93
-                </MaterialHeaderButtons>,
205
+    return (
206
+      <View
207
+        style={{
208
+          flex: 1,
209
+          padding: 20,
210
+        }}>
211
+        <Card.Title
212
+          title={title}
213
+          left={(): React.Node => (
214
+            <Avatar.Icon
215
+              icon={isDryer ? 'tumble-dryer' : 'washing-machine'}
216
+              color={props.theme.colors.text}
217
+              style={{backgroundColor: 'transparent'}}
218
+            />
219
+          )}
220
+        />
221
+        <Card.Content>
222
+          <Text>{message}</Text>
223
+        </Card.Content>
224
+
225
+        {button.onPress !== undefined ? (
226
+          <Card.Actions>
227
+            <Button
228
+              icon={button.icon}
229
+              mode="contained"
230
+              onPress={button.onPress}
231
+              style={{marginLeft: 'auto', marginRight: 'auto'}}>
232
+              {button.text}
233
+            </Button>
234
+          </Card.Actions>
235
+        ) : null}
236
+      </View>
237
+    );
238
+  }
239
+
240
+  /**
241
+   * Gets the section render item
242
+   *
243
+   * @param section The section to render
244
+   * @return {*}
245
+   */
246
+  getRenderSectionHeader = ({
247
+    section,
248
+  }: {
249
+    section: {title: string},
250
+  }): React.Node => {
251
+    const isDryer = section.title === i18n.t('screens.proxiwash.dryers');
252
+    const nbAvailable = this.getMachineAvailableNumber(isDryer);
253
+    return (
254
+      <ProxiwashSectionHeader
255
+        title={section.title}
256
+        nbAvailable={nbAvailable}
257
+        isDryer={isDryer}
258
+      />
259
+    );
260
+  };
261
+
262
+  /**
263
+   * Gets the list item to be rendered
264
+   *
265
+   * @param item The object containing the item's FetchedData
266
+   * @param section The object describing the current SectionList section
267
+   * @returns {React.Node}
268
+   */
269
+  getRenderItem = ({
270
+    item,
271
+    section,
272
+  }: {
273
+    item: ProxiwashMachineType,
274
+    section: {title: string},
275
+  }): React.Node => {
276
+    const {machinesWatched} = this.state;
277
+    const isDryer = section.title === i18n.t('screens.proxiwash.dryers');
278
+    return (
279
+      <ProxiwashListItem
280
+        item={item}
281
+        onPress={this.showModal}
282
+        isWatched={isMachineWatched(item, machinesWatched)}
283
+        isDryer={isDryer}
284
+        height={LIST_ITEM_HEIGHT}
285
+      />
286
+    );
287
+  };
288
+
289
+  /**
290
+   * Extracts the key for the given item
291
+   *
292
+   * @param item The item to extract the key from
293
+   * @return {*} The extracted key
294
+   */
295
+  getKeyExtractor = (item: ProxiwashMachineType): string => item.number;
296
+
297
+  /**
298
+   * Setups notifications for the machine with the given ID.
299
+   * One notification will be sent at the end of the program.
300
+   * Another will be send a few minutes before the end, based on the value of reminderNotifTime
301
+   *
302
+   * @param machine The machine to watch
303
+   */
304
+  setupNotifications(machine: ProxiwashMachineType) {
305
+    const {machinesWatched} = this.state;
306
+    if (!isMachineWatched(machine, machinesWatched)) {
307
+      Notifications.setupMachineNotification(
308
+        machine.number,
309
+        true,
310
+        getMachineEndDate(machine),
311
+      )
312
+        .then(() => {
313
+          this.saveNotificationToState(machine);
314
+        })
315
+        .catch(() => {
316
+          ProxiwashScreen.showNotificationsDisabledWarning();
94 317
         });
318
+    } else {
319
+      Notifications.setupMachineNotification(machine.number, false, null).then(
320
+        () => {
321
+          this.removeNotificationFromState(machine);
322
+        },
323
+      );
95 324
     }
96
-
97
-    /**
98
-     * Callback used when pressing the about button.
99
-     * This will open the ProxiwashAboutScreen.
100
-     */
101
-    onAboutPress = () => this.props.navigation.navigate('proxiwash-about');
102
-
103
-    /**
104
-     * Extracts the key for the given item
105
-     *
106
-     * @param item The item to extract the key from
107
-     * @return {*} The extracted key
108
-     */
109
-    getKeyExtractor = (item: Machine) => item.number;
110
-
111
-    /**
112
-     * Setups notifications for the machine with the given ID.
113
-     * One notification will be sent at the end of the program.
114
-     * Another will be send a few minutes before the end, based on the value of reminderNotifTime
115
-     *
116
-     * @param machine The machine to watch
117
-     */
118
-    setupNotifications(machine: Machine) {
119
-        if (!isMachineWatched(machine, this.state.machinesWatched)) {
120
-            Notifications.setupMachineNotification(machine.number, true, getMachineEndDate(machine))
121
-                .then(() => {
122
-                    this.saveNotificationToState(machine);
123
-                })
124
-                .catch(() => {
125
-                    this.showNotificationsDisabledWarning();
126
-                });
127
-        } else {
128
-            Notifications.setupMachineNotification(machine.number, false, null)
129
-                .then(() => {
130
-                    this.removeNotificationFromState(machine);
131
-                });
132
-        }
133
-    }
134
-
135
-    /**
136
-     * Shows a warning telling the user notifications are disabled for the app
137
-     */
138
-    showNotificationsDisabledWarning() {
139
-        Alert.alert(
140
-            i18n.t("screens.proxiwash.modal.notificationErrorTitle"),
141
-            i18n.t("screens.proxiwash.modal.notificationErrorDescription"),
142
-        );
143
-    }
144
-
145
-    /**
146
-     * Adds the given notifications associated to a machine ID to the watchlist, and saves the array to the preferences
147
-     *
148
-     * @param machine
149
-     */
150
-    saveNotificationToState(machine: Machine) {
151
-        let data = this.state.machinesWatched;
152
-        data.push(machine);
153
-        this.saveNewWatchedList(data);
325
+  }
326
+
327
+  /**
328
+   * Gets the number of machines available
329
+   *
330
+   * @param isDryer True if we are only checking for dryer, false for washers
331
+   * @return {number} The number of machines available
332
+   */
333
+  getMachineAvailableNumber(isDryer: boolean): number {
334
+    let data;
335
+    if (isDryer) data = this.fetchedData.dryers;
336
+    else data = this.fetchedData.washers;
337
+    let count = 0;
338
+    data.forEach((machine: ProxiwashMachineType) => {
339
+      if (machine.state === ProxiwashConstants.machineStates.AVAILABLE)
340
+        count += 1;
341
+    });
342
+    return count;
343
+  }
344
+
345
+  /**
346
+   * Creates the dataset to be used by the FlatList
347
+   *
348
+   * @param fetchedData
349
+   * @return {*}
350
+   */
351
+  createDataset = (fetchedData: {
352
+    dryers: Array<ProxiwashMachineType>,
353
+    washers: Array<ProxiwashMachineType>,
354
+  }): SectionListDataType<ProxiwashMachineType> => {
355
+    const {state} = this;
356
+    let data = fetchedData;
357
+    if (AprilFoolsManager.getInstance().isAprilFoolsEnabled()) {
358
+      data = JSON.parse(JSON.stringify(fetchedData)); // Deep copy
359
+      AprilFoolsManager.getNewProxiwashDryerOrderedList(data.dryers);
360
+      AprilFoolsManager.getNewProxiwashWasherOrderedList(data.washers);
154 361
     }
155
-
156
-    /**
157
-     * Removes the given index from the watchlist array and saves it to preferences
158
-     *
159
-     * @param machine
160
-     */
161
-    removeNotificationFromState(machine: Machine) {
162
-        let data = this.state.machinesWatched;
163
-        for (let i = 0; i < data.length; i++) {
164
-            if (data[i].number === machine.number && data[i].endTime === machine.endTime) {
165
-                data.splice(i, 1);
166
-                break;
167
-            }
168
-        }
169
-        this.saveNewWatchedList(data);
170
-    }
171
-
172
-    saveNewWatchedList(list: Array<Machine>) {
173
-        this.setState({machinesWatched: list});
174
-        AsyncStorageManager.set(AsyncStorageManager.PREFERENCES.proxiwashWatchedMachines.key, list);
362
+    this.fetchedData = data;
363
+    this.state.machinesWatched = getCleanedMachineWatched(
364
+      state.machinesWatched,
365
+      [...data.dryers, ...data.washers],
366
+    );
367
+    return [
368
+      {
369
+        title: i18n.t('screens.proxiwash.dryers'),
370
+        icon: 'tumble-dryer',
371
+        data: data.dryers === undefined ? [] : data.dryers,
372
+        keyExtractor: this.getKeyExtractor,
373
+      },
374
+      {
375
+        title: i18n.t('screens.proxiwash.washers'),
376
+        icon: 'washing-machine',
377
+        data: data.washers === undefined ? [] : data.washers,
378
+        keyExtractor: this.getKeyExtractor,
379
+      },
380
+    ];
381
+  };
382
+
383
+  /**
384
+   * Shows a modal for the given item
385
+   *
386
+   * @param title The title to use
387
+   * @param item The item to display information for in the modal
388
+   * @param isDryer True if the given item is a dryer
389
+   */
390
+  showModal = (title: string, item: ProxiwashMachineType, isDryer: boolean) => {
391
+    this.setState({
392
+      modalCurrentDisplayItem: this.getModalContent(title, item, isDryer),
393
+    });
394
+    if (this.modalRef) {
395
+      this.modalRef.open();
175 396
     }
176
-
177
-    /**
178
-     * Creates the dataset to be used by the flatlist
179
-     *
180
-     * @param fetchedData
181
-     * @return {*}
182
-     */
183
-    createDataset = (fetchedData: Object) => {
184
-        let data = fetchedData;
185
-        if (AprilFoolsManager.getInstance().isAprilFoolsEnabled()) {
186
-            data = JSON.parse(JSON.stringify(fetchedData)); // Deep copy
187
-            AprilFoolsManager.getNewProxiwashDryerOrderedList(data.dryers);
188
-            AprilFoolsManager.getNewProxiwashWasherOrderedList(data.washers);
189
-        }
190
-        this.fetchedData = data;
191
-        this.state.machinesWatched =
192
-            getCleanedMachineWatched(this.state.machinesWatched, [...data.dryers, ...data.washers]);
193
-        return [
194
-            {
195
-                title: i18n.t('screens.proxiwash.dryers'),
196
-                icon: 'tumble-dryer',
197
-                data: data.dryers === undefined ? [] : data.dryers,
198
-                keyExtractor: this.getKeyExtractor
199
-            },
200
-            {
201
-                title: i18n.t('screens.proxiwash.washers'),
202
-                icon: 'washing-machine',
203
-                data: data.washers === undefined ? [] : data.washers,
204
-                keyExtractor: this.getKeyExtractor
397
+  };
398
+
399
+  /**
400
+   * Adds the given notifications associated to a machine ID to the watchlist, and saves the array to the preferences
401
+   *
402
+   * @param machine
403
+   */
404
+  saveNotificationToState(machine: ProxiwashMachineType) {
405
+    const {machinesWatched} = this.state;
406
+    const data = machinesWatched;
407
+    data.push(machine);
408
+    this.saveNewWatchedList(data);
409
+  }
410
+
411
+  /**
412
+   * Removes the given index from the watchlist array and saves it to preferences
413
+   *
414
+   * @param selectedMachine
415
+   */
416
+  removeNotificationFromState(selectedMachine: ProxiwashMachineType) {
417
+    const {machinesWatched} = this.state;
418
+    const newList = [...machinesWatched];
419
+    machinesWatched.forEach((machine: ProxiwashMachineType, index: number) => {
420
+      if (
421
+        machine.number === selectedMachine.number &&
422
+        machine.endTime === selectedMachine.endTime
423
+      )
424
+        newList.splice(index, 1);
425
+    });
426
+    this.saveNewWatchedList(newList);
427
+  }
428
+
429
+  saveNewWatchedList(list: Array<ProxiwashMachineType>) {
430
+    this.setState({machinesWatched: list});
431
+    AsyncStorageManager.set(
432
+      AsyncStorageManager.PREFERENCES.proxiwashWatchedMachines.key,
433
+      list,
434
+    );
435
+  }
436
+
437
+  render(): React.Node {
438
+    const {state} = this;
439
+    const {navigation} = this.props;
440
+    return (
441
+      <View style={{flex: 1}}>
442
+        <View
443
+          style={{
444
+            position: 'absolute',
445
+            width: '100%',
446
+            height: '100%',
447
+          }}>
448
+          <WebSectionList
449
+            createDataset={this.createDataset}
450
+            navigation={navigation}
451
+            fetchUrl={DATA_URL}
452
+            renderItem={this.getRenderItem}
453
+            renderSectionHeader={this.getRenderSectionHeader}
454
+            autoRefreshTime={REFRESH_TIME}
455
+            refreshOnFocus
456
+            updateData={state.machinesWatched.length}
457
+          />
458
+        </View>
459
+        <MascotPopup
460
+          prefKey={AsyncStorageManager.PREFERENCES.proxiwashShowBanner.key}
461
+          title={i18n.t('screens.proxiwash.mascotDialog.title')}
462
+          message={i18n.t('screens.proxiwash.mascotDialog.message')}
463
+          icon="information"
464
+          buttons={{
465
+            action: null,
466
+            cancel: {
467
+              message: i18n.t('screens.proxiwash.mascotDialog.ok'),
468
+              icon: 'check',
205 469
             },
206
-        ];
207
-    };
208
-
209
-    /**
210
-     * Shows a modal for the given item
211
-     *
212
-     * @param title The title to use
213
-     * @param item The item to display information for in the modal
214
-     * @param isDryer True if the given item is a dryer
215
-     */
216
-    showModal = (title: string, item: Object, isDryer: boolean) => {
217
-        this.setState({
218
-            modalCurrentDisplayItem: this.getModalContent(title, item, isDryer)
219
-        });
220
-        if (this.modalRef) {
221
-            this.modalRef.open();
222
-        }
223
-    };
224
-
225
-    /**
226
-     * Callback used when the user clicks on enable notifications for a machine
227
-     *
228
-     * @param machine The machine to set notifications for
229
-     */
230
-    onSetupNotificationsPress(machine: Machine) {
231
-        if (this.modalRef) {
232
-            this.modalRef.close();
233
-        }
234
-        this.setupNotifications(machine);
235
-    }
236
-
237
-    /**
238
-     * Generates the modal content.
239
-     * This shows information for the given machine.
240
-     *
241
-     * @param title The title to use
242
-     * @param item The item to display information for in the modal
243
-     * @param isDryer True if the given item is a dryer
244
-     * @return {*}
245
-     */
246
-    getModalContent(title: string, item: Machine, isDryer: boolean) {
247
-        let button = {
248
-            text: i18n.t("screens.proxiwash.modal.ok"),
249
-            icon: '',
250
-            onPress: undefined
251
-        };
252
-        let message = modalStateStrings[item.state];
253
-        const onPress = this.onSetupNotificationsPress.bind(this, item);
254
-        if (item.state === ProxiwashConstants.machineStates.RUNNING) {
255
-            let remainingTime = parseInt(item.remainingTime)
256
-            if (remainingTime < 0)
257
-                remainingTime = 0;
258
-
259
-            button =
260
-                {
261
-                    text: isMachineWatched(item, this.state.machinesWatched) ?
262
-                        i18n.t("screens.proxiwash.modal.disableNotifications") :
263
-                        i18n.t("screens.proxiwash.modal.enableNotifications"),
264
-                    icon: '',
265
-                    onPress: onPress
266
-                }
267
-            ;
268
-            message = i18n.t('screens.proxiwash.modal.running',
269
-                {
270
-                    start: item.startTime,
271
-                    end: item.endTime,
272
-                    remaining: remainingTime,
273
-                    program: item.program
274
-                });
275
-        } else if (item.state === ProxiwashConstants.machineStates.AVAILABLE) {
276
-            if (isDryer)
277
-                message += '\n' + i18n.t('screens.proxiwash.dryersTariff');
278
-            else
279
-                message += '\n' + i18n.t('screens.proxiwash.washersTariff');
280
-        }
281
-        return (
282
-            <View style={{
283
-                flex: 1,
284
-                padding: 20
285
-            }}>
286
-                <Card.Title
287
-                    title={title}
288
-                    left={() => <Avatar.Icon
289
-                        icon={isDryer ? 'tumble-dryer' : 'washing-machine'}
290
-                        color={this.props.theme.colors.text}
291
-                        style={{backgroundColor: 'transparent'}}/>}
292
-
293
-                />
294
-                <Card.Content>
295
-                    <Text>{message}</Text>
296
-                </Card.Content>
297
-
298
-                {button.onPress !== undefined ?
299
-                    <Card.Actions>
300
-                        <Button
301
-                            icon={button.icon}
302
-                            mode="contained"
303
-                            onPress={button.onPress}
304
-                            style={{marginLeft: 'auto', marginRight: 'auto'}}
305
-                        >
306
-                            {button.text}
307
-                        </Button>
308
-                    </Card.Actions> : null}
309
-            </View>
310
-        );
311
-    }
312
-
313
-    /**
314
-     * Callback used when receiving modal ref
315
-     *
316
-     * @param ref
317
-     */
318
-    onModalRef = (ref: Object) => {
319
-        this.modalRef = ref;
320
-    };
321
-
322
-    /**
323
-     * Gets the number of machines available
324
-     *
325
-     * @param isDryer True if we are only checking for dryer, false for washers
326
-     * @return {number} The number of machines available
327
-     */
328
-    getMachineAvailableNumber(isDryer: boolean) {
329
-        let data;
330
-        if (isDryer)
331
-            data = this.fetchedData.dryers;
332
-        else
333
-            data = this.fetchedData.washers;
334
-        let count = 0;
335
-        for (let i = 0; i < data.length; i++) {
336
-            if (data[i].state === ProxiwashConstants.machineStates.AVAILABLE)
337
-                count += 1;
338
-        }
339
-        return count;
340
-    }
341
-
342
-    /**
343
-     * Gets the section render item
344
-     *
345
-     * @param section The section to render
346
-     * @return {*}
347
-     */
348
-    getRenderSectionHeader = ({section}: Object) => {
349
-        const isDryer = section.title === i18n.t('screens.proxiwash.dryers');
350
-        const nbAvailable = this.getMachineAvailableNumber(isDryer);
351
-        return (
352
-            <ProxiwashSectionHeader
353
-                title={section.title}
354
-                nbAvailable={nbAvailable}
355
-                isDryer={isDryer}/>
356
-        );
357
-    };
358
-
359
-    /**
360
-     * Gets the list item to be rendered
361
-     *
362
-     * @param item The object containing the item's FetchedData
363
-     * @param section The object describing the current SectionList section
364
-     * @returns {React.Node}
365
-     */
366
-    getRenderItem = ({item, section}: Object) => {
367
-        const isDryer = section.title === i18n.t('screens.proxiwash.dryers');
368
-        return (
369
-            <ProxiwashListItem
370
-                item={item}
371
-                onPress={this.showModal}
372
-                isWatched={isMachineWatched(item, this.state.machinesWatched)}
373
-                isDryer={isDryer}
374
-                height={LIST_ITEM_HEIGHT}
375
-            />
376
-        );
377
-    };
378
-
379
-    render() {
380
-        const nav = this.props.navigation;
381
-        return (
382
-            <View
383
-                style={{flex: 1}}
384
-            >
385
-                <View style={{
386
-                    position: "absolute",
387
-                    width: "100%",
388
-                    height: "100%",
389
-                }}>
390
-                    <WebSectionList
391
-                        createDataset={this.createDataset}
392
-                        navigation={nav}
393
-                        fetchUrl={DATA_URL}
394
-                        renderItem={this.getRenderItem}
395
-                        renderSectionHeader={this.getRenderSectionHeader}
396
-                        autoRefreshTime={REFRESH_TIME}
397
-                        refreshOnFocus={true}
398
-                        updateData={this.state.machinesWatched.length}/>
399
-                </View>
400
-                <MascotPopup
401
-                    prefKey={AsyncStorageManager.PREFERENCES.proxiwashShowBanner.key}
402
-                    title={i18n.t("screens.proxiwash.mascotDialog.title")}
403
-                    message={i18n.t("screens.proxiwash.mascotDialog.message")}
404
-                    icon={"information"}
405
-                    buttons={{
406
-                        action: null,
407
-                        cancel: {
408
-                            message: i18n.t("screens.proxiwash.mascotDialog.ok"),
409
-                            icon: "check",
410
-                        }
411
-                    }}
412
-                    emotion={MASCOT_STYLE.NORMAL}
413
-                />
414
-                <CustomModal onRef={this.onModalRef}>
415
-                    {this.state.modalCurrentDisplayItem}
416
-                </CustomModal>
417
-            </View>
418
-        );
419
-    }
470
+          }}
471
+          emotion={MASCOT_STYLE.NORMAL}
472
+        />
473
+        <CustomModal onRef={this.onModalRef}>
474
+          {state.modalCurrentDisplayItem}
475
+        </CustomModal>
476
+      </View>
477
+    );
478
+  }
420 479
 }
421 480
 
422 481
 export default withTheme(ProxiwashScreen);

+ 55
- 46
src/utils/Proxiwash.js View File

@@ -1,6 +1,6 @@
1 1
 // @flow
2 2
 
3
-import type {Machine} from "../screens/Proxiwash/ProxiwashScreen";
3
+import type {ProxiwashMachineType} from '../screens/Proxiwash/ProxiwashScreen';
4 4
 
5 5
 /**
6 6
  * Gets the machine end Date object.
@@ -11,42 +11,43 @@ import type {Machine} from "../screens/Proxiwash/ProxiwashScreen";
11 11
  * @param machine The machine to get the date from
12 12
  * @returns {Date} The date object representing the end time.
13 13
  */
14
-export function getMachineEndDate(machine: Machine): Date | null {
15
-    const array = machine.endTime.split(":");
16
-    let endDate = new Date(Date.now());
17
-    endDate.setHours(parseInt(array[0]), parseInt(array[1]));
14
+export function getMachineEndDate(machine: ProxiwashMachineType): Date | null {
15
+  const array = machine.endTime.split(':');
16
+  let endDate = new Date(Date.now());
17
+  endDate.setHours(parseInt(array[0], 10), parseInt(array[1], 10));
18 18
 
19
-    let limit = new Date(Date.now());
20
-    if (endDate < limit) {
21
-        if (limit.getHours() > 12) {
22
-            limit.setHours(limit.getHours() - 12);
23
-            if (endDate < limit)
24
-                endDate.setDate(endDate.getDate() + 1);
25
-            else
26
-                endDate = null;
27
-        } else
28
-            endDate = null;
29
-    }
19
+  const limit = new Date(Date.now());
20
+  if (endDate < limit) {
21
+    if (limit.getHours() > 12) {
22
+      limit.setHours(limit.getHours() - 12);
23
+      if (endDate < limit) endDate.setDate(endDate.getDate() + 1);
24
+      else endDate = null;
25
+    } else endDate = null;
26
+  }
30 27
 
31
-    return endDate;
28
+  return endDate;
32 29
 }
33 30
 
34 31
 /**
35 32
  * Checks whether the machine of the given ID has scheduled notifications
36 33
  *
37 34
  * @param machine The machine to check
38
- * @param machineList The machine list
35
+ * @param machinesWatched The machine list
39 36
  * @returns {boolean}
40 37
  */
41
-export function isMachineWatched(machine: Machine, machineList: Array<Machine>) {
42
-    let watched = false;
43
-    for (let i = 0; i < machineList.length; i++) {
44
-        if (machineList[i].number === machine.number && machineList[i].endTime === machine.endTime) {
45
-            watched = true;
46
-            break;
47
-        }
48
-    }
49
-    return watched;
38
+export function isMachineWatched(
39
+  machine: ProxiwashMachineType,
40
+  machinesWatched: Array<ProxiwashMachineType>,
41
+): boolean {
42
+  let watched = false;
43
+  machinesWatched.forEach((watchedMachine: ProxiwashMachineType) => {
44
+    if (
45
+      watchedMachine.number === machine.number &&
46
+      watchedMachine.endTime === machine.endTime
47
+    )
48
+      watched = true;
49
+  });
50
+  return watched;
50 51
 }
51 52
 
52 53
 /**
@@ -54,14 +55,17 @@ export function isMachineWatched(machine: Machine, machineList: Array<Machine>)
54 55
  *
55 56
  * @param id The machine's ID
56 57
  * @param allMachines The machine list
57
- * @returns {null|Machine} The machine or null if not found
58
+ * @returns {null|ProxiwashMachineType} The machine or null if not found
58 59
  */
59
-export function getMachineOfId(id: string, allMachines: Array<Machine>) {
60
-    for (let i = 0; i < allMachines.length; i++) {
61
-        if (allMachines[i].number === id)
62
-            return allMachines[i];
63
-    }
64
-    return null;
60
+export function getMachineOfId(
61
+  id: string,
62
+  allMachines: Array<ProxiwashMachineType>,
63
+): ProxiwashMachineType | null {
64
+  let machineFound = null;
65
+  allMachines.forEach((machine: ProxiwashMachineType) => {
66
+    if (machine.number === id) machineFound = machine;
67
+  });
68
+  return machineFound;
65 69
 }
66 70
 
67 71
 /**
@@ -71,17 +75,22 @@ export function getMachineOfId(id: string, allMachines: Array<Machine>) {
71 75
  *
72 76
  * @param machineWatchedList The current machine watch list
73 77
  * @param allMachines The current full machine list
74
- * @returns {Array<Machine>}
78
+ * @returns {Array<ProxiwashMachineType>}
75 79
  */
76
-export function getCleanedMachineWatched(machineWatchedList: Array<Machine>, allMachines: Array<Machine>) {
77
-    let newList = [];
78
-    for (let i = 0; i < machineWatchedList.length; i++) {
79
-        let machine = getMachineOfId(machineWatchedList[i].number, allMachines);
80
-        if (machine !== null
81
-            && machineWatchedList[i].number === machine.number
82
-            && machineWatchedList[i].endTime === machine.endTime) {
83
-            newList.push(machine);
84
-        }
80
+export function getCleanedMachineWatched(
81
+  machineWatchedList: Array<ProxiwashMachineType>,
82
+  allMachines: Array<ProxiwashMachineType>,
83
+): Array<ProxiwashMachineType> {
84
+  const newList = [];
85
+  machineWatchedList.forEach((watchedMachine: ProxiwashMachineType) => {
86
+    const machine = getMachineOfId(watchedMachine.number, allMachines);
87
+    if (
88
+      machine != null &&
89
+      watchedMachine.number === machine.number &&
90
+      watchedMachine.endTime === machine.endTime
91
+    ) {
92
+      newList.push(machine);
85 93
     }
86
-    return newList;
87
-}
94
+  });
95
+  return newList;
96
+}

Loading…
Cancel
Save