Browse Source

Improve vote screen to match linter

Arnaud Vergnet 3 years ago
parent
commit
3d9bfdea4c
1 changed files with 338 additions and 302 deletions
  1. 338
    302
      src/screens/Amicale/VoteScreen.js

+ 338
- 302
src/screens/Amicale/VoteScreen.js View File

@@ -1,46 +1,47 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import {RefreshControl, View} from "react-native";
5
-import AuthenticatedScreen from "../../components/Amicale/AuthenticatedScreen";
6
-import {getTimeOnlyString, stringToDate} from "../../utils/Planning";
7
-import VoteTease from "../../components/Amicale/Vote/VoteTease";
8
-import VoteSelect from "../../components/Amicale/Vote/VoteSelect";
9
-import VoteResults from "../../components/Amicale/Vote/VoteResults";
10
-import VoteWait from "../../components/Amicale/Vote/VoteWait";
11
-import {StackNavigationProp} from "@react-navigation/stack";
12
-import i18n from "i18n-js";
13
-import {MASCOT_STYLE} from "../../components/Mascot/Mascot";
14
-import MascotPopup from "../../components/Mascot/MascotPopup";
15
-import AsyncStorageManager from "../../managers/AsyncStorageManager";
16
-import {Button} from "react-native-paper";
17
-import VoteNotAvailable from "../../components/Amicale/Vote/VoteNotAvailable";
18
-import CollapsibleFlatList from "../../components/Collapsible/CollapsibleFlatList";
19
-
20
-export type team = {
21
-    id: number,
22
-    name: string,
23
-    votes: number,
24
-}
4
+import {RefreshControl, View} from 'react-native';
5
+import {StackNavigationProp} from '@react-navigation/stack';
6
+import i18n from 'i18n-js';
7
+import {Button} from 'react-native-paper';
8
+import AuthenticatedScreen from '../../components/Amicale/AuthenticatedScreen';
9
+import {getTimeOnlyString, stringToDate} from '../../utils/Planning';
10
+import VoteTease from '../../components/Amicale/Vote/VoteTease';
11
+import VoteSelect from '../../components/Amicale/Vote/VoteSelect';
12
+import VoteResults from '../../components/Amicale/Vote/VoteResults';
13
+import VoteWait from '../../components/Amicale/Vote/VoteWait';
14
+import {MASCOT_STYLE} from '../../components/Mascot/Mascot';
15
+import MascotPopup from '../../components/Mascot/MascotPopup';
16
+import AsyncStorageManager from '../../managers/AsyncStorageManager';
17
+import VoteNotAvailable from '../../components/Amicale/Vote/VoteNotAvailable';
18
+import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
19
+import type {ApiGenericDataType} from '../../utils/WebData';
20
+
21
+export type VoteTeamType = {
22
+  id: number,
23
+  name: string,
24
+  votes: number,
25
+};
25 26
 
26
-type teamResponse = {
27
-    has_voted: boolean,
28
-    teams: Array<team>,
27
+type TeamResponseType = {
28
+  has_voted: boolean,
29
+  teams: Array<VoteTeamType>,
29 30
 };
30 31
 
31
-type stringVoteDates = {
32
-    date_begin: string,
33
-    date_end: string,
34
-    date_result_begin: string,
35
-    date_result_end: string,
36
-}
32
+type VoteDatesStringType = {
33
+  date_begin: string,
34
+  date_end: string,
35
+  date_result_begin: string,
36
+  date_result_end: string,
37
+};
37 38
 
38
-type objectVoteDates = {
39
-    date_begin: Date,
40
-    date_end: Date,
41
-    date_result_begin: Date,
42
-    date_result_end: Date,
43
-}
39
+type VoteDatesObjectType = {
40
+  date_begin: Date,
41
+  date_end: Date,
42
+  date_result_begin: Date,
43
+  date_result_end: Date,
44
+};
44 45
 
45 46
 // const FAKE_DATE = {
46 47
 //     "date_begin": "2020-08-19 15:50",
@@ -92,287 +93,322 @@ type objectVoteDates = {
92 93
 
93 94
 const MIN_REFRESH_TIME = 5 * 1000;
94 95
 
95
-type Props = {
96
-    navigation: StackNavigationProp
97
-}
96
+type PropsType = {
97
+  navigation: StackNavigationProp,
98
+};
98 99
 
99
-type State = {
100
-    hasVoted: boolean,
101
-    mascotDialogVisible: boolean,
102
-}
100
+type StateType = {
101
+  hasVoted: boolean,
102
+  mascotDialogVisible: boolean,
103
+};
103 104
 
104 105
 /**
105 106
  * Screen displaying vote information and controls
106 107
  */
107
-export default class VoteScreen extends React.Component<Props, State> {
108
-
109
-    state = {
110
-        hasVoted: false,
111
-        mascotDialogVisible: AsyncStorageManager.getBool(AsyncStorageManager.PREFERENCES.voteShowBanner.key),
112
-    };
108
+export default class VoteScreen extends React.Component<PropsType, StateType> {
109
+  teams: Array<VoteTeamType>;
113 110
 
114
-    teams: Array<team>;
115
-    hasVoted: boolean;
116
-    datesString: null | stringVoteDates;
117
-    dates: null | objectVoteDates;
111
+  hasVoted: boolean;
118 112
 
119
-    today: Date;
113
+  datesString: null | VoteDatesStringType;
120 114
 
121
-    mainFlatListData: Array<{ key: string }>;
122
-    lastRefresh: Date | null;
115
+  dates: null | VoteDatesObjectType;
123 116
 
124
-    authRef: { current: null | AuthenticatedScreen };
117
+  today: Date;
125 118
 
126
-    constructor() {
127
-        super();
128
-        this.hasVoted = false;
129
-        this.today = new Date();
130
-        this.authRef = React.createRef();
131
-        this.lastRefresh = null;
132
-        this.mainFlatListData = [
133
-            {key: 'main'},
134
-            {key: 'info'},
135
-        ]
136
-    }
137
-
138
-    /**
139
-     * Reloads vote data if last refresh delta is smaller than the minimum refresh time
140
-     */
141
-    reloadData = () => {
142
-        let canRefresh;
143
-        const lastRefresh = this.lastRefresh;
144
-        if (lastRefresh != null)
145
-            canRefresh = (new Date().getTime() - lastRefresh.getTime()) > MIN_REFRESH_TIME;
146
-        else
147
-            canRefresh = true;
148
-        if (canRefresh && this.authRef.current != null)
149
-            this.authRef.current.reload()
150
-    };
151
-
152
-    /**
153
-     * Generates the objects containing string and Date representations of key vote dates
154
-     */
155
-    generateDateObject() {
156
-        const strings = this.datesString;
157
-        if (strings != null) {
158
-            const dateBegin = stringToDate(strings.date_begin);
159
-            const dateEnd = stringToDate(strings.date_end);
160
-            const dateResultBegin = stringToDate(strings.date_result_begin);
161
-            const dateResultEnd = stringToDate(strings.date_result_end);
162
-            if (dateBegin != null && dateEnd != null && dateResultBegin != null && dateResultEnd != null) {
163
-                this.dates = {
164
-                    date_begin: dateBegin,
165
-                    date_end: dateEnd,
166
-                    date_result_begin: dateResultBegin,
167
-                    date_result_end: dateResultEnd,
168
-                };
169
-            } else
170
-                this.dates = null;
171
-        } else
172
-            this.dates = null;
173
-    }
174
-
175
-    /**
176
-     * Gets the string representation of the given date.
177
-     *
178
-     * If the given date is the same day as today, only return the tile.
179
-     * Otherwise, return the full date.
180
-     *
181
-     * @param date The Date object representation of the wanted date
182
-     * @param dateString The string representation of the wanted date
183
-     * @returns {string}
184
-     */
185
-    getDateString(date: Date, dateString: string): string {
186
-        if (this.today.getDate() === date.getDate()) {
187
-            const str = getTimeOnlyString(dateString);
188
-            return str != null ? str : "";
189
-        } else
190
-            return dateString;
191
-    }
192
-
193
-    isVoteRunning() {
194
-        return this.dates != null && this.today > this.dates.date_begin && this.today < this.dates.date_end;
195
-    }
119
+  mainFlatListData: Array<{key: string}>;
196 120
 
197
-    isVoteStarted() {
198
-        return this.dates != null && this.today > this.dates.date_begin;
199
-    }
121
+  lastRefresh: Date | null;
200 122
 
201
-    isResultRunning() {
202
-        return this.dates != null && this.today > this.dates.date_result_begin && this.today < this.dates.date_result_end;
203
-    }
123
+  authRef: {current: null | AuthenticatedScreen};
204 124
 
205
-    isResultStarted() {
206
-        return this.dates != null && this.today > this.dates.date_result_begin;
207
-    }
208
-
209
-    mainRenderItem = ({item}: { item: { key: string } }) => {
210
-        if (item.key === 'info')
211
-            return (
212
-                <View>
213
-                    <Button
214
-                        mode={"contained"}
215
-                        icon={"help-circle"}
216
-                        onPress={this.showMascotDialog}
217
-                        style={{
218
-                            marginLeft: "auto",
219
-                            marginRight: "auto",
220
-                            marginTop: 20
221
-                        }}>
222
-                        {i18n.t("screens.vote.mascotDialog.title")}
223
-                    </Button>
224
-                </View>
225
-            );
226
-        else
227
-            return this.getContent();
228
-    };
229
-
230
-    getScreen = (data: Array<{ [key: string]: any } | null>) => {
231
-        // data[0] = FAKE_TEAMS2;
232
-        // data[1] = FAKE_DATE;
233
-        this.lastRefresh = new Date();
234
-
235
-        const teams: teamResponse | null = data[0];
236
-        const dateStrings: stringVoteDates | null = data[1];
237
-
238
-        if (dateStrings != null && dateStrings.date_begin == null)
239
-            this.datesString = null;
240
-        else
241
-            this.datesString = dateStrings;
242
-
243
-        if (teams != null) {
244
-            this.teams = teams.teams;
245
-            this.hasVoted = teams.has_voted;
246
-        }
247
-
248
-        this.generateDateObject();
249
-        return (
250
-            <CollapsibleFlatList
251
-                data={this.mainFlatListData}
252
-                refreshControl={
253
-                    <RefreshControl
254
-                        refreshing={false}
255
-                        onRefresh={this.reloadData}
256
-                    />
257
-                }
258
-                extraData={this.state.hasVoted.toString()}
259
-                renderItem={this.mainRenderItem}
260
-            />
261
-        );
125
+  constructor() {
126
+    super();
127
+    this.state = {
128
+      hasVoted: false,
129
+      mascotDialogVisible: AsyncStorageManager.getBool(
130
+        AsyncStorageManager.PREFERENCES.voteShowBanner.key,
131
+      ),
262 132
     };
263
-
264
-    getContent() {
265
-        if (!this.isVoteStarted())
266
-            return this.getTeaseVoteCard();
267
-        else if (this.isVoteRunning() && (!this.hasVoted && !this.state.hasVoted))
268
-            return this.getVoteCard();
269
-        else if (!this.isResultStarted())
270
-            return this.getWaitVoteCard();
271
-        else if (this.isResultRunning())
272
-            return this.getVoteResultCard();
273
-        else
274
-            return <VoteNotAvailable/>;
133
+    this.hasVoted = false;
134
+    this.today = new Date();
135
+    this.authRef = React.createRef();
136
+    this.lastRefresh = null;
137
+    this.mainFlatListData = [{key: 'main'}, {key: 'info'}];
138
+  }
139
+
140
+  /**
141
+   * Gets the string representation of the given date.
142
+   *
143
+   * If the given date is the same day as today, only return the tile.
144
+   * Otherwise, return the full date.
145
+   *
146
+   * @param date The Date object representation of the wanted date
147
+   * @param dateString The string representation of the wanted date
148
+   * @returns {string}
149
+   */
150
+  getDateString(date: Date, dateString: string): string {
151
+    if (this.today.getDate() === date.getDate()) {
152
+      const str = getTimeOnlyString(dateString);
153
+      return str != null ? str : '';
275 154
     }
276
-
277
-    onVoteSuccess = () => this.setState({hasVoted: true});
278
-
279
-    /**
280
-     * The user has not voted yet, and the votes are open
281
-     */
282
-    getVoteCard() {
283
-        return <VoteSelect teams={this.teams} onVoteSuccess={this.onVoteSuccess} onVoteError={this.reloadData}/>;
155
+    return dateString;
156
+  }
157
+
158
+  getMainRenderItem = ({item}: {item: {key: string}}): React.Node => {
159
+    if (item.key === 'info')
160
+      return (
161
+        <View>
162
+          <Button
163
+            mode="contained"
164
+            icon="help-circle"
165
+            onPress={this.showMascotDialog}
166
+            style={{
167
+              marginLeft: 'auto',
168
+              marginRight: 'auto',
169
+              marginTop: 20,
170
+            }}>
171
+            {i18n.t('screens.vote.mascotDialog.title')}
172
+          </Button>
173
+        </View>
174
+      );
175
+    return this.getContent();
176
+  };
177
+
178
+  getScreen = (data: Array<ApiGenericDataType | null>): React.Node => {
179
+    const {state} = this;
180
+    // data[0] = FAKE_TEAMS2;
181
+    // data[1] = FAKE_DATE;
182
+    this.lastRefresh = new Date();
183
+
184
+    const teams: TeamResponseType | null = data[0];
185
+    const dateStrings: VoteDatesStringType | null = data[1];
186
+
187
+    if (dateStrings != null && dateStrings.date_begin == null)
188
+      this.datesString = null;
189
+    else this.datesString = dateStrings;
190
+
191
+    if (teams != null) {
192
+      this.teams = teams.teams;
193
+      this.hasVoted = teams.has_voted;
284 194
     }
285 195
 
286
-    /**
287
-     * Votes have ended, results can be displayed
288
-     */
289
-    getVoteResultCard() {
290
-        if (this.dates != null && this.datesString != null)
291
-            return <VoteResults
292
-                teams={this.teams}
293
-                dateEnd={this.getDateString(
294
-                    this.dates.date_result_end,
295
-                    this.datesString.date_result_end)}
296
-            />;
297
-        else
298
-            return <VoteNotAvailable/>;
299
-    }
300
-
301
-    /**
302
-     * Vote will open shortly
303
-     */
304
-    getTeaseVoteCard() {
305
-        if (this.dates != null && this.datesString != null)
306
-            return <VoteTease
307
-                startDate={this.getDateString(this.dates.date_begin, this.datesString.date_begin)}/>;
308
-        else
309
-            return <VoteNotAvailable/>;
310
-    }
311
-
312
-    /**
313
-     * Votes have ended, or user has voted waiting for results
314
-     */
315
-    getWaitVoteCard() {
316
-        let startDate = null;
317
-        if (this.dates != null && this.datesString != null && this.dates.date_result_begin != null)
318
-            startDate = this.getDateString(this.dates.date_result_begin, this.datesString.date_result_begin);
319
-        return <VoteWait startDate={startDate} hasVoted={this.hasVoted || this.state.hasVoted}
320
-                         justVoted={this.state.hasVoted}
321
-                         isVoteRunning={this.isVoteRunning()}/>;
322
-    }
323
-
324
-    showMascotDialog = () => {
325
-        this.setState({mascotDialogVisible: true})
326
-    };
327
-
328
-    hideMascotDialog = () => {
329
-        AsyncStorageManager.set(AsyncStorageManager.PREFERENCES.voteShowBanner.key, false);
330
-        this.setState({mascotDialogVisible: false})
331
-    };
332
-
333
-    /**
334
-     * Renders the authenticated screen.
335
-     *
336
-     * Teams and dates are not mandatory to allow showing the information box even if api requests fail
337
-     *
338
-     * @returns {*}
339
-     */
340
-    render() {
341
-        return (
342
-            <View style={{flex: 1}}>
343
-                <AuthenticatedScreen
344
-                    {...this.props}
345
-                    ref={this.authRef}
346
-                    requests={[
347
-                        {
348
-                            link: 'elections/teams',
349
-                            params: {},
350
-                            mandatory: false,
351
-                        },
352
-                        {
353
-                            link: 'elections/dates',
354
-                            params: {},
355
-                            mandatory: false,
356
-                        },
357
-                    ]}
358
-                    renderFunction={this.getScreen}
359
-                />
360
-                <MascotPopup
361
-                    visible={this.state.mascotDialogVisible}
362
-                    title={i18n.t("screens.vote.mascotDialog.title")}
363
-                    message={i18n.t("screens.vote.mascotDialog.message")}
364
-                    icon={"vote"}
365
-                    buttons={{
366
-                        action: null,
367
-                        cancel: {
368
-                            message: i18n.t("screens.vote.mascotDialog.button"),
369
-                            icon: "check",
370
-                            onPress: this.hideMascotDialog,
371
-                        }
372
-                    }}
373
-                    emotion={MASCOT_STYLE.CUTE}
374
-                />
375
-            </View>
376
-        );
377
-    }
196
+    this.generateDateObject();
197
+    return (
198
+      <CollapsibleFlatList
199
+        data={this.mainFlatListData}
200
+        refreshControl={
201
+          <RefreshControl refreshing={false} onRefresh={this.reloadData} />
202
+        }
203
+        extraData={state.hasVoted.toString()}
204
+        renderItem={this.getMainRenderItem}
205
+      />
206
+    );
207
+  };
208
+
209
+  getContent(): React.Node {
210
+    const {state} = this;
211
+    if (!this.isVoteStarted()) return this.getTeaseVoteCard();
212
+    if (this.isVoteRunning() && !this.hasVoted && !state.hasVoted)
213
+      return this.getVoteCard();
214
+    if (!this.isResultStarted()) return this.getWaitVoteCard();
215
+    if (this.isResultRunning()) return this.getVoteResultCard();
216
+    return <VoteNotAvailable />;
217
+  }
218
+
219
+  onVoteSuccess = (): void => this.setState({hasVoted: true});
220
+
221
+  /**
222
+   * The user has not voted yet, and the votes are open
223
+   */
224
+  getVoteCard(): React.Node {
225
+    return (
226
+      <VoteSelect
227
+        teams={this.teams}
228
+        onVoteSuccess={this.onVoteSuccess}
229
+        onVoteError={this.reloadData}
230
+      />
231
+    );
232
+  }
233
+
234
+  /**
235
+   * Votes have ended, results can be displayed
236
+   */
237
+  getVoteResultCard(): React.Node {
238
+    if (this.dates != null && this.datesString != null)
239
+      return (
240
+        <VoteResults
241
+          teams={this.teams}
242
+          dateEnd={this.getDateString(
243
+            this.dates.date_result_end,
244
+            this.datesString.date_result_end,
245
+          )}
246
+        />
247
+      );
248
+    return <VoteNotAvailable />;
249
+  }
250
+
251
+  /**
252
+   * Vote will open shortly
253
+   */
254
+  getTeaseVoteCard(): React.Node {
255
+    if (this.dates != null && this.datesString != null)
256
+      return (
257
+        <VoteTease
258
+          startDate={this.getDateString(
259
+            this.dates.date_begin,
260
+            this.datesString.date_begin,
261
+          )}
262
+        />
263
+      );
264
+    return <VoteNotAvailable />;
265
+  }
266
+
267
+  /**
268
+   * Votes have ended, or user has voted waiting for results
269
+   */
270
+  getWaitVoteCard(): React.Node {
271
+    const {state} = this;
272
+    let startDate = null;
273
+    if (
274
+      this.dates != null &&
275
+      this.datesString != null &&
276
+      this.dates.date_result_begin != null
277
+    )
278
+      startDate = this.getDateString(
279
+        this.dates.date_result_begin,
280
+        this.datesString.date_result_begin,
281
+      );
282
+    return (
283
+      <VoteWait
284
+        startDate={startDate}
285
+        hasVoted={this.hasVoted || state.hasVoted}
286
+        justVoted={state.hasVoted}
287
+        isVoteRunning={this.isVoteRunning()}
288
+      />
289
+    );
290
+  }
291
+
292
+  /**
293
+   * Reloads vote data if last refresh delta is smaller than the minimum refresh time
294
+   */
295
+  reloadData = () => {
296
+    let canRefresh;
297
+    const {lastRefresh} = this;
298
+    if (lastRefresh != null)
299
+      canRefresh =
300
+        new Date().getTime() - lastRefresh.getTime() > MIN_REFRESH_TIME;
301
+    else canRefresh = true;
302
+    if (canRefresh && this.authRef.current != null)
303
+      this.authRef.current.reload();
304
+  };
305
+
306
+  showMascotDialog = () => {
307
+    this.setState({mascotDialogVisible: true});
308
+  };
309
+
310
+  hideMascotDialog = () => {
311
+    AsyncStorageManager.set(
312
+      AsyncStorageManager.PREFERENCES.voteShowBanner.key,
313
+      false,
314
+    );
315
+    this.setState({mascotDialogVisible: false});
316
+  };
317
+
318
+  isVoteStarted(): boolean {
319
+    return this.dates != null && this.today > this.dates.date_begin;
320
+  }
321
+
322
+  isResultRunning(): boolean {
323
+    return (
324
+      this.dates != null &&
325
+      this.today > this.dates.date_result_begin &&
326
+      this.today < this.dates.date_result_end
327
+    );
328
+  }
329
+
330
+  isResultStarted(): boolean {
331
+    return this.dates != null && this.today > this.dates.date_result_begin;
332
+  }
333
+
334
+  isVoteRunning(): boolean {
335
+    return (
336
+      this.dates != null &&
337
+      this.today > this.dates.date_begin &&
338
+      this.today < this.dates.date_end
339
+    );
340
+  }
341
+
342
+  /**
343
+   * Generates the objects containing string and Date representations of key vote dates
344
+   */
345
+  generateDateObject() {
346
+    const strings = this.datesString;
347
+    if (strings != null) {
348
+      const dateBegin = stringToDate(strings.date_begin);
349
+      const dateEnd = stringToDate(strings.date_end);
350
+      const dateResultBegin = stringToDate(strings.date_result_begin);
351
+      const dateResultEnd = stringToDate(strings.date_result_end);
352
+      if (
353
+        dateBegin != null &&
354
+        dateEnd != null &&
355
+        dateResultBegin != null &&
356
+        dateResultEnd != null
357
+      ) {
358
+        this.dates = {
359
+          date_begin: dateBegin,
360
+          date_end: dateEnd,
361
+          date_result_begin: dateResultBegin,
362
+          date_result_end: dateResultEnd,
363
+        };
364
+      } else this.dates = null;
365
+    } else this.dates = null;
366
+  }
367
+
368
+  /**
369
+   * Renders the authenticated screen.
370
+   *
371
+   * Teams and dates are not mandatory to allow showing the information box even if api requests fail
372
+   *
373
+   * @returns {*}
374
+   */
375
+  render(): React.Node {
376
+    const {props, state} = this;
377
+    return (
378
+      <View style={{flex: 1}}>
379
+        <AuthenticatedScreen
380
+          navigation={props.navigation}
381
+          ref={this.authRef}
382
+          requests={[
383
+            {
384
+              link: 'elections/teams',
385
+              params: {},
386
+              mandatory: false,
387
+            },
388
+            {
389
+              link: 'elections/dates',
390
+              params: {},
391
+              mandatory: false,
392
+            },
393
+          ]}
394
+          renderFunction={this.getScreen}
395
+        />
396
+        <MascotPopup
397
+          visible={state.mascotDialogVisible}
398
+          title={i18n.t('screens.vote.mascotDialog.title')}
399
+          message={i18n.t('screens.vote.mascotDialog.message')}
400
+          icon="vote"
401
+          buttons={{
402
+            action: null,
403
+            cancel: {
404
+              message: i18n.t('screens.vote.mascotDialog.button'),
405
+              icon: 'check',
406
+              onPress: this.hideMascotDialog,
407
+            },
408
+          }}
409
+          emotion={MASCOT_STYLE.CUTE}
410
+        />
411
+      </View>
412
+    );
413
+  }
378 414
 }

Loading…
Cancel
Save