Browse Source

Update list components to use TypeScript

Arnaud Vergnet 1 year ago
parent
commit
e4adcd0057

src/components/Lists/CardList/CardList.js → src/components/Lists/CardList/CardList.tsx View File

@@ -17,19 +17,16 @@
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, Dimensions} from 'react-native';
24
-import type {ViewStyle} from 'react-native/Libraries/StyleSheet/StyleSheet';
21
+import {Animated, Dimensions, ViewStyle} from 'react-native';
25 22
 import ImageListItem from './ImageListItem';
26 23
 import CardListItem from './CardListItem';
27 24
 import type {ServiceItemType} from '../../../managers/ServicesManager';
28 25
 
29 26
 type PropsType = {
30
-  dataset: Array<ServiceItemType>,
31
-  isHorizontal?: boolean,
32
-  contentContainerStyle?: ViewStyle | null,
27
+  dataset: Array<ServiceItemType>;
28
+  isHorizontal?: boolean;
29
+  contentContainerStyle?: ViewStyle;
33 30
 };
34 31
 
35 32
 export default class CardList extends React.Component<PropsType> {
@@ -45,12 +42,12 @@ export default class CardList extends React.Component<PropsType> {
45 42
   constructor(props: PropsType) {
46 43
     super(props);
47 44
     this.windowWidth = Dimensions.get('window').width;
48
-    this.horizontalItemSize = this.windowWidth / 4; // So that we can fit 3 items and a part of the 4th => user knows he can scroll
45
+    this.horizontalItemSize = this.windowWidth / 4; // So that we can fit 3 items, and a part of the 4th => user knows he can scroll
49 46
   }
50 47
 
51
-  getRenderItem = ({item}: {item: ServiceItemType}): React.Node => {
48
+  getRenderItem = ({item}: {item: ServiceItemType}) => {
52 49
     const {props} = this;
53
-    if (props.isHorizontal)
50
+    if (props.isHorizontal) {
54 51
       return (
55 52
         <ImageListItem
56 53
           item={item}
@@ -58,12 +55,13 @@ export default class CardList extends React.Component<PropsType> {
58 55
           width={this.horizontalItemSize}
59 56
         />
60 57
       );
58
+    }
61 59
     return <CardListItem item={item} key={item.title} />;
62 60
   };
63 61
 
64 62
   keyExtractor = (item: ServiceItemType): string => item.key;
65 63
 
66
-  render(): React.Node {
64
+  render() {
67 65
     const {props} = this;
68 66
     let containerStyle = {};
69 67
     if (props.isHorizontal) {
@@ -84,7 +82,7 @@ export default class CardList extends React.Component<PropsType> {
84 82
         }
85 83
         pagingEnabled={props.isHorizontal}
86 84
         snapToInterval={
87
-          props.isHorizontal ? (this.horizontalItemSize + 5) * 3 : null
85
+          props.isHorizontal ? (this.horizontalItemSize + 5) * 3 : undefined
88 86
         }
89 87
       />
90 88
     );

src/components/Lists/CardList/CardListItem.js → src/components/Lists/CardList/CardListItem.tsx View File

@@ -17,45 +17,38 @@
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 {Caption, Card, Paragraph, TouchableRipple} from 'react-native-paper';
24 22
 import {View} from 'react-native';
25 23
 import type {ServiceItemType} from '../../../managers/ServicesManager';
26 24
 
27 25
 type PropsType = {
28
-  item: ServiceItemType,
26
+  item: ServiceItemType;
29 27
 };
30 28
 
31
-export default class CardListItem extends React.Component<PropsType> {
32
-  shouldComponentUpdate(): boolean {
33
-    return false;
34
-  }
35
-
36
-  render(): React.Node {
37
-    const {props} = this;
38
-    const {item} = props;
39
-    const source =
40
-      typeof item.image === 'number' ? item.image : {uri: item.image};
41
-    return (
42
-      <Card
43
-        style={{
44
-          width: '40%',
45
-          margin: 5,
46
-          marginLeft: 'auto',
47
-          marginRight: 'auto',
48
-        }}>
49
-        <TouchableRipple style={{flex: 1}} onPress={item.onPress}>
50
-          <View>
51
-            <Card.Cover style={{height: 80}} source={source} />
52
-            <Card.Content>
53
-              <Paragraph>{item.title}</Paragraph>
54
-              <Caption>{item.subtitle}</Caption>
55
-            </Card.Content>
56
-          </View>
57
-        </TouchableRipple>
58
-      </Card>
59
-    );
60
-  }
29
+function CardListItem(props: PropsType) {
30
+  const {item} = props;
31
+  const source =
32
+    typeof item.image === 'number' ? item.image : {uri: item.image};
33
+  return (
34
+    <Card
35
+      style={{
36
+        width: '40%',
37
+        margin: 5,
38
+        marginLeft: 'auto',
39
+        marginRight: 'auto',
40
+      }}>
41
+      <TouchableRipple style={{flex: 1}} onPress={item.onPress}>
42
+        <View>
43
+          <Card.Cover style={{height: 80}} source={source} />
44
+          <Card.Content>
45
+            <Paragraph>{item.title}</Paragraph>
46
+            <Caption>{item.subtitle}</Caption>
47
+          </Card.Content>
48
+        </View>
49
+      </TouchableRipple>
50
+    </Card>
51
+  );
61 52
 }
53
+
54
+export default React.memo(CardListItem, () => true);

+ 0
- 73
src/components/Lists/CardList/ImageListItem.js View File

@@ -1,73 +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 {Text, TouchableRipple} from 'react-native-paper';
24
-import {Image, View} from 'react-native';
25
-import type {ServiceItemType} from '../../../managers/ServicesManager';
26
-
27
-type PropsType = {
28
-  item: ServiceItemType,
29
-  width: number,
30
-};
31
-
32
-export default class ImageListItem extends React.Component<PropsType> {
33
-  shouldComponentUpdate(): boolean {
34
-    return false;
35
-  }
36
-
37
-  render(): React.Node {
38
-    const {props} = this;
39
-    const {item} = props;
40
-    const source =
41
-      typeof item.image === 'number' ? item.image : {uri: item.image};
42
-    return (
43
-      <TouchableRipple
44
-        style={{
45
-          width: props.width,
46
-          height: props.width + 40,
47
-          margin: 5,
48
-        }}
49
-        onPress={item.onPress}>
50
-        <View>
51
-          <Image
52
-            style={{
53
-              width: props.width - 20,
54
-              height: props.width - 20,
55
-              marginLeft: 'auto',
56
-              marginRight: 'auto',
57
-            }}
58
-            source={source}
59
-          />
60
-          <Text
61
-            style={{
62
-              marginTop: 5,
63
-              marginLeft: 'auto',
64
-              marginRight: 'auto',
65
-              textAlign: 'center',
66
-            }}>
67
-            {item.title}
68
-          </Text>
69
-        </View>
70
-      </TouchableRipple>
71
-    );
72
-  }
73
-}

+ 66
- 0
src/components/Lists/CardList/ImageListItem.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 {Text, TouchableRipple} from 'react-native-paper';
22
+import {Image, View} from 'react-native';
23
+import type {ServiceItemType} from '../../../managers/ServicesManager';
24
+
25
+type PropsType = {
26
+  item: ServiceItemType;
27
+  width: number;
28
+};
29
+
30
+function ImageListItem(props: PropsType) {
31
+  const {item} = props;
32
+  const source =
33
+    typeof item.image === 'number' ? item.image : {uri: item.image};
34
+  return (
35
+    <TouchableRipple
36
+      style={{
37
+        width: props.width,
38
+        height: props.width + 40,
39
+        margin: 5,
40
+      }}
41
+      onPress={item.onPress}>
42
+      <View>
43
+        <Image
44
+          style={{
45
+            width: props.width - 20,
46
+            height: props.width - 20,
47
+            marginLeft: 'auto',
48
+            marginRight: 'auto',
49
+          }}
50
+          source={source}
51
+        />
52
+        <Text
53
+          style={{
54
+            marginTop: 5,
55
+            marginLeft: 'auto',
56
+            marginRight: 'auto',
57
+            textAlign: 'center',
58
+          }}>
59
+          {item.title}
60
+        </Text>
61
+      </View>
62
+    </TouchableRipple>
63
+  );
64
+}
65
+
66
+export default React.memo(ImageListItem, () => true);

src/components/Lists/Clubs/ClubListHeader.js → src/components/Lists/Clubs/ClubListHeader.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 {Card, Chip, List, Text} from 'react-native-paper';
24 22
 import {StyleSheet, View} from 'react-native';
@@ -26,12 +24,11 @@ import i18n from 'i18n-js';
26 24
 import AnimatedAccordion from '../../Animations/AnimatedAccordion';
27 25
 import {isItemInCategoryFilter} from '../../../utils/Search';
28 26
 import type {ClubCategoryType} from '../../../screens/Amicale/Clubs/ClubListScreen';
29
-import type {ListIconPropsType} from '../../../constants/PaperStyles';
30 27
 
31 28
 type PropsType = {
32
-  categories: Array<ClubCategoryType>,
33
-  onChipSelect: (id: number) => void,
34
-  selectedCategories: Array<number>,
29
+  categories: Array<ClubCategoryType>;
30
+  onChipSelect: (id: number) => void;
31
+  selectedCategories: Array<number>;
35 32
 };
36 33
 
37 34
 const styles = StyleSheet.create({
@@ -54,16 +51,8 @@ const styles = StyleSheet.create({
54 51
   },
55 52
 });
56 53
 
57
-class ClubListHeader extends React.Component<PropsType> {
58
-  shouldComponentUpdate(nextProps: PropsType): boolean {
59
-    const {props} = this;
60
-    return (
61
-      nextProps.selectedCategories.length !== props.selectedCategories.length
62
-    );
63
-  }
64
-
65
-  getChipRender = (category: ClubCategoryType, key: string): React.Node => {
66
-    const {props} = this;
54
+function ClubListHeader(props: PropsType) {
55
+  const getChipRender = (category: ClubCategoryType, key: string) => {
67 56
     const onPress = (): void => props.onChipSelect(category.id);
68 57
     return (
69 58
       <Chip
@@ -80,32 +69,39 @@ class ClubListHeader extends React.Component<PropsType> {
80 69
     );
81 70
   };
82 71
 
83
-  getCategoriesRender(): React.Node {
84
-    const {props} = this;
85
-    const final = [];
72
+  const getCategoriesRender = () => {
73
+    const final: Array<React.ReactNode> = [];
86 74
     props.categories.forEach((cat: ClubCategoryType) => {
87
-      final.push(this.getChipRender(cat, cat.id.toString()));
75
+      final.push(getChipRender(cat, cat.id.toString()));
88 76
     });
89 77
     return final;
90
-  }
78
+  };
91 79
 
92
-  render(): React.Node {
93
-    return (
94
-      <Card style={styles.card}>
95
-        <AnimatedAccordion
96
-          title={i18n.t('screens.clubs.categories')}
97
-          left={(props: ListIconPropsType): React.Node => (
98
-            <List.Icon color={props.color} style={props.style} icon="star" />
99
-          )}
100
-          opened>
101
-          <Text style={styles.text}>
102
-            {i18n.t('screens.clubs.categoriesFilterMessage')}
103
-          </Text>
104
-          <View style={styles.chipContainer}>{this.getCategoriesRender()}</View>
105
-        </AnimatedAccordion>
106
-      </Card>
107
-    );
108
-  }
80
+  return (
81
+    <Card style={styles.card}>
82
+      <AnimatedAccordion
83
+        title={i18n.t('screens.clubs.categories')}
84
+        left={(iconProps) => (
85
+          <List.Icon
86
+            color={iconProps.color}
87
+            style={iconProps.style}
88
+            icon="star"
89
+          />
90
+        )}
91
+        opened>
92
+        <Text style={styles.text}>
93
+          {i18n.t('screens.clubs.categoriesFilterMessage')}
94
+        </Text>
95
+        <View style={styles.chipContainer}>{getCategoriesRender()}</View>
96
+      </AnimatedAccordion>
97
+    </Card>
98
+  );
109 99
 }
110 100
 
111
-export default ClubListHeader;
101
+const areEqual = (prevProps: PropsType, nextProps: PropsType): boolean => {
102
+  return (
103
+    prevProps.selectedCategories.length === nextProps.selectedCategories.length
104
+  );
105
+};
106
+
107
+export default React.memo(ClubListHeader, areEqual);

src/components/Lists/Clubs/ClubListItem.js → src/components/Lists/Clubs/ClubListItem.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, Chip, List, withTheme} from 'react-native-paper';
24 22
 import {View} from 'react-native';
@@ -26,14 +24,13 @@ import type {
26 24
   ClubCategoryType,
27 25
   ClubType,
28 26
 } from '../../../screens/Amicale/Clubs/ClubListScreen';
29
-import type {CustomThemeType} from '../../../managers/ThemeManager';
30 27
 
31 28
 type PropsType = {
32
-  onPress: () => void,
33
-  categoryTranslator: (id: number) => ClubCategoryType,
34
-  item: ClubType,
35
-  height: number,
36
-  theme: CustomThemeType,
29
+  onPress: () => void;
30
+  categoryTranslator: (id: number) => ClubCategoryType;
31
+  item: ClubType;
32
+  height: number;
33
+  theme: ReactNativePaper.Theme;
37 34
 };
38 35
 
39 36
 class ClubListItem extends React.Component<PropsType> {
@@ -48,9 +45,9 @@ class ClubListItem extends React.Component<PropsType> {
48 45
     return false;
49 46
   }
50 47
 
51
-  getCategoriesRender(categories: Array<number | null>): React.Node {
48
+  getCategoriesRender(categories: Array<number | null>) {
52 49
     const {props} = this;
53
-    const final = [];
50
+    const final: Array<React.ReactNode> = [];
54 51
     categories.forEach((cat: number | null) => {
55 52
       if (cat != null) {
56 53
         const category: ClubCategoryType = props.categoryTranslator(cat);
@@ -66,9 +63,9 @@ class ClubListItem extends React.Component<PropsType> {
66 63
     return <View style={{flexDirection: 'row'}}>{final}</View>;
67 64
   }
68 65
 
69
-  render(): React.Node {
66
+  render() {
70 67
     const {props} = this;
71
-    const categoriesRender = (): React.Node =>
68
+    const categoriesRender = () =>
72 69
       this.getCategoriesRender(props.item.category);
73 70
     const {colors} = props.theme;
74 71
     return (
@@ -76,7 +73,7 @@ class ClubListItem extends React.Component<PropsType> {
76 73
         title={props.item.name}
77 74
         description={categoriesRender}
78 75
         onPress={props.onPress}
79
-        left={(): React.Node => (
76
+        left={() => (
80 77
           <Avatar.Image
81 78
             style={{
82 79
               backgroundColor: 'transparent',
@@ -87,7 +84,7 @@ class ClubListItem extends React.Component<PropsType> {
87 84
             source={{uri: props.item.logo}}
88 85
           />
89 86
         )}
90
-        right={(): React.Node => (
87
+        right={() => (
91 88
           <Avatar.Icon
92 89
             style={{
93 90
               marginTop: 'auto',

+ 0
- 108
src/components/Lists/DashboardEdit/DashboardEditAccordion.js View File

@@ -1,108 +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 {withTheme} from 'react-native-paper';
24
-import {FlatList, Image, View} from 'react-native';
25
-import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
26
-import DashboardEditItem from './DashboardEditItem';
27
-import AnimatedAccordion from '../../Animations/AnimatedAccordion';
28
-import type {
29
-  ServiceCategoryType,
30
-  ServiceItemType,
31
-} from '../../../managers/ServicesManager';
32
-import type {CustomThemeType} from '../../../managers/ThemeManager';
33
-
34
-type PropsType = {
35
-  item: ServiceCategoryType,
36
-  activeDashboard: Array<string>,
37
-  onPress: (service: ServiceItemType) => void,
38
-  theme: CustomThemeType,
39
-};
40
-
41
-const LIST_ITEM_HEIGHT = 64;
42
-
43
-class DashboardEditAccordion extends React.Component<PropsType> {
44
-  getRenderItem = ({item}: {item: ServiceItemType}): React.Node => {
45
-    const {props} = this;
46
-    return (
47
-      <DashboardEditItem
48
-        height={LIST_ITEM_HEIGHT}
49
-        item={item}
50
-        isActive={props.activeDashboard.includes(item.key)}
51
-        onPress={() => {
52
-          props.onPress(item);
53
-        }}
54
-      />
55
-    );
56
-  };
57
-
58
-  getItemLayout = (
59
-    data: ?Array<ServiceItemType>,
60
-    index: number,
61
-  ): {length: number, offset: number, index: number} => ({
62
-    length: LIST_ITEM_HEIGHT,
63
-    offset: LIST_ITEM_HEIGHT * index,
64
-    index,
65
-  });
66
-
67
-  render(): React.Node {
68
-    const {props} = this;
69
-    const {item} = props;
70
-    return (
71
-      <View>
72
-        <AnimatedAccordion
73
-          title={item.title}
74
-          left={(): React.Node =>
75
-            typeof item.image === 'number' ? (
76
-              <Image
77
-                source={item.image}
78
-                style={{
79
-                  width: 40,
80
-                  height: 40,
81
-                }}
82
-              />
83
-            ) : (
84
-              <MaterialCommunityIcons
85
-                // $FlowFixMe
86
-                name={item.image}
87
-                color={props.theme.colors.primary}
88
-                size={40}
89
-              />
90
-            )
91
-          }>
92
-          {/* $FlowFixMe */}
93
-          <FlatList
94
-            data={item.content}
95
-            extraData={props.activeDashboard.toString()}
96
-            renderItem={this.getRenderItem}
97
-            listKey={item.key}
98
-            // Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
99
-            getItemLayout={this.getItemLayout}
100
-            removeClippedSubviews
101
-          />
102
-        </AnimatedAccordion>
103
-      </View>
104
-    );
105
-  }
106
-}
107
-
108
-export default withTheme(DashboardEditAccordion);

+ 100
- 0
src/components/Lists/DashboardEdit/DashboardEditAccordion.tsx View File

@@ -0,0 +1,100 @@
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 {useTheme} from 'react-native-paper';
22
+import {FlatList, Image, View} from 'react-native';
23
+import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
24
+import DashboardEditItem from './DashboardEditItem';
25
+import AnimatedAccordion from '../../Animations/AnimatedAccordion';
26
+import type {
27
+  ServiceCategoryType,
28
+  ServiceItemType,
29
+} from '../../../managers/ServicesManager';
30
+
31
+type PropsType = {
32
+  item: ServiceCategoryType;
33
+  activeDashboard: Array<string>;
34
+  onPress: (service: ServiceItemType) => void;
35
+};
36
+
37
+const LIST_ITEM_HEIGHT = 64;
38
+
39
+function DashboardEditAccordion(props: PropsType) {
40
+  const theme = useTheme();
41
+
42
+  const getRenderItem = ({item}: {item: ServiceItemType}) => {
43
+    return (
44
+      <DashboardEditItem
45
+        height={LIST_ITEM_HEIGHT}
46
+        item={item}
47
+        isActive={props.activeDashboard.includes(item.key)}
48
+        onPress={() => {
49
+          props.onPress(item);
50
+        }}
51
+      />
52
+    );
53
+  };
54
+
55
+  const getItemLayout = (
56
+    data: Array<ServiceItemType> | null | undefined,
57
+    index: number,
58
+  ): {length: number; offset: number; index: number} => ({
59
+    length: LIST_ITEM_HEIGHT,
60
+    offset: LIST_ITEM_HEIGHT * index,
61
+    index,
62
+  });
63
+
64
+  const {item} = props;
65
+  return (
66
+    <View>
67
+      <AnimatedAccordion
68
+        title={item.title}
69
+        left={() =>
70
+          typeof item.image === 'number' ? (
71
+            <Image
72
+              source={item.image}
73
+              style={{
74
+                width: 40,
75
+                height: 40,
76
+              }}
77
+            />
78
+          ) : (
79
+            <MaterialCommunityIcons
80
+              name={item.image}
81
+              color={theme.colors.primary}
82
+              size={40}
83
+            />
84
+          )
85
+        }>
86
+        <FlatList
87
+          data={item.content}
88
+          extraData={props.activeDashboard.toString()}
89
+          renderItem={getRenderItem}
90
+          listKey={item.key}
91
+          // Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
92
+          getItemLayout={getItemLayout}
93
+          removeClippedSubviews
94
+        />
95
+      </AnimatedAccordion>
96
+    </View>
97
+  );
98
+}
99
+
100
+export default DashboardEditAccordion;

+ 0
- 81
src/components/Lists/DashboardEdit/DashboardEditItem.js View File

@@ -1,81 +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} from 'react-native';
24
-import {List, withTheme} from 'react-native-paper';
25
-import type {CustomThemeType} from '../../../managers/ThemeManager';
26
-import type {ServiceItemType} from '../../../managers/ServicesManager';
27
-import type {ListIconPropsType} from '../../../constants/PaperStyles';
28
-
29
-type PropsType = {
30
-  item: ServiceItemType,
31
-  isActive: boolean,
32
-  height: number,
33
-  onPress: () => void,
34
-  theme: CustomThemeType,
35
-};
36
-
37
-class DashboardEditItem extends React.Component<PropsType> {
38
-  shouldComponentUpdate(nextProps: PropsType): boolean {
39
-    const {isActive} = this.props;
40
-    return nextProps.isActive !== isActive;
41
-  }
42
-
43
-  render(): React.Node {
44
-    const {item, onPress, height, isActive, theme} = this.props;
45
-    return (
46
-      <List.Item
47
-        title={item.title}
48
-        description={item.subtitle}
49
-        onPress={isActive ? null : onPress}
50
-        left={(): React.Node => (
51
-          <Image
52
-            source={{uri: item.image}}
53
-            style={{
54
-              width: 40,
55
-              height: 40,
56
-            }}
57
-          />
58
-        )}
59
-        right={(props: ListIconPropsType): React.Node =>
60
-          isActive ? (
61
-            <List.Icon
62
-              style={props.style}
63
-              icon="check"
64
-              color={theme.colors.success}
65
-            />
66
-          ) : null
67
-        }
68
-        style={{
69
-          height,
70
-          justifyContent: 'center',
71
-          paddingLeft: 30,
72
-          backgroundColor: isActive
73
-            ? theme.colors.proxiwashFinishedColor
74
-            : 'transparent',
75
-        }}
76
-      />
77
-    );
78
-  }
79
-}
80
-
81
-export default withTheme(DashboardEditItem);

+ 76
- 0
src/components/Lists/DashboardEdit/DashboardEditItem.tsx View File

@@ -0,0 +1,76 @@
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} from 'react-native';
22
+import {List, useTheme} from 'react-native-paper';
23
+import type {ServiceItemType} from '../../../managers/ServicesManager';
24
+
25
+type PropsType = {
26
+  item: ServiceItemType;
27
+  isActive: boolean;
28
+  height: number;
29
+  onPress: () => void;
30
+};
31
+
32
+function DashboardEditItem(props: PropsType) {
33
+  const theme = useTheme();
34
+  const {item, onPress, height, isActive} = props;
35
+  return (
36
+    <List.Item
37
+      title={item.title}
38
+      description={item.subtitle}
39
+      onPress={isActive ? undefined : onPress}
40
+      left={() => (
41
+        <Image
42
+          source={
43
+            typeof item.image === 'string' ? {uri: item.image} : item.image
44
+          }
45
+          style={{
46
+            width: 40,
47
+            height: 40,
48
+          }}
49
+        />
50
+      )}
51
+      right={(iconProps) =>
52
+        isActive ? (
53
+          <List.Icon
54
+            style={iconProps.style}
55
+            icon="check"
56
+            color={theme.colors.success}
57
+          />
58
+        ) : null
59
+      }
60
+      style={{
61
+        height,
62
+        justifyContent: 'center',
63
+        paddingLeft: 30,
64
+        backgroundColor: isActive
65
+          ? theme.colors.proxiwashFinishedColor
66
+          : 'transparent',
67
+      }}
68
+    />
69
+  );
70
+}
71
+
72
+const areEqual = (prevProps: PropsType, nextProps: PropsType): boolean => {
73
+  return nextProps.isActive !== prevProps.isActive;
74
+};
75
+
76
+export default React.memo(DashboardEditItem, areEqual);

+ 0
- 77
src/components/Lists/DashboardEdit/DashboardEditPreviewItem.js View File

@@ -1,77 +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 {TouchableRipple, withTheme} from 'react-native-paper';
24
-import {Dimensions, Image, View} from 'react-native';
25
-import type {CustomThemeType} from '../../../managers/ThemeManager';
26
-
27
-type PropsType = {
28
-  image: string,
29
-  isActive: boolean,
30
-  onPress: () => void,
31
-  theme: CustomThemeType,
32
-};
33
-
34
-/**
35
- * Component used to render a small dashboard item
36
- */
37
-class DashboardEditPreviewItem extends React.Component<PropsType> {
38
-  itemSize: number;
39
-
40
-  constructor(props: PropsType) {
41
-    super(props);
42
-    this.itemSize = Dimensions.get('window').width / 8;
43
-  }
44
-
45
-  render(): React.Node {
46
-    const {props} = this;
47
-    return (
48
-      <TouchableRipple
49
-        onPress={props.onPress}
50
-        borderless
51
-        style={{
52
-          marginLeft: 5,
53
-          marginRight: 5,
54
-          backgroundColor: props.isActive
55
-            ? props.theme.colors.textDisabled
56
-            : 'transparent',
57
-          borderRadius: 5,
58
-        }}>
59
-        <View
60
-          style={{
61
-            width: this.itemSize,
62
-            height: this.itemSize,
63
-          }}>
64
-          <Image
65
-            source={{uri: props.image}}
66
-            style={{
67
-              width: '100%',
68
-              height: '100%',
69
-            }}
70
-          />
71
-        </View>
72
-      </TouchableRipple>
73
-    );
74
-  }
75
-}
76
-
77
-export default withTheme(DashboardEditPreviewItem);

+ 66
- 0
src/components/Lists/DashboardEdit/DashboardEditPreviewItem.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 {TouchableRipple, useTheme} from 'react-native-paper';
22
+import {Dimensions, Image, View} from 'react-native';
23
+
24
+type PropsType = {
25
+  image: string;
26
+  isActive: boolean;
27
+  onPress: () => void;
28
+};
29
+
30
+/**
31
+ * Component used to render a small dashboard item
32
+ */
33
+function DashboardEditPreviewItem(props: PropsType) {
34
+  const theme = useTheme();
35
+  const itemSize = Dimensions.get('window').width / 8;
36
+
37
+  return (
38
+    <TouchableRipple
39
+      onPress={props.onPress}
40
+      borderless
41
+      style={{
42
+        marginLeft: 5,
43
+        marginRight: 5,
44
+        backgroundColor: props.isActive
45
+          ? theme.colors.textDisabled
46
+          : 'transparent',
47
+        borderRadius: 5,
48
+      }}>
49
+      <View
50
+        style={{
51
+          width: itemSize,
52
+          height: itemSize,
53
+        }}>
54
+        <Image
55
+          source={{uri: props.image}}
56
+          style={{
57
+            width: '100%',
58
+            height: '100%',
59
+          }}
60
+        />
61
+      </View>
62
+    </TouchableRipple>
63
+  );
64
+}
65
+
66
+export default DashboardEditPreviewItem;

+ 0
- 132
src/components/Lists/Equipment/EquipmentListItem.js View File

@@ -1,132 +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, List, withTheme} from 'react-native-paper';
24
-import i18n from 'i18n-js';
25
-import {StackNavigationProp} from '@react-navigation/stack';
26
-import type {CustomThemeType} from '../../../managers/ThemeManager';
27
-import type {DeviceType} from '../../../screens/Amicale/Equipment/EquipmentListScreen';
28
-import {
29
-  getFirstEquipmentAvailability,
30
-  getRelativeDateString,
31
-  isEquipmentAvailable,
32
-} from '../../../utils/EquipmentBooking';
33
-
34
-type PropsType = {
35
-  navigation: StackNavigationProp,
36
-  userDeviceRentDates: [string, string],
37
-  item: DeviceType,
38
-  height: number,
39
-  theme: CustomThemeType,
40
-};
41
-
42
-class EquipmentListItem extends React.Component<PropsType> {
43
-  shouldComponentUpdate(nextProps: PropsType): boolean {
44
-    const {userDeviceRentDates} = this.props;
45
-    return nextProps.userDeviceRentDates !== userDeviceRentDates;
46
-  }
47
-
48
-  render(): React.Node {
49
-    const {item, userDeviceRentDates, navigation, height, theme} = this.props;
50
-    const isRented = userDeviceRentDates != null;
51
-    const isAvailable = isEquipmentAvailable(item);
52
-    const firstAvailability = getFirstEquipmentAvailability(item);
53
-
54
-    let onPress;
55
-    if (isRented)
56
-      onPress = () => {
57
-        navigation.navigate('equipment-confirm', {
58
-          item,
59
-          dates: userDeviceRentDates,
60
-        });
61
-      };
62
-    else
63
-      onPress = () => {
64
-        navigation.navigate('equipment-rent', {item});
65
-      };
66
-
67
-    let description;
68
-    if (isRented) {
69
-      const start = new Date(userDeviceRentDates[0]);
70
-      const end = new Date(userDeviceRentDates[1]);
71
-      if (start.getTime() !== end.getTime())
72
-        description = i18n.t('screens.equipment.bookingPeriod', {
73
-          begin: getRelativeDateString(start),
74
-          end: getRelativeDateString(end),
75
-        });
76
-      else
77
-        description = i18n.t('screens.equipment.bookingDay', {
78
-          date: getRelativeDateString(start),
79
-        });
80
-    } else if (isAvailable)
81
-      description = i18n.t('screens.equipment.bail', {cost: item.caution});
82
-    else
83
-      description = i18n.t('screens.equipment.available', {
84
-        date: getRelativeDateString(firstAvailability),
85
-      });
86
-
87
-    let icon;
88
-    if (isRented) icon = 'bookmark-check';
89
-    else if (isAvailable) icon = 'check-circle-outline';
90
-    else icon = 'update';
91
-
92
-    let color;
93
-    if (isRented) color = theme.colors.warning;
94
-    else if (isAvailable) color = theme.colors.success;
95
-    else color = theme.colors.primary;
96
-
97
-    return (
98
-      <List.Item
99
-        title={item.name}
100
-        description={description}
101
-        onPress={onPress}
102
-        left={({size}: {size: number}): React.Node => (
103
-          <Avatar.Icon
104
-            size={size}
105
-            style={{
106
-              backgroundColor: 'transparent',
107
-            }}
108
-            icon={icon}
109
-            color={color}
110
-          />
111
-        )}
112
-        right={(): React.Node => (
113
-          <Avatar.Icon
114
-            style={{
115
-              marginTop: 'auto',
116
-              marginBottom: 'auto',
117
-              backgroundColor: 'transparent',
118
-            }}
119
-            size={48}
120
-            icon="chevron-right"
121
-          />
122
-        )}
123
-        style={{
124
-          height,
125
-          justifyContent: 'center',
126
-        }}
127
-      />
128
-    );
129
-  }
130
-}
131
-
132
-export default withTheme(EquipmentListItem);

+ 136
- 0
src/components/Lists/Equipment/EquipmentListItem.tsx View File

@@ -0,0 +1,136 @@
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, List, useTheme} from 'react-native-paper';
22
+import i18n from 'i18n-js';
23
+import {StackNavigationProp} from '@react-navigation/stack';
24
+import type {DeviceType} from '../../../screens/Amicale/Equipment/EquipmentListScreen';
25
+import {
26
+  getFirstEquipmentAvailability,
27
+  getRelativeDateString,
28
+  isEquipmentAvailable,
29
+} from '../../../utils/EquipmentBooking';
30
+
31
+type PropsType = {
32
+  navigation: StackNavigationProp<any>;
33
+  userDeviceRentDates: [string, string];
34
+  item: DeviceType;
35
+  height: number;
36
+};
37
+
38
+function EquipmentListItem(props: PropsType) {
39
+  const theme = useTheme();
40
+  const {item, userDeviceRentDates, navigation, height} = props;
41
+  const isRented = userDeviceRentDates != null;
42
+  const isAvailable = isEquipmentAvailable(item);
43
+  const firstAvailability = getFirstEquipmentAvailability(item);
44
+
45
+  let onPress;
46
+  if (isRented) {
47
+    onPress = () => {
48
+      navigation.navigate('equipment-confirm', {
49
+        item,
50
+        dates: userDeviceRentDates,
51
+      });
52
+    };
53
+  } else {
54
+    onPress = () => {
55
+      navigation.navigate('equipment-rent', {item});
56
+    };
57
+  }
58
+
59
+  let description;
60
+  if (isRented) {
61
+    const start = new Date(userDeviceRentDates[0]);
62
+    const end = new Date(userDeviceRentDates[1]);
63
+    if (start.getTime() !== end.getTime()) {
64
+      description = i18n.t('screens.equipment.bookingPeriod', {
65
+        begin: getRelativeDateString(start),
66
+        end: getRelativeDateString(end),
67
+      });
68
+    } else {
69
+      description = i18n.t('screens.equipment.bookingDay', {
70
+        date: getRelativeDateString(start),
71
+      });
72
+    }
73
+  } else if (isAvailable) {
74
+    description = i18n.t('screens.equipment.bail', {cost: item.caution});
75
+  } else {
76
+    description = i18n.t('screens.equipment.available', {
77
+      date: getRelativeDateString(firstAvailability),
78
+    });
79
+  }
80
+
81
+  let icon: string;
82
+  if (isRented) {
83
+    icon = 'bookmark-check';
84
+  } else if (isAvailable) {
85
+    icon = 'check-circle-outline';
86
+  } else {
87
+    icon = 'update';
88
+  }
89
+
90
+  let color: string;
91
+  if (isRented) {
92
+    color = theme.colors.warning;
93
+  } else if (isAvailable) {
94
+    color = theme.colors.success;
95
+  } else {
96
+    color = theme.colors.primary;
97
+  }
98
+
99
+  return (
100
+    <List.Item
101
+      title={item.name}
102
+      description={description}
103
+      onPress={onPress}
104
+      left={() => (
105
+        <Avatar.Icon
106
+          style={{
107
+            backgroundColor: 'transparent',
108
+          }}
109
+          icon={icon}
110
+          color={color}
111
+        />
112
+      )}
113
+      right={() => (
114
+        <Avatar.Icon
115
+          style={{
116
+            marginTop: 'auto',
117
+            marginBottom: 'auto',
118
+            backgroundColor: 'transparent',
119
+          }}
120
+          size={48}
121
+          icon="chevron-right"
122
+        />
123
+      )}
124
+      style={{
125
+        height,
126
+        justifyContent: 'center',
127
+      }}
128
+    />
129
+  );
130
+}
131
+
132
+const areEqual = (prevProps: PropsType, nextProps: PropsType): boolean => {
133
+  return nextProps.userDeviceRentDates !== prevProps.userDeviceRentDates;
134
+};
135
+
136
+export default React.memo(EquipmentListItem, areEqual);

src/components/Lists/PlanexGroups/GroupListAccordion.js → src/components/Lists/PlanexGroups/GroupListAccordion.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 {List, withTheme} from 'react-native-paper';
24 22
 import {FlatList, View} from 'react-native';
@@ -29,17 +27,15 @@ import type {
29 27
   PlanexGroupType,
30 28
   PlanexGroupCategoryType,
31 29
 } from '../../../screens/Planex/GroupSelectionScreen';
32
-import type {CustomThemeType} from '../../../managers/ThemeManager';
33
-import type {ListIconPropsType} from '../../../constants/PaperStyles';
34 30
 
35 31
 type PropsType = {
36
-  item: PlanexGroupCategoryType,
37
-  favorites: Array<PlanexGroupType>,
38
-  onGroupPress: (PlanexGroupType) => void,
39
-  onFavoritePress: (PlanexGroupType) => void,
40
-  currentSearchString: string,
41
-  height: number,
42
-  theme: CustomThemeType,
32
+  item: PlanexGroupCategoryType;
33
+  favorites: Array<PlanexGroupType>;
34
+  onGroupPress: (data: PlanexGroupType) => void;
35
+  onFavoritePress: (data: PlanexGroupType) => void;
36
+  currentSearchString: string;
37
+  height: number;
38
+  theme: ReactNativePaper.Theme;
43 39
 };
44 40
 
45 41
 const LIST_ITEM_HEIGHT = 64;
@@ -55,7 +51,7 @@ class GroupListAccordion extends React.Component<PropsType> {
55 51
     );
56 52
   }
57 53
 
58
-  getRenderItem = ({item}: {item: PlanexGroupType}): React.Node => {
54
+  getRenderItem = ({item}: {item: PlanexGroupType}) => {
59 55
     const {props} = this;
60 56
     const onPress = () => {
61 57
       props.onGroupPress(item);
@@ -77,18 +73,19 @@ class GroupListAccordion extends React.Component<PropsType> {
77 73
   getData(): Array<PlanexGroupType> {
78 74
     const {props} = this;
79 75
     const originalData = props.item.content;
80
-    const displayData = [];
76
+    const displayData: Array<PlanexGroupType> = [];
81 77
     originalData.forEach((data: PlanexGroupType) => {
82
-      if (stringMatchQuery(data.name, props.currentSearchString))
78
+      if (stringMatchQuery(data.name, props.currentSearchString)) {
83 79
         displayData.push(data);
80
+      }
84 81
     });
85 82
     return displayData;
86 83
   }
87 84
 
88 85
   itemLayout = (
89
-    data: ?Array<PlanexGroupType>,
86
+    data: Array<PlanexGroupType> | null | undefined,
90 87
     index: number,
91
-  ): {length: number, offset: number, index: number} => ({
88
+  ): {length: number; offset: number; index: number} => ({
92 89
     length: LIST_ITEM_HEIGHT,
93 90
     offset: LIST_ITEM_HEIGHT * index,
94 91
     index,
@@ -96,7 +93,7 @@ class GroupListAccordion extends React.Component<PropsType> {
96 93
 
97 94
   keyExtractor = (item: PlanexGroupType): string => item.id.toString();
98 95
 
99
-  render(): React.Node {
96
+  render() {
100 97
     const {props} = this;
101 98
     const {item} = this.props;
102 99
     return (
@@ -107,7 +104,7 @@ class GroupListAccordion extends React.Component<PropsType> {
107 104
             height: props.height,
108 105
             justifyContent: 'center',
109 106
           }}
110
-          left={(iconProps: ListIconPropsType): React.Node =>
107
+          left={(iconProps) =>
111 108
             item.id === 0 ? (
112 109
               <List.Icon
113 110
                 style={iconProps.style}

src/components/Lists/PlanexGroups/GroupListItem.js → src/components/Lists/PlanexGroups/GroupListItem.tsx View File

@@ -17,23 +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 21
 import {List, TouchableRipple, withTheme} from 'react-native-paper';
24 22
 import * as Animatable from 'react-native-animatable';
25 23
 import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
26
-import type {CustomThemeType} from '../../../managers/ThemeManager';
27 24
 import type {PlanexGroupType} from '../../../screens/Planex/GroupSelectionScreen';
28
-import type {ListIconPropsType} from '../../../constants/PaperStyles';
29 25
 
30 26
 type PropsType = {
31
-  theme: CustomThemeType,
32
-  onPress: () => void,
33
-  onStarPress: () => void,
34
-  item: PlanexGroupType,
35
-  favorites: Array<PlanexGroupType>,
36
-  height: number,
27
+  theme: ReactNativePaper.Theme;
28
+  onPress: () => void;
29
+  onStarPress: () => void;
30
+  item: PlanexGroupType;
31
+  favorites: Array<PlanexGroupType>;
32
+  height: number;
37 33
 };
38 34
 
39 35
 const REPLACE_REGEX = /_/g;
@@ -41,10 +37,11 @@ const REPLACE_REGEX = /_/g;
41 37
 class GroupListItem extends React.Component<PropsType> {
42 38
   isFav: boolean;
43 39
 
44
-  starRef: null | Animatable.View;
40
+  starRef: {current: null | Animatable.View};
45 41
 
46 42
   constructor(props: PropsType) {
47 43
     super(props);
44
+    this.starRef = React.createRef<Animatable.View>();
48 45
     this.isFav = this.isGroupInFavorites(props.favorites);
49 46
   }
50 47
 
@@ -52,7 +49,9 @@ class GroupListItem extends React.Component<PropsType> {
52 49
     const {favorites} = this.props;
53 50
     const favChanged = favorites.length !== nextProps.favorites.length;
54 51
     let newFavState = this.isFav;
55
-    if (favChanged) newFavState = this.isGroupInFavorites(nextProps.favorites);
52
+    if (favChanged) {
53
+      newFavState = this.isGroupInFavorites(nextProps.favorites);
54
+    }
56 55
     const shouldUpdate = this.isFav !== newFavState;
57 56
     this.isFav = newFavState;
58 57
     return shouldUpdate;
@@ -61,9 +60,12 @@ class GroupListItem extends React.Component<PropsType> {
61 60
   onStarPress = () => {
62 61
     const {props} = this;
63 62
     const ref = this.starRef;
64
-    if (ref != null) {
65
-      if (this.isFav) ref.rubberBand();
66
-      else ref.swing();
63
+    if (ref.current) {
64
+      if (this.isFav) {
65
+        ref.current.rubberBand();
66
+      } else {
67
+        ref.current.swing();
68
+      }
67 69
     }
68 70
     props.onStarPress();
69 71
   };
@@ -71,31 +73,29 @@ class GroupListItem extends React.Component<PropsType> {
71 73
   isGroupInFavorites(favorites: Array<PlanexGroupType>): boolean {
72 74
     const {item} = this.props;
73 75
     for (let i = 0; i < favorites.length; i += 1) {
74
-      if (favorites[i].id === item.id) return true;
76
+      if (favorites[i].id === item.id) {
77
+        return true;
78
+      }
75 79
     }
76 80
     return false;
77 81
   }
78 82
 
79
-  render(): React.Node {
83
+  render() {
80 84
     const {props} = this;
81 85
     const {colors} = props.theme;
82 86
     return (
83 87
       <List.Item
84 88
         title={props.item.name.replace(REPLACE_REGEX, ' ')}
85 89
         onPress={props.onPress}
86
-        left={(iconProps: ListIconPropsType): React.Node => (
90
+        left={(iconProps) => (
87 91
           <List.Icon
88 92
             color={iconProps.color}
89 93
             style={iconProps.style}
90 94
             icon="chevron-right"
91 95
           />
92 96
         )}
93
-        right={(iconProps: ListIconPropsType): React.Node => (
94
-          <Animatable.View
95
-            ref={(ref: Animatable.View) => {
96
-              this.starRef = ref;
97
-            }}
98
-            useNativeDriver>
97
+        right={(iconProps) => (
98
+          <Animatable.View ref={this.starRef} useNativeDriver>
99 99
             <TouchableRipple
100 100
               onPress={this.onStarPress}
101 101
               style={{

+ 0
- 68
src/components/Lists/Proximo/ProximoListItem.js View File

@@ -1,68 +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, List, Text, withTheme} from 'react-native-paper';
24
-import i18n from 'i18n-js';
25
-import type {ProximoArticleType} from '../../../screens/Services/Proximo/ProximoMainScreen';
26
-
27
-type PropsType = {
28
-  onPress: () => void,
29
-  color: string,
30
-  item: ProximoArticleType,
31
-  height: number,
32
-};
33
-
34
-class ProximoListItem extends React.Component<PropsType> {
35
-  shouldComponentUpdate(): boolean {
36
-    return false;
37
-  }
38
-
39
-  render(): React.Node {
40
-    const {props} = this;
41
-    return (
42
-      <List.Item
43
-        title={props.item.name}
44
-        description={`${props.item.quantity} ${i18n.t(
45
-          'screens.proximo.inStock',
46
-        )}`}
47
-        descriptionStyle={{color: props.color}}
48
-        onPress={props.onPress}
49
-        left={(): React.Node => (
50
-          <Avatar.Image
51
-            style={{backgroundColor: 'transparent'}}
52
-            size={64}
53
-            source={{uri: props.item.image}}
54
-          />
55
-        )}
56
-        right={(): React.Node => (
57
-          <Text style={{fontWeight: 'bold'}}>{props.item.price}€</Text>
58
-        )}
59
-        style={{
60
-          height: props.height,
61
-          justifyContent: 'center',
62
-        }}
63
-      />
64
-    );
65
-  }
66
-}
67
-
68
-export default withTheme(ProximoListItem);

+ 59
- 0
src/components/Lists/Proximo/ProximoListItem.tsx View File

@@ -0,0 +1,59 @@
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, List, Text} from 'react-native-paper';
22
+import i18n from 'i18n-js';
23
+import type {ProximoArticleType} from '../../../screens/Services/Proximo/ProximoMainScreen';
24
+
25
+type PropsType = {
26
+  onPress: () => void;
27
+  color: string;
28
+  item: ProximoArticleType;
29
+  height: number;
30
+};
31
+
32
+function ProximoListItem(props: PropsType) {
33
+  return (
34
+    <List.Item
35
+      title={props.item.name}
36
+      description={`${props.item.quantity} ${i18n.t(
37
+        'screens.proximo.inStock',
38
+      )}`}
39
+      descriptionStyle={{color: props.color}}
40
+      onPress={props.onPress}
41
+      left={() => (
42
+        <Avatar.Image
43
+          style={{backgroundColor: 'transparent'}}
44
+          size={64}
45
+          source={{uri: props.item.image}}
46
+        />
47
+      )}
48
+      right={() => (
49
+        <Text style={{fontWeight: 'bold'}}>{props.item.price}€</Text>
50
+      )}
51
+      style={{
52
+        height: props.height,
53
+        justifyContent: 'center',
54
+      }}
55
+    />
56
+  );
57
+}
58
+
59
+export default React.memo(ProximoListItem, () => true);

src/components/Lists/Proxiwash/ProxiwashListItem.js → src/components/Lists/Proxiwash/ProxiwashListItem.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,
@@ -34,20 +32,19 @@ import i18n from 'i18n-js';
34 32
 import * as Animatable from 'react-native-animatable';
35 33
 import ProxiwashConstants from '../../../constants/ProxiwashConstants';
36 34
 import AprilFoolsManager from '../../../managers/AprilFoolsManager';
37
-import type {CustomThemeType} from '../../../managers/ThemeManager';
38 35
 import type {ProxiwashMachineType} from '../../../screens/Proxiwash/ProxiwashScreen';
39 36
 
40 37
 type PropsType = {
41
-  item: ProxiwashMachineType,
42
-  theme: CustomThemeType,
38
+  item: ProxiwashMachineType;
39
+  theme: ReactNativePaper.Theme;
43 40
   onPress: (
44 41
     title: string,
45 42
     item: ProxiwashMachineType,
46 43
     isDryer: boolean,
47
-  ) => void,
48
-  isWatched: boolean,
49
-  isDryer: boolean,
50
-  height: number,
44
+  ) => void;
45
+  isWatched: boolean;
46
+  isDryer: boolean;
47
+  height: number;
51 48
 };
52 49
 
53 50
 const AnimatedIcon = Animatable.createAnimatableComponent(Avatar.Icon);
@@ -89,10 +86,11 @@ class ProxiwashListItem extends React.Component<PropsType> {
89 86
 
90 87
     let displayNumber = props.item.number;
91 88
     const displayMaxWeight = props.item.maxWeight;
92
-    if (AprilFoolsManager.getInstance().isAprilFoolsEnabled())
89
+    if (AprilFoolsManager.getInstance().isAprilFoolsEnabled()) {
93 90
       displayNumber = AprilFoolsManager.getProxiwashMachineDisplayNumber(
94 91
         parseInt(props.item.number, 10),
95 92
       );
93
+    }
96 94
 
97 95
     this.title = props.isDryer
98 96
       ? i18n.t('screens.proxiwash.dryer')
@@ -159,10 +157,10 @@ class ProxiwashListItem extends React.Component<PropsType> {
159 157
       colors.proxiwashUnknownColor;
160 158
   }
161 159
 
162
-  render(): React.Node {
160
+  render() {
163 161
     const {props} = this;
164 162
     const {colors} = props.theme;
165
-    const machineState = props.item.state;
163
+    const machineState = parseInt(props.item.state, 10);
166 164
     const isRunning = machineState === ProxiwashConstants.machineStates.RUNNING;
167 165
     const isReady = machineState === ProxiwashConstants.machineStates.AVAILABLE;
168 166
     const description = isRunning
@@ -171,10 +169,13 @@ class ProxiwashListItem extends React.Component<PropsType> {
171 169
     const stateIcon = ProxiwashConstants.stateIcons[machineState];
172 170
     const stateString = this.stateStrings[machineState];
173 171
     let progress;
174
-    if (isRunning && props.item.donePercent !== '')
172
+    if (isRunning && props.item.donePercent !== '') {
175 173
       progress = parseFloat(props.item.donePercent) / 100;
176
-    else if (isRunning) progress = 0;
177
-    else progress = 1;
174
+    } else if (isRunning) {
175
+      progress = 0;
176
+    } else {
177
+      progress = 1;
178
+    }
178 179
 
179 180
     const icon = props.isWatched ? (
180 181
       <AnimatedIcon
@@ -224,8 +225,8 @@ class ProxiwashListItem extends React.Component<PropsType> {
224 225
             justifyContent: 'center',
225 226
           }}
226 227
           onPress={this.onListItemPress}
227
-          left={(): React.Node => icon}
228
-          right={(): React.Node => (
228
+          left={() => icon}
229
+          right={() => (
229 230
             <View style={{flexDirection: 'row'}}>
230 231
               <View style={{justifyContent: 'center'}}>
231 232
                 <Text

src/components/Lists/Proxiwash/ProxiwashSectionHeader.js → src/components/Lists/Proxiwash/ProxiwashSectionHeader.tsx View File

@@ -17,19 +17,16 @@
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, Text, withTheme} from 'react-native-paper';
24 22
 import {StyleSheet, View} from 'react-native';
25 23
 import i18n from 'i18n-js';
26
-import type {CustomThemeType} from '../../../managers/ThemeManager';
27 24
 
28 25
 type PropsType = {
29
-  theme: CustomThemeType,
30
-  title: string,
31
-  isDryer: boolean,
32
-  nbAvailable: number,
26
+  theme: ReactNativePaper.Theme;
27
+  title: string;
28
+  isDryer: boolean;
29
+  nbAvailable: number;
33 30
 };
34 31
 
35 32
 const styles = StyleSheet.create({
@@ -61,7 +58,7 @@ class ProxiwashListItem extends React.Component<PropsType> {
61 58
     );
62 59
   }
63 60
 
64
-  render(): React.Node {
61
+  render() {
65 62
     const {props} = this;
66 63
     const subtitle = `${props.nbAvailable} ${
67 64
       props.nbAvailable <= 1

+ 1
- 1
src/managers/ServicesManager.ts View File

@@ -94,7 +94,7 @@ export type ServiceItemType = {
94 94
   key: string;
95 95
   title: string;
96 96
   subtitle: string;
97
-  image: string;
97
+  image: string | number;
98 98
   onPress: () => void;
99 99
   badgeFunction?: (dashboard: FullDashboardType) => number;
100 100
 };

Loading…
Cancel
Save