Improved home screen items render cycle

This commit is contained in:
Arnaud Vergnet 2020-04-12 19:56:29 +02:00
parent 00e7da4bd2
commit 3d57361908
6 changed files with 263 additions and 223 deletions

View file

@ -10,13 +10,10 @@ type Props = {
theme: Object, theme: Object,
} }
class ActionsDashBoardItem extends React.PureComponent<Props> { class ActionsDashBoardItem extends React.Component<Props> {
colors: Object; shouldComponentUpdate(nextProps: Props): boolean {
return (nextProps.theme.dark !== this.props.theme.dark)
constructor(props) {
super(props);
this.colors = this.props.theme.colors;
} }
openDrawer = () => this.props.navigation.openDrawer(); openDrawer = () => this.props.navigation.openDrawer();
@ -24,10 +21,11 @@ class ActionsDashBoardItem extends React.PureComponent<Props> {
gotToSettings = () => this.props.navigation.navigate("settings"); gotToSettings = () => this.props.navigation.navigate("settings");
render() { render() {
console.log('render action dashboard');
return ( return (
<Card style={{ <Card style={{
...styles.card, ...styles.card,
borderColor: this.colors.primary, borderColor: this.props.theme.colors.primary,
}}> }}>
<Card.Content style={styles.content}> <Card.Content style={styles.content}>
<Button <Button

View file

@ -1,36 +1,61 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {Avatar, Card, withTheme} from 'react-native-paper'; import {Avatar, Card, Text, withTheme} from 'react-native-paper';
import {StyleSheet} from "react-native"; import {StyleSheet} from "react-native";
import i18n from "i18n-js";
type Props = {
eventNumber: number;
clickAction: Function,
theme: Object,
}
/** /**
* Component used to display a dashboard item containing a preview event * Component used to display a dashboard item containing a preview event
*
* @param props Props to pass to the component
* @return {*}
*/ */
function EventDashBoardItem(props) { class EventDashBoardItem extends React.Component<Props> {
const {colors} = props.theme;
const iconColor = props.isAvailable ? shouldComponentUpdate(nextProps: Props) {
return (nextProps.theme.dark !== this.props.theme.dark)
|| (nextProps.eventNumber !== this.props.eventNumber);
}
render() {
const props = this.props;
const colors = props.theme.colors;
const isAvailable = props.eventNumber > 0;
const iconColor = isAvailable ?
colors.planningColor : colors.planningColor :
colors.textDisabled; colors.textDisabled;
const textColor = props.isAvailable ? const textColor = isAvailable ?
colors.text : colors.text :
colors.textDisabled; colors.textDisabled;
let subtitle;
if (isAvailable) {
subtitle =
<Text>
<Text style={{fontWeight: "bold"}}>{props.eventNumber}</Text>
<Text>
{props.eventNumber > 1
? i18n.t('homeScreen.dashboard.todayEventsSubtitlePlural')
: i18n.t('homeScreen.dashboard.todayEventsSubtitle')}
</Text>
</Text>;
} else
subtitle = i18n.t('homeScreen.dashboard.todayEventsSubtitleNA');
return ( return (
<Card <Card
style={styles.card} style={styles.card}
onPress={props.clickAction}> onPress={props.clickAction}>
<Card.Title <Card.Title
title={props.title} title={i18n.t('homeScreen.dashboard.todayEventsTitle')}
titleStyle={{color: textColor}} titleStyle={{color: textColor}}
subtitle={props.subtitle} subtitle={subtitle}
subtitleStyle={{color: textColor}} subtitleStyle={{color: textColor}}
left={() => left={() =>
<Avatar.Icon <Avatar.Icon
icon={props.icon} icon={'calendar-range'}
color={iconColor} color={iconColor}
size={60} size={60}
style={styles.avatar}/>} style={styles.avatar}/>}
@ -40,6 +65,8 @@ function EventDashBoardItem(props) {
</Card.Content> </Card.Content>
</Card> </Card>
); );
}
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({

View file

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import {Avatar, Button, Card, Text, withTheme} from 'react-native-paper'; import {Avatar, Button, Card, Text} from 'react-native-paper';
import {View} from "react-native"; import {View} from "react-native";
import Autolink from "react-native-autolink"; import Autolink from "react-native-autolink";
import i18n from "i18n-js"; import i18n from "i18n-js";
@ -7,16 +7,25 @@ import ImageModal from 'react-native-image-modal';
const ICON_AMICALE = require('../../../assets/amicale.png'); const ICON_AMICALE = require('../../../assets/amicale.png');
type Props = {
theme: Object,
title: string,
subtitle: string,
full_picture: string,
message: string,
onOutLinkPress: Function,
}
/** /**
* Component used to display a feed item * Component used to display a feed item
*/ */
class FeedItem extends React.Component { class FeedItem extends React.Component<Props> {
shouldComponentUpdate() { shouldComponentUpdate() {
return false; return false;
} }
/** /**
* Gets the amicale INSAT logo * Gets the amicale INSAT logo
* *
@ -30,6 +39,7 @@ class FeedItem extends React.Component {
} }
render() { render() {
console.log('render feed');
return ( return (
<Card style={{margin: 10}}> <Card style={{margin: 10}}>
<Card.Title <Card.Title
@ -72,4 +82,4 @@ class FeedItem extends React.Component {
} }
} }
export default withTheme(FeedItem); export default FeedItem;

View file

@ -1,19 +1,24 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {StyleSheet, View} from "react-native"; import {StyleSheet} from "react-native";
import i18n from "i18n-js"; import i18n from "i18n-js";
import {Avatar, Button, Card} from 'react-native-paper'; import {Avatar, Button, Card} from 'react-native-paper';
import {getFormattedEventTime, isDescriptionEmpty} from "../../utils/Planning"; import {getFormattedEventTime, isDescriptionEmpty} from "../../utils/Planning";
import CustomHTML from "../Custom/CustomHTML"; import CustomHTML from "../Custom/CustomHTML";
type Props = {
event: Object,
clickAction: Function,
}
/** /**
* Component used to display an event preview if an event is available * Component used to display an event preview if an event is available
*
* @param props Props to pass to the component
* @return {*}
*/ */
function PreviewEventDashboardItem(props : Object) { class PreviewEventDashboardItem extends React.Component<Props> {
render() {
const props = this.props;
const isEmpty = props.event === undefined const isEmpty = props.event === undefined
? true ? true
: isDescriptionEmpty(props.event['description']); : isDescriptionEmpty(props.event['description']);
@ -55,7 +60,8 @@ function PreviewEventDashboardItem(props : Object) {
</Card> </Card>
); );
} else } else
return <View/> return null;
}
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({

View file

@ -1,15 +1,33 @@
// @flow
import * as React from 'react'; import * as React from 'react';
import {Badge, IconButton, withTheme} from 'react-native-paper'; import {Badge, IconButton, withTheme} from 'react-native-paper';
import {View} from "react-native"; import {View} from "react-native";
type Props = {
color: string,
icon: string,
clickAction: Function,
isAvailable: boolean,
badgeNumber: number,
theme: Object,
};
/** /**
* Component used to render a small dashboard item * Component used to render a small dashboard item
*
* @param props Props to pass to the component
* @return {*}
*/ */
function SmallDashboardItem(props) { class SmallDashboardItem extends React.Component<Props> {
const {colors} = props.theme;
shouldComponentUpdate(nextProps: Props) {
return (nextProps.theme.dark !== this.props.theme.dark)
|| (nextProps.isAvailable !== this.props.isAvailable)
|| (nextProps.badgeNumber !== this.props.badgeNumber);
}
render() {
const props = this.props;
const colors = props.theme.colors;
return ( return (
<View> <View>
<IconButton <IconButton
@ -35,6 +53,8 @@ function SmallDashboardItem(props) {
} }
</View> </View>
); );
}
} }
export default withTheme(SmallDashboardItem); export default withTheme(SmallDashboardItem);

View file

@ -1,11 +1,11 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {StyleSheet, View} from 'react-native'; import {FlatList, StyleSheet, View} from 'react-native';
import i18n from "i18n-js"; import i18n from "i18n-js";
import DashboardItem from "../components/Home/EventDashboardItem"; import DashboardItem from "../components/Home/EventDashboardItem";
import WebSectionList from "../components/Lists/WebSectionList"; import WebSectionList from "../components/Lists/WebSectionList";
import {FAB, Text, withTheme} from 'react-native-paper'; import {FAB, withTheme} from 'react-native-paper';
import FeedItem from "../components/Home/FeedItem"; import FeedItem from "../components/Home/FeedItem";
import SquareDashboardItem from "../components/Home/SmallDashboardItem"; import SquareDashboardItem from "../components/Home/SmallDashboardItem";
import PreviewEventDashboardItem from "../components/Home/PreviewEventDashboardItem"; import PreviewEventDashboardItem from "../components/Home/PreviewEventDashboardItem";
@ -159,24 +159,58 @@ class HomeScreen extends React.Component<Props> {
content: undefined content: undefined
}, },
]; ];
for (let [key, value] of Object.entries(dashboardData)) { for (let [key, value: number | Object | Array<string>] of Object.entries(dashboardData)) {
switch (key) { switch (key) {
case 'available_machines':
dataset[0]['content'][0] = {
id: 'washers',
data: value.washers,
icon: 'washing-machine',
color: this.colors.proxiwashColor,
onPress: this.onProxiwashClick,
isAvailable: value.washers > 0
};
dataset[0]['content'][1] = {
...dataset[0]['content'][0],
id: 'dryers',
data: value.dryers,
icon: 'tumble-dryer',
isAvailable: value.dryers > 0
};
break;
case 'available_tutorials':
dataset[0]['content'][2] = {
id: key,
data: value,
icon: 'school',
color: this.colors.tutorinsaColor,
onPress: this.onTutorInsaClick,
isAvailable: parseInt(value) > 0
};
break;
case 'proximo_articles':
dataset[0]['content'][3] = {
id: key,
data: value,
icon: 'shopping',
color: this.colors.proximoColor,
onPress: this.onProximoClick,
isAvailable: parseInt(value) > 0
};
break;
case 'today_menu':
dataset[0]['content'][4] = {
id: key,
data: 0,
icon: 'silverware-fork-knife',
color: this.colors.menu,
onPress: this.onMenuClick,
isAvailable: value.length > 0
};
break;
case 'today_events': case 'today_events':
dataset[2]['content'] = value; dataset[2]['content'] = value;
break; break;
case 'available_machines':
dataset[0]['content'][0] = {id: key, data: value};
break;
case 'available_tutorials':
dataset[0]['content'][1] = {id: key, data: value};
break;
case 'proximo_articles':
dataset[0]['content'][2] = {id: key, data: value};
break;
case 'today_menu':
dataset[0]['content'][3] = {id: key, data: value};
break;
} }
} }
return dataset return dataset
@ -191,14 +225,14 @@ class HomeScreen extends React.Component<Props> {
getDashboardItem(item: Object) { getDashboardItem(item: Object) {
let content = item['content']; let content = item['content'];
if (item['id'] === 'event') if (item['id'] === 'event')
return this.getDashboardEventItem(content); return this.getDashboardEvent(content);
else if (item['id'] === 'top') else if (item['id'] === 'top')
return this.getDashboardTopItem(content); return this.getDashboardRow(content);
else else
return this.getActionsDashboardItem(); return this.getDashboardActions();
} }
getActionsDashboardItem() { getDashboardActions() {
return <ActionsDashBoardItem {...this.props}/>; return <ActionsDashBoardItem {...this.props}/>;
} }
@ -317,6 +351,8 @@ class HomeScreen extends React.Component<Props> {
return displayEvent; return displayEvent;
} }
onEventContainerClick = () => this.props.navigation.navigate('planning');
/** /**
* Gets the event render item. * Gets the event render item.
* If a preview is available, it will be rendered inside * If a preview is available, it will be rendered inside
@ -324,42 +360,17 @@ class HomeScreen extends React.Component<Props> {
* @param content * @param content
* @return {*} * @return {*}
*/ */
getDashboardEventItem(content: Array<Object>) { getDashboardEvent(content: Array<Object>) {
let icon = 'calendar-range';
let title = i18n.t('homeScreen.dashboard.todayEventsTitle');
let subtitle;
let futureEvents = this.getFutureEvents(content); let futureEvents = this.getFutureEvents(content);
let isAvailable = futureEvents.length > 0;
if (isAvailable) {
subtitle =
<Text>
<Text style={{fontWeight: "bold"}}>{futureEvents.length}</Text>
<Text>
{
futureEvents.length > 1 ?
i18n.t('homeScreen.dashboard.todayEventsSubtitlePlural') :
i18n.t('homeScreen.dashboard.todayEventsSubtitle')
}
</Text>
</Text>;
} else
subtitle = i18n.t('homeScreen.dashboard.todayEventsSubtitleNA');
let displayEvent = this.getDisplayEvent(futureEvents); let displayEvent = this.getDisplayEvent(futureEvents);
const clickContainerAction = () => this.props.navigation.navigate('planning'); const clickPreviewAction = () =>
const clickPreviewAction = () => this.props.navigation.navigate('home-planning-information', {data: displayEvent}); this.props.navigation.navigate('home-planning-information', {data: displayEvent});
return ( return (
<DashboardItem <DashboardItem
{...this.props} eventNumber={futureEvents.length}
subtitle={subtitle} clickAction={this.onEventContainerClick}
icon={icon}
clickAction={clickContainerAction}
title={title}
isAvailable={isAvailable}
> >
<PreviewEventDashboardItem <PreviewEventDashboardItem
{...this.props}
event={displayEvent} event={displayEvent}
clickAction={clickPreviewAction} clickAction={clickPreviewAction}
/> />
@ -367,66 +378,34 @@ class HomeScreen extends React.Component<Props> {
); );
} }
dashboardRowRenderItem = ({item}: Object) => {
return(
<SquareDashboardItem
color={item.color}
icon={item.icon}
clickAction={item.onPress}
isAvailable={item.isAvailable}
badgeNumber={item.data}
/>
);
};
/** /**
* Gets a classic dashboard item. * Gets a classic dashboard item.
* *
* @param content * @param content
* @return {*} * @return {*}
*/ */
getDashboardTopItem(content: Array<Object>) { getDashboardRow(content: Array<Object>) {
let proxiwashData = content[0]['data']; return <FlatList
let tutorinsaData = content[1]['data']; data={content}
let proximoData = content[2]['data']; renderItem={this.dashboardRowRenderItem}
let menuData = content[3]['data']; horizontal={true}
return ( contentContainerStyle={{
<View style={{ marginLeft: 'auto',
flex: 1, marginRight: 'auto',
flexDirection: 'row', }}
justifyContent: 'center', />;
flexWrap: 'wrap',
margin: 10,
}}>
<SquareDashboardItem
color={this.colors.proxiwashColor}
icon={'washing-machine'}
clickAction={this.onProxiwashClick}
isAvailable={parseInt(proxiwashData['washers']) > 0}
badgeNumber={proxiwashData['washers']}
/>
<SquareDashboardItem
color={this.colors.proxiwashColor}
icon={'tumble-dryer'}
clickAction={this.onProxiwashClick}
isAvailable={parseInt(proxiwashData['dryers']) > 0}
badgeNumber={proxiwashData['dryers']}
/>
<SquareDashboardItem
color={this.colors.tutorinsaColor}
icon={'school'}
clickAction={this.onTutorInsaClick}
isAvailable={tutorinsaData > 0}
badgeNumber={tutorinsaData}
/>
<SquareDashboardItem
color={this.colors.proximoColor}
icon={'shopping'}
clickAction={this.onProximoClick}
isAvailable={parseInt(proximoData) > 0}
badgeNumber={parseInt(proximoData)}
/>
<SquareDashboardItem
color={this.colors.menuColor}
icon={'silverware-fork-knife'}
clickAction={this.onMenuClick}
isAvailable={menuData.length > 0}
badgeNumber={0}
/>
</View>
);
}
openLink(link: string) {
Linking.openURL(link);
} }
/** /**
@ -436,7 +415,7 @@ class HomeScreen extends React.Component<Props> {
* @return {*} * @return {*}
*/ */
getFeedItem(item: Object) { getFeedItem(item: Object) {
const onOutLinkPress = this.openLink.bind(this, item.permalink_url); const onOutLinkPress = () => Linking.openURL(item.permalink_url);
return ( return (
<FeedItem <FeedItem
title={NAME_AMICALE} title={NAME_AMICALE}