Browse Source

Improved home screen items render cycle

Arnaud Vergnet 4 years ago
parent
commit
3d57361908

+ 5
- 7
src/components/Home/ActionsDashboardItem.js View File

@@ -10,13 +10,10 @@ type Props = {
10 10
     theme: Object,
11 11
 }
12 12
 
13
-class ActionsDashBoardItem extends React.PureComponent<Props> {
13
+class ActionsDashBoardItem extends React.Component<Props> {
14 14
 
15
-    colors: Object;
16
-
17
-    constructor(props) {
18
-        super(props);
19
-        this.colors = this.props.theme.colors;
15
+    shouldComponentUpdate(nextProps: Props): boolean {
16
+        return (nextProps.theme.dark !== this.props.theme.dark)
20 17
     }
21 18
 
22 19
     openDrawer = () => this.props.navigation.openDrawer();
@@ -24,10 +21,11 @@ class ActionsDashBoardItem extends React.PureComponent<Props> {
24 21
     gotToSettings = () => this.props.navigation.navigate("settings");
25 22
 
26 23
     render() {
24
+        console.log('render action dashboard');
27 25
         return (
28 26
             <Card style={{
29 27
                 ...styles.card,
30
-                borderColor: this.colors.primary,
28
+                borderColor: this.props.theme.colors.primary,
31 29
             }}>
32 30
                 <Card.Content style={styles.content}>
33 31
                     <Button

+ 61
- 34
src/components/Home/EventDashboardItem.js View File

@@ -1,45 +1,72 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import {Avatar, Card, withTheme} from 'react-native-paper';
4
+import {Avatar, Card, Text, withTheme} from 'react-native-paper';
5 5
 import {StyleSheet} from "react-native";
6
+import i18n from "i18n-js";
7
+
8
+type Props = {
9
+    eventNumber: number;
10
+    clickAction: Function,
11
+    theme: Object,
12
+}
6 13
 
7 14
 /**
8 15
  * Component used to display a dashboard item containing a preview event
9
- *
10
- * @param props Props to pass to the component
11
- * @return {*}
12 16
  */
13
-function EventDashBoardItem(props) {
14
-    const {colors} = props.theme;
15
-    const iconColor = props.isAvailable ?
16
-        colors.planningColor :
17
-        colors.textDisabled;
18
-    const textColor = props.isAvailable ?
19
-        colors.text :
20
-        colors.textDisabled;
21
-    return (
22
-        <Card
23
-            style={styles.card}
24
-            onPress={props.clickAction}>
25
-
26
-            <Card.Title
27
-                title={props.title}
28
-                titleStyle={{color: textColor}}
29
-                subtitle={props.subtitle}
30
-                subtitleStyle={{color: textColor}}
31
-                left={() =>
32
-                    <Avatar.Icon
33
-                        icon={props.icon}
34
-                        color={iconColor}
35
-                        size={60}
36
-                        style={styles.avatar}/>}
37
-            />
38
-            <Card.Content>
39
-                {props.children}
40
-            </Card.Content>
41
-        </Card>
42
-    );
17
+class EventDashBoardItem extends React.Component<Props> {
18
+
19
+    shouldComponentUpdate(nextProps: Props) {
20
+        return (nextProps.theme.dark !== this.props.theme.dark)
21
+            || (nextProps.eventNumber !== this.props.eventNumber);
22
+    }
23
+
24
+    render() {
25
+        const props = this.props;
26
+        const colors = props.theme.colors;
27
+        const isAvailable = props.eventNumber > 0;
28
+        const iconColor = isAvailable ?
29
+            colors.planningColor :
30
+            colors.textDisabled;
31
+        const textColor = isAvailable ?
32
+            colors.text :
33
+            colors.textDisabled;
34
+        let subtitle;
35
+        if (isAvailable) {
36
+            subtitle =
37
+                <Text>
38
+                    <Text style={{fontWeight: "bold"}}>{props.eventNumber}</Text>
39
+                    <Text>
40
+                        {props.eventNumber > 1
41
+                            ? i18n.t('homeScreen.dashboard.todayEventsSubtitlePlural')
42
+                            : i18n.t('homeScreen.dashboard.todayEventsSubtitle')}
43
+                    </Text>
44
+                </Text>;
45
+        } else
46
+            subtitle = i18n.t('homeScreen.dashboard.todayEventsSubtitleNA');
47
+        return (
48
+            <Card
49
+                style={styles.card}
50
+                onPress={props.clickAction}>
51
+                <Card.Title
52
+                    title={i18n.t('homeScreen.dashboard.todayEventsTitle')}
53
+                    titleStyle={{color: textColor}}
54
+                    subtitle={subtitle}
55
+                    subtitleStyle={{color: textColor}}
56
+                    left={() =>
57
+                        <Avatar.Icon
58
+                            icon={'calendar-range'}
59
+                            color={iconColor}
60
+                            size={60}
61
+                            style={styles.avatar}/>}
62
+                />
63
+                <Card.Content>
64
+                    {props.children}
65
+                </Card.Content>
66
+            </Card>
67
+        );
68
+    }
69
+
43 70
 }
44 71
 
45 72
 const styles = StyleSheet.create({

+ 14
- 4
src/components/Home/FeedItem.js View File

@@ -1,5 +1,5 @@
1 1
 import * as React from 'react';
2
-import {Avatar, Button, Card, Text, withTheme} from 'react-native-paper';
2
+import {Avatar, Button, Card, Text} from 'react-native-paper';
3 3
 import {View} from "react-native";
4 4
 import Autolink from "react-native-autolink";
5 5
 import i18n from "i18n-js";
@@ -7,16 +7,25 @@ import ImageModal from 'react-native-image-modal';
7 7
 
8 8
 const ICON_AMICALE = require('../../../assets/amicale.png');
9 9
 
10
+type Props = {
11
+    theme: Object,
12
+    title: string,
13
+    subtitle: string,
14
+    full_picture: string,
15
+    message: string,
16
+    onOutLinkPress: Function,
17
+}
18
+
19
+
10 20
 /**
11 21
  * Component used to display a feed item
12 22
  */
13
-class FeedItem extends React.Component {
23
+class FeedItem extends React.Component<Props> {
14 24
 
15 25
     shouldComponentUpdate() {
16 26
         return false;
17 27
     }
18 28
 
19
-
20 29
     /**
21 30
      * Gets the amicale INSAT logo
22 31
      *
@@ -30,6 +39,7 @@ class FeedItem extends React.Component {
30 39
     }
31 40
 
32 41
     render() {
42
+        console.log('render feed');
33 43
         return (
34 44
             <Card style={{margin: 10}}>
35 45
                 <Card.Title
@@ -72,4 +82,4 @@ class FeedItem extends React.Component {
72 82
     }
73 83
 }
74 84
 
75
-export default withTheme(FeedItem);
85
+export default FeedItem;

+ 51
- 45
src/components/Home/PreviewEventDashboardItem.js View File

@@ -1,61 +1,67 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import {StyleSheet, View} from "react-native";
4
+import {StyleSheet} from "react-native";
5 5
 import i18n from "i18n-js";
6 6
 import {Avatar, Button, Card} from 'react-native-paper';
7 7
 import {getFormattedEventTime, isDescriptionEmpty} from "../../utils/Planning";
8 8
 import CustomHTML from "../Custom/CustomHTML";
9 9
 
10
+type Props = {
11
+    event: Object,
12
+    clickAction: Function,
13
+}
14
+
10 15
 /**
11 16
  * Component used to display an event preview if an event is available
12
- *
13
- * @param props Props to pass to the component
14
- * @return {*}
15 17
  */
16
-function PreviewEventDashboardItem(props : Object) {
17
-    const isEmpty = props.event === undefined
18
-        ? true
19
-        : isDescriptionEmpty(props.event['description']);
18
+class PreviewEventDashboardItem extends React.Component<Props> {
20 19
 
21
-    if (props.event !== undefined && props.event !== null) {
22
-        const hasImage = props.event['logo'] !== '' && props.event['logo'] !== null;
23
-        const getImage = () => <Avatar.Image
24
-            source={{uri: props.event['logo']}}
25
-            size={50}
26
-            style={styles.avatar}/>;
27
-        return (
28
-            <Card
29
-                style={styles.card}
30
-                onPress={props.clickAction}
31
-                elevation={3}
32
-            >
33
-                {hasImage ?
34
-                    <Card.Title
35
-                        title={props.event['title']}
36
-                        subtitle={getFormattedEventTime(props.event['date_begin'], props.event['date_end'])}
37
-                        left={getImage}
38
-                    /> :
39
-                    <Card.Title
40
-                        title={props.event['title']}
41
-                        subtitle={getFormattedEventTime(props.event['date_begin'], props.event['date_end'])}
42
-                    />}
43
-                {!isEmpty ?
44
-                    <Card.Content style={styles.content}>
45
-                        <CustomHTML html={props.event['description']}/>
46
-                    </Card.Content> : null}
20
+    render() {
21
+        const props = this.props;
22
+        const isEmpty = props.event === undefined
23
+            ? true
24
+            : isDescriptionEmpty(props.event['description']);
47 25
 
48
-                <Card.Actions style={styles.actions}>
49
-                    <Button
50
-                        icon={'chevron-right'}
51
-                    >
52
-                        {i18n.t("homeScreen.dashboard.seeMore")}
53
-                    </Button>
54
-                </Card.Actions>
55
-            </Card>
56
-        );
57
-    } else
58
-        return <View/>
26
+        if (props.event !== undefined && props.event !== null) {
27
+            const hasImage = props.event['logo'] !== '' && props.event['logo'] !== null;
28
+            const getImage = () => <Avatar.Image
29
+                source={{uri: props.event['logo']}}
30
+                size={50}
31
+                style={styles.avatar}/>;
32
+            return (
33
+                <Card
34
+                    style={styles.card}
35
+                    onPress={props.clickAction}
36
+                    elevation={3}
37
+                >
38
+                    {hasImage ?
39
+                        <Card.Title
40
+                            title={props.event['title']}
41
+                            subtitle={getFormattedEventTime(props.event['date_begin'], props.event['date_end'])}
42
+                            left={getImage}
43
+                        /> :
44
+                        <Card.Title
45
+                            title={props.event['title']}
46
+                            subtitle={getFormattedEventTime(props.event['date_begin'], props.event['date_end'])}
47
+                        />}
48
+                    {!isEmpty ?
49
+                        <Card.Content style={styles.content}>
50
+                            <CustomHTML html={props.event['description']}/>
51
+                        </Card.Content> : null}
52
+
53
+                    <Card.Actions style={styles.actions}>
54
+                        <Button
55
+                            icon={'chevron-right'}
56
+                        >
57
+                            {i18n.t("homeScreen.dashboard.seeMore")}
58
+                        </Button>
59
+                    </Card.Actions>
60
+                </Card>
61
+            );
62
+        } else
63
+            return null;
64
+    }
59 65
 }
60 66
 
61 67
 const styles = StyleSheet.create({

+ 49
- 29
src/components/Home/SmallDashboardItem.js View File

@@ -1,40 +1,60 @@
1
+// @flow
2
+
1 3
 import * as React from 'react';
2 4
 import {Badge, IconButton, withTheme} from 'react-native-paper';
3 5
 import {View} from "react-native";
4 6
 
7
+
8
+type Props = {
9
+    color: string,
10
+    icon: string,
11
+    clickAction: Function,
12
+    isAvailable: boolean,
13
+    badgeNumber: number,
14
+    theme: Object,
15
+};
16
+
5 17
 /**
6 18
  * Component used to render a small dashboard item
7
- *
8
- * @param props Props to pass to the component
9
- * @return {*}
10 19
  */
11
-function SmallDashboardItem(props) {
12
-    const {colors} = props.theme;
13
-    return (
14
-        <View>
15
-            <IconButton
16
-                icon={props.icon}
17
-                color={
18
-                    props.isAvailable
19
-                        ? props.color
20
-                        : colors.textDisabled
20
+class SmallDashboardItem extends React.Component<Props> {
21
+
22
+    shouldComponentUpdate(nextProps: Props) {
23
+        return (nextProps.theme.dark !== this.props.theme.dark)
24
+            || (nextProps.isAvailable !== this.props.isAvailable)
25
+            || (nextProps.badgeNumber !== this.props.badgeNumber);
26
+    }
27
+
28
+    render() {
29
+        const props = this.props;
30
+        const colors = props.theme.colors;
31
+        return (
32
+            <View>
33
+                <IconButton
34
+                    icon={props.icon}
35
+                    color={
36
+                        props.isAvailable
37
+                            ? props.color
38
+                            : colors.textDisabled
39
+                    }
40
+                    size={35}
41
+                    onPress={props.clickAction}
42
+                />
43
+                {
44
+                    props.badgeNumber > 0 ?
45
+                        <Badge
46
+                            style={{
47
+                                position: 'absolute',
48
+                                top: 5,
49
+                                right: 5
50
+                            }}>
51
+                            {props.badgeNumber}
52
+                        </Badge> : null
21 53
                 }
22
-                size={35}
23
-                onPress={props.clickAction}
24
-            />
25
-            {
26
-                props.badgeNumber > 0 ?
27
-                    <Badge
28
-                        style={{
29
-                            position: 'absolute',
30
-                            top: 5,
31
-                            right: 5
32
-                        }}>
33
-                        {props.badgeNumber}
34
-                    </Badge> : null
35
-            }
36
-        </View>
37
-    );
54
+            </View>
55
+        );
56
+    }
57
+
38 58
 }
39 59
 
40 60
 export default withTheme(SmallDashboardItem);

+ 79
- 100
src/screens/HomeScreen.js View File

@@ -1,11 +1,11 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import {StyleSheet, View} from 'react-native';
4
+import {FlatList, StyleSheet, View} from 'react-native';
5 5
 import i18n from "i18n-js";
6 6
 import DashboardItem from "../components/Home/EventDashboardItem";
7 7
 import WebSectionList from "../components/Lists/WebSectionList";
8
-import {FAB, Text, withTheme} from 'react-native-paper';
8
+import {FAB, withTheme} from 'react-native-paper';
9 9
 import FeedItem from "../components/Home/FeedItem";
10 10
 import SquareDashboardItem from "../components/Home/SmallDashboardItem";
11 11
 import PreviewEventDashboardItem from "../components/Home/PreviewEventDashboardItem";
@@ -159,24 +159,58 @@ class HomeScreen extends React.Component<Props> {
159 159
                 content: undefined
160 160
             },
161 161
         ];
162
-        for (let [key, value] of Object.entries(dashboardData)) {
162
+        for (let [key, value: number | Object | Array<string>] of Object.entries(dashboardData)) {
163 163
             switch (key) {
164
-                case 'today_events':
165
-                    dataset[2]['content'] = value;
166
-                    break;
167 164
                 case 'available_machines':
168
-                    dataset[0]['content'][0] = {id: key, data: value};
165
+                    dataset[0]['content'][0] = {
166
+                        id: 'washers',
167
+                        data: value.washers,
168
+                        icon: 'washing-machine',
169
+                        color: this.colors.proxiwashColor,
170
+                        onPress: this.onProxiwashClick,
171
+                        isAvailable: value.washers > 0
172
+                    };
173
+                    dataset[0]['content'][1] = {
174
+                        ...dataset[0]['content'][0],
175
+                        id: 'dryers',
176
+                        data: value.dryers,
177
+                        icon: 'tumble-dryer',
178
+                        isAvailable: value.dryers > 0
179
+                    };
169 180
                     break;
170 181
                 case 'available_tutorials':
171
-                    dataset[0]['content'][1] = {id: key, data: value};
182
+                    dataset[0]['content'][2] = {
183
+                        id: key,
184
+                        data: value,
185
+                        icon: 'school',
186
+                        color: this.colors.tutorinsaColor,
187
+                        onPress: this.onTutorInsaClick,
188
+                        isAvailable: parseInt(value) > 0
189
+                    };
172 190
                     break;
173 191
                 case 'proximo_articles':
174
-                    dataset[0]['content'][2] = {id: key, data: value};
192
+                    dataset[0]['content'][3] = {
193
+                        id: key,
194
+                        data: value,
195
+                        icon: 'shopping',
196
+                        color: this.colors.proximoColor,
197
+                        onPress: this.onProximoClick,
198
+                        isAvailable: parseInt(value) > 0
199
+                    };
175 200
                     break;
176 201
                 case 'today_menu':
177
-                    dataset[0]['content'][3] = {id: key, data: value};
202
+                    dataset[0]['content'][4] = {
203
+                        id: key,
204
+                        data: 0,
205
+                        icon: 'silverware-fork-knife',
206
+                        color: this.colors.menu,
207
+                        onPress: this.onMenuClick,
208
+                        isAvailable: value.length > 0
209
+                    };
210
+                    break;
211
+                case 'today_events':
212
+                    dataset[2]['content'] = value;
178 213
                     break;
179
-
180 214
             }
181 215
         }
182 216
         return dataset
@@ -191,14 +225,14 @@ class HomeScreen extends React.Component<Props> {
191 225
     getDashboardItem(item: Object) {
192 226
         let content = item['content'];
193 227
         if (item['id'] === 'event')
194
-            return this.getDashboardEventItem(content);
228
+            return this.getDashboardEvent(content);
195 229
         else if (item['id'] === 'top')
196
-            return this.getDashboardTopItem(content);
230
+            return this.getDashboardRow(content);
197 231
         else
198
-            return this.getActionsDashboardItem();
232
+            return this.getDashboardActions();
199 233
     }
200 234
 
201
-    getActionsDashboardItem() {
235
+    getDashboardActions() {
202 236
         return <ActionsDashBoardItem {...this.props}/>;
203 237
     }
204 238
 
@@ -317,6 +351,8 @@ class HomeScreen extends React.Component<Props> {
317 351
         return displayEvent;
318 352
     }
319 353
 
354
+    onEventContainerClick = () => this.props.navigation.navigate('planning');
355
+
320 356
     /**
321 357
      * Gets the event render item.
322 358
      * If a preview is available, it will be rendered inside
@@ -324,42 +360,17 @@ class HomeScreen extends React.Component<Props> {
324 360
      * @param content
325 361
      * @return {*}
326 362
      */
327
-    getDashboardEventItem(content: Array<Object>) {
328
-        let icon = 'calendar-range';
329
-        let title = i18n.t('homeScreen.dashboard.todayEventsTitle');
330
-        let subtitle;
363
+    getDashboardEvent(content: Array<Object>) {
331 364
         let futureEvents = this.getFutureEvents(content);
332
-        let isAvailable = futureEvents.length > 0;
333
-        if (isAvailable) {
334
-            subtitle =
335
-                <Text>
336
-                    <Text style={{fontWeight: "bold"}}>{futureEvents.length}</Text>
337
-                    <Text>
338
-                        {
339
-                            futureEvents.length > 1 ?
340
-                                i18n.t('homeScreen.dashboard.todayEventsSubtitlePlural') :
341
-                                i18n.t('homeScreen.dashboard.todayEventsSubtitle')
342
-                        }
343
-                    </Text>
344
-                </Text>;
345
-        } else
346
-            subtitle = i18n.t('homeScreen.dashboard.todayEventsSubtitleNA');
347
-
348 365
         let displayEvent = this.getDisplayEvent(futureEvents);
349
-        const clickContainerAction = () => this.props.navigation.navigate('planning');
350
-        const clickPreviewAction = () => this.props.navigation.navigate('home-planning-information', {data: displayEvent});
351
-
366
+        const clickPreviewAction = () =>
367
+            this.props.navigation.navigate('home-planning-information', {data: displayEvent});
352 368
         return (
353 369
             <DashboardItem
354
-                {...this.props}
355
-                subtitle={subtitle}
356
-                icon={icon}
357
-                clickAction={clickContainerAction}
358
-                title={title}
359
-                isAvailable={isAvailable}
370
+                eventNumber={futureEvents.length}
371
+                clickAction={this.onEventContainerClick}
360 372
             >
361 373
                 <PreviewEventDashboardItem
362
-                    {...this.props}
363 374
                     event={displayEvent}
364 375
                     clickAction={clickPreviewAction}
365 376
                 />
@@ -367,66 +378,34 @@ class HomeScreen extends React.Component<Props> {
367 378
         );
368 379
     }
369 380
 
381
+    dashboardRowRenderItem = ({item}: Object) => {
382
+        return(
383
+            <SquareDashboardItem
384
+                color={item.color}
385
+                icon={item.icon}
386
+                clickAction={item.onPress}
387
+                isAvailable={item.isAvailable}
388
+                badgeNumber={item.data}
389
+            />
390
+        );
391
+    };
392
+
370 393
     /**
371 394
      * Gets a classic dashboard item.
372 395
      *
373 396
      * @param content
374 397
      * @return {*}
375 398
      */
376
-    getDashboardTopItem(content: Array<Object>) {
377
-        let proxiwashData = content[0]['data'];
378
-        let tutorinsaData = content[1]['data'];
379
-        let proximoData = content[2]['data'];
380
-        let menuData = content[3]['data'];
381
-        return (
382
-            <View style={{
383
-                flex: 1,
384
-                flexDirection: 'row',
385
-                justifyContent: 'center',
386
-                flexWrap: 'wrap',
387
-                margin: 10,
388
-            }}>
389
-                <SquareDashboardItem
390
-                    color={this.colors.proxiwashColor}
391
-                    icon={'washing-machine'}
392
-                    clickAction={this.onProxiwashClick}
393
-                    isAvailable={parseInt(proxiwashData['washers']) > 0}
394
-                    badgeNumber={proxiwashData['washers']}
395
-                />
396
-                <SquareDashboardItem
397
-                    color={this.colors.proxiwashColor}
398
-                    icon={'tumble-dryer'}
399
-                    clickAction={this.onProxiwashClick}
400
-                    isAvailable={parseInt(proxiwashData['dryers']) > 0}
401
-                    badgeNumber={proxiwashData['dryers']}
402
-                />
403
-                <SquareDashboardItem
404
-                    color={this.colors.tutorinsaColor}
405
-                    icon={'school'}
406
-                    clickAction={this.onTutorInsaClick}
407
-                    isAvailable={tutorinsaData > 0}
408
-                    badgeNumber={tutorinsaData}
409
-                />
410
-                <SquareDashboardItem
411
-                    color={this.colors.proximoColor}
412
-                    icon={'shopping'}
413
-                    clickAction={this.onProximoClick}
414
-                    isAvailable={parseInt(proximoData) > 0}
415
-                    badgeNumber={parseInt(proximoData)}
416
-                />
417
-                <SquareDashboardItem
418
-                    color={this.colors.menuColor}
419
-                    icon={'silverware-fork-knife'}
420
-                    clickAction={this.onMenuClick}
421
-                    isAvailable={menuData.length > 0}
422
-                    badgeNumber={0}
423
-                />
424
-            </View>
425
-        );
426
-    }
427
-
428
-    openLink(link: string) {
429
-        Linking.openURL(link);
399
+    getDashboardRow(content: Array<Object>) {
400
+        return <FlatList
401
+                data={content}
402
+                renderItem={this.dashboardRowRenderItem}
403
+                horizontal={true}
404
+                contentContainerStyle={{
405
+                marginLeft: 'auto',
406
+                marginRight: 'auto',
407
+                }}
408
+            />;
430 409
     }
431 410
 
432 411
     /**
@@ -436,7 +415,7 @@ class HomeScreen extends React.Component<Props> {
436 415
      * @return {*}
437 416
      */
438 417
     getFeedItem(item: Object) {
439
-        const onOutLinkPress = this.openLink.bind(this, item.permalink_url);
418
+        const onOutLinkPress = () => Linking.openURL(item.permalink_url);
440 419
         return (
441 420
             <FeedItem
442 421
                 title={NAME_AMICALE}

Loading…
Cancel
Save