Browse Source

Update Amicale and related components to use TypeScript

Arnaud Vergnet 1 year ago
parent
commit
f95635136e

src/components/Amicale/AuthenticatedScreen.js → src/components/Amicale/AuthenticatedScreen.tsx View File

@@ -17,37 +17,34 @@
17 17
  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18 18
  */
19 19
 
20
-// @flow
21
-
22 20
 import * as React from 'react';
23 21
 import {StackNavigationProp} from '@react-navigation/stack';
24 22
 import ConnectionManager from '../../managers/ConnectionManager';
25
-import type {ApiGenericDataType} from '../../utils/WebData';
26 23
 import {ERROR_TYPE} from '../../utils/WebData';
27 24
 import ErrorView from '../Screens/ErrorView';
28 25
 import BasicLoadingScreen from '../Screens/BasicLoadingScreen';
29 26
 
30
-type PropsType = {
31
-  navigation: StackNavigationProp,
27
+type PropsType<T> = {
28
+  navigation: StackNavigationProp<any>;
32 29
   requests: Array<{
33
-    link: string,
34
-    params: {...},
35
-    mandatory: boolean,
36
-  }>,
37
-  renderFunction: (Array<ApiGenericDataType | null>) => React.Node,
30
+    link: string;
31
+    params: object;
32
+    mandatory: boolean;
33
+  }>;
34
+  renderFunction: (data: Array<T | null>) => React.ReactNode;
38 35
   errorViewOverride?: Array<{
39
-    errorCode: number,
40
-    message: string,
41
-    icon: string,
42
-    showRetryButton: boolean,
43
-  }> | null,
36
+    errorCode: number;
37
+    message: string;
38
+    icon: string;
39
+    showRetryButton: boolean;
40
+  }> | null;
44 41
 };
45 42
 
46 43
 type StateType = {
47
-  loading: boolean,
44
+  loading: boolean;
48 45
 };
49 46
 
50
-class AuthenticatedScreen extends React.Component<PropsType, StateType> {
47
+class AuthenticatedScreen<T> extends React.Component<PropsType<T>, StateType> {
51 48
   static defaultProps = {
52 49
     errorViewOverride: null,
53 50
   };
@@ -58,13 +55,14 @@ class AuthenticatedScreen extends React.Component<PropsType, StateType> {
58 55
 
59 56
   errors: Array<number>;
60 57
 
61
-  fetchedData: Array<ApiGenericDataType | null>;
58
+  fetchedData: Array<T | null>;
62 59
 
63
-  constructor(props: PropsType) {
60
+  constructor(props: PropsType<T>) {
64 61
     super(props);
65 62
     this.state = {
66 63
       loading: true,
67 64
     };
65
+    this.currentUserToken = null;
68 66
     this.connectionManager = ConnectionManager.getInstance();
69 67
     props.navigation.addListener('focus', this.onScreenFocus);
70 68
     this.fetchedData = new Array(props.requests.length);
@@ -91,20 +89,20 @@ class AuthenticatedScreen extends React.Component<PropsType, StateType> {
91 89
    * @param index The index for the data
92 90
    * @param error The error code received
93 91
    */
94
-  onRequestFinished(
95
-    data: ApiGenericDataType | null,
96
-    index: number,
97
-    error?: number,
98
-  ) {
92
+  onRequestFinished(data: T | null, index: number, error?: number) {
99 93
     const {props} = this;
100 94
     if (index >= 0 && index < props.requests.length) {
101 95
       this.fetchedData[index] = data;
102 96
       this.errors[index] = error != null ? error : ERROR_TYPE.SUCCESS;
103 97
     }
104 98
     // Token expired, logout user
105
-    if (error === ERROR_TYPE.BAD_TOKEN) this.connectionManager.disconnect();
99
+    if (error === ERROR_TYPE.BAD_TOKEN) {
100
+      this.connectionManager.disconnect();
101
+    }
106 102
 
107
-    if (this.allRequestsFinished()) this.setState({loading: false});
103
+    if (this.allRequestsFinished()) {
104
+      this.setState({loading: false});
105
+    }
108 106
   }
109 107
 
110 108
   /**
@@ -132,7 +130,7 @@ class AuthenticatedScreen extends React.Component<PropsType, StateType> {
132 130
    *
133 131
    * @return {*}
134 132
    */
135
-  getErrorRender(): React.Node {
133
+  getErrorRender() {
136 134
     const {props} = this;
137 135
     const errorCode = this.getError();
138 136
     let shouldOverride = false;
@@ -169,18 +167,18 @@ class AuthenticatedScreen extends React.Component<PropsType, StateType> {
169 167
    */
170 168
   fetchData = () => {
171 169
     const {state, props} = this;
172
-    if (!state.loading) this.setState({loading: true});
170
+    if (!state.loading) {
171
+      this.setState({loading: true});
172
+    }
173 173
 
174 174
     if (this.connectionManager.isLoggedIn()) {
175 175
       for (let i = 0; i < props.requests.length; i += 1) {
176 176
         this.connectionManager
177
-          .authenticatedRequest(
177
+          .authenticatedRequest<T>(
178 178
             props.requests[i].link,
179 179
             props.requests[i].params,
180 180
           )
181
-          .then((response: ApiGenericDataType): void =>
182
-            this.onRequestFinished(response, i),
183
-          )
181
+          .then((response: T): void => this.onRequestFinished(response, i))
184 182
           .catch((error: number): void =>
185 183
             this.onRequestFinished(null, i, error),
186 184
           );
@@ -200,7 +198,9 @@ class AuthenticatedScreen extends React.Component<PropsType, StateType> {
200 198
   allRequestsFinished(): boolean {
201 199
     let finished = true;
202 200
     this.errors.forEach((error: number | null) => {
203
-      if (error == null) finished = false;
201
+      if (error == null) {
202
+        finished = false;
203
+      }
204 204
     });
205 205
     return finished;
206 206
   }
@@ -212,11 +212,14 @@ class AuthenticatedScreen extends React.Component<PropsType, StateType> {
212 212
     this.fetchData();
213 213
   }
214 214
 
215
-  render(): React.Node {
215
+  render() {
216 216
     const {state, props} = this;
217
-    if (state.loading) return <BasicLoadingScreen />;
218
-    if (this.getError() === ERROR_TYPE.SUCCESS)
217
+    if (state.loading) {
218
+      return <BasicLoadingScreen />;
219
+    }
220
+    if (this.getError() === ERROR_TYPE.SUCCESS) {
219 221
       return props.renderFunction(this.fetchedData);
222
+    }
220 223
     return this.getErrorRender();
221 224
   }
222 225
 }

src/components/Amicale/LogoutDialog.js → src/components/Amicale/LogoutDialog.tsx View File

@@ -17,28 +17,25 @@
17 17
  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18 18
  */
19 19
 
20
-// @flow
21
-
22 20
 import * as React from 'react';
23 21
 import i18n from 'i18n-js';
24
-import {StackNavigationProp} from '@react-navigation/stack';
25 22
 import LoadingConfirmDialog from '../Dialogs/LoadingConfirmDialog';
26 23
 import ConnectionManager from '../../managers/ConnectionManager';
24
+import {useNavigation} from '@react-navigation/native';
27 25
 
28 26
 type PropsType = {
29
-  navigation: StackNavigationProp,
30
-  visible: boolean,
31
-  onDismiss: () => void,
27
+  visible: boolean;
28
+  onDismiss: () => void;
32 29
 };
33 30
 
34
-class LogoutDialog extends React.PureComponent<PropsType> {
35
-  onClickAccept = async (): Promise<void> => {
36
-    const {props} = this;
31
+function LogoutDialog(props: PropsType) {
32
+  const navigation = useNavigation();
33
+  const onClickAccept = async (): Promise<void> => {
37 34
     return new Promise((resolve: () => void) => {
38 35
       ConnectionManager.getInstance()
39 36
         .disconnect()
40 37
         .then(() => {
41
-          props.navigation.reset({
38
+          navigation.reset({
42 39
             index: 0,
43 40
             routes: [{name: 'main'}],
44 41
           });
@@ -48,19 +45,16 @@ class LogoutDialog extends React.PureComponent<PropsType> {
48 45
     });
49 46
   };
50 47
 
51
-  render(): React.Node {
52
-    const {props} = this;
53
-    return (
54
-      <LoadingConfirmDialog
55
-        visible={props.visible}
56
-        onDismiss={props.onDismiss}
57
-        onAccept={this.onClickAccept}
58
-        title={i18n.t('dialog.disconnect.title')}
59
-        titleLoading={i18n.t('dialog.disconnect.titleLoading')}
60
-        message={i18n.t('dialog.disconnect.message')}
61
-      />
62
-    );
63
-  }
48
+  return (
49
+    <LoadingConfirmDialog
50
+      visible={props.visible}
51
+      onDismiss={props.onDismiss}
52
+      onAccept={onClickAccept}
53
+      title={i18n.t('dialog.disconnect.title')}
54
+      titleLoading={i18n.t('dialog.disconnect.titleLoading')}
55
+      message={i18n.t('dialog.disconnect.message')}
56
+    />
57
+  );
64 58
 }
65 59
 
66 60
 export default LogoutDialog;

src/components/Amicale/Vote/VoteNotAvailable.js → src/components/Amicale/Vote/VoteNotAvailable.tsx View File

@@ -17,42 +17,29 @@
17 17
  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18 18
  */
19 19
 
20
-// @flow
21
-
22
-import * as React from 'react';
20
+import React from 'react';
23 21
 import {View} from 'react-native';
24
-import {Headline, withTheme} from 'react-native-paper';
22
+import {Headline, useTheme} from 'react-native-paper';
25 23
 import i18n from 'i18n-js';
26
-import type {CustomThemeType} from '../../../managers/ThemeManager';
27
-
28
-type PropsType = {
29
-  theme: CustomThemeType,
30
-};
31
-
32
-class VoteNotAvailable extends React.Component<PropsType> {
33
-  shouldComponentUpdate(): boolean {
34
-    return false;
35
-  }
36 24
 
37
-  render(): React.Node {
38
-    const {props} = this;
39
-    return (
40
-      <View
25
+function VoteNotAvailable() {
26
+  const theme = useTheme();
27
+  return (
28
+    <View
29
+      style={{
30
+        width: '100%',
31
+        marginTop: 10,
32
+        marginBottom: 10,
33
+      }}>
34
+      <Headline
41 35
         style={{
42
-          width: '100%',
43
-          marginTop: 10,
44
-          marginBottom: 10,
36
+          color: theme.colors.textDisabled,
37
+          textAlign: 'center',
45 38
         }}>
46
-        <Headline
47
-          style={{
48
-            color: props.theme.colors.textDisabled,
49
-            textAlign: 'center',
50
-          }}>
51
-          {i18n.t('screens.vote.noVote')}
52
-        </Headline>
53
-      </View>
54
-    );
55
-  }
39
+        {i18n.t('screens.vote.noVote')}
40
+      </Headline>
41
+    </View>
42
+  );
56 43
 }
57 44
 
58
-export default withTheme(VoteNotAvailable);
45
+export default VoteNotAvailable;

src/components/Amicale/Vote/VoteResults.js → src/components/Amicale/Vote/VoteResults.tsx View File

@@ -17,8 +17,6 @@
17 17
  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18 18
  */
19 19
 
20
-// @flow
21
-
22 20
 import * as React from 'react';
23 21
 import {
24 22
   Avatar,
@@ -31,16 +29,11 @@ import {
31 29
 import {FlatList, StyleSheet} from 'react-native';
32 30
 import i18n from 'i18n-js';
33 31
 import type {VoteTeamType} from '../../../screens/Amicale/VoteScreen';
34
-import type {CustomThemeType} from '../../../managers/ThemeManager';
35
-import type {
36
-  CardTitleIconPropsType,
37
-  ListIconPropsType,
38
-} from '../../../constants/PaperStyles';
39 32
 
40 33
 type PropsType = {
41
-  teams: Array<VoteTeamType>,
42
-  dateEnd: string,
43
-  theme: CustomThemeType,
34
+  teams: Array<VoteTeamType>;
35
+  dateEnd: string;
36
+  theme: ReactNativePaper.Theme;
44 37
 };
45 38
 
46 39
 const styles = StyleSheet.create({
@@ -58,10 +51,10 @@ class VoteResults extends React.Component<PropsType> {
58 51
   winnerIds: Array<number>;
59 52
 
60 53
   constructor(props: PropsType) {
61
-    super();
54
+    super(props);
62 55
     props.teams.sort(this.sortByVotes);
63
-    this.getTotalVotes(props.teams);
64
-    this.getWinnerIds(props.teams);
56
+    this.totalVotes = this.getTotalVotes(props.teams);
57
+    this.winnerIds = this.getWinnerIds(props.teams);
65 58
   }
66 59
 
67 60
   shouldComponentUpdate(): boolean {
@@ -69,26 +62,31 @@ class VoteResults extends React.Component<PropsType> {
69 62
   }
70 63
 
71 64
   getTotalVotes(teams: Array<VoteTeamType>) {
72
-    this.totalVotes = 0;
65
+    let totalVotes = 0;
73 66
     for (let i = 0; i < teams.length; i += 1) {
74
-      this.totalVotes += teams[i].votes;
67
+      totalVotes += teams[i].votes;
75 68
     }
69
+    return totalVotes;
76 70
   }
77 71
 
78 72
   getWinnerIds(teams: Array<VoteTeamType>) {
79 73
     const max = teams[0].votes;
80
-    this.winnerIds = [];
74
+    let winnerIds = [];
81 75
     for (let i = 0; i < teams.length; i += 1) {
82
-      if (teams[i].votes === max) this.winnerIds.push(teams[i].id);
83
-      else break;
76
+      if (teams[i].votes === max) {
77
+        winnerIds.push(teams[i].id);
78
+      } else {
79
+        break;
80
+      }
84 81
     }
82
+    return winnerIds;
85 83
   }
86 84
 
87 85
   sortByVotes = (a: VoteTeamType, b: VoteTeamType): number => b.votes - a.votes;
88 86
 
89 87
   voteKeyExtractor = (item: VoteTeamType): string => item.id.toString();
90 88
 
91
-  resultRenderItem = ({item}: {item: VoteTeamType}): React.Node => {
89
+  resultRenderItem = ({item}: {item: VoteTeamType}) => {
92 90
     const isWinner = this.winnerIds.indexOf(item.id) !== -1;
93 91
     const isDraw = this.winnerIds.length > 1;
94 92
     const {props} = this;
@@ -101,7 +99,7 @@ class VoteResults extends React.Component<PropsType> {
101 99
         <List.Item
102 100
           title={item.name}
103 101
           description={`${item.votes} ${i18n.t('screens.vote.results.votes')}`}
104
-          left={(iconProps: ListIconPropsType): React.Node =>
102
+          left={(iconProps) =>
105 103
             isWinner ? (
106 104
               <List.Icon
107 105
                 style={iconProps.style}
@@ -125,7 +123,7 @@ class VoteResults extends React.Component<PropsType> {
125 123
     );
126 124
   };
127 125
 
128
-  render(): React.Node {
126
+  render() {
129 127
     const {props} = this;
130 128
     return (
131 129
       <Card style={styles.card}>
@@ -134,15 +132,14 @@ class VoteResults extends React.Component<PropsType> {
134 132
           subtitle={`${i18n.t('screens.vote.results.subtitle')} ${
135 133
             props.dateEnd
136 134
           }`}
137
-          left={(iconProps: CardTitleIconPropsType): React.Node => (
135
+          left={(iconProps) => (
138 136
             <Avatar.Icon size={iconProps.size} icon="podium-gold" />
139 137
           )}
140 138
         />
141 139
         <Card.Content>
142
-          <Subheading>{`${i18n.t('screens.vote.results.totalVotes')} ${
143
-            this.totalVotes
144
-          }`}</Subheading>
145
-          {/* $FlowFixMe */}
140
+          <Subheading>
141
+            {`${i18n.t('screens.vote.results.totalVotes')} ${this.totalVotes}`}
142
+          </Subheading>
146 143
           <FlatList
147 144
             data={props.teams}
148 145
             keyExtractor={this.voteKeyExtractor}

src/components/Amicale/Vote/VoteSelect.js → src/components/Amicale/Vote/VoteSelect.tsx View File

@@ -17,8 +17,6 @@
17 17
  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18 18
  */
19 19
 
20
-// @flow
21
-
22 20
 import * as React from 'react';
23 21
 import {Avatar, Button, Card, RadioButton} from 'react-native-paper';
24 22
 import {FlatList, StyleSheet, View} from 'react-native';
@@ -27,19 +25,18 @@ import ConnectionManager from '../../../managers/ConnectionManager';
27 25
 import LoadingConfirmDialog from '../../Dialogs/LoadingConfirmDialog';
28 26
 import ErrorDialog from '../../Dialogs/ErrorDialog';
29 27
 import type {VoteTeamType} from '../../../screens/Amicale/VoteScreen';
30
-import type {CardTitleIconPropsType} from '../../../constants/PaperStyles';
31 28
 
32 29
 type PropsType = {
33
-  teams: Array<VoteTeamType>,
34
-  onVoteSuccess: () => void,
35
-  onVoteError: () => void,
30
+  teams: Array<VoteTeamType>;
31
+  onVoteSuccess: () => void;
32
+  onVoteError: () => void;
36 33
 };
37 34
 
38 35
 type StateType = {
39
-  selectedTeam: string,
40
-  voteDialogVisible: boolean,
41
-  errorDialogVisible: boolean,
42
-  currentError: number,
36
+  selectedTeam: string;
37
+  voteDialogVisible: boolean;
38
+  errorDialogVisible: boolean;
39
+  currentError: number;
43 40
 };
44 41
 
45 42
 const styles = StyleSheet.create({
@@ -53,10 +50,10 @@ const styles = StyleSheet.create({
53 50
 
54 51
 export default class VoteSelect extends React.PureComponent<
55 52
   PropsType,
56
-  StateType,
53
+  StateType
57 54
 > {
58
-  constructor() {
59
-    super();
55
+  constructor(props: PropsType) {
56
+    super(props);
60 57
     this.state = {
61 58
       selectedTeam: 'none',
62 59
       voteDialogVisible: false,
@@ -70,7 +67,7 @@ export default class VoteSelect extends React.PureComponent<
70 67
 
71 68
   voteKeyExtractor = (item: VoteTeamType): string => item.id.toString();
72 69
 
73
-  voteRenderItem = ({item}: {item: VoteTeamType}): React.Node => (
70
+  voteRenderItem = ({item}: {item: VoteTeamType}) => (
74 71
     <RadioButton.Item label={item.name} value={item.id.toString()} />
75 72
   );
76 73
 
@@ -111,7 +108,7 @@ export default class VoteSelect extends React.PureComponent<
111 108
     props.onVoteError();
112 109
   };
113 110
 
114
-  render(): React.Node {
111
+  render() {
115 112
     const {state, props} = this;
116 113
     return (
117 114
       <View>
@@ -119,7 +116,7 @@ export default class VoteSelect extends React.PureComponent<
119 116
           <Card.Title
120 117
             title={i18n.t('screens.vote.select.title')}
121 118
             subtitle={i18n.t('screens.vote.select.subtitle')}
122
-            left={(iconProps: CardTitleIconPropsType): React.Node => (
119
+            left={(iconProps) => (
123 120
               <Avatar.Icon size={iconProps.size} icon="alert-decagram" />
124 121
             )}
125 122
           />
@@ -127,7 +124,6 @@ export default class VoteSelect extends React.PureComponent<
127 124
             <RadioButton.Group
128 125
               onValueChange={this.onVoteSelectionChange}
129 126
               value={state.selectedTeam}>
130
-              {/* $FlowFixMe */}
131 127
               <FlatList
132 128
                 data={props.teams}
133 129
                 keyExtractor={this.voteKeyExtractor}

src/components/Amicale/Vote/VoteTease.js → src/components/Amicale/Vote/VoteTease.tsx View File

@@ -17,16 +17,13 @@
17 17
  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18 18
  */
19 19
 
20
-// @flow
21
-
22 20
 import * as React from 'react';
23 21
 import {Avatar, Card, Paragraph} from 'react-native-paper';
24 22
 import {StyleSheet} from 'react-native';
25 23
 import i18n from 'i18n-js';
26
-import type {CardTitleIconPropsType} from '../../../constants/PaperStyles';
27 24
 
28 25
 type PropsType = {
29
-  startDate: string,
26
+  startDate: string;
30 27
 };
31 28
 
32 29
 const styles = StyleSheet.create({
@@ -38,28 +35,19 @@ const styles = StyleSheet.create({
38 35
   },
39 36
 });
40 37
 
41
-export default class VoteTease extends React.Component<PropsType> {
42
-  shouldComponentUpdate(): boolean {
43
-    return false;
44
-  }
45
-
46
-  render(): React.Node {
47
-    const {props} = this;
48
-    return (
49
-      <Card style={styles.card}>
50
-        <Card.Title
51
-          title={i18n.t('screens.vote.tease.title')}
52
-          subtitle={i18n.t('screens.vote.tease.subtitle')}
53
-          left={(iconProps: CardTitleIconPropsType): React.Node => (
54
-            <Avatar.Icon size={iconProps.size} icon="vote" />
55
-          )}
56
-        />
57
-        <Card.Content>
58
-          <Paragraph>
59
-            {`${i18n.t('screens.vote.tease.message')} ${props.startDate}`}
60
-          </Paragraph>
61
-        </Card.Content>
62
-      </Card>
63
-    );
64
-  }
38
+export default function VoteTease(props: PropsType) {
39
+  return (
40
+    <Card style={styles.card}>
41
+      <Card.Title
42
+        title={i18n.t('screens.vote.tease.title')}
43
+        subtitle={i18n.t('screens.vote.tease.subtitle')}
44
+        left={(iconProps) => <Avatar.Icon size={iconProps.size} icon="vote" />}
45
+      />
46
+      <Card.Content>
47
+        <Paragraph>
48
+          {`${i18n.t('screens.vote.tease.message')} ${props.startDate}`}
49
+        </Paragraph>
50
+      </Card.Content>
51
+    </Card>
52
+  );
65 53
 }

+ 0
- 93
src/components/Amicale/Vote/VoteWait.js View File

@@ -1,93 +0,0 @@
1
-/*
2
- * Copyright (c) 2019 - 2020 Arnaud Vergnet.
3
- *
4
- * This file is part of Campus INSAT.
5
- *
6
- * Campus INSAT is free software: you can redistribute it and/or modify
7
- *  it under the terms of the GNU General Public License as published by
8
- * the Free Software Foundation, either version 3 of the License, or
9
- * (at your option) any later version.
10
- *
11
- * Campus INSAT is distributed in the hope that it will be useful,
12
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
- * GNU General Public License for more details.
15
- *
16
- * You should have received a copy of the GNU General Public License
17
- * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18
- */
19
-
20
-// @flow
21
-
22
-import * as React from 'react';
23
-import {Avatar, Card, Paragraph, withTheme} from 'react-native-paper';
24
-import {StyleSheet} from 'react-native';
25
-import i18n from 'i18n-js';
26
-import type {CustomThemeType} from '../../../managers/ThemeManager';
27
-import type {CardTitleIconPropsType} from '../../../constants/PaperStyles';
28
-
29
-type PropsType = {
30
-  startDate: string | null,
31
-  justVoted: boolean,
32
-  hasVoted: boolean,
33
-  isVoteRunning: boolean,
34
-  theme: CustomThemeType,
35
-};
36
-
37
-const styles = StyleSheet.create({
38
-  card: {
39
-    margin: 10,
40
-  },
41
-  icon: {
42
-    backgroundColor: 'transparent',
43
-  },
44
-});
45
-
46
-class VoteWait extends React.Component<PropsType> {
47
-  shouldComponentUpdate(): boolean {
48
-    return false;
49
-  }
50
-
51
-  render(): React.Node {
52
-    const {props} = this;
53
-    const {startDate} = props;
54
-    return (
55
-      <Card style={styles.card}>
56
-        <Card.Title
57
-          title={
58
-            props.isVoteRunning
59
-              ? i18n.t('screens.vote.wait.titleSubmitted')
60
-              : i18n.t('screens.vote.wait.titleEnded')
61
-          }
62
-          subtitle={i18n.t('screens.vote.wait.subtitle')}
63
-          left={(iconProps: CardTitleIconPropsType): React.Node => (
64
-            <Avatar.Icon size={iconProps.size} icon="progress-check" />
65
-          )}
66
-        />
67
-        <Card.Content>
68
-          {props.justVoted ? (
69
-            <Paragraph style={{color: props.theme.colors.success}}>
70
-              {i18n.t('screens.vote.wait.messageSubmitted')}
71
-            </Paragraph>
72
-          ) : null}
73
-          {props.hasVoted ? (
74
-            <Paragraph style={{color: props.theme.colors.success}}>
75
-              {i18n.t('screens.vote.wait.messageVoted')}
76
-            </Paragraph>
77
-          ) : null}
78
-          {startDate != null ? (
79
-            <Paragraph>
80
-              {`${i18n.t('screens.vote.wait.messageDate')} ${startDate}`}
81
-            </Paragraph>
82
-          ) : (
83
-            <Paragraph>
84
-              {i18n.t('screens.vote.wait.messageDateUndefined')}
85
-            </Paragraph>
86
-          )}
87
-        </Card.Content>
88
-      </Card>
89
-    );
90
-  }
91
-}
92
-
93
-export default withTheme(VoteWait);

+ 80
- 0
src/components/Amicale/Vote/VoteWait.tsx View File

@@ -0,0 +1,80 @@
1
+/*
2
+ * Copyright (c) 2019 - 2020 Arnaud Vergnet.
3
+ *
4
+ * This file is part of Campus INSAT.
5
+ *
6
+ * Campus INSAT is free software: you can redistribute it and/or modify
7
+ *  it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Campus INSAT is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18
+ */
19
+
20
+import * as React from 'react';
21
+import {Avatar, Card, Paragraph, useTheme} from 'react-native-paper';
22
+import {StyleSheet} from 'react-native';
23
+import i18n from 'i18n-js';
24
+
25
+type PropsType = {
26
+  startDate: string | null;
27
+  justVoted: boolean;
28
+  hasVoted: boolean;
29
+  isVoteRunning: boolean;
30
+};
31
+
32
+const styles = StyleSheet.create({
33
+  card: {
34
+    margin: 10,
35
+  },
36
+  icon: {
37
+    backgroundColor: 'transparent',
38
+  },
39
+});
40
+
41
+export default function VoteWait(props: PropsType) {
42
+  const theme = useTheme();
43
+  const {startDate} = props;
44
+  return (
45
+    <Card style={styles.card}>
46
+      <Card.Title
47
+        title={
48
+          props.isVoteRunning
49
+            ? i18n.t('screens.vote.wait.titleSubmitted')
50
+            : i18n.t('screens.vote.wait.titleEnded')
51
+        }
52
+        subtitle={i18n.t('screens.vote.wait.subtitle')}
53
+        left={(iconProps) => (
54
+          <Avatar.Icon size={iconProps.size} icon="progress-check" />
55
+        )}
56
+      />
57
+      <Card.Content>
58
+        {props.justVoted ? (
59
+          <Paragraph style={{color: theme.colors.success}}>
60
+            {i18n.t('screens.vote.wait.messageSubmitted')}
61
+          </Paragraph>
62
+        ) : null}
63
+        {props.hasVoted ? (
64
+          <Paragraph style={{color: theme.colors.success}}>
65
+            {i18n.t('screens.vote.wait.messageVoted')}
66
+          </Paragraph>
67
+        ) : null}
68
+        {startDate != null ? (
69
+          <Paragraph>
70
+            {`${i18n.t('screens.vote.wait.messageDate')} ${startDate}`}
71
+          </Paragraph>
72
+        ) : (
73
+          <Paragraph>
74
+            {i18n.t('screens.vote.wait.messageDateUndefined')}
75
+          </Paragraph>
76
+        )}
77
+      </Card.Content>
78
+    </Card>
79
+  );
80
+}

+ 0
- 78
src/components/Collapsible/CollapsibleComponent.js View File

@@ -1,78 +0,0 @@
1
-/*
2
- * Copyright (c) 2019 - 2020 Arnaud Vergnet.
3
- *
4
- * This file is part of Campus INSAT.
5
- *
6
- * Campus INSAT is free software: you can redistribute it and/or modify
7
- *  it under the terms of the GNU General Public License as published by
8
- * the Free Software Foundation, either version 3 of the License, or
9
- * (at your option) any later version.
10
- *
11
- * Campus INSAT is distributed in the hope that it will be useful,
12
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
- * GNU General Public License for more details.
15
- *
16
- * You should have received a copy of the GNU General Public License
17
- * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18
- */
19
-
20
-// @flow
21
-
22
-import * as React from 'react';
23
-import {Collapsible} from 'react-navigation-collapsible';
24
-import withCollapsible from '../../utils/withCollapsible';
25
-import CustomTabBar from '../Tabbar/CustomTabBar';
26
-
27
-export type CollapsibleComponentPropsType = {
28
-  children?: React.Node,
29
-  hasTab?: boolean,
30
-  onScroll?: (event: SyntheticEvent<EventTarget>) => void,
31
-};
32
-
33
-type PropsType = {
34
-  ...CollapsibleComponentPropsType,
35
-  collapsibleStack: Collapsible,
36
-  // eslint-disable-next-line flowtype/no-weak-types
37
-  component: any,
38
-};
39
-
40
-class CollapsibleComponent extends React.Component<PropsType> {
41
-  static defaultProps = {
42
-    children: null,
43
-    hasTab: false,
44
-    onScroll: null,
45
-  };
46
-
47
-  onScroll = (event: SyntheticEvent<EventTarget>) => {
48
-    const {props} = this;
49
-    if (props.onScroll) props.onScroll(event);
50
-  };
51
-
52
-  render(): React.Node {
53
-    const {props} = this;
54
-    const Comp = props.component;
55
-    const {
56
-      containerPaddingTop,
57
-      scrollIndicatorInsetTop,
58
-      onScrollWithListener,
59
-    } = props.collapsibleStack;
60
-
61
-    return (
62
-      <Comp
63
-        // eslint-disable-next-line react/jsx-props-no-spreading
64
-        {...props}
65
-        onScroll={onScrollWithListener(this.onScroll)}
66
-        contentContainerStyle={{
67
-          paddingTop: containerPaddingTop,
68
-          paddingBottom: props.hasTab ? CustomTabBar.TAB_BAR_HEIGHT : 0,
69
-          minHeight: '100%',
70
-        }}
71
-        scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}>
72
-        {props.children}
73
-      </Comp>
74
-    );
75
-  }
76
-}
77
-
78
-export default withCollapsible(CollapsibleComponent);

+ 63
- 0
src/components/Collapsible/CollapsibleComponent.tsx View File

@@ -0,0 +1,63 @@
1
+/*
2
+ * Copyright (c) 2019 - 2020 Arnaud Vergnet.
3
+ *
4
+ * This file is part of Campus INSAT.
5
+ *
6
+ * Campus INSAT is free software: you can redistribute it and/or modify
7
+ *  it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Campus INSAT is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18
+ */
19
+
20
+import * as React from 'react';
21
+import {useCollapsibleStack} from 'react-navigation-collapsible';
22
+import CustomTabBar from '../Tabbar/CustomTabBar';
23
+import {NativeScrollEvent, NativeSyntheticEvent} from 'react-native';
24
+
25
+export interface CollapsibleComponentPropsType {
26
+  children?: React.ReactNode;
27
+  hasTab?: boolean;
28
+  onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
29
+}
30
+
31
+interface PropsType extends CollapsibleComponentPropsType {
32
+  component: React.ComponentType<any>;
33
+}
34
+
35
+function CollapsibleComponent(props: PropsType) {
36
+  const onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
37
+    if (props.onScroll) {
38
+      props.onScroll(event);
39
+    }
40
+  };
41
+  const Comp = props.component;
42
+  const {
43
+    containerPaddingTop,
44
+    scrollIndicatorInsetTop,
45
+    onScrollWithListener,
46
+  } = useCollapsibleStack();
47
+
48
+  return (
49
+    <Comp
50
+      {...props}
51
+      onScroll={onScrollWithListener(onScroll)}
52
+      contentContainerStyle={{
53
+        paddingTop: containerPaddingTop,
54
+        paddingBottom: props.hasTab ? CustomTabBar.TAB_BAR_HEIGHT : 0,
55
+        minHeight: '100%',
56
+      }}
57
+      scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}>
58
+      {props.children}
59
+    </Comp>
60
+  );
61
+}
62
+
63
+export default CollapsibleComponent;

src/components/Collapsible/CollapsibleFlatList.js → src/components/Collapsible/CollapsibleFlatList.tsx View File

@@ -17,29 +17,19 @@
17 17
  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18 18
  */
19 19
 
20
-// @flow
21
-
22 20
 import * as React from 'react';
23
-import {Animated} from 'react-native';
21
+import {Animated, FlatListProps} from 'react-native';
24 22
 import type {CollapsibleComponentPropsType} from './CollapsibleComponent';
25 23
 import CollapsibleComponent from './CollapsibleComponent';
26 24
 
27
-type PropsType = {
28
-  ...CollapsibleComponentPropsType,
29
-};
25
+type Props<T> = FlatListProps<T> & CollapsibleComponentPropsType;
30 26
 
31
-// eslint-disable-next-line react/prefer-stateless-function
32
-class CollapsibleFlatList extends React.Component<PropsType> {
33
-  render(): React.Node {
34
-    const {props} = this;
35
-    return (
36
-      <CollapsibleComponent // eslint-disable-next-line react/jsx-props-no-spreading
37
-        {...props}
38
-        component={Animated.FlatList}>
39
-        {props.children}
40
-      </CollapsibleComponent>
41
-    );
42
-  }
27
+function CollapsibleFlatList<T>(props: Props<T>) {
28
+  return (
29
+    <CollapsibleComponent {...props} component={Animated.FlatList}>
30
+      {props.children}
31
+    </CollapsibleComponent>
32
+  );
43 33
 }
44 34
 
45 35
 export default CollapsibleFlatList;

src/components/Collapsible/CollapsibleScrollView.js → src/components/Collapsible/CollapsibleScrollView.tsx View File

@@ -17,29 +17,19 @@
17 17
  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18 18
  */
19 19
 
20
-// @flow
21
-
22 20
 import * as React from 'react';
23
-import {Animated} from 'react-native';
21
+import {Animated, ScrollViewProps} from 'react-native';
24 22
 import type {CollapsibleComponentPropsType} from './CollapsibleComponent';
25 23
 import CollapsibleComponent from './CollapsibleComponent';
26 24
 
27
-type PropsType = {
28
-  ...CollapsibleComponentPropsType,
29
-};
25
+type Props = ScrollViewProps & CollapsibleComponentPropsType;
30 26
 
31
-// eslint-disable-next-line react/prefer-stateless-function
32
-class CollapsibleScrollView extends React.Component<PropsType> {
33
-  render(): React.Node {
34
-    const {props} = this;
35
-    return (
36
-      <CollapsibleComponent // eslint-disable-next-line react/jsx-props-no-spreading
37
-        {...props}
38
-        component={Animated.ScrollView}>
39
-        {props.children}
40
-      </CollapsibleComponent>
41
-    );
42
-  }
27
+function CollapsibleScrollView(props: Props) {
28
+  return (
29
+    <CollapsibleComponent {...props} component={Animated.ScrollView}>
30
+      {props.children}
31
+    </CollapsibleComponent>
32
+  );
43 33
 }
44 34
 
45 35
 export default CollapsibleScrollView;

src/components/Collapsible/CollapsibleSectionList.js → src/components/Collapsible/CollapsibleSectionList.tsx View File

@@ -17,29 +17,19 @@
17 17
  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18 18
  */
19 19
 
20
-// @flow
21
-
22 20
 import * as React from 'react';
23
-import {Animated} from 'react-native';
21
+import {Animated, SectionListProps} from 'react-native';
24 22
 import type {CollapsibleComponentPropsType} from './CollapsibleComponent';
25 23
 import CollapsibleComponent from './CollapsibleComponent';
26 24
 
27
-type PropsType = {
28
-  ...CollapsibleComponentPropsType,
29
-};
25
+type Props<T> = SectionListProps<T> & CollapsibleComponentPropsType;
30 26
 
31
-// eslint-disable-next-line react/prefer-stateless-function
32
-class CollapsibleSectionList extends React.Component<PropsType> {
33
-  render(): React.Node {
34
-    const {props} = this;
35
-    return (
36
-      <CollapsibleComponent // eslint-disable-next-line react/jsx-props-no-spreading
37
-        {...props}
38
-        component={Animated.SectionList}>
39
-        {props.children}
40
-      </CollapsibleComponent>
41
-    );
42
-  }
27
+function CollapsibleSectionList<T>(props: Props<T>) {
28
+  return (
29
+    <CollapsibleComponent {...props} component={Animated.SectionList}>
30
+      {props.children}
31
+    </CollapsibleComponent>
32
+  );
43 33
 }
44 34
 
45 35
 export default CollapsibleSectionList;

src/components/Screens/BasicLoadingScreen.js → src/components/Screens/BasicLoadingScreen.tsx View File

@@ -21,8 +21,11 @@
21 21
 
22 22
 import * as React from 'react';
23 23
 import {View} from 'react-native';
24
-import {ActivityIndicator, withTheme} from 'react-native-paper';
25
-import type {CustomThemeType} from '../../managers/ThemeManager';
24
+import {ActivityIndicator, useTheme} from 'react-native-paper';
25
+
26
+type Props = {
27
+  isAbsolute?: boolean;
28
+};
26 29
 
27 30
 /**
28 31
  * Component used to display a header button
@@ -30,29 +33,21 @@ import type {CustomThemeType} from '../../managers/ThemeManager';
30 33
  * @param props Props to pass to the component
31 34
  * @return {*}
32 35
  */
33
-function BasicLoadingScreen(props: {
34
-  theme: CustomThemeType,
35
-  isAbsolute: boolean,
36
-}): React.Node {
37
-  const {theme, isAbsolute} = props;
38
-  const {colors} = theme;
39
-  let position;
40
-  if (isAbsolute != null && isAbsolute) position = 'absolute';
41
-
36
+export default function BasicLoadingScreen(props: Props) {
37
+  const theme = useTheme();
38
+  const {isAbsolute} = props;
42 39
   return (
43 40
     <View
44 41
       style={{
45
-        backgroundColor: colors.background,
46
-        position,
42
+        backgroundColor: theme.colors.background,
43
+        position: isAbsolute ? 'absolute' : 'relative',
47 44
         top: 0,
48 45
         right: 0,
49 46
         width: '100%',
50 47
         height: '100%',
51 48
         justifyContent: 'center',
52 49
       }}>
53
-      <ActivityIndicator animating size="large" color={colors.primary} />
50
+      <ActivityIndicator animating size="large" color={theme.colors.primary} />
54 51
     </View>
55 52
   );
56 53
 }
57
-
58
-export default withTheme(BasicLoadingScreen);

src/components/Screens/ErrorView.js → src/components/Screens/ErrorView.tsx View File

@@ -17,8 +17,6 @@
17 17
  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18 18
  */
19 19
 
20
-// @flow
21
-
22 20
 import * as React from 'react';
23 21
 import {Button, Subheading, withTheme} from 'react-native-paper';
24 22
 import {StyleSheet, View} from 'react-native';
@@ -27,17 +25,16 @@ import i18n from 'i18n-js';
27 25
 import * as Animatable from 'react-native-animatable';
28 26
 import {StackNavigationProp} from '@react-navigation/stack';
29 27
 import {ERROR_TYPE} from '../../utils/WebData';
30
-import type {CustomThemeType} from '../../managers/ThemeManager';
31 28
 
32 29
 type PropsType = {
33
-  navigation: StackNavigationProp,
34
-  theme: CustomThemeType,
35
-  route: {name: string},
36
-  onRefresh?: () => void,
37
-  errorCode?: number,
38
-  icon?: string,
39
-  message?: string,
40
-  showRetryButton?: boolean,
30
+  navigation?: StackNavigationProp<any>;
31
+  theme: ReactNativePaper.Theme;
32
+  route?: {name: string};
33
+  onRefresh?: () => void;
34
+  errorCode?: number;
35
+  icon?: string;
36
+  message?: string;
37
+  showRetryButton?: boolean;
41 38
 };
42 39
 
43 40
 const styles = StyleSheet.create({
@@ -82,9 +79,11 @@ class ErrorView extends React.PureComponent<PropsType> {
82 79
   constructor(props: PropsType) {
83 80
     super(props);
84 81
     this.icon = '';
82
+    this.showLoginButton = false;
83
+    this.message = '';
85 84
   }
86 85
 
87
-  getRetryButton(): React.Node {
86
+  getRetryButton() {
88 87
     const {props} = this;
89 88
     return (
90 89
       <Button
@@ -97,7 +96,7 @@ class ErrorView extends React.PureComponent<PropsType> {
97 96
     );
98 97
   }
99 98
 
100
-  getLoginButton(): React.Node {
99
+  getLoginButton() {
101 100
     return (
102 101
       <Button
103 102
         mode="contained"
@@ -111,10 +110,12 @@ class ErrorView extends React.PureComponent<PropsType> {
111 110
 
112 111
   goToLogin = () => {
113 112
     const {props} = this;
114
-    props.navigation.navigate('login', {
115
-      screen: 'login',
116
-      params: {nextScreen: props.route.name},
117
-    });
113
+    if (props.navigation) {
114
+      props.navigation.navigate('login', {
115
+        screen: 'login',
116
+        params: {nextScreen: props.route ? props.route.name : undefined},
117
+      });
118
+    }
118 119
   };
119 120
 
120 121
   generateMessage() {
@@ -169,13 +170,17 @@ class ErrorView extends React.PureComponent<PropsType> {
169 170
     }
170 171
   }
171 172
 
172
-  render(): React.Node {
173
+  render() {
173 174
     const {props} = this;
174 175
     this.generateMessage();
175 176
     let button;
176
-    if (this.showLoginButton) button = this.getLoginButton();
177
-    else if (props.showRetryButton) button = this.getRetryButton();
178
-    else button = null;
177
+    if (this.showLoginButton) {
178
+      button = this.getLoginButton();
179
+    } else if (props.showRetryButton) {
180
+      button = this.getRetryButton();
181
+    } else {
182
+      button = null;
183
+    }
179 184
 
180 185
     return (
181 186
       <Animatable.View

src/screens/Amicale/VoteScreen.js → src/screens/Amicale/VoteScreen.tsx View File

@@ -17,8 +17,6 @@
17 17
  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18 18
  */
19 19
 
20
-// @flow
21
-
22 20
 import * as React from 'react';
23 21
 import {RefreshControl, View} from 'react-native';
24 22
 import {StackNavigationProp} from '@react-navigation/stack';
@@ -35,31 +33,30 @@ import MascotPopup from '../../components/Mascot/MascotPopup';
35 33
 import AsyncStorageManager from '../../managers/AsyncStorageManager';
36 34
 import VoteNotAvailable from '../../components/Amicale/Vote/VoteNotAvailable';
37 35
 import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
38
-import type {ApiGenericDataType} from '../../utils/WebData';
39 36
 
40 37
 export type VoteTeamType = {
41
-  id: number,
42
-  name: string,
43
-  votes: number,
38
+  id: number;
39
+  name: string;
40
+  votes: number;
44 41
 };
45 42
 
46 43
 type TeamResponseType = {
47
-  has_voted: boolean,
48
-  teams: Array<VoteTeamType>,
44
+  has_voted: boolean;
45
+  teams: Array<VoteTeamType>;
49 46
 };
50 47
 
51 48
 type VoteDatesStringType = {
52
-  date_begin: string,
53
-  date_end: string,
54
-  date_result_begin: string,
55
-  date_result_end: string,
49
+  date_begin: string;
50
+  date_end: string;
51
+  date_result_begin: string;
52
+  date_result_end: string;
56 53
 };
57 54
 
58 55
 type VoteDatesObjectType = {
59
-  date_begin: Date,
60
-  date_end: Date,
61
-  date_result_begin: Date,
62
-  date_result_end: Date,
56
+  date_begin: Date;
57
+  date_end: Date;
58
+  date_result_begin: Date;
59
+  date_result_end: Date;
63 60
 };
64 61
 
65 62
 // const FAKE_DATE = {
@@ -113,12 +110,12 @@ type VoteDatesObjectType = {
113 110
 const MIN_REFRESH_TIME = 5 * 1000;
114 111
 
115 112
 type PropsType = {
116
-  navigation: StackNavigationProp,
113
+  navigation: StackNavigationProp<any>;
117 114
 };
118 115
 
119 116
 type StateType = {
120
-  hasVoted: boolean,
121
-  mascotDialogVisible: boolean,
117
+  hasVoted: boolean;
118
+  mascotDialogVisible: boolean;
122 119
 };
123 120
 
124 121
 /**
@@ -139,10 +136,13 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
139 136
 
140 137
   lastRefresh: Date | null;
141 138
 
142
-  authRef: {current: null | AuthenticatedScreen};
139
+  authRef: {current: null | AuthenticatedScreen<any>};
143 140
 
144
-  constructor() {
145
-    super();
141
+  constructor(props: PropsType) {
142
+    super(props);
143
+    this.teams = [];
144
+    this.datesString = null;
145
+    this.dates = null;
146 146
     this.state = {
147 147
       hasVoted: false,
148 148
       mascotDialogVisible: AsyncStorageManager.getBool(
@@ -174,8 +174,8 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
174 174
     return dateString;
175 175
   }
176 176
 
177
-  getMainRenderItem = ({item}: {item: {key: string}}): React.Node => {
178
-    if (item.key === 'info')
177
+  getMainRenderItem = ({item}: {item: {key: string}}) => {
178
+    if (item.key === 'info') {
179 179
       return (
180 180
         <View>
181 181
           <Button
@@ -191,21 +191,24 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
191 191
           </Button>
192 192
         </View>
193 193
       );
194
+    }
194 195
     return this.getContent();
195 196
   };
196 197
 
197
-  getScreen = (data: Array<ApiGenericDataType | null>): React.Node => {
198
+  getScreen = (data: Array<TeamResponseType | VoteDatesStringType | null>) => {
198 199
     const {state} = this;
199 200
     // data[0] = FAKE_TEAMS2;
200 201
     // data[1] = FAKE_DATE;
201 202
     this.lastRefresh = new Date();
202 203
 
203
-    const teams: TeamResponseType | null = data[0];
204
-    const dateStrings: VoteDatesStringType | null = data[1];
204
+    const teams = data[0] as TeamResponseType | null;
205
+    const dateStrings = data[1] as VoteDatesStringType | null;
205 206
 
206
-    if (dateStrings != null && dateStrings.date_begin == null)
207
+    if (dateStrings != null && dateStrings.date_begin == null) {
207 208
       this.datesString = null;
208
-    else this.datesString = dateStrings;
209
+    } else {
210
+      this.datesString = dateStrings;
211
+    }
209 212
 
210 213
     if (teams != null) {
211 214
       this.teams = teams.teams;
@@ -225,13 +228,20 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
225 228
     );
226 229
   };
227 230
 
228
-  getContent(): React.Node {
231
+  getContent() {
229 232
     const {state} = this;
230
-    if (!this.isVoteStarted()) return this.getTeaseVoteCard();
231
-    if (this.isVoteRunning() && !this.hasVoted && !state.hasVoted)
233
+    if (!this.isVoteStarted()) {
234
+      return this.getTeaseVoteCard();
235
+    }
236
+    if (this.isVoteRunning() && !this.hasVoted && !state.hasVoted) {
232 237
       return this.getVoteCard();
233
-    if (!this.isResultStarted()) return this.getWaitVoteCard();
234
-    if (this.isResultRunning()) return this.getVoteResultCard();
238
+    }
239
+    if (!this.isResultStarted()) {
240
+      return this.getWaitVoteCard();
241
+    }
242
+    if (this.isResultRunning()) {
243
+      return this.getVoteResultCard();
244
+    }
235 245
     return <VoteNotAvailable />;
236 246
   }
237 247
 
@@ -240,7 +250,7 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
240 250
   /**
241 251
    * The user has not voted yet, and the votes are open
242 252
    */
243
-  getVoteCard(): React.Node {
253
+  getVoteCard() {
244 254
     return (
245 255
       <VoteSelect
246 256
         teams={this.teams}
@@ -253,8 +263,8 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
253 263
   /**
254 264
    * Votes have ended, results can be displayed
255 265
    */
256
-  getVoteResultCard(): React.Node {
257
-    if (this.dates != null && this.datesString != null)
266
+  getVoteResultCard() {
267
+    if (this.dates != null && this.datesString != null) {
258 268
       return (
259 269
         <VoteResults
260 270
           teams={this.teams}
@@ -264,14 +274,15 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
264 274
           )}
265 275
         />
266 276
       );
277
+    }
267 278
     return <VoteNotAvailable />;
268 279
   }
269 280
 
270 281
   /**
271 282
    * Vote will open shortly
272 283
    */
273
-  getTeaseVoteCard(): React.Node {
274
-    if (this.dates != null && this.datesString != null)
284
+  getTeaseVoteCard() {
285
+    if (this.dates != null && this.datesString != null) {
275 286
       return (
276 287
         <VoteTease
277 288
           startDate={this.getDateString(
@@ -280,24 +291,26 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
280 291
           )}
281 292
         />
282 293
       );
294
+    }
283 295
     return <VoteNotAvailable />;
284 296
   }
285 297
 
286 298
   /**
287 299
    * Votes have ended, or user has voted waiting for results
288 300
    */
289
-  getWaitVoteCard(): React.Node {
301
+  getWaitVoteCard() {
290 302
     const {state} = this;
291 303
     let startDate = null;
292 304
     if (
293 305
       this.dates != null &&
294 306
       this.datesString != null &&
295 307
       this.dates.date_result_begin != null
296
-    )
308
+    ) {
297 309
       startDate = this.getDateString(
298 310
         this.dates.date_result_begin,
299 311
         this.datesString.date_result_begin,
300 312
       );
313
+    }
301 314
     return (
302 315
       <VoteWait
303 316
         startDate={startDate}
@@ -314,12 +327,15 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
314 327
   reloadData = () => {
315 328
     let canRefresh;
316 329
     const {lastRefresh} = this;
317
-    if (lastRefresh != null)
330
+    if (lastRefresh != null) {
318 331
       canRefresh =
319 332
         new Date().getTime() - lastRefresh.getTime() > MIN_REFRESH_TIME;
320
-    else canRefresh = true;
321
-    if (canRefresh && this.authRef.current != null)
333
+    } else {
334
+      canRefresh = true;
335
+    }
336
+    if (canRefresh && this.authRef.current != null) {
322 337
       this.authRef.current.reload();
338
+    }
323 339
   };
324 340
 
325 341
   showMascotDialog = () => {
@@ -380,8 +396,12 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
380 396
           date_result_begin: dateResultBegin,
381 397
           date_result_end: dateResultEnd,
382 398
         };
383
-      } else this.dates = null;
384
-    } else this.dates = null;
399
+      } else {
400
+        this.dates = null;
401
+      }
402
+    } else {
403
+      this.dates = null;
404
+    }
385 405
   }
386 406
 
387 407
   /**
@@ -391,11 +411,11 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
391 411
    *
392 412
    * @returns {*}
393 413
    */
394
-  render(): React.Node {
414
+  render() {
395 415
     const {props, state} = this;
396 416
     return (
397 417
       <View style={{flex: 1}}>
398
-        <AuthenticatedScreen
418
+        <AuthenticatedScreen<TeamResponseType | VoteDatesStringType>
399 419
           navigation={props.navigation}
400 420
           ref={this.authRef}
401 421
           requests={[
@@ -418,7 +438,6 @@ export default class VoteScreen extends React.Component<PropsType, StateType> {
418 438
           message={i18n.t('screens.vote.mascotDialog.message')}
419 439
           icon="vote"
420 440
           buttons={{
421
-            action: null,
422 441
             cancel: {
423 442
               message: i18n.t('screens.vote.mascotDialog.button'),
424 443
               icon: 'check',

+ 1
- 3
src/utils/WebData.ts View File

@@ -35,8 +35,6 @@ export type ApiDataLoginType = {
35 35
   token: string;
36 36
 };
37 37
 
38
-export type ApiGenericDataType = {[key: string]: any};
39
-
40 38
 type ApiResponseType<T> = {
41 39
   error: number;
42 40
   data: T;
@@ -70,7 +68,7 @@ export function isApiResponseValid<T>(response: ApiResponseType<T>): boolean {
70 68
  * @param path The API path from the API endpoint
71 69
  * @param method The HTTP method to use (GET or POST)
72 70
  * @param params The params to use for this request
73
- * @returns {Promise<ApiGenericDataType>}
71
+ * @returns {Promise<T>}
74 72
  */
75 73
 export async function apiRequest<T>(
76 74
   path: string,

Loading…
Cancel
Save