Browse Source

Improve Services components to match linter

Arnaud Vergnet 1 year ago
parent
commit
33d98b024b

+ 59
- 58
src/components/Lists/CardList/CardList.js View File

@@ -1,72 +1,73 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import {Animated, Dimensions} from "react-native";
5
-import ImageListItem from "./ImageListItem";
6
-import CardListItem from "./CardListItem";
7
-import type {ViewStyle} from "react-native/Libraries/StyleSheet/StyleSheet";
4
+import {Animated, Dimensions} from 'react-native';
5
+import type {ViewStyle} from 'react-native/Libraries/StyleSheet/StyleSheet';
6
+import ImageListItem from './ImageListItem';
7
+import CardListItem from './CardListItem';
8
+import type {ServiceItemType} from '../../../managers/ServicesManager';
8 9
 
9
-type Props = {
10
-    dataset: Array<cardItem>,
11
-    isHorizontal: boolean,
12
-    contentContainerStyle?: ViewStyle,
13
-}
14
-
15
-export type cardItem = {
16
-    key: string,
17
-    title: string,
18
-    subtitle: string,
19
-    image: string | number,
20
-    onPress: () => void,
10
+type PropsType = {
11
+  dataset: Array<ServiceItemType>,
12
+  isHorizontal?: boolean,
13
+  contentContainerStyle?: ViewStyle | null,
21 14
 };
22 15
 
23
-export type cardList = Array<cardItem>;
16
+export default class CardList extends React.Component<PropsType> {
17
+  static defaultProps = {
18
+    isHorizontal: false,
19
+    contentContainerStyle: null,
20
+  };
24 21
 
22
+  windowWidth: number;
25 23
 
26
-export default class CardList extends React.Component<Props> {
24
+  horizontalItemSize: number;
27 25
 
28
-    static defaultProps = {
29
-        isHorizontal: false,
30
-    }
26
+  constructor(props: PropsType) {
27
+    super(props);
28
+    this.windowWidth = Dimensions.get('window').width;
29
+    this.horizontalItemSize = this.windowWidth / 4; // So that we can fit 3 items and a part of the 4th => user knows he can scroll
30
+  }
31 31
 
32
-    windowWidth: number;
33
-    horizontalItemSize: number;
34
-
35
-    constructor(props: Props) {
36
-        super(props);
37
-        this.windowWidth = Dimensions.get('window').width;
38
-        this.horizontalItemSize = this.windowWidth/4; // So that we can fit 3 items and a part of the 4th => user knows he can scroll
39
-    }
32
+  getRenderItem = ({item}: {item: ServiceItemType}): React.Node => {
33
+    const {props} = this;
34
+    if (props.isHorizontal)
35
+      return (
36
+        <ImageListItem
37
+          item={item}
38
+          key={item.title}
39
+          width={this.horizontalItemSize}
40
+        />
41
+      );
42
+    return <CardListItem item={item} key={item.title} />;
43
+  };
40 44
 
41
-    renderItem = ({item}: { item: cardItem }) => {
42
-        if (this.props.isHorizontal)
43
-            return <ImageListItem item={item} key={item.title} width={this.horizontalItemSize}/>;
44
-        else
45
-            return <CardListItem item={item} key={item.title}/>;
46
-    };
45
+  keyExtractor = (item: ServiceItemType): string => item.key;
47 46
 
48
-    keyExtractor = (item: cardItem) => item.key;
49
-
50
-    render() {
51
-        let containerStyle = {};
52
-        if (this.props.isHorizontal) {
53
-            containerStyle = {
54
-                height: this.horizontalItemSize + 50,
55
-                justifyContent: 'space-around',
56
-            };
57
-        }
58
-        return (
59
-            <Animated.FlatList
60
-                {...this.props}
61
-                data={this.props.dataset}
62
-                renderItem={this.renderItem}
63
-                keyExtractor={this.keyExtractor}
64
-                numColumns={this.props.isHorizontal ? undefined : 2}
65
-                horizontal={this.props.isHorizontal}
66
-                contentContainerStyle={this.props.isHorizontal ? containerStyle : this.props.contentContainerStyle}
67
-                pagingEnabled={this.props.isHorizontal}
68
-                snapToInterval={this.props.isHorizontal ? (this.horizontalItemSize+5)*3 : null}
69
-            />
70
-        );
47
+  render(): React.Node {
48
+    const {props} = this;
49
+    let containerStyle = {};
50
+    if (props.isHorizontal) {
51
+      containerStyle = {
52
+        height: this.horizontalItemSize + 50,
53
+        justifyContent: 'space-around',
54
+      };
71 55
     }
56
+    return (
57
+      <Animated.FlatList
58
+        data={props.dataset}
59
+        renderItem={this.getRenderItem}
60
+        keyExtractor={this.keyExtractor}
61
+        numColumns={props.isHorizontal ? undefined : 2}
62
+        horizontal={props.isHorizontal}
63
+        contentContainerStyle={
64
+          props.isHorizontal ? containerStyle : props.contentContainerStyle
65
+        }
66
+        pagingEnabled={props.isHorizontal}
67
+        snapToInterval={
68
+          props.isHorizontal ? (this.horizontalItemSize + 5) * 3 : null
69
+        }
70
+      />
71
+    );
72
+  }
72 73
 }

+ 34
- 43
src/components/Lists/CardList/CardListItem.js View File

@@ -2,50 +2,41 @@
2 2
 
3 3
 import * as React from 'react';
4 4
 import {Caption, Card, Paragraph, TouchableRipple} from 'react-native-paper';
5
-import {View} from "react-native";
6
-import type {cardItem} from "./CardList";
5
+import {View} from 'react-native';
6
+import type {ServiceItemType} from '../../../managers/ServicesManager';
7 7
 
8
-type Props = {
9
-    item: cardItem,
10
-}
11
-
12
-export default class CardListItem extends React.Component<Props> {
13
-
14
-    shouldComponentUpdate() {
15
-        return false;
16
-    }
8
+type PropsType = {
9
+  item: ServiceItemType,
10
+};
17 11
 
18
-    render() {
19
-        const props = this.props;
20
-        const item = props.item;
21
-        const source = typeof item.image === "number"
22
-            ? item.image
23
-            : {uri: item.image};
24
-        return (
25
-            <Card
26
-                style={{
27
-                    width: '40%',
28
-                    margin: 5,
29
-                    marginLeft: 'auto',
30
-                    marginRight: 'auto',
31
-                }}
32
-            >
33
-                <TouchableRipple
34
-                    style={{flex: 1}}
35
-                    onPress={item.onPress}>
36
-                    <View>
37
-                        <Card.Cover
38
-                            style={{height: 80}}
39
-                            source={source}
40
-                        />
41
-                        <Card.Content>
42
-                            <Paragraph>{item.title}</Paragraph>
43
-                            <Caption>{item.subtitle}</Caption>
44
-                        </Card.Content>
45
-                    </View>
46
-                </TouchableRipple>
12
+export default class CardListItem extends React.Component<PropsType> {
13
+  shouldComponentUpdate(): boolean {
14
+    return false;
15
+  }
47 16
 
48
-            </Card>
49
-        );
50
-    }
17
+  render(): React.Node {
18
+    const {props} = this;
19
+    const {item} = props;
20
+    const source =
21
+      typeof item.image === 'number' ? item.image : {uri: item.image};
22
+    return (
23
+      <Card
24
+        style={{
25
+          width: '40%',
26
+          margin: 5,
27
+          marginLeft: 'auto',
28
+          marginRight: 'auto',
29
+        }}>
30
+        <TouchableRipple style={{flex: 1}} onPress={item.onPress}>
31
+          <View>
32
+            <Card.Cover style={{height: 80}} source={source} />
33
+            <Card.Content>
34
+              <Paragraph>{item.title}</Paragraph>
35
+              <Caption>{item.subtitle}</Caption>
36
+            </Card.Content>
37
+          </View>
38
+        </TouchableRipple>
39
+      </Card>
40
+    );
41
+  }
51 42
 }

+ 45
- 46
src/components/Lists/CardList/ImageListItem.js View File

@@ -3,53 +3,52 @@
3 3
 import * as React from 'react';
4 4
 import {Text, TouchableRipple} from 'react-native-paper';
5 5
 import {Image, View} from 'react-native';
6
-import type {cardItem} from "./CardList";
6
+import type {ServiceItemType} from '../../../managers/ServicesManager';
7 7
 
8
-type Props = {
9
-    item: cardItem,
10
-    width: number,
11
-}
12
-
13
-export default class ImageListItem extends React.Component<Props> {
8
+type PropsType = {
9
+  item: ServiceItemType,
10
+  width: number,
11
+};
14 12
 
15
-    shouldComponentUpdate() {
16
-        return false;
17
-    }
13
+export default class ImageListItem extends React.Component<PropsType> {
14
+  shouldComponentUpdate(): boolean {
15
+    return false;
16
+  }
18 17
 
19
-    render() {
20
-        const item = this.props.item;
21
-        const source = typeof item.image === "number"
22
-            ? item.image
23
-            : {uri: item.image};
24
-        return (
25
-            <TouchableRipple
26
-                style={{
27
-                    width: this.props.width,
28
-                    height: this.props.width + 40,
29
-                    margin: 5,
30
-                }}
31
-                onPress={item.onPress}
32
-            >
33
-                <View>
34
-                    <Image
35
-                        style={{
36
-                            width: this.props.width - 20,
37
-                            height: this.props.width - 20,
38
-                            marginLeft: 'auto',
39
-                            marginRight: 'auto',
40
-                        }}
41
-                        source={source}
42
-                    />
43
-                    <Text style={{
44
-                        marginTop: 5,
45
-                        marginLeft: 'auto',
46
-                        marginRight: 'auto',
47
-                        textAlign: 'center'
48
-                    }}>
49
-                        {item.title}
50
-                    </Text>
51
-                </View>
52
-            </TouchableRipple>
53
-        );
54
-    }
18
+  render(): React.Node {
19
+    const {props} = this;
20
+    const {item} = props;
21
+    const source =
22
+      typeof item.image === 'number' ? item.image : {uri: item.image};
23
+    return (
24
+      <TouchableRipple
25
+        style={{
26
+          width: props.width,
27
+          height: props.width + 40,
28
+          margin: 5,
29
+        }}
30
+        onPress={item.onPress}>
31
+        <View>
32
+          <Image
33
+            style={{
34
+              width: props.width - 20,
35
+              height: props.width - 20,
36
+              marginLeft: 'auto',
37
+              marginRight: 'auto',
38
+            }}
39
+            source={source}
40
+          />
41
+          <Text
42
+            style={{
43
+              marginTop: 5,
44
+              marginLeft: 'auto',
45
+              marginRight: 'auto',
46
+              textAlign: 'center',
47
+            }}>
48
+            {item.title}
49
+          </Text>
50
+        </View>
51
+      </TouchableRipple>
52
+    );
53
+  }
55 54
 }

+ 2
- 2
src/managers/DashboardManager.js View File

@@ -1,12 +1,12 @@
1 1
 // @flow
2 2
 
3
-import type {ServiceItem} from './ServicesManager';
3
+import type {ServiceItemType} from './ServicesManager';
4 4
 import ServicesManager from './ServicesManager';
5 5
 import {getSublistWithIds} from '../utils/Utils';
6 6
 import AsyncStorageManager from './AsyncStorageManager';
7 7
 
8 8
 export default class DashboardManager extends ServicesManager {
9
-  getCurrentDashboard(): Array<ServiceItem | null> {
9
+  getCurrentDashboard(): Array<ServiceItemType | null> {
10 10
     const dashboardIdList = AsyncStorageManager.getObject(
11 11
       AsyncStorageManager.PREFERENCES.dashboardItems.key,
12 12
     );

+ 352
- 346
src/managers/ServicesManager.js View File

@@ -1,378 +1,384 @@
1 1
 // @flow
2 2
 
3
-import i18n from "i18n-js";
4
-import AvailableWebsites from "../constants/AvailableWebsites";
5
-import {StackNavigationProp} from "@react-navigation/stack";
6
-import ConnectionManager from "./ConnectionManager";
7
-import type {fullDashboard} from "../screens/Home/HomeScreen";
3
+import i18n from 'i18n-js';
4
+import {StackNavigationProp} from '@react-navigation/stack';
5
+import AvailableWebsites from '../constants/AvailableWebsites';
6
+import ConnectionManager from './ConnectionManager';
7
+import type {FullDashboardType} from '../screens/Home/HomeScreen';
8
+import getStrippedServicesList from '../utils/Services';
8 9
 
9 10
 // AMICALE
10
-const CLUBS_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Clubs.png";
11
-const PROFILE_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/ProfilAmicaliste.png";
12
-const EQUIPMENT_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Materiel.png";
13
-const VOTE_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Vote.png";
14
-const AMICALE_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/WebsiteAmicale.png";
11
+const CLUBS_IMAGE =
12
+  'https://etud.insa-toulouse.fr/~amicale_app/images/Clubs.png';
13
+const PROFILE_IMAGE =
14
+  'https://etud.insa-toulouse.fr/~amicale_app/images/ProfilAmicaliste.png';
15
+const EQUIPMENT_IMAGE =
16
+  'https://etud.insa-toulouse.fr/~amicale_app/images/Materiel.png';
17
+const VOTE_IMAGE = 'https://etud.insa-toulouse.fr/~amicale_app/images/Vote.png';
18
+const AMICALE_IMAGE =
19
+  'https://etud.insa-toulouse.fr/~amicale_app/images/WebsiteAmicale.png';
15 20
 
16 21
 // STUDENTS
17
-const PROXIMO_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Proximo.png"
18
-const WIKETUD_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Wiketud.png";
19
-const EE_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/EEC.png";
20
-const TUTORINSA_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/TutorINSA.png";
22
+const PROXIMO_IMAGE =
23
+  'https://etud.insa-toulouse.fr/~amicale_app/images/Proximo.png';
24
+const WIKETUD_IMAGE =
25
+  'https://etud.insa-toulouse.fr/~amicale_app/images/Wiketud.png';
26
+const EE_IMAGE = 'https://etud.insa-toulouse.fr/~amicale_app/images/EEC.png';
27
+const TUTORINSA_IMAGE =
28
+  'https://etud.insa-toulouse.fr/~amicale_app/images/TutorINSA.png';
21 29
 
22 30
 // INSA
23
-const BIB_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Bib.png";
24
-const RU_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/RU.png";
25
-const ROOM_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Salles.png";
26
-const EMAIL_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Bluemind.png";
27
-const ENT_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/ENT.png";
28
-const ACCOUNT_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Account.png";
31
+const BIB_IMAGE = 'https://etud.insa-toulouse.fr/~amicale_app/images/Bib.png';
32
+const RU_IMAGE = 'https://etud.insa-toulouse.fr/~amicale_app/images/RU.png';
33
+const ROOM_IMAGE =
34
+  'https://etud.insa-toulouse.fr/~amicale_app/images/Salles.png';
35
+const EMAIL_IMAGE =
36
+  'https://etud.insa-toulouse.fr/~amicale_app/images/Bluemind.png';
37
+const ENT_IMAGE = 'https://etud.insa-toulouse.fr/~amicale_app/images/ENT.png';
38
+const ACCOUNT_IMAGE =
39
+  'https://etud.insa-toulouse.fr/~amicale_app/images/Account.png';
29 40
 
30 41
 // SPECIAL
31
-const WASHER_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/ProxiwashLaveLinge.png";
32
-const DRYER_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/ProxiwashSecheLinge.png";
42
+const WASHER_IMAGE =
43
+  'https://etud.insa-toulouse.fr/~amicale_app/images/ProxiwashLaveLinge.png';
44
+const DRYER_IMAGE =
45
+  'https://etud.insa-toulouse.fr/~amicale_app/images/ProxiwashSecheLinge.png';
33 46
 
34
-const AMICALE_LOGO = require("../../assets/amicale.png");
47
+const AMICALE_LOGO = require('../../assets/amicale.png');
35 48
 
36 49
 export const SERVICES_KEY = {
37
-    CLUBS: "clubs",
38
-    PROFILE: "profile",
39
-    EQUIPMENT: "equipment",
40
-    AMICALE_WEBSITE: "amicale_website",
41
-    VOTE: "vote",
42
-    PROXIMO: "proximo",
43
-    WIKETUD: "wiketud",
44
-    ELUS_ETUDIANTS: "elus_etudiants",
45
-    TUTOR_INSA: "tutor_insa",
46
-    RU: "ru",
47
-    AVAILABLE_ROOMS: "available_rooms",
48
-    BIB: "bib",
49
-    EMAIL: "email",
50
-    ENT: "ent",
51
-    INSA_ACCOUNT: "insa_account",
52
-    WASHERS: "washers",
53
-    DRYERS: "dryers",
54
-}
50
+  CLUBS: 'clubs',
51
+  PROFILE: 'profile',
52
+  EQUIPMENT: 'equipment',
53
+  AMICALE_WEBSITE: 'amicale_website',
54
+  VOTE: 'vote',
55
+  PROXIMO: 'proximo',
56
+  WIKETUD: 'wiketud',
57
+  ELUS_ETUDIANTS: 'elus_etudiants',
58
+  TUTOR_INSA: 'tutor_insa',
59
+  RU: 'ru',
60
+  AVAILABLE_ROOMS: 'available_rooms',
61
+  BIB: 'bib',
62
+  EMAIL: 'email',
63
+  ENT: 'ent',
64
+  INSA_ACCOUNT: 'insa_account',
65
+  WASHERS: 'washers',
66
+  DRYERS: 'dryers',
67
+};
55 68
 
56 69
 export const SERVICES_CATEGORIES_KEY = {
57
-    AMICALE: "amicale",
58
-    STUDENTS: "students",
59
-    INSA: "insa",
60
-    SPECIAL: "special",
61
-}
62
-
70
+  AMICALE: 'amicale',
71
+  STUDENTS: 'students',
72
+  INSA: 'insa',
73
+  SPECIAL: 'special',
74
+};
63 75
 
64
-export type ServiceItem = {
65
-    key: string,
66
-    title: string,
67
-    subtitle: string,
68
-    image: string,
69
-    onPress: () => void,
70
-    badgeFunction?: (dashboard: fullDashboard) => number,
71
-}
72
-
73
-export type ServiceCategory = {
74
-    key: string,
75
-    title: string,
76
-    subtitle: string,
77
-    image: string | number,
78
-    content: Array<ServiceItem>
79
-}
76
+export type ServiceItemType = {
77
+  key: string,
78
+  title: string,
79
+  subtitle: string,
80
+  image: string,
81
+  onPress: () => void,
82
+  badgeFunction?: (dashboard: FullDashboardType) => number,
83
+};
80 84
 
85
+export type ServiceCategoryType = {
86
+  key: string,
87
+  title: string,
88
+  subtitle: string,
89
+  image: string | number,
90
+  content: Array<ServiceItemType>,
91
+};
81 92
 
82 93
 export default class ServicesManager {
94
+  navigation: StackNavigationProp;
83 95
 
84
-    navigation: StackNavigationProp;
96
+  amicaleDataset: Array<ServiceItemType>;
85 97
 
86
-    amicaleDataset: Array<ServiceItem>;
87
-    studentsDataset: Array<ServiceItem>;
88
-    insaDataset: Array<ServiceItem>;
89
-    specialDataset: Array<ServiceItem>;
98
+  studentsDataset: Array<ServiceItemType>;
90 99
 
91
-    categoriesDataset: Array<ServiceCategory>;
100
+  insaDataset: Array<ServiceItemType>;
92 101
 
93
-    constructor(nav: StackNavigationProp) {
94
-        this.navigation = nav;
95
-        this.amicaleDataset = [
96
-            {
97
-                key: SERVICES_KEY.CLUBS,
98
-                title: i18n.t('screens.clubs.title'),
99
-                subtitle: i18n.t('screens.services.descriptions.clubs'),
100
-                image: CLUBS_IMAGE,
101
-                onPress: () => this.onAmicaleServicePress("club-list"),
102
-            },
103
-            {
104
-                key: SERVICES_KEY.PROFILE,
105
-                title: i18n.t('screens.profile.title'),
106
-                subtitle: i18n.t('screens.services.descriptions.profile'),
107
-                image: PROFILE_IMAGE,
108
-                onPress: () => this.onAmicaleServicePress("profile"),
109
-            },
110
-            {
111
-                key: SERVICES_KEY.EQUIPMENT,
112
-                title: i18n.t('screens.equipment.title'),
113
-                subtitle: i18n.t('screens.services.descriptions.equipment'),
114
-                image: EQUIPMENT_IMAGE,
115
-                onPress: () => this.onAmicaleServicePress("equipment-list"),
116
-            },
117
-            {
118
-                key: SERVICES_KEY.AMICALE_WEBSITE,
119
-                title: i18n.t('screens.websites.amicale'),
120
-                subtitle: i18n.t('screens.services.descriptions.amicaleWebsite'),
121
-                image: AMICALE_IMAGE,
122
-                onPress: () => nav.navigate("website", {
123
-                    host: AvailableWebsites.websites.AMICALE,
124
-                    title: i18n.t('screens.websites.amicale')
125
-                }),
126
-            },
127
-            {
128
-                key: SERVICES_KEY.VOTE,
129
-                title: i18n.t('screens.vote.title'),
130
-                subtitle: i18n.t('screens.services.descriptions.vote'),
131
-                image: VOTE_IMAGE,
132
-                onPress: () => this.onAmicaleServicePress("vote"),
133
-            },
134
-        ];
135
-        this.studentsDataset = [
136
-            {
137
-                key: SERVICES_KEY.PROXIMO,
138
-                title: i18n.t('screens.proximo.title'),
139
-                subtitle: i18n.t('screens.services.descriptions.proximo'),
140
-                image: PROXIMO_IMAGE,
141
-                onPress: () => nav.navigate("proximo"),
142
-                badgeFunction: (dashboard: fullDashboard) => dashboard.proximo_articles
143
-            },
144
-            {
145
-                key: SERVICES_KEY.WIKETUD,
146
-                title: "Wiketud",
147
-                subtitle: i18n.t('screens.services.descriptions.wiketud'),
148
-                image: WIKETUD_IMAGE,
149
-                onPress: () => nav.navigate("website", {host: AvailableWebsites.websites.WIKETUD, title: "Wiketud"}),
150
-            },
151
-            {
152
-                key: SERVICES_KEY.ELUS_ETUDIANTS,
153
-                title: "Élus Étudiants",
154
-                subtitle: i18n.t('screens.services.descriptions.elusEtudiants'),
155
-                image: EE_IMAGE,
156
-                onPress: () => nav.navigate("website", {
157
-                    host: AvailableWebsites.websites.ELUS_ETUDIANTS,
158
-                    title: "Élus Étudiants"
159
-                }),
160
-            },
161
-            {
162
-                key: SERVICES_KEY.TUTOR_INSA,
163
-                title: "Tutor'INSA",
164
-                subtitle: i18n.t('screens.services.descriptions.tutorInsa'),
165
-                image: TUTORINSA_IMAGE,
166
-                onPress: () => nav.navigate("website", {
167
-                    host: AvailableWebsites.websites.TUTOR_INSA,
168
-                    title: "Tutor'INSA"
169
-                }),
170
-                badgeFunction: (dashboard: fullDashboard) => dashboard.available_tutorials
171
-            },
172
-        ];
173
-        this.insaDataset = [
174
-            {
175
-                key: SERVICES_KEY.RU,
176
-                title: i18n.t('screens.menu.title'),
177
-                subtitle: i18n.t('screens.services.descriptions.self'),
178
-                image: RU_IMAGE,
179
-                onPress: () => nav.navigate("self-menu"),
180
-                badgeFunction: (dashboard: fullDashboard) => dashboard.today_menu.length
181
-            },
182
-            {
183
-                key: SERVICES_KEY.AVAILABLE_ROOMS,
184
-                title: i18n.t('screens.websites.rooms'),
185
-                subtitle: i18n.t('screens.services.descriptions.availableRooms'),
186
-                image: ROOM_IMAGE,
187
-                onPress: () => nav.navigate("website", {
188
-                    host: AvailableWebsites.websites.AVAILABLE_ROOMS,
189
-                    title: i18n.t('screens.websites.rooms')
190
-                }),
191
-            },
192
-            {
193
-                key: SERVICES_KEY.BIB,
194
-                title: i18n.t('screens.websites.bib'),
195
-                subtitle: i18n.t('screens.services.descriptions.bib'),
196
-                image: BIB_IMAGE,
197
-                onPress: () => nav.navigate("website", {
198
-                    host: AvailableWebsites.websites.BIB,
199
-                    title: i18n.t('screens.websites.bib')
200
-                }),
201
-            },
202
-            {
203
-                key: SERVICES_KEY.EMAIL,
204
-                title: i18n.t('screens.websites.mails'),
205
-                subtitle: i18n.t('screens.services.descriptions.mails'),
206
-                image: EMAIL_IMAGE,
207
-                onPress: () => nav.navigate("website", {
208
-                    host: AvailableWebsites.websites.BLUEMIND,
209
-                    title: i18n.t('screens.websites.mails')
210
-                }),
211
-            },
212
-            {
213
-                key: SERVICES_KEY.ENT,
214
-                title: i18n.t('screens.websites.ent'),
215
-                subtitle: i18n.t('screens.services.descriptions.ent'),
216
-                image: ENT_IMAGE,
217
-                onPress: () => nav.navigate("website", {
218
-                    host: AvailableWebsites.websites.ENT,
219
-                    title: i18n.t('screens.websites.ent')
220
-                }),
221
-            },
222
-            {
223
-                key: SERVICES_KEY.INSA_ACCOUNT,
224
-                title: i18n.t('screens.insaAccount.title'),
225
-                subtitle: i18n.t('screens.services.descriptions.insaAccount'),
226
-                image: ACCOUNT_IMAGE,
227
-                onPress: () => nav.navigate("website", {
228
-                    host: AvailableWebsites.websites.INSA_ACCOUNT,
229
-                    title: i18n.t('screens.insaAccount.title')
230
-                }),
231
-            },
232
-        ];
233
-        this.specialDataset = [
234
-            {
235
-                key: SERVICES_KEY.WASHERS,
236
-                title: i18n.t('screens.proxiwash.washers'),
237
-                subtitle: i18n.t('screens.services.descriptions.washers'),
238
-                image: WASHER_IMAGE,
239
-                onPress: () => nav.navigate("proxiwash"),
240
-                badgeFunction: (dashboard: fullDashboard) => dashboard.available_washers
241
-            },
242
-            {
243
-                key: SERVICES_KEY.DRYERS,
244
-                title: i18n.t('screens.proxiwash.dryers'),
245
-                subtitle: i18n.t('screens.services.descriptions.washers'),
246
-                image: DRYER_IMAGE,
247
-                onPress: () => nav.navigate("proxiwash"),
248
-                badgeFunction: (dashboard: fullDashboard) => dashboard.available_dryers
249
-            }
250
-        ];
251
-        this.categoriesDataset = [
252
-            {
253
-                key: SERVICES_CATEGORIES_KEY.AMICALE,
254
-                title: i18n.t("screens.services.categories.amicale"),
255
-                subtitle: i18n.t("screens.services.more"),
256
-                image: AMICALE_LOGO,
257
-                content: this.amicaleDataset
258
-            },
259
-            {
260
-                key: SERVICES_CATEGORIES_KEY.STUDENTS,
261
-                title: i18n.t("screens.services.categories.students"),
262
-                subtitle: i18n.t("screens.services.more"),
263
-                image: 'account-group',
264
-                content: this.studentsDataset
265
-            },
266
-            {
267
-                key: SERVICES_CATEGORIES_KEY.INSA,
268
-                title: i18n.t("screens.services.categories.insa"),
269
-                subtitle: i18n.t("screens.services.more"),
270
-                image: 'school',
271
-                content: this.insaDataset
272
-            },
273
-            {
274
-                key: SERVICES_CATEGORIES_KEY.SPECIAL,
275
-                title: i18n.t("screens.services.categories.special"),
276
-                subtitle: i18n.t("screens.services.categories.special"),
277
-                image: 'star',
278
-                content: this.specialDataset
279
-            },
280
-        ];
281
-    }
102
+  specialDataset: Array<ServiceItemType>;
282 103
 
283
-    /**
284
-     * Redirects the user to the login screen if he is not logged in
285
-     *
286
-     * @param route
287
-     * @returns {null}
288
-     */
289
-    onAmicaleServicePress(route: string) {
290
-        if (ConnectionManager.getInstance().isLoggedIn())
291
-            this.navigation.navigate(route);
292
-        else
293
-            this.navigation.navigate("login", {nextScreen: route});
294
-    }
104
+  categoriesDataset: Array<ServiceCategoryType>;
295 105
 
296
-    /**
297
-     * Gets the given services list without items of the given ids
298
-     *
299
-     * @param idList The ids of items to remove
300
-     * @param sourceList The item list to use as source
301
-     * @returns {[]}
302
-     */
303
-    getStrippedList(idList: Array<string>, sourceList: Array<{key: string, [key: string]: any}>) {
304
-        let newArray = [];
305
-        for (let i = 0; i < sourceList.length; i++) {
306
-            const item = sourceList[i];
307
-            if (!(idList.includes(item.key)))
308
-                newArray.push(item);
309
-        }
310
-        return newArray;
311
-    }
106
+  constructor(nav: StackNavigationProp) {
107
+    this.navigation = nav;
108
+    this.amicaleDataset = [
109
+      {
110
+        key: SERVICES_KEY.CLUBS,
111
+        title: i18n.t('screens.clubs.title'),
112
+        subtitle: i18n.t('screens.services.descriptions.clubs'),
113
+        image: CLUBS_IMAGE,
114
+        onPress: (): void => this.onAmicaleServicePress('club-list'),
115
+      },
116
+      {
117
+        key: SERVICES_KEY.PROFILE,
118
+        title: i18n.t('screens.profile.title'),
119
+        subtitle: i18n.t('screens.services.descriptions.profile'),
120
+        image: PROFILE_IMAGE,
121
+        onPress: (): void => this.onAmicaleServicePress('profile'),
122
+      },
123
+      {
124
+        key: SERVICES_KEY.EQUIPMENT,
125
+        title: i18n.t('screens.equipment.title'),
126
+        subtitle: i18n.t('screens.services.descriptions.equipment'),
127
+        image: EQUIPMENT_IMAGE,
128
+        onPress: (): void => this.onAmicaleServicePress('equipment-list'),
129
+      },
130
+      {
131
+        key: SERVICES_KEY.AMICALE_WEBSITE,
132
+        title: i18n.t('screens.websites.amicale'),
133
+        subtitle: i18n.t('screens.services.descriptions.amicaleWebsite'),
134
+        image: AMICALE_IMAGE,
135
+        onPress: (): void =>
136
+          nav.navigate('website', {
137
+            host: AvailableWebsites.websites.AMICALE,
138
+            title: i18n.t('screens.websites.amicale'),
139
+          }),
140
+      },
141
+      {
142
+        key: SERVICES_KEY.VOTE,
143
+        title: i18n.t('screens.vote.title'),
144
+        subtitle: i18n.t('screens.services.descriptions.vote'),
145
+        image: VOTE_IMAGE,
146
+        onPress: (): void => this.onAmicaleServicePress('vote'),
147
+      },
148
+    ];
149
+    this.studentsDataset = [
150
+      {
151
+        key: SERVICES_KEY.PROXIMO,
152
+        title: i18n.t('screens.proximo.title'),
153
+        subtitle: i18n.t('screens.services.descriptions.proximo'),
154
+        image: PROXIMO_IMAGE,
155
+        onPress: (): void => nav.navigate('proximo'),
156
+        badgeFunction: (dashboard: FullDashboardType): number =>
157
+          dashboard.proximo_articles,
158
+      },
159
+      {
160
+        key: SERVICES_KEY.WIKETUD,
161
+        title: 'Wiketud',
162
+        subtitle: i18n.t('screens.services.descriptions.wiketud'),
163
+        image: WIKETUD_IMAGE,
164
+        onPress: (): void =>
165
+          nav.navigate('website', {
166
+            host: AvailableWebsites.websites.WIKETUD,
167
+            title: 'Wiketud',
168
+          }),
169
+      },
170
+      {
171
+        key: SERVICES_KEY.ELUS_ETUDIANTS,
172
+        title: 'Élus Étudiants',
173
+        subtitle: i18n.t('screens.services.descriptions.elusEtudiants'),
174
+        image: EE_IMAGE,
175
+        onPress: (): void =>
176
+          nav.navigate('website', {
177
+            host: AvailableWebsites.websites.ELUS_ETUDIANTS,
178
+            title: 'Élus Étudiants',
179
+          }),
180
+      },
181
+      {
182
+        key: SERVICES_KEY.TUTOR_INSA,
183
+        title: "Tutor'INSA",
184
+        subtitle: i18n.t('screens.services.descriptions.tutorInsa'),
185
+        image: TUTORINSA_IMAGE,
186
+        onPress: (): void =>
187
+          nav.navigate('website', {
188
+            host: AvailableWebsites.websites.TUTOR_INSA,
189
+            title: "Tutor'INSA",
190
+          }),
191
+        badgeFunction: (dashboard: FullDashboardType): number =>
192
+          dashboard.available_tutorials,
193
+      },
194
+    ];
195
+    this.insaDataset = [
196
+      {
197
+        key: SERVICES_KEY.RU,
198
+        title: i18n.t('screens.menu.title'),
199
+        subtitle: i18n.t('screens.services.descriptions.self'),
200
+        image: RU_IMAGE,
201
+        onPress: (): void => nav.navigate('self-menu'),
202
+        badgeFunction: (dashboard: FullDashboardType): number =>
203
+          dashboard.today_menu.length,
204
+      },
205
+      {
206
+        key: SERVICES_KEY.AVAILABLE_ROOMS,
207
+        title: i18n.t('screens.websites.rooms'),
208
+        subtitle: i18n.t('screens.services.descriptions.availableRooms'),
209
+        image: ROOM_IMAGE,
210
+        onPress: (): void =>
211
+          nav.navigate('website', {
212
+            host: AvailableWebsites.websites.AVAILABLE_ROOMS,
213
+            title: i18n.t('screens.websites.rooms'),
214
+          }),
215
+      },
216
+      {
217
+        key: SERVICES_KEY.BIB,
218
+        title: i18n.t('screens.websites.bib'),
219
+        subtitle: i18n.t('screens.services.descriptions.bib'),
220
+        image: BIB_IMAGE,
221
+        onPress: (): void =>
222
+          nav.navigate('website', {
223
+            host: AvailableWebsites.websites.BIB,
224
+            title: i18n.t('screens.websites.bib'),
225
+          }),
226
+      },
227
+      {
228
+        key: SERVICES_KEY.EMAIL,
229
+        title: i18n.t('screens.websites.mails'),
230
+        subtitle: i18n.t('screens.services.descriptions.mails'),
231
+        image: EMAIL_IMAGE,
232
+        onPress: (): void =>
233
+          nav.navigate('website', {
234
+            host: AvailableWebsites.websites.BLUEMIND,
235
+            title: i18n.t('screens.websites.mails'),
236
+          }),
237
+      },
238
+      {
239
+        key: SERVICES_KEY.ENT,
240
+        title: i18n.t('screens.websites.ent'),
241
+        subtitle: i18n.t('screens.services.descriptions.ent'),
242
+        image: ENT_IMAGE,
243
+        onPress: (): void =>
244
+          nav.navigate('website', {
245
+            host: AvailableWebsites.websites.ENT,
246
+            title: i18n.t('screens.websites.ent'),
247
+          }),
248
+      },
249
+      {
250
+        key: SERVICES_KEY.INSA_ACCOUNT,
251
+        title: i18n.t('screens.insaAccount.title'),
252
+        subtitle: i18n.t('screens.services.descriptions.insaAccount'),
253
+        image: ACCOUNT_IMAGE,
254
+        onPress: (): void =>
255
+          nav.navigate('website', {
256
+            host: AvailableWebsites.websites.INSA_ACCOUNT,
257
+            title: i18n.t('screens.insaAccount.title'),
258
+          }),
259
+      },
260
+    ];
261
+    this.specialDataset = [
262
+      {
263
+        key: SERVICES_KEY.WASHERS,
264
+        title: i18n.t('screens.proxiwash.washers'),
265
+        subtitle: i18n.t('screens.services.descriptions.washers'),
266
+        image: WASHER_IMAGE,
267
+        onPress: (): void => nav.navigate('proxiwash'),
268
+        badgeFunction: (dashboard: FullDashboardType): number =>
269
+          dashboard.available_washers,
270
+      },
271
+      {
272
+        key: SERVICES_KEY.DRYERS,
273
+        title: i18n.t('screens.proxiwash.dryers'),
274
+        subtitle: i18n.t('screens.services.descriptions.washers'),
275
+        image: DRYER_IMAGE,
276
+        onPress: (): void => nav.navigate('proxiwash'),
277
+        badgeFunction: (dashboard: FullDashboardType): number =>
278
+          dashboard.available_dryers,
279
+      },
280
+    ];
281
+    this.categoriesDataset = [
282
+      {
283
+        key: SERVICES_CATEGORIES_KEY.AMICALE,
284
+        title: i18n.t('screens.services.categories.amicale'),
285
+        subtitle: i18n.t('screens.services.more'),
286
+        image: AMICALE_LOGO,
287
+        content: this.amicaleDataset,
288
+      },
289
+      {
290
+        key: SERVICES_CATEGORIES_KEY.STUDENTS,
291
+        title: i18n.t('screens.services.categories.students'),
292
+        subtitle: i18n.t('screens.services.more'),
293
+        image: 'account-group',
294
+        content: this.studentsDataset,
295
+      },
296
+      {
297
+        key: SERVICES_CATEGORIES_KEY.INSA,
298
+        title: i18n.t('screens.services.categories.insa'),
299
+        subtitle: i18n.t('screens.services.more'),
300
+        image: 'school',
301
+        content: this.insaDataset,
302
+      },
303
+      {
304
+        key: SERVICES_CATEGORIES_KEY.SPECIAL,
305
+        title: i18n.t('screens.services.categories.special'),
306
+        subtitle: i18n.t('screens.services.categories.special'),
307
+        image: 'star',
308
+        content: this.specialDataset,
309
+      },
310
+    ];
311
+  }
312 312
 
313
-    /**
314
-     * Gets the list of amicale's services
315
-     *
316
-     * @param excludedItems Ids of items to exclude from the returned list
317
-     * @returns {Array<ServiceItem>}
318
-     */
319
-    getAmicaleServices(excludedItems?: Array<string>) {
320
-        if (excludedItems != null)
321
-            return this.getStrippedList(excludedItems, this.amicaleDataset)
322
-        else
323
-            return this.amicaleDataset;
324
-    }
313
+  /**
314
+   * Redirects the user to the login screen if he is not logged in
315
+   *
316
+   * @param route
317
+   * @returns {null}
318
+   */
319
+  onAmicaleServicePress(route: string) {
320
+    if (ConnectionManager.getInstance().isLoggedIn())
321
+      this.navigation.navigate(route);
322
+    else this.navigation.navigate('login', {nextScreen: route});
323
+  }
325 324
 
326
-    /**
327
-     * Gets the list of students' services
328
-     *
329
-     * @param excludedItems Ids of items to exclude from the returned list
330
-     * @returns {Array<ServiceItem>}
331
-     */
332
-    getStudentServices(excludedItems?: Array<string>) {
333
-        if (excludedItems != null)
334
-            return this.getStrippedList(excludedItems, this.studentsDataset)
335
-        else
336
-            return this.studentsDataset;
337
-    }
325
+  /**
326
+   * Gets the list of amicale's services
327
+   *
328
+   * @param excludedItems Ids of items to exclude from the returned list
329
+   * @returns {Array<ServiceItemType>}
330
+   */
331
+  getAmicaleServices(excludedItems?: Array<string>): Array<ServiceItemType> {
332
+    if (excludedItems != null)
333
+      return getStrippedServicesList(excludedItems, this.amicaleDataset);
334
+    return this.amicaleDataset;
335
+  }
338 336
 
339
-    /**
340
-     * Gets the list of INSA's services
341
-     *
342
-     * @param excludedItems Ids of items to exclude from the returned list
343
-     * @returns {Array<ServiceItem>}
344
-     */
345
-    getINSAServices(excludedItems?: Array<string>) {
346
-        if (excludedItems != null)
347
-            return this.getStrippedList(excludedItems, this.insaDataset)
348
-        else
349
-            return this.insaDataset;
350
-    }
337
+  /**
338
+   * Gets the list of students' services
339
+   *
340
+   * @param excludedItems Ids of items to exclude from the returned list
341
+   * @returns {Array<ServiceItemType>}
342
+   */
343
+  getStudentServices(excludedItems?: Array<string>): Array<ServiceItemType> {
344
+    if (excludedItems != null)
345
+      return getStrippedServicesList(excludedItems, this.studentsDataset);
346
+    return this.studentsDataset;
347
+  }
351 348
 
352
-    /**
353
-     * Gets the list of special services
354
-     *
355
-     * @param excludedItems Ids of items to exclude from the returned list
356
-     * @returns {Array<ServiceItem>}
357
-     */
358
-    getSpecialServices(excludedItems?: Array<string>) {
359
-        if (excludedItems != null)
360
-            return this.getStrippedList(excludedItems, this.specialDataset)
361
-        else
362
-            return this.specialDataset;
363
-    }
349
+  /**
350
+   * Gets the list of INSA's services
351
+   *
352
+   * @param excludedItems Ids of items to exclude from the returned list
353
+   * @returns {Array<ServiceItemType>}
354
+   */
355
+  getINSAServices(excludedItems?: Array<string>): Array<ServiceItemType> {
356
+    if (excludedItems != null)
357
+      return getStrippedServicesList(excludedItems, this.insaDataset);
358
+    return this.insaDataset;
359
+  }
364 360
 
365
-    /**
366
-     * Gets all services sorted by category
367
-     *
368
-     * @param excludedItems Ids of categories to exclude from the returned list
369
-     * @returns {Array<ServiceCategory>}
370
-     */
371
-    getCategories(excludedItems?: Array<string>) {
372
-        if (excludedItems != null)
373
-            return this.getStrippedList(excludedItems, this.categoriesDataset)
374
-        else
375
-            return this.categoriesDataset;
376
-    }
361
+  /**
362
+   * Gets the list of special services
363
+   *
364
+   * @param excludedItems Ids of items to exclude from the returned list
365
+   * @returns {Array<ServiceItemType>}
366
+   */
367
+  getSpecialServices(excludedItems?: Array<string>): Array<ServiceItemType> {
368
+    if (excludedItems != null)
369
+      return getStrippedServicesList(excludedItems, this.specialDataset);
370
+    return this.specialDataset;
371
+  }
377 372
 
373
+  /**
374
+   * Gets all services sorted by category
375
+   *
376
+   * @param excludedItems Ids of categories to exclude from the returned list
377
+   * @returns {Array<ServiceCategoryType>}
378
+   */
379
+  getCategories(excludedItems?: Array<string>): Array<ServiceCategoryType> {
380
+    if (excludedItems != null)
381
+      return getStrippedServicesList(excludedItems, this.categoriesDataset);
382
+    return this.categoriesDataset;
383
+  }
378 384
 }

+ 3
- 3
src/screens/Home/HomeScreen.js View File

@@ -26,7 +26,7 @@ import AsyncStorageManager from '../../managers/AsyncStorageManager';
26 26
 import {MASCOT_STYLE} from '../../components/Mascot/Mascot';
27 27
 import MascotPopup from '../../components/Mascot/MascotPopup';
28 28
 import DashboardManager from '../../managers/DashboardManager';
29
-import type {ServiceItem} from '../../managers/ServicesManager';
29
+import type {ServiceItemType} from '../../managers/ServicesManager';
30 30
 import {getDisplayEvent, getFutureEvents} from '../../utils/Home';
31 31
 // import DATA from "../dashboard_data.json";
32 32
 
@@ -230,7 +230,7 @@ class HomeScreen extends React.Component<PropsType, StateType> {
230 230
    * @param content
231 231
    * @return {*}
232 232
    */
233
-  getDashboardRow(content: Array<ServiceItem | null>): React.Node {
233
+  getDashboardRow(content: Array<ServiceItemType | null>): React.Node {
234 234
     return (
235 235
       // $FlowFixMe
236 236
       <FlatList
@@ -256,7 +256,7 @@ class HomeScreen extends React.Component<PropsType, StateType> {
256 256
   getDashboardRowRenderItem = ({
257 257
     item,
258 258
   }: {
259
-    item: ServiceItem | null,
259
+    item: ServiceItemType | null,
260 260
   }): React.Node => {
261 261
     if (item != null)
262 262
       return (

+ 155
- 142
src/screens/Services/ServicesScreen.js View File

@@ -1,149 +1,162 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import type {cardList} from "../../components/Lists/CardList/CardList";
5
-import CardList from "../../components/Lists/CardList/CardList";
6
-import {Image, View} from "react-native";
7
-import {Avatar, Card, Divider, List, TouchableRipple, withTheme} from "react-native-paper";
8
-import type {CustomTheme} from "../../managers/ThemeManager";
4
+import {Image, View} from 'react-native';
5
+import {
6
+  Avatar,
7
+  Card,
8
+  Divider,
9
+  List,
10
+  TouchableRipple,
11
+  withTheme,
12
+} from 'react-native-paper';
9 13
 import i18n from 'i18n-js';
10
-import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton";
11
-import {StackNavigationProp} from "@react-navigation/stack";
12
-import {MASCOT_STYLE} from "../../components/Mascot/Mascot";
13
-import MascotPopup from "../../components/Mascot/MascotPopup";
14
-import AsyncStorageManager from "../../managers/AsyncStorageManager";
15
-import ServicesManager, {SERVICES_CATEGORIES_KEY} from "../../managers/ServicesManager";
16
-import CollapsibleFlatList from "../../components/Collapsible/CollapsibleFlatList";
17
-
18
-type Props = {
19
-    navigation: StackNavigationProp,
20
-    theme: CustomTheme,
21
-}
22
-
23
-export type listItem = {
24
-    title: string,
25
-    description: string,
26
-    image: string | number,
27
-    content: cardList,
28
-}
29
-
30
-
31
-class ServicesScreen extends React.Component<Props> {
32
-
33
-    finalDataset: Array<listItem>
34
-
35
-    constructor(props) {
36
-        super(props);
37
-        const services = new ServicesManager(props.navigation);
38
-        this.finalDataset = services.getCategories([SERVICES_CATEGORIES_KEY.SPECIAL])
39
-    }
40
-
41
-    componentDidMount() {
42
-        this.props.navigation.setOptions({
43
-            headerRight: this.getAboutButton,
44
-        });
45
-    }
46
-
47
-    getAboutButton = () =>
48
-        <MaterialHeaderButtons>
49
-            <Item title="information" iconName="information" onPress={this.onAboutPress}/>
50
-        </MaterialHeaderButtons>;
51
-
52
-    onAboutPress = () => this.props.navigation.navigate('amicale-contact');
53
-
54
-    /**
55
-     * Gets the list title image for the list.
56
-     *
57
-     * If the source is a string, we are using an icon.
58
-     * If the source is a number, we are using an internal image.
59
-     *
60
-     * @param props Props to pass to the component
61
-     * @param source The source image to display. Can be a string for icons or a number for local images
62
-     * @returns {*}
63
-     */
64
-    getListTitleImage(props, source: string | number) {
65
-        if (typeof source === "number")
66
-            return <Image
67
-                size={48}
68
-                source={source}
69
-                style={{
70
-                    width: 48,
71
-                    height: 48,
72
-                }}/>
73
-        else
74
-            return <Avatar.Icon
75
-                {...props}
76
-                size={48}
77
-                icon={source}
78
-                color={this.props.theme.colors.primary}
79
-                style={{backgroundColor: 'transparent'}}
80
-            />
81
-    }
82
-
83
-    /**
84
-     * A list item showing a list of available services for the current category
85
-     *
86
-     * @param item
87
-     * @returns {*}
88
-     */
89
-    renderItem = ({item}: { item: listItem }) => {
90
-        return (
91
-            <TouchableRipple
92
-                style={{
93
-                    margin: 5,
94
-                    marginBottom: 20,
95
-                }}
96
-                onPress={() => this.props.navigation.navigate("services-section", {data: item})}
97
-            >
98
-                <View>
99
-                    <Card.Title
100
-                        title={item.title}
101
-                        subtitle={item.description}
102
-                        left={(props) => this.getListTitleImage(props, item.image)}
103
-                        right={(props) => <List.Icon {...props} icon="chevron-right"/>}
104
-                    />
105
-                    <CardList
106
-                        dataset={item.content}
107
-                        isHorizontal={true}
108
-                    />
109
-                </View>
110
-            </TouchableRipple>
111
-
112
-        );
113
-    };
114
-
115
-    keyExtractor = (item: listItem) => {
116
-        return item.title;
117
-    }
118
-
119
-    render() {
120
-        return (
121
-            <View>
122
-                <CollapsibleFlatList
123
-                    data={this.finalDataset}
124
-                    renderItem={this.renderItem}
125
-                    keyExtractor={this.keyExtractor}
126
-                    ItemSeparatorComponent={() => <Divider/>}
127
-                    hasTab={true}
128
-                />
129
-                <MascotPopup
130
-                    prefKey={AsyncStorageManager.PREFERENCES.servicesShowBanner.key}
131
-                    title={i18n.t("screens.services.mascotDialog.title")}
132
-                    message={i18n.t("screens.services.mascotDialog.message")}
133
-                    icon={"cloud-question"}
134
-                    buttons={{
135
-                        action: null,
136
-                        cancel: {
137
-                            message: i18n.t("screens.services.mascotDialog.button"),
138
-                            icon: "check",
139
-                            onPress: this.onHideMascotDialog,
140
-                        }
141
-                    }}
142
-                    emotion={MASCOT_STYLE.WINK}
143
-                />
144
-            </View>
145
-        );
146
-    }
14
+import {StackNavigationProp} from '@react-navigation/stack';
15
+import CardList from '../../components/Lists/CardList/CardList';
16
+import type {CustomTheme} from '../../managers/ThemeManager';
17
+import MaterialHeaderButtons, {
18
+  Item,
19
+} from '../../components/Overrides/CustomHeaderButton';
20
+import {MASCOT_STYLE} from '../../components/Mascot/Mascot';
21
+import MascotPopup from '../../components/Mascot/MascotPopup';
22
+import AsyncStorageManager from '../../managers/AsyncStorageManager';
23
+import ServicesManager, {
24
+  SERVICES_CATEGORIES_KEY,
25
+} from '../../managers/ServicesManager';
26
+import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
27
+import type {ServiceCategoryType} from '../../managers/ServicesManager';
28
+
29
+type PropsType = {
30
+  navigation: StackNavigationProp,
31
+  theme: CustomTheme,
32
+};
33
+
34
+class ServicesScreen extends React.Component<PropsType> {
35
+  finalDataset: Array<ServiceCategoryType>;
36
+
37
+  constructor(props: PropsType) {
38
+    super(props);
39
+    const services = new ServicesManager(props.navigation);
40
+    this.finalDataset = services.getCategories([
41
+      SERVICES_CATEGORIES_KEY.SPECIAL,
42
+    ]);
43
+  }
44
+
45
+  componentDidMount() {
46
+    const {props} = this;
47
+    props.navigation.setOptions({
48
+      headerRight: this.getAboutButton,
49
+    });
50
+  }
51
+
52
+  getAboutButton = (): React.Node => (
53
+    <MaterialHeaderButtons>
54
+      <Item
55
+        title="information"
56
+        iconName="information"
57
+        onPress={this.onAboutPress}
58
+      />
59
+    </MaterialHeaderButtons>
60
+  );
61
+
62
+  onAboutPress = () => {
63
+    const {props} = this;
64
+    props.navigation.navigate('amicale-contact');
65
+  };
66
+
67
+  /**
68
+   * Gets the list title image for the list.
69
+   *
70
+   * If the source is a string, we are using an icon.
71
+   * If the source is a number, we are using an internal image.
72
+   *
73
+   * @param source The source image to display. Can be a string for icons or a number for local images
74
+   * @returns {*}
75
+   */
76
+  getListTitleImage(source: string | number): React.Node {
77
+    const {props} = this;
78
+    if (typeof source === 'number')
79
+      return (
80
+        <Image
81
+          size={48}
82
+          source={source}
83
+          style={{
84
+            width: 48,
85
+            height: 48,
86
+          }}
87
+        />
88
+      );
89
+    return (
90
+      <Avatar.Icon
91
+        size={48}
92
+        icon={source}
93
+        color={props.theme.colors.primary}
94
+        style={{backgroundColor: 'transparent'}}
95
+      />
96
+    );
97
+  }
98
+
99
+  /**
100
+   * A list item showing a list of available services for the current category
101
+   *
102
+   * @param item
103
+   * @returns {*}
104
+   */
105
+  getRenderItem = ({item}: {item: ServiceCategoryType}): React.Node => {
106
+    const {props} = this;
107
+    return (
108
+      <TouchableRipple
109
+        style={{
110
+          margin: 5,
111
+          marginBottom: 20,
112
+        }}
113
+        onPress={() => {
114
+          props.navigation.navigate('services-section', {data: item});
115
+        }}>
116
+        <View>
117
+          <Card.Title
118
+            title={item.title}
119
+            subtitle={item.subtitle}
120
+            left={(): React.Node => this.getListTitleImage(item.image)}
121
+            right={({size}: {size: number}): React.Node => (
122
+              <List.Icon size={size} icon="chevron-right" />
123
+            )}
124
+          />
125
+          <CardList dataset={item.content} isHorizontal />
126
+        </View>
127
+      </TouchableRipple>
128
+    );
129
+  };
130
+
131
+  keyExtractor = (item: ServiceCategoryType): string => item.title;
132
+
133
+  render(): React.Node {
134
+    return (
135
+      <View>
136
+        <CollapsibleFlatList
137
+          data={this.finalDataset}
138
+          renderItem={this.getRenderItem}
139
+          keyExtractor={this.keyExtractor}
140
+          ItemSeparatorComponent={(): React.Node => <Divider />}
141
+          hasTab
142
+        />
143
+        <MascotPopup
144
+          prefKey={AsyncStorageManager.PREFERENCES.servicesShowBanner.key}
145
+          title={i18n.t('screens.services.mascotDialog.title')}
146
+          message={i18n.t('screens.services.mascotDialog.message')}
147
+          icon="cloud-question"
148
+          buttons={{
149
+            action: null,
150
+            cancel: {
151
+              message: i18n.t('screens.services.mascotDialog.button'),
152
+              icon: 'check',
153
+            },
154
+          }}
155
+          emotion={MASCOT_STYLE.WINK}
156
+        />
157
+      </View>
158
+    );
159
+  }
147 160
 }
148 161
 
149 162
 export default withTheme(ServicesScreen);

+ 53
- 46
src/screens/Services/ServicesSectionScreen.js View File

@@ -1,58 +1,65 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import CardList from "../../components/Lists/CardList/CardList";
5
-import CustomTabBar from "../../components/Tabbar/CustomTabBar";
6
-import {withCollapsible} from "../../utils/withCollapsible";
7
-import {Collapsible} from "react-navigation-collapsible";
8
-import {CommonActions} from "@react-navigation/native";
9
-import type {listItem} from "./ServicesScreen";
10
-import {StackNavigationProp} from "@react-navigation/stack";
4
+import {Collapsible} from 'react-navigation-collapsible';
5
+import {CommonActions} from '@react-navigation/native';
6
+import {StackNavigationProp} from '@react-navigation/stack';
7
+import CardList from '../../components/Lists/CardList/CardList';
8
+import CustomTabBar from '../../components/Tabbar/CustomTabBar';
9
+import {withCollapsible} from '../../utils/withCollapsible';
10
+import type {ServiceCategoryType} from '../../managers/ServicesManager';
11 11
 
12
-type Props = {
13
-    navigation: StackNavigationProp,
14
-    route: { params: { data: listItem | null } },
15
-    collapsibleStack: Collapsible,
16
-}
17
-
18
-class ServicesSectionScreen extends React.Component<Props> {
12
+type PropsType = {
13
+  navigation: StackNavigationProp,
14
+  route: {params: {data: ServiceCategoryType | null}},
15
+  collapsibleStack: Collapsible,
16
+};
19 17
 
20
-    finalDataset: listItem;
18
+class ServicesSectionScreen extends React.Component<PropsType> {
19
+  finalDataset: ServiceCategoryType;
21 20
 
22
-    constructor(props) {
23
-        super(props);
24
-        this.handleNavigationParams();
25
-    }
21
+  constructor(props: PropsType) {
22
+    super(props);
23
+    this.handleNavigationParams();
24
+  }
26 25
 
27
-    /**
28
-     * Recover the list to display from navigation parameters
29
-     */
30
-    handleNavigationParams() {
31
-        if (this.props.route.params != null) {
32
-            if (this.props.route.params.data != null) {
33
-                this.finalDataset = this.props.route.params.data;
34
-                // reset params to prevent infinite loop
35
-                this.props.navigation.dispatch(CommonActions.setParams({data: null}));
36
-                this.props.navigation.setOptions({
37
-                    headerTitle: this.finalDataset.title,
38
-                });
39
-            }
40
-        }
26
+  /**
27
+   * Recover the list to display from navigation parameters
28
+   */
29
+  handleNavigationParams() {
30
+    const {props} = this;
31
+    if (props.route.params != null) {
32
+      if (props.route.params.data != null) {
33
+        this.finalDataset = props.route.params.data;
34
+        // reset params to prevent infinite loop
35
+        props.navigation.dispatch(CommonActions.setParams({data: null}));
36
+        props.navigation.setOptions({
37
+          headerTitle: this.finalDataset.title,
38
+        });
39
+      }
41 40
     }
41
+  }
42 42
 
43
-    render() {
44
-        const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
45
-        return <CardList
46
-            dataset={this.finalDataset.content}
47
-            isHorizontal={false}
48
-            onScroll={onScroll}
49
-            contentContainerStyle={{
50
-                paddingTop: containerPaddingTop,
51
-                paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20
52
-            }}
53
-            scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
54
-        />
55
-    }
43
+  render(): React.Node {
44
+    const {props} = this;
45
+    const {
46
+      containerPaddingTop,
47
+      scrollIndicatorInsetTop,
48
+      onScroll,
49
+    } = props.collapsibleStack;
50
+    return (
51
+      <CardList
52
+        dataset={this.finalDataset.content}
53
+        isHorizontal={false}
54
+        onScroll={onScroll}
55
+        contentContainerStyle={{
56
+          paddingTop: containerPaddingTop,
57
+          paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20,
58
+        }}
59
+        scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
60
+      />
61
+    );
62
+  }
56 63
 }
57 64
 
58 65
 export default withCollapsible(ServicesSectionScreen);

+ 19
- 0
src/utils/Services.js View File

@@ -0,0 +1,19 @@
1
+// @flow
2
+
3
+/**
4
+ * Gets the given services list without items of the given ids
5
+ *
6
+ * @param idList The ids of items to remove
7
+ * @param sourceList The item list to use as source
8
+ * @returns {[]}
9
+ */
10
+export default function getStrippedServicesList<T>(
11
+  idList: Array<string>,
12
+  sourceList: Array<{key: string, ...T}>,
13
+): Array<{key: string, ...T}> {
14
+  const newArray = [];
15
+  sourceList.forEach((item: {key: string, ...T}) => {
16
+    if (!idList.includes(item.key)) newArray.push(item);
17
+  });
18
+  return newArray;
19
+}

Loading…
Cancel
Save