Browse Source

Improved flow typing for the vote screen

Arnaud Vergnet 4 years ago
parent
commit
6dbce2cc3e

+ 5
- 4
src/components/Amicale/AuthenticatedScreen.js View File

@@ -5,15 +5,16 @@ import ConnectionManager from "../../managers/ConnectionManager";
5 5
 import {ERROR_TYPE} from "../../utils/WebData";
6 6
 import ErrorView from "../Screens/ErrorView";
7 7
 import BasicLoadingScreen from "../Screens/BasicLoadingScreen";
8
+import {StackNavigationProp} from "@react-navigation/stack";
8 9
 
9 10
 type Props = {
10
-    navigation: Object,
11
+    navigation: StackNavigationProp,
11 12
     requests: Array<{
12 13
         link: string,
13 14
         params: Object,
14 15
         mandatory: boolean
15 16
     }>,
16
-    renderFunction: Function,
17
+    renderFunction: (Array<{ [key: string]: any } | null>) => React.Node,
17 18
 }
18 19
 
19 20
 type State = {
@@ -29,7 +30,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
29 30
     currentUserToken: string | null;
30 31
     connectionManager: ConnectionManager;
31 32
     errors: Array<number>;
32
-    fetchedData: Array<Object>;
33
+    fetchedData: Array<{ [key: string]: any } | null>;
33 34
 
34 35
     constructor(props: Object) {
35 36
         super(props);
@@ -88,7 +89,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
88 89
      * @param index The index for the data
89 90
      * @param error The error code received
90 91
      */
91
-    onRequestFinished(data: Object | null, index: number, error: number) {
92
+    onRequestFinished(data: { [key: string]: any } | null, index: number, error: number) {
92 93
         if (index >= 0 && index < this.props.requests.length) {
93 94
             this.fetchedData[index] = data;
94 95
             this.errors[index] = error;

+ 4
- 3
src/components/Amicale/LogoutDialog.js View File

@@ -4,17 +4,18 @@ import * as React from 'react';
4 4
 import i18n from 'i18n-js';
5 5
 import LoadingConfirmDialog from "../Dialogs/LoadingConfirmDialog";
6 6
 import ConnectionManager from "../../managers/ConnectionManager";
7
+import {StackNavigationProp} from "@react-navigation/stack";
7 8
 
8 9
 type Props = {
9
-    navigation: Object,
10
+    navigation: StackNavigationProp,
10 11
     visible: boolean,
11
-    onDismiss: Function,
12
+    onDismiss: () => void,
12 13
 }
13 14
 
14 15
 class LogoutDialog extends React.PureComponent<Props> {
15 16
 
16 17
     onClickAccept = async () => {
17
-        return new Promise((resolve, reject) => {
18
+        return new Promise((resolve) => {
18 19
             ConnectionManager.getInstance().disconnect()
19 20
                 .then(() => {
20 21
                     this.props.navigation.reset({

+ 15
- 13
src/components/Amicale/Vote/VoteResults.js View File

@@ -1,24 +1,25 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import {Avatar, Card, List, ProgressBar, Subheading, withTheme} from "react-native-paper";
4
+import {Avatar, Card, List, ProgressBar, Subheading, Theme, withTheme} from "react-native-paper";
5 5
 import {FlatList, StyleSheet} from "react-native";
6 6
 import i18n from 'i18n-js';
7
+import type {team} from "../../../screens/Amicale/VoteScreen";
8
+
7 9
 
8 10
 type Props = {
9
-    teams: Array<Object>,
11
+    teams: Array<team>,
10 12
     dateEnd: string,
13
+    theme: Theme,
11 14
 }
12 15
 
13 16
 class VoteResults extends React.Component<Props> {
14 17
 
15 18
     totalVotes: number;
16 19
     winnerIds: Array<number>;
17
-    colors: Object;
18 20
 
19 21
     constructor(props) {
20 22
         super();
21
-        this.colors = props.theme.colors;
22 23
         props.teams.sort(this.sortByVotes);
23 24
         this.getTotalVotes(props.teams);
24 25
         this.getWinnerIds(props.teams);
@@ -28,16 +29,16 @@ class VoteResults extends React.Component<Props> {
28 29
         return false;
29 30
     }
30 31
 
31
-    sortByVotes = (a: Object, b: Object) => b.votes - a.votes;
32
+    sortByVotes = (a: team, b: team) => b.votes - a.votes;
32 33
 
33
-    getTotalVotes(teams: Array<Object>) {
34
+    getTotalVotes(teams: Array<team>) {
34 35
         this.totalVotes = 0;
35 36
         for (let i = 0; i < teams.length; i++) {
36 37
             this.totalVotes += teams[i].votes;
37 38
         }
38 39
     }
39 40
 
40
-    getWinnerIds(teams: Array<Object>){
41
+    getWinnerIds(teams: Array<team>){
41 42
         let max = teams[0].votes;
42 43
         this.winnerIds= [];
43 44
         for (let i = 0; i < teams.length; i++) {
@@ -48,11 +49,12 @@ class VoteResults extends React.Component<Props> {
48 49
         }
49 50
     }
50 51
 
51
-    voteKeyExtractor = (item: Object) => item.id.toString();
52
+    voteKeyExtractor = (item: team) => item.id.toString();
52 53
 
53
-    resultRenderItem = ({item}: Object) => {
54
+    resultRenderItem = ({item}: {item: team}) => {
54 55
         const isWinner = this.winnerIds.indexOf(item.id) !== -1;
55 56
         const isDraw = this.winnerIds.length > 1;
57
+        const colors = this.props.theme.colors;
56 58
         return (
57 59
             <Card style={{
58 60
                 marginTop: 10,
@@ -62,16 +64,16 @@ class VoteResults extends React.Component<Props> {
62 64
                     title={item.name}
63 65
                     description={item.votes + ' ' + i18n.t('voteScreen.results.votes')}
64 66
                     left={props => isWinner
65
-                        ? <List.Icon {...props} icon={isDraw ? "trophy-outline" : "trophy"} color={this.colors.primary}/>
67
+                        ? <List.Icon {...props} icon={isDraw ? "trophy-outline" : "trophy"} color={colors.primary}/>
66 68
                         : null}
67 69
                     titleStyle={{
68 70
                         color: isWinner
69
-                            ? this.colors.primary
70
-                            : this.colors.text
71
+                            ? colors.primary
72
+                            : colors.text
71 73
                     }}
72 74
                     style={{padding: 0}}
73 75
                 />
74
-                <ProgressBar progress={item.votes / this.totalVotes} color={this.colors.primary}/>
76
+                <ProgressBar progress={item.votes / this.totalVotes} color={colors.primary}/>
75 77
             </Card>
76 78
         );
77 79
     };

+ 12
- 10
src/components/Amicale/Vote/VoteSelect.js View File

@@ -7,11 +7,12 @@ import ConnectionManager from "../../../managers/ConnectionManager";
7 7
 import LoadingConfirmDialog from "../../Dialogs/LoadingConfirmDialog";
8 8
 import ErrorDialog from "../../Dialogs/ErrorDialog";
9 9
 import i18n from 'i18n-js';
10
+import type {team} from "../../../screens/Amicale/VoteScreen";
10 11
 
11 12
 type Props = {
12
-    teams: Array<Object>,
13
-    onVoteSuccess: Function,
14
-    onVoteError: Function,
13
+    teams: Array<team>,
14
+    onVoteSuccess: () => void,
15
+    onVoteError: () => void,
15 16
 }
16 17
 
17 18
 type State = {
@@ -33,16 +34,16 @@ export default class VoteSelect extends React.PureComponent<Props, State> {
33 34
 
34 35
     onVoteSelectionChange = (team: string) => this.setState({selectedTeam: team});
35 36
 
36
-    voteKeyExtractor = (item: Object) => item.id.toString();
37
+    voteKeyExtractor = (item: team) => item.id.toString();
37 38
 
38
-    voteRenderItem = ({item}: Object) => <RadioButton.Item label={item.name} value={item.id.toString()}/>;
39
+    voteRenderItem = ({item}: { item: team }) => <RadioButton.Item label={item.name} value={item.id.toString()}/>;
39 40
 
40 41
     showVoteDialog = () => this.setState({voteDialogVisible: true});
41 42
 
42 43
     onVoteDialogDismiss = () => this.setState({voteDialogVisible: false,});
43 44
 
44 45
     onVoteDialogAccept = async () => {
45
-        return new Promise((resolve, reject) => {
46
+        return new Promise((resolve) => {
46 47
             ConnectionManager.getInstance().authenticatedRequest(
47 48
                 "elections/vote",
48 49
                 {"team": parseInt(this.state.selectedTeam)})
@@ -76,10 +77,11 @@ export default class VoteSelect extends React.PureComponent<Props, State> {
76 77
                     <Card.Title
77 78
                         title={i18n.t('voteScreen.select.title')}
78 79
                         subtitle={i18n.t('voteScreen.select.subtitle')}
79
-                        left={(props) => <Avatar.Icon
80
-                            {...props}
81
-                            icon={"alert-decagram"}
82
-                        />}
80
+                        left={(props) =>
81
+                            <Avatar.Icon
82
+                                {...props}
83
+                                icon={"alert-decagram"}
84
+                            />}
83 85
                     />
84 86
                     <Card.Content>
85 87
                         <RadioButton.Group

+ 1
- 3
src/components/Amicale/Vote/VoteTitle.js View File

@@ -7,9 +7,7 @@ import i18n from 'i18n-js';
7 7
 
8 8
 const ICON_AMICALE = require('../../../../assets/amicale.png');
9 9
 
10
-type Props = {}
11
-
12
-export default class VoteTitle extends React.Component<Props> {
10
+export default class VoteTitle extends React.Component<{}> {
13 11
 
14 12
     shouldComponentUpdate() {
15 13
         return false;

+ 8
- 12
src/components/Amicale/Vote/VoteWait.js View File

@@ -1,7 +1,7 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import {ActivityIndicator, Card, Paragraph, withTheme} from "react-native-paper";
4
+import {ActivityIndicator, Card, Paragraph, Theme, withTheme} from "react-native-paper";
5 5
 import {StyleSheet} from "react-native";
6 6
 import i18n from 'i18n-js';
7 7
 
@@ -10,22 +10,18 @@ type Props = {
10 10
     justVoted: boolean,
11 11
     hasVoted: boolean,
12 12
     isVoteRunning: boolean,
13
+    theme: Theme,
13 14
 }
14 15
 
15 16
 class VoteWait extends React.Component<Props> {
16 17
 
17
-    colors: Object;
18
-
19
-    constructor(props) {
20
-        super(props);
21
-        this.colors = props.theme.colors;
22
-    }
23
-
24 18
     shouldComponentUpdate() {
25 19
         return false;
26 20
     }
27 21
 
28 22
     render() {
23
+        const colors = this.props.theme.colors;
24
+        const startDate = this.props.startDate;
29 25
         return (
30 26
             <Card style={styles.card}>
31 27
                 <Card.Title
@@ -38,22 +34,22 @@ class VoteWait extends React.Component<Props> {
38 34
                 <Card.Content>
39 35
                     {
40 36
                         this.props.justVoted
41
-                            ? <Paragraph style={{color: this.colors.success}}>
37
+                            ? <Paragraph style={{color: colors.success}}>
42 38
                                 {i18n.t('voteScreen.wait.messageSubmitted')}
43 39
                             </Paragraph>
44 40
                             : null
45 41
                     }
46 42
                     {
47 43
                         this.props.hasVoted
48
-                            ? <Paragraph style={{color: this.colors.success}}>
44
+                            ? <Paragraph style={{color: colors.success}}>
49 45
                                 {i18n.t('voteScreen.wait.messageVoted')}
50 46
                             </Paragraph>
51 47
                             : null
52 48
                     }
53 49
                     {
54
-                        this.props.startDate !== null
50
+                        startDate != null
55 51
                             ? <Paragraph>
56
-                                {i18n.t('voteScreen.wait.messageDate') + ' ' + this.props.startDate}
52
+                                {i18n.t('voteScreen.wait.messageDate') + ' ' + startDate}
57 53
                             </Paragraph>
58 54
                             : <Paragraph>{i18n.t('voteScreen.wait.messageDateUndefined')}</Paragraph>
59 55
                     }

+ 125
- 77
src/screens/Amicale/VoteScreen.js View File

@@ -10,53 +10,78 @@ import VoteSelect from "../../components/Amicale/Vote/VoteSelect";
10 10
 import VoteResults from "../../components/Amicale/Vote/VoteResults";
11 11
 import VoteWait from "../../components/Amicale/Vote/VoteWait";
12 12
 
13
-const FAKE_DATE = {
14
-    "date_begin": "2020-04-09 15:50",
15
-    "date_end": "2020-04-09 15:50",
16
-    "date_result_begin": "2020-04-09 15:50",
17
-    "date_result_end": "2020-04-09 22:50",
18
-};
13
+export type team = {
14
+    id: number,
15
+    name: string,
16
+    votes: number,
17
+}
19 18
 
20
-const FAKE_DATE2 = {
21
-    "date_begin": null,
22
-    "date_end": null,
23
-    "date_result_begin": null,
24
-    "date_result_end": null,
19
+type teamResponse = {
20
+    has_voted: boolean,
21
+    teams: Array<team>,
25 22
 };
26 23
 
27
-const FAKE_TEAMS = {
28
-    has_voted: false,
29
-    teams: [
30
-        {
31
-            id: 1,
32
-            name: "TEST TEAM1",
33
-        },
34
-        {
35
-            id: 2,
36
-            name: "TEST TEAM2",
37
-        },
38
-    ],
39
-};
40
-const FAKE_TEAMS2 = {
41
-    has_voted: false,
42
-    teams: [
43
-        {
44
-            id: 1,
45
-            name: "TEST TEAM1",
46
-            votes: 9,
47
-        },
48
-        {
49
-            id: 2,
50
-            name: "TEST TEAM2",
51
-            votes: 9,
52
-        },
53
-        {
54
-            id: 3,
55
-            name: "TEST TEAM3",
56
-            votes: 5,
57
-        },
58
-    ],
59
-};
24
+type stringVoteDates = {
25
+    date_begin: string,
26
+    date_end: string,
27
+    date_result_begin: string,
28
+    date_result_end: string,
29
+}
30
+
31
+type objectVoteDates = {
32
+    date_begin: Date,
33
+    date_end: Date,
34
+    date_result_begin: Date,
35
+    date_result_end: Date,
36
+}
37
+
38
+// const FAKE_DATE = {
39
+//     "date_begin": "2020-04-19 15:50",
40
+//     "date_end": "2020-04-19 15:50",
41
+//     "date_result_begin": "2020-04-19 19:50",
42
+//     "date_result_end": "2020-04-19 22:50",
43
+// };
44
+//
45
+// const FAKE_DATE2 = {
46
+//     "date_begin": null,
47
+//     "date_end": null,
48
+//     "date_result_begin": null,
49
+//     "date_result_end": null,
50
+// };
51
+//
52
+// const FAKE_TEAMS = {
53
+//     has_voted: false,
54
+//     teams: [
55
+//         {
56
+//             id: 1,
57
+//             name: "TEST TEAM1",
58
+//         },
59
+//         {
60
+//             id: 2,
61
+//             name: "TEST TEAM2",
62
+//         },
63
+//     ],
64
+// };
65
+// const FAKE_TEAMS2 = {
66
+//     has_voted: false,
67
+//     teams: [
68
+//         {
69
+//             id: 1,
70
+//             name: "TEST TEAM1",
71
+//             votes: 9,
72
+//         },
73
+//         {
74
+//             id: 2,
75
+//             name: "TEST TEAM2",
76
+//             votes: 9,
77
+//         },
78
+//         {
79
+//             id: 3,
80
+//             name: "TEST TEAM3",
81
+//             votes: 5,
82
+//         },
83
+//     ],
84
+// };
60 85
 
61 86
 const MIN_REFRESH_TIME = 5 * 1000;
62 87
 
@@ -74,17 +99,17 @@ export default class VoteScreen extends React.Component<Props, State> {
74 99
         hasVoted: false,
75 100
     };
76 101
 
77
-    teams: Array<Object>;
102
+    teams: Array<team>;
78 103
     hasVoted: boolean;
79
-    datesString: Object;
80
-    dates: Object;
104
+    datesString: null | stringVoteDates;
105
+    dates: null | objectVoteDates;
81 106
 
82 107
     today: Date;
83 108
 
84
-    mainFlatListData: Array<Object>;
109
+    mainFlatListData: Array<{ key: string }>;
85 110
     lastRefresh: Date;
86 111
 
87
-    authRef: Object;
112
+    authRef: { current: null | AuthenticatedScreen };
88 113
 
89 114
     constructor() {
90 115
         super();
@@ -103,69 +128,81 @@ export default class VoteScreen extends React.Component<Props, State> {
103 128
             canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) > MIN_REFRESH_TIME;
104 129
         else
105 130
             canRefresh = true;
106
-        if (canRefresh)
131
+        if (canRefresh && this.authRef.current != null)
107 132
             this.authRef.current.reload()
108 133
     };
109 134
 
110 135
     generateDateObject() {
111
-        this.dates = {
112
-            date_begin: stringToDate(this.datesString.date_begin),
113
-            date_end: stringToDate(this.datesString.date_end),
114
-            date_result_begin: stringToDate(this.datesString.date_result_begin),
115
-            date_result_end: stringToDate(this.datesString.date_result_end),
116
-        };
136
+        const strings = this.datesString;
137
+        if (strings != null) {
138
+            const dateBegin = stringToDate(strings.date_begin);
139
+            const dateEnd = stringToDate(strings.date_end);
140
+            const dateResultBegin = stringToDate(strings.date_result_begin);
141
+            const dateResultEnd = stringToDate(strings.date_result_end);
142
+            if (dateBegin != null && dateEnd != null && dateResultBegin != null && dateResultEnd != null) {
143
+                this.dates = {
144
+                    date_begin: dateBegin,
145
+                    date_end: dateEnd,
146
+                    date_result_begin: dateResultBegin,
147
+                    date_result_end: dateResultEnd,
148
+                };
149
+            } else
150
+                this.dates = null;
151
+        } else
152
+            this.dates = null;
117 153
     }
118 154
 
119 155
     getDateString(date: Date, dateString: string): string {
120 156
         if (this.today.getDate() === date.getDate()) {
121 157
             const str = getTimeOnlyString(dateString);
122
-            return str !== null ? str : "";
158
+            return str != null ? str : "";
123 159
         } else
124 160
             return dateString;
125 161
     }
126 162
 
127
-    isVoteAvailable() {
128
-        return this.dates.date_begin !== null;
129
-    }
130
-
131 163
     isVoteRunning() {
132
-        return this.today > this.dates.date_begin && this.today < this.dates.date_end;
164
+        return this.dates != null && this.today > this.dates.date_begin && this.today < this.dates.date_end;
133 165
     }
134 166
 
135 167
     isVoteStarted() {
136
-        return this.today > this.dates.date_begin;
168
+        return this.dates != null && this.today > this.dates.date_begin;
137 169
     }
138 170
 
139 171
     isResultRunning() {
140
-        return this.today > this.dates.date_result_begin && this.today < this.dates.date_result_end;
172
+        return this.dates != null && this.today > this.dates.date_result_begin && this.today < this.dates.date_result_end;
141 173
     }
142 174
 
143 175
     isResultStarted() {
144
-        return this.today > this.dates.date_result_begin;
176
+        return this.dates != null && this.today > this.dates.date_result_begin;
145 177
     }
146 178
 
147 179
     mainRenderItem = ({item}: Object) => {
148 180
         if (item.key === 'info')
149 181
             return <VoteTitle/>;
150
-        else if (item.key === 'main' && this.isVoteAvailable())
182
+        else if (item.key === 'main' && this.dates != null)
151 183
             return this.getContent();
152 184
         else
153 185
             return null;
154 186
     };
155 187
 
156
-    getScreen = (data: Array<Object | null>) => {
188
+    getScreen = (data: Array<{ [key: string]: any } | null>) => {
157 189
         // data[0] = FAKE_TEAMS2;
158 190
         // data[1] = FAKE_DATE;
159 191
         this.lastRefresh = new Date();
160 192
 
161
-        if (data[1] === null)
162
-            data[1] = {date_begin: null};
193
+        const teams : teamResponse | null = data[0];
194
+        const dateStrings : stringVoteDates | null = data[1];
163 195
 
164
-        if (data[0] !== null) {
165
-            this.teams = data[0].teams;
166
-            this.hasVoted = data[0].has_voted;
196
+        if (dateStrings != null && dateStrings.date_begin == null)
197
+            this.datesString = null;
198
+        else
199
+            this.datesString = dateStrings;
200
+
201
+        if (teams != null) {
202
+            this.teams = teams.teams;
203
+            this.hasVoted = teams.has_voted;
167 204
         }
168
-        this.datesString = data[1];
205
+
169 206
         this.generateDateObject();
170 207
         return (
171 208
             <View>
@@ -211,15 +248,26 @@ export default class VoteScreen extends React.Component<Props, State> {
211 248
      * Votes have ended, results can be displayed
212 249
      */
213 250
     getVoteResultCard() {
214
-        return <VoteResults teams={this.teams}
215
-                            dateEnd={this.getDateString(this.dates.date_result_end, this.datesString.date_result_end)}/>;
251
+        if (this.dates != null && this.datesString != null)
252
+            return <VoteResults
253
+                teams={this.teams}
254
+                dateEnd={this.getDateString(
255
+                    this.dates.date_result_end,
256
+                    this.datesString.date_result_end)}
257
+            />;
258
+        else
259
+            return null;
216 260
     }
217 261
 
218 262
     /**
219 263
      * Vote will open shortly
220 264
      */
221 265
     getTeaseVoteCard() {
222
-        return <VoteTease startDate={this.getDateString(this.dates.date_begin, this.datesString.date_begin)}/>;
266
+        if (this.dates != null && this.datesString != null)
267
+            return <VoteTease
268
+                startDate={this.getDateString(this.dates.date_begin, this.datesString.date_begin)}/>;
269
+        else
270
+            return null;
223 271
     }
224 272
 
225 273
     /**
@@ -227,7 +275,7 @@ export default class VoteScreen extends React.Component<Props, State> {
227 275
      */
228 276
     getWaitVoteCard() {
229 277
         let startDate = null;
230
-        if (this.dates.date_result_begin !== null)
278
+        if (this.dates != null && this.datesString != null && this.dates.date_result_begin != null)
231 279
             startDate = this.getDateString(this.dates.date_result_begin, this.datesString.date_result_begin);
232 280
         return <VoteWait startDate={startDate} hasVoted={this.hasVoted || this.state.hasVoted}
233 281
                          justVoted={this.state.hasVoted}

Loading…
Cancel
Save