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

View file

@ -1,45 +1,72 @@
// @flow
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 i18n from "i18n-js";
type Props = {
eventNumber: number;
clickAction: Function,
theme: Object,
}
/**
* Component used to display a dashboard item containing a preview event
*
* @param props Props to pass to the component
* @return {*}
*/
function EventDashBoardItem(props) {
const {colors} = props.theme;
const iconColor = props.isAvailable ?
colors.planningColor :
colors.textDisabled;
const textColor = props.isAvailable ?
colors.text :
colors.textDisabled;
return (
<Card
style={styles.card}
onPress={props.clickAction}>
class EventDashBoardItem extends React.Component<Props> {
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.textDisabled;
const textColor = isAvailable ?
colors.text :
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 (
<Card
style={styles.card}
onPress={props.clickAction}>
<Card.Title
title={i18n.t('homeScreen.dashboard.todayEventsTitle')}
titleStyle={{color: textColor}}
subtitle={subtitle}
subtitleStyle={{color: textColor}}
left={() =>
<Avatar.Icon
icon={'calendar-range'}
color={iconColor}
size={60}
style={styles.avatar}/>}
/>
<Card.Content>
{props.children}
</Card.Content>
</Card>
);
}
<Card.Title
title={props.title}
titleStyle={{color: textColor}}
subtitle={props.subtitle}
subtitleStyle={{color: textColor}}
left={() =>
<Avatar.Icon
icon={props.icon}
color={iconColor}
size={60}
style={styles.avatar}/>}
/>
<Card.Content>
{props.children}
</Card.Content>
</Card>
);
}
const styles = StyleSheet.create({

View file

@ -1,5 +1,5 @@
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 Autolink from "react-native-autolink";
import i18n from "i18n-js";
@ -7,16 +7,25 @@ import ImageModal from 'react-native-image-modal';
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
*/
class FeedItem extends React.Component {
class FeedItem extends React.Component<Props> {
shouldComponentUpdate() {
return false;
}
/**
* Gets the amicale INSAT logo
*
@ -30,6 +39,7 @@ class FeedItem extends React.Component {
}
render() {
console.log('render feed');
return (
<Card style={{margin: 10}}>
<Card.Title
@ -72,4 +82,4 @@ class FeedItem extends React.Component {
}
}
export default withTheme(FeedItem);
export default FeedItem;

View file

@ -1,61 +1,67 @@
// @flow
import * as React from 'react';
import {StyleSheet, View} from "react-native";
import {StyleSheet} from "react-native";
import i18n from "i18n-js";
import {Avatar, Button, Card} from 'react-native-paper';
import {getFormattedEventTime, isDescriptionEmpty} from "../../utils/Planning";
import CustomHTML from "../Custom/CustomHTML";
type Props = {
event: Object,
clickAction: Function,
}
/**
* 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) {
const isEmpty = props.event === undefined
? true
: isDescriptionEmpty(props.event['description']);
class PreviewEventDashboardItem extends React.Component<Props> {
if (props.event !== undefined && props.event !== null) {
const hasImage = props.event['logo'] !== '' && props.event['logo'] !== null;
const getImage = () => <Avatar.Image
source={{uri: props.event['logo']}}
size={50}
style={styles.avatar}/>;
return (
<Card
style={styles.card}
onPress={props.clickAction}
elevation={3}
>
{hasImage ?
<Card.Title
title={props.event['title']}
subtitle={getFormattedEventTime(props.event['date_begin'], props.event['date_end'])}
left={getImage}
/> :
<Card.Title
title={props.event['title']}
subtitle={getFormattedEventTime(props.event['date_begin'], props.event['date_end'])}
/>}
{!isEmpty ?
<Card.Content style={styles.content}>
<CustomHTML html={props.event['description']}/>
</Card.Content> : null}
render() {
const props = this.props;
const isEmpty = props.event === undefined
? true
: isDescriptionEmpty(props.event['description']);
<Card.Actions style={styles.actions}>
<Button
icon={'chevron-right'}
>
{i18n.t("homeScreen.dashboard.seeMore")}
</Button>
</Card.Actions>
</Card>
);
} else
return <View/>
if (props.event !== undefined && props.event !== null) {
const hasImage = props.event['logo'] !== '' && props.event['logo'] !== null;
const getImage = () => <Avatar.Image
source={{uri: props.event['logo']}}
size={50}
style={styles.avatar}/>;
return (
<Card
style={styles.card}
onPress={props.clickAction}
elevation={3}
>
{hasImage ?
<Card.Title
title={props.event['title']}
subtitle={getFormattedEventTime(props.event['date_begin'], props.event['date_end'])}
left={getImage}
/> :
<Card.Title
title={props.event['title']}
subtitle={getFormattedEventTime(props.event['date_begin'], props.event['date_end'])}
/>}
{!isEmpty ?
<Card.Content style={styles.content}>
<CustomHTML html={props.event['description']}/>
</Card.Content> : null}
<Card.Actions style={styles.actions}>
<Button
icon={'chevron-right'}
>
{i18n.t("homeScreen.dashboard.seeMore")}
</Button>
</Card.Actions>
</Card>
);
} else
return null;
}
}
const styles = StyleSheet.create({

View file

@ -1,40 +1,60 @@
// @flow
import * as React from 'react';
import {Badge, IconButton, withTheme} from 'react-native-paper';
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
*
* @param props Props to pass to the component
* @return {*}
*/
function SmallDashboardItem(props) {
const {colors} = props.theme;
return (
<View>
<IconButton
icon={props.icon}
color={
props.isAvailable
? props.color
: colors.textDisabled
class SmallDashboardItem extends React.Component<Props> {
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 (
<View>
<IconButton
icon={props.icon}
color={
props.isAvailable
? props.color
: colors.textDisabled
}
size={35}
onPress={props.clickAction}
/>
{
props.badgeNumber > 0 ?
<Badge
style={{
position: 'absolute',
top: 5,
right: 5
}}>
{props.badgeNumber}
</Badge> : null
}
size={35}
onPress={props.clickAction}
/>
{
props.badgeNumber > 0 ?
<Badge
style={{
position: 'absolute',
top: 5,
right: 5
}}>
{props.badgeNumber}
</Badge> : null
}
</View>
);
</View>
);
}
}
export default withTheme(SmallDashboardItem);

View file

@ -1,11 +1,11 @@
// @flow
import * as React from 'react';
import {StyleSheet, View} from 'react-native';
import {FlatList, StyleSheet, View} from 'react-native';
import i18n from "i18n-js";
import DashboardItem from "../components/Home/EventDashboardItem";
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 SquareDashboardItem from "../components/Home/SmallDashboardItem";
import PreviewEventDashboardItem from "../components/Home/PreviewEventDashboardItem";
@ -159,24 +159,58 @@ class HomeScreen extends React.Component<Props> {
content: undefined
},
];
for (let [key, value] of Object.entries(dashboardData)) {
for (let [key, value: number | Object | Array<string>] of Object.entries(dashboardData)) {
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':
dataset[2]['content'] = value;
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
@ -191,14 +225,14 @@ class HomeScreen extends React.Component<Props> {
getDashboardItem(item: Object) {
let content = item['content'];
if (item['id'] === 'event')
return this.getDashboardEventItem(content);
return this.getDashboardEvent(content);
else if (item['id'] === 'top')
return this.getDashboardTopItem(content);
return this.getDashboardRow(content);
else
return this.getActionsDashboardItem();
return this.getDashboardActions();
}
getActionsDashboardItem() {
getDashboardActions() {
return <ActionsDashBoardItem {...this.props}/>;
}
@ -317,6 +351,8 @@ class HomeScreen extends React.Component<Props> {
return displayEvent;
}
onEventContainerClick = () => this.props.navigation.navigate('planning');
/**
* Gets the event render item.
* If a preview is available, it will be rendered inside
@ -324,42 +360,17 @@ class HomeScreen extends React.Component<Props> {
* @param content
* @return {*}
*/
getDashboardEventItem(content: Array<Object>) {
let icon = 'calendar-range';
let title = i18n.t('homeScreen.dashboard.todayEventsTitle');
let subtitle;
getDashboardEvent(content: Array<Object>) {
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);
const clickContainerAction = () => this.props.navigation.navigate('planning');
const clickPreviewAction = () => this.props.navigation.navigate('home-planning-information', {data: displayEvent});
const clickPreviewAction = () =>
this.props.navigation.navigate('home-planning-information', {data: displayEvent});
return (
<DashboardItem
{...this.props}
subtitle={subtitle}
icon={icon}
clickAction={clickContainerAction}
title={title}
isAvailable={isAvailable}
eventNumber={futureEvents.length}
clickAction={this.onEventContainerClick}
>
<PreviewEventDashboardItem
{...this.props}
event={displayEvent}
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.
*
* @param content
* @return {*}
*/
getDashboardTopItem(content: Array<Object>) {
let proxiwashData = content[0]['data'];
let tutorinsaData = content[1]['data'];
let proximoData = content[2]['data'];
let menuData = content[3]['data'];
return (
<View style={{
flex: 1,
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);
getDashboardRow(content: Array<Object>) {
return <FlatList
data={content}
renderItem={this.dashboardRowRenderItem}
horizontal={true}
contentContainerStyle={{
marginLeft: 'auto',
marginRight: 'auto',
}}
/>;
}
/**
@ -436,7 +415,7 @@ class HomeScreen extends React.Component<Props> {
* @return {*}
*/
getFeedItem(item: Object) {
const onOutLinkPress = this.openLink.bind(this, item.permalink_url);
const onOutLinkPress = () => Linking.openURL(item.permalink_url);
return (
<FeedItem
title={NAME_AMICALE}