Browse Source

Update clubs screens to use TypeScript

Arnaud Vergnet 3 years ago
parent
commit
5977ce257b

+ 11
- 9
src/components/Lists/Clubs/ClubListItem.tsx View File

@@ -27,7 +27,7 @@ import type {
27 27
 
28 28
 type PropsType = {
29 29
   onPress: () => void;
30
-  categoryTranslator: (id: number) => ClubCategoryType;
30
+  categoryTranslator: (id: number) => ClubCategoryType | null;
31 31
   item: ClubType;
32 32
   height: number;
33 33
   theme: ReactNativePaper.Theme;
@@ -50,14 +50,16 @@ class ClubListItem extends React.Component<PropsType> {
50 50
     const final: Array<React.ReactNode> = [];
51 51
     categories.forEach((cat: number | null) => {
52 52
       if (cat != null) {
53
-        const category: ClubCategoryType = props.categoryTranslator(cat);
54
-        final.push(
55
-          <Chip
56
-            style={{marginRight: 5, marginBottom: 5}}
57
-            key={`${props.item.id}:${category.id}`}>
58
-            {category.name}
59
-          </Chip>,
60
-        );
53
+        const category = props.categoryTranslator(cat);
54
+        if (category) {
55
+          final.push(
56
+            <Chip
57
+              style={{marginRight: 5, marginBottom: 5}}
58
+              key={`${props.item.id}:${category.id}`}>
59
+              {category.name}
60
+            </Chip>,
61
+          );
62
+        }
61 63
       }
62 64
     });
63 65
     return <View style={{flexDirection: 'row'}}>{final}</View>;

+ 0
- 72
src/screens/Amicale/Clubs/ClubAboutScreen.js View File

@@ -1,72 +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 {Image, View} from 'react-native';
24
-import {Card, Avatar, Text, withTheme} from 'react-native-paper';
25
-import i18n from 'i18n-js';
26
-import Autolink from 'react-native-autolink';
27
-import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
28
-import AMICALE_ICON from '../../../../assets/amicale.png';
29
-import type {CardTitleIconPropsType} from '../../../constants/PaperStyles';
30
-
31
-const CONTACT_LINK = 'clubs@amicale-insat.fr';
32
-
33
-// eslint-disable-next-line react/prefer-stateless-function
34
-class ClubAboutScreen extends React.Component<null> {
35
-  render(): React.Node {
36
-    return (
37
-      <CollapsibleScrollView style={{padding: 5}}>
38
-        <View
39
-          style={{
40
-            width: '100%',
41
-            height: 100,
42
-            marginTop: 20,
43
-            marginBottom: 20,
44
-            justifyContent: 'center',
45
-            alignItems: 'center',
46
-          }}>
47
-          <Image
48
-            source={AMICALE_ICON}
49
-            style={{flex: 1, resizeMode: 'contain'}}
50
-            resizeMode="contain"
51
-          />
52
-        </View>
53
-        <Text>{i18n.t('screens.clubs.about.text')}</Text>
54
-        <Card style={{margin: 5}}>
55
-          <Card.Title
56
-            title={i18n.t('screens.clubs.about.title')}
57
-            subtitle={i18n.t('screens.clubs.about.subtitle')}
58
-            left={(iconProps: CardTitleIconPropsType): React.Node => (
59
-              <Avatar.Icon size={iconProps.size} icon="information" />
60
-            )}
61
-          />
62
-          <Card.Content>
63
-            <Text>{i18n.t('screens.clubs.about.message')}</Text>
64
-            <Autolink text={CONTACT_LINK} component={Text} />
65
-          </Card.Content>
66
-        </Card>
67
-      </CollapsibleScrollView>
68
-    );
69
-  }
70
-}
71
-
72
-export default withTheme(ClubAboutScreen);

+ 66
- 0
src/screens/Amicale/Clubs/ClubAboutScreen.tsx View File

@@ -0,0 +1,66 @@
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 {Image, View} from 'react-native';
22
+import {Card, Avatar, Text} from 'react-native-paper';
23
+import i18n from 'i18n-js';
24
+import Autolink from 'react-native-autolink';
25
+import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
26
+const AMICALE_ICON = require('../../../../assets/amicale.png');
27
+
28
+const CONTACT_LINK = 'clubs@amicale-insat.fr';
29
+
30
+function ClubAboutScreen() {
31
+  return (
32
+    <CollapsibleScrollView style={{padding: 5}}>
33
+      <View
34
+        style={{
35
+          width: '100%',
36
+          height: 100,
37
+          marginTop: 20,
38
+          marginBottom: 20,
39
+          justifyContent: 'center',
40
+          alignItems: 'center',
41
+        }}>
42
+        <Image
43
+          source={AMICALE_ICON}
44
+          style={{flex: 1, resizeMode: 'contain'}}
45
+          resizeMode="contain"
46
+        />
47
+      </View>
48
+      <Text>{i18n.t('screens.clubs.about.text')}</Text>
49
+      <Card style={{margin: 5}}>
50
+        <Card.Title
51
+          title={i18n.t('screens.clubs.about.title')}
52
+          subtitle={i18n.t('screens.clubs.about.subtitle')}
53
+          left={(iconProps) => (
54
+            <Avatar.Icon size={iconProps.size} icon="information" />
55
+          )}
56
+        />
57
+        <Card.Content>
58
+          <Text>{i18n.t('screens.clubs.about.message')}</Text>
59
+          <Autolink<typeof Text> text={CONTACT_LINK} component={Text} />
60
+        </Card.Content>
61
+      </Card>
62
+    </CollapsibleScrollView>
63
+  );
64
+}
65
+
66
+export default ClubAboutScreen;

src/screens/Amicale/Clubs/ClubDisplayScreen.js → src/screens/Amicale/Clubs/ClubDisplayScreen.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 {Linking, View} from 'react-native';
24 22
 import {
@@ -35,24 +33,20 @@ import AuthenticatedScreen from '../../../components/Amicale/AuthenticatedScreen
35 33
 import CustomHTML from '../../../components/Overrides/CustomHTML';
36 34
 import CustomTabBar from '../../../components/Tabbar/CustomTabBar';
37 35
 import type {ClubCategoryType, ClubType} from './ClubListScreen';
38
-import type {CustomThemeType} from '../../../managers/ThemeManager';
39 36
 import {ERROR_TYPE} from '../../../utils/WebData';
40 37
 import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
41
-import type {ApiGenericDataType} from '../../../utils/WebData';
42
-import type {CardTitleIconPropsType} from '../../../constants/PaperStyles';
43 38
 import ImageGalleryButton from '../../../components/Media/ImageGalleryButton';
44 39
 
45 40
 type PropsType = {
46
-  navigation: StackNavigationProp,
41
+  navigation: StackNavigationProp<any>;
47 42
   route: {
48 43
     params?: {
49
-      data?: ClubType,
50
-      categories?: Array<ClubCategoryType>,
51
-      clubId?: number,
52
-    },
53
-    ...
54
-  },
55
-  theme: CustomThemeType,
44
+      data?: ClubType;
45
+      categories?: Array<ClubCategoryType>;
46
+      clubId?: number;
47
+    };
48
+  };
49
+  theme: ReactNativePaper.Theme;
56 50
 };
57 51
 
58 52
 const AMICALE_MAIL = 'clubs@amicale-insat.fr';
@@ -73,21 +67,20 @@ class ClubDisplayScreen extends React.Component<PropsType> {
73 67
 
74 68
   constructor(props: PropsType) {
75 69
     super(props);
76
-    if (props.route.params != null) {
77
-      if (
78
-        props.route.params.data != null &&
79
-        props.route.params.categories != null
80
-      ) {
81
-        this.displayData = props.route.params.data;
82
-        this.categories = props.route.params.categories;
83
-        this.clubId = props.route.params.data.id;
84
-        this.shouldFetchData = false;
85
-      } else if (props.route.params.clubId != null) {
86
-        this.displayData = null;
87
-        this.categories = null;
88
-        this.clubId = props.route.params.clubId;
89
-        this.shouldFetchData = true;
90
-      }
70
+    this.displayData = null;
71
+    this.categories = null;
72
+    this.clubId = props.route.params?.clubId ? props.route.params.clubId : 0;
73
+    this.shouldFetchData = true;
74
+
75
+    if (
76
+      props.route.params &&
77
+      props.route.params.data &&
78
+      props.route.params.categories
79
+    ) {
80
+      this.displayData = props.route.params.data;
81
+      this.categories = props.route.params.categories;
82
+      this.clubId = props.route.params.data.id;
83
+      this.shouldFetchData = false;
91 84
     }
92 85
   }
93 86
 
@@ -101,7 +94,9 @@ class ClubDisplayScreen extends React.Component<PropsType> {
101 94
     let categoryName = '';
102 95
     if (this.categories !== null) {
103 96
       this.categories.forEach((item: ClubCategoryType) => {
104
-        if (id === item.id) categoryName = item.name;
97
+        if (id === item.id) {
98
+          categoryName = item.name;
99
+        }
105 100
       });
106 101
     }
107 102
     return categoryName;
@@ -113,10 +108,12 @@ class ClubDisplayScreen extends React.Component<PropsType> {
113 108
    * @param categories The categories to display (max 2)
114 109
    * @returns {null|*}
115 110
    */
116
-  getCategoriesRender(categories: Array<number | null>): React.Node {
117
-    if (this.categories == null) return null;
111
+  getCategoriesRender(categories: Array<number | null>) {
112
+    if (this.categories == null) {
113
+      return null;
114
+    }
118 115
 
119
-    const final = [];
116
+    const final: Array<React.ReactNode> = [];
120 117
     categories.forEach((cat: number | null) => {
121 118
       if (cat != null) {
122 119
         final.push(
@@ -136,9 +133,9 @@ class ClubDisplayScreen extends React.Component<PropsType> {
136 133
    * @param email The club contact email
137 134
    * @returns {*}
138 135
    */
139
-  getManagersRender(managers: Array<string>, email: string | null): React.Node {
136
+  getManagersRender(managers: Array<string>, email: string | null) {
140 137
     const {props} = this;
141
-    const managersListView = [];
138
+    const managersListView: Array<React.ReactNode> = [];
142 139
     managers.forEach((item: string) => {
143 140
       managersListView.push(<Paragraph key={item}>{item}</Paragraph>);
144 141
     });
@@ -153,7 +150,7 @@ class ClubDisplayScreen extends React.Component<PropsType> {
153 150
               ? i18n.t('screens.clubs.managersSubtitle')
154 151
               : i18n.t('screens.clubs.managersUnavailable')
155 152
           }
156
-          left={(iconProps: CardTitleIconPropsType): React.Node => (
153
+          left={(iconProps) => (
157 154
             <Avatar.Icon
158 155
               size={iconProps.size}
159 156
               style={{backgroundColor: 'transparent'}}
@@ -181,10 +178,7 @@ class ClubDisplayScreen extends React.Component<PropsType> {
181 178
    * @param hasManagers True if the club has managers
182 179
    * @returns {*}
183 180
    */
184
-  static getEmailButton(
185
-    email: string | null,
186
-    hasManagers: boolean,
187
-  ): React.Node {
181
+  static getEmailButton(email: string | null, hasManagers: boolean) {
188 182
     const destinationEmail =
189 183
       email != null && hasManagers ? email : AMICALE_MAIL;
190 184
     const text =
@@ -206,20 +200,15 @@ class ClubDisplayScreen extends React.Component<PropsType> {
206 200
     );
207 201
   }
208 202
 
209
-  getScreen = (response: Array<ApiGenericDataType | null>): React.Node => {
210
-    const {navigation} = this.props;
211
-    let data: ClubType | null = null;
212
-    if (response[0] != null) {
213
-      [data] = response;
214
-      this.updateHeaderTitle(data);
215
-    }
203
+  getScreen = (response: Array<ClubType | null>) => {
204
+    let data: ClubType | null = response[0];
216 205
     if (data != null) {
206
+      this.updateHeaderTitle(data);
217 207
       return (
218 208
         <CollapsibleScrollView style={{paddingLeft: 5, paddingRight: 5}} hasTab>
219 209
           {this.getCategoriesRender(data.category)}
220 210
           {data.logo !== null ? (
221 211
             <ImageGalleryButton
222
-              navigation={navigation}
223 212
               images={[{url: data.logo}]}
224 213
               style={{
225 214
                 width: 300,
@@ -259,9 +248,9 @@ class ClubDisplayScreen extends React.Component<PropsType> {
259 248
     props.navigation.setOptions({title: data.name});
260 249
   }
261 250
 
262
-  render(): React.Node {
251
+  render() {
263 252
     const {props} = this;
264
-    if (this.shouldFetchData)
253
+    if (this.shouldFetchData) {
265 254
       return (
266 255
         <AuthenticatedScreen
267 256
           navigation={props.navigation}
@@ -283,6 +272,7 @@ class ClubDisplayScreen extends React.Component<PropsType> {
283 272
           ]}
284 273
         />
285 274
       );
275
+    }
286 276
     return this.getScreen([this.displayData]);
287 277
   }
288 278
 }

src/screens/Amicale/Clubs/ClubListScreen.js → src/screens/Amicale/Clubs/ClubListScreen.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 {Platform} from 'react-native';
24 22
 import {Searchbar} from 'react-native-paper';
@@ -34,27 +32,27 @@ import MaterialHeaderButtons, {
34 32
 import CollapsibleFlatList from '../../../components/Collapsible/CollapsibleFlatList';
35 33
 
36 34
 export type ClubCategoryType = {
37
-  id: number,
38
-  name: string,
35
+  id: number;
36
+  name: string;
39 37
 };
40 38
 
41 39
 export type ClubType = {
42
-  id: number,
43
-  name: string,
44
-  description: string,
45
-  logo: string,
46
-  email: string | null,
47
-  category: Array<number | null>,
48
-  responsibles: Array<string>,
40
+  id: number;
41
+  name: string;
42
+  description: string;
43
+  logo: string;
44
+  email: string | null;
45
+  category: Array<number | null>;
46
+  responsibles: Array<string>;
49 47
 };
50 48
 
51 49
 type PropsType = {
52
-  navigation: StackNavigationProp,
50
+  navigation: StackNavigationProp<any>;
53 51
 };
54 52
 
55 53
 type StateType = {
56
-  currentlySelectedCategories: Array<number>,
57
-  currentSearchString: string,
54
+  currentlySelectedCategories: Array<number>;
55
+  currentSearchString: string;
58 56
 };
59 57
 
60 58
 const LIST_ITEM_HEIGHT = 96;
@@ -62,8 +60,9 @@ const LIST_ITEM_HEIGHT = 96;
62 60
 class ClubListScreen extends React.Component<PropsType, StateType> {
63 61
   categories: Array<ClubCategoryType>;
64 62
 
65
-  constructor() {
66
-    super();
63
+  constructor(props: PropsType) {
64
+    super(props);
65
+    this.categories = [];
67 66
     this.state = {
68 67
       currentlySelectedCategories: [],
69 68
       currentSearchString: '',
@@ -114,7 +113,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
114 113
    *
115 114
    * @return {*}
116 115
    */
117
-  getSearchBar = (): React.Node => {
116
+  getSearchBar = () => {
118 117
     return (
119 118
       <Searchbar
120 119
         placeholder={i18n.t('screens.proximo.search')}
@@ -131,7 +130,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
131 130
    * Gets the header button
132 131
    * @return {*}
133 132
    */
134
-  getHeaderButtons = (): React.Node => {
133
+  getHeaderButtons = () => {
135 134
     const onPress = () => {
136 135
       const {props} = this;
137 136
       props.navigation.navigate('club-about');
@@ -145,12 +144,12 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
145 144
 
146 145
   getScreen = (
147 146
     data: Array<{
148
-      categories: Array<ClubCategoryType>,
149
-      clubs: Array<ClubType>,
147
+      categories: Array<ClubCategoryType>;
148
+      clubs: Array<ClubType>;
150 149
     } | null>,
151
-  ): React.Node => {
152
-    let categoryList = [];
153
-    let clubList = [];
150
+  ) => {
151
+    let categoryList: Array<ClubCategoryType> = [];
152
+    let clubList: Array<ClubType> = [];
154 153
     if (data[0] != null) {
155 154
       categoryList = data[0].categories;
156 155
       clubList = data[0].clubs;
@@ -174,7 +173,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
174 173
    *
175 174
    * @returns {*}
176 175
    */
177
-  getListHeader(): React.Node {
176
+  getListHeader() {
178 177
     const {state} = this;
179 178
     return (
180 179
       <ClubListHeader
@@ -194,12 +193,14 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
194 193
   getCategoryOfId = (id: number): ClubCategoryType | null => {
195 194
     let cat = null;
196 195
     this.categories.forEach((item: ClubCategoryType) => {
197
-      if (id === item.id) cat = item;
196
+      if (id === item.id) {
197
+        cat = item;
198
+      }
198 199
     });
199 200
     return cat;
200 201
   };
201 202
 
202
-  getRenderItem = ({item}: {item: ClubType}): React.Node => {
203
+  getRenderItem = ({item}: {item: ClubType}) => {
203 204
     const onPress = () => {
204 205
       this.onListItemPress(item);
205 206
     };
@@ -219,9 +220,9 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
219 220
   keyExtractor = (item: ClubType): string => item.id.toString();
220 221
 
221 222
   itemLayout = (
222
-    data: {...},
223
+    data: Array<ClubType> | null | undefined,
223 224
     index: number,
224
-  ): {length: number, offset: number, index: number} => ({
225
+  ): {length: number; offset: number; index: number} => ({
225 226
     length: LIST_ITEM_HEIGHT,
226 227
     offset: LIST_ITEM_HEIGHT * index,
227 228
     index,
@@ -240,17 +241,23 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
240 241
     const {state} = this;
241 242
     const newCategoriesState = [...state.currentlySelectedCategories];
242 243
     let newStrState = state.currentSearchString;
243
-    if (filterStr !== null) newStrState = filterStr;
244
+    if (filterStr !== null) {
245
+      newStrState = filterStr;
246
+    }
244 247
     if (categoryId !== null) {
245 248
       const index = newCategoriesState.indexOf(categoryId);
246
-      if (index === -1) newCategoriesState.push(categoryId);
247
-      else newCategoriesState.splice(index, 1);
249
+      if (index === -1) {
250
+        newCategoriesState.push(categoryId);
251
+      } else {
252
+        newCategoriesState.splice(index, 1);
253
+      }
248 254
     }
249
-    if (filterStr !== null || categoryId !== null)
255
+    if (filterStr !== null || categoryId !== null) {
250 256
       this.setState({
251 257
         currentSearchString: newStrState,
252 258
         currentlySelectedCategories: newCategoriesState,
253 259
       });
260
+    }
254 261
   }
255 262
 
256 263
   /**
@@ -264,12 +271,13 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
264 271
     let shouldRender =
265 272
       state.currentlySelectedCategories.length === 0 ||
266 273
       isItemInCategoryFilter(state.currentlySelectedCategories, item.category);
267
-    if (shouldRender)
274
+    if (shouldRender) {
268 275
       shouldRender = stringMatchQuery(item.name, state.currentSearchString);
276
+    }
269 277
     return shouldRender;
270 278
   }
271 279
 
272
-  render(): React.Node {
280
+  render() {
273 281
     const {props} = this;
274 282
     return (
275 283
       <AuthenticatedScreen

Loading…
Cancel
Save