Improve Dashboard edit components to match linter

This commit is contained in:
Arnaud Vergnet 2020-08-04 09:31:27 +02:00
parent 93d12b27f8
commit 70365136ac
4 changed files with 312 additions and 271 deletions

View file

@ -2,71 +2,88 @@
import * as React from 'react'; import * as React from 'react';
import {withTheme} from 'react-native-paper'; import {withTheme} from 'react-native-paper';
import {FlatList, Image, View} from "react-native"; import {FlatList, Image, View} from 'react-native';
import DashboardEditItem from "./DashboardEditItem"; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import AnimatedAccordion from "../../Animations/AnimatedAccordion"; import DashboardEditItem from './DashboardEditItem';
import type {ServiceCategory, ServiceItem} from "../../../managers/ServicesManager"; import AnimatedAccordion from '../../Animations/AnimatedAccordion';
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons"; import type {
import type {CustomTheme} from "../../../managers/ThemeManager"; ServiceCategoryType,
ServiceItemType,
} from '../../../managers/ServicesManager';
import type {CustomTheme} from '../../../managers/ThemeManager';
type Props = { type PropsType = {
item: ServiceCategory, item: ServiceCategoryType,
activeDashboard: Array<string>, activeDashboard: Array<string>,
onPress: (service: ServiceItem) => void, onPress: (service: ServiceItemType) => void,
theme: CustomTheme, theme: CustomTheme,
} };
const LIST_ITEM_HEIGHT = 64; const LIST_ITEM_HEIGHT = 64;
class DashboardEditAccordion extends React.Component<Props> { class DashboardEditAccordion extends React.Component<PropsType> {
getRenderItem = ({item}: {item: ServiceItemType}): React.Node => {
const {props} = this;
return (
<DashboardEditItem
height={LIST_ITEM_HEIGHT}
item={item}
isActive={props.activeDashboard.includes(item.key)}
onPress={() => {
props.onPress(item);
}}
/>
);
};
renderItem = ({item}: { item: ServiceItem }) => { getItemLayout = (
return ( data: ?Array<ServiceItemType>,
<DashboardEditItem index: number,
height={LIST_ITEM_HEIGHT} ): {length: number, offset: number, index: number} => ({
item={item} length: LIST_ITEM_HEIGHT,
isActive={this.props.activeDashboard.includes(item.key)} offset: LIST_ITEM_HEIGHT * index,
onPress={() => this.props.onPress(item)}/> index,
); });
}
itemLayout = (data, index) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index}); render(): React.Node {
const {props} = this;
render() { const {item} = props;
const item = this.props.item; return (
return ( <View>
<View> <AnimatedAccordion
<AnimatedAccordion title={item.title}
title={item.title} left={(): React.Node =>
left={props => typeof item.image === "number" typeof item.image === 'number' ? (
? <Image <Image
{...props} source={item.image}
source={item.image} style={{
style={{ width: 40,
width: 40, height: 40,
height: 40 }}
}} />
/> ) : (
: <MaterialCommunityIcons <MaterialCommunityIcons
//$FlowFixMe // $FlowFixMe
name={item.image} name={item.image}
color={this.props.theme.colors.primary} color={props.theme.colors.primary}
size={40}/>} size={40}
> />
{/*$FlowFixMe*/} )
<FlatList }>
data={item.content} {/* $FlowFixMe */}
extraData={this.props.activeDashboard.toString()} <FlatList
renderItem={this.renderItem} data={item.content}
listKey={item.key} extraData={props.activeDashboard.toString()}
// Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration renderItem={this.getRenderItem}
getItemLayout={this.itemLayout} listKey={item.key}
removeClippedSubviews={true} // Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
/> getItemLayout={this.getItemLayout}
</AnimatedAccordion> removeClippedSubviews
</View> />
); </AnimatedAccordion>
} </View>
);
}
} }
export default withTheme(DashboardEditAccordion) export default withTheme(DashboardEditAccordion);

View file

@ -1,55 +1,61 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {Image} from "react-native"; import {Image} from 'react-native';
import {List, withTheme} from 'react-native-paper'; import {List, withTheme} from 'react-native-paper';
import type {CustomTheme} from "../../../managers/ThemeManager"; import type {CustomTheme} from '../../../managers/ThemeManager';
import type {ServiceItem} from "../../../managers/ServicesManager"; import type {ServiceItemType} from '../../../managers/ServicesManager';
type Props = { type PropsType = {
item: ServiceItem, item: ServiceItemType,
isActive: boolean, isActive: boolean,
height: number, height: number,
onPress: () => void, onPress: () => void,
theme: CustomTheme, theme: CustomTheme,
} };
class DashboardEditItem extends React.Component<Props> { class DashboardEditItem extends React.Component<PropsType> {
shouldComponentUpdate(nextProps: PropsType): boolean {
const {isActive} = this.props;
return nextProps.isActive !== isActive;
}
shouldComponentUpdate(nextProps: Props) { render(): React.Node {
return (nextProps.isActive !== this.props.isActive); const {props} = this;
} return (
<List.Item
render() { title={props.item.title}
return ( description={props.item.subtitle}
<List.Item onPress={props.isActive ? null : props.onPress}
title={this.props.item.title} left={(): React.Node => (
description={this.props.item.subtitle} <Image
onPress={this.props.isActive ? null : this.props.onPress} source={{uri: props.item.image}}
left={props => style={{
<Image width: 40,
{...props} height: 40,
source={{uri: this.props.item.image}} }}
style={{ />
width: 40, )}
height: 40 right={({size}: {size: number}): React.Node =>
}} props.isActive ? (
/>} <List.Icon
right={props => this.props.isActive size={size}
? <List.Icon icon="check"
{...props} color={props.theme.colors.success}
icon={"check"}
color={this.props.theme.colors.success}
/> : null}
style={{
height: this.props.height,
justifyContent: 'center',
paddingLeft: 30,
backgroundColor: this.props.isActive ? this.props.theme.colors.proxiwashFinishedColor : "transparent"
}}
/> />
); ) : null
} }
style={{
height: props.height,
justifyContent: 'center',
paddingLeft: 30,
backgroundColor: props.isActive
? props.theme.colors.proxiwashFinishedColor
: 'transparent',
}}
/>
);
}
} }
export default withTheme(DashboardEditItem); export default withTheme(DashboardEditItem);

View file

@ -2,57 +2,57 @@
import * as React from 'react'; import * as React from 'react';
import {TouchableRipple, withTheme} from 'react-native-paper'; import {TouchableRipple, withTheme} from 'react-native-paper';
import {Dimensions, Image, View} from "react-native"; import {Dimensions, Image, View} from 'react-native';
import type {CustomTheme} from "../../../managers/ThemeManager"; import type {CustomTheme} from '../../../managers/ThemeManager';
type Props = { type PropsType = {
image: string, image: string,
isActive: boolean, isActive: boolean,
onPress: () => void, onPress: () => void,
theme: CustomTheme, theme: CustomTheme,
}; };
/** /**
* Component used to render a small dashboard item * Component used to render a small dashboard item
*/ */
class DashboardEditPreviewItem extends React.Component<Props> { class DashboardEditPreviewItem extends React.Component<PropsType> {
itemSize: number;
itemSize: number; constructor(props: PropsType) {
super(props);
constructor(props: Props) { this.itemSize = Dimensions.get('window').width / 8;
super(props); }
this.itemSize = Dimensions.get('window').width / 8;
}
render() {
const props = this.props;
return (
<TouchableRipple
onPress={this.props.onPress}
borderless={true}
style={{
marginLeft: 5,
marginRight: 5,
backgroundColor: this.props.isActive ? this.props.theme.colors.textDisabled : "transparent",
borderRadius: 5
}}
>
<View style={{
width: this.itemSize,
height: this.itemSize,
}}>
<Image
source={{uri: props.image}}
style={{
width: "100%",
height: "100%",
}}
/>
</View>
</TouchableRipple>
);
}
render(): React.Node {
const {props} = this;
return (
<TouchableRipple
onPress={props.onPress}
borderless
style={{
marginLeft: 5,
marginRight: 5,
backgroundColor: props.isActive
? props.theme.colors.textDisabled
: 'transparent',
borderRadius: 5,
}}>
<View
style={{
width: this.itemSize,
height: this.itemSize,
}}>
<Image
source={{uri: props.image}}
style={{
width: '100%',
height: '100%',
}}
/>
</View>
</TouchableRipple>
);
}
} }
export default withTheme(DashboardEditPreviewItem) export default withTheme(DashboardEditPreviewItem);

View file

@ -1,148 +1,166 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from '@react-navigation/stack';
import type {CustomTheme} from "../../../managers/ThemeManager"; import {Button, Card, Paragraph, withTheme} from 'react-native-paper';
import {Button, Card, Paragraph, withTheme} from "react-native-paper"; import {FlatList} from 'react-native';
import type {ServiceCategory, ServiceItem} from "../../../managers/ServicesManager"; import {View} from 'react-native-animatable';
import DashboardManager from "../../../managers/DashboardManager"; import i18n from 'i18n-js';
import DashboardItem from "../../../components/Home/EventDashboardItem"; import type {
import {FlatList} from "react-native"; ServiceCategoryType,
import {View} from "react-native-animatable"; ServiceItemType,
import DashboardEditAccordion from "../../../components/Lists/DashboardEdit/DashboardEditAccordion"; } from '../../../managers/ServicesManager';
import DashboardEditPreviewItem from "../../../components/Lists/DashboardEdit/DashboardEditPreviewItem"; import DashboardManager from '../../../managers/DashboardManager';
import AsyncStorageManager from "../../../managers/AsyncStorageManager"; import DashboardItem from '../../../components/Home/EventDashboardItem';
import i18n from "i18n-js"; import DashboardEditAccordion from '../../../components/Lists/DashboardEdit/DashboardEditAccordion';
import CollapsibleFlatList from "../../../components/Collapsible/CollapsibleFlatList"; import DashboardEditPreviewItem from '../../../components/Lists/DashboardEdit/DashboardEditPreviewItem';
import AsyncStorageManager from '../../../managers/AsyncStorageManager';
import CollapsibleFlatList from '../../../components/Collapsible/CollapsibleFlatList';
type Props = { type PropsType = {
navigation: StackNavigationProp, navigation: StackNavigationProp,
theme: CustomTheme,
}; };
type State = { type StateType = {
currentDashboard: Array<ServiceItem>, currentDashboard: Array<ServiceItemType | null>,
currentDashboardIdList: Array<string>, currentDashboardIdList: Array<string>,
activeItem: number, activeItem: number,
}; };
/** /**
* Class defining the Settings screen. This screen shows controls to modify app preferences. * Class defining the Settings screen. This screen shows controls to modify app preferences.
*/ */
class DashboardEditScreen extends React.Component<Props, State> { class DashboardEditScreen extends React.Component<PropsType, StateType> {
content: Array<ServiceCategoryType>;
content: Array<ServiceCategory>; initialDashboard: Array<ServiceItemType | null>;
initialDashboard: Array<ServiceItem>;
initialDashboardIdList: Array<string>;
constructor(props: Props) { initialDashboardIdList: Array<string>;
super(props);
let dashboardManager = new DashboardManager(this.props.navigation);
this.initialDashboardIdList = AsyncStorageManager.getObject(AsyncStorageManager.PREFERENCES.dashboardItems.key);
this.initialDashboard = dashboardManager.getCurrentDashboard();
this.state = {
currentDashboard: [...this.initialDashboard],
currentDashboardIdList: [...this.initialDashboardIdList],
activeItem: 0,
}
this.content = dashboardManager.getCategories();
}
dashboardRowRenderItem = ({item, index}: { item: DashboardItem, index: number }) => { constructor(props: PropsType) {
return ( super(props);
<DashboardEditPreviewItem const dashboardManager = new DashboardManager(props.navigation);
image={item.image} this.initialDashboardIdList = AsyncStorageManager.getObject(
onPress={() => this.setState({activeItem: index})} AsyncStorageManager.PREFERENCES.dashboardItems.key,
isActive={this.state.activeItem === index} );
/> this.initialDashboard = dashboardManager.getCurrentDashboard();
); this.state = {
currentDashboard: [...this.initialDashboard],
currentDashboardIdList: [...this.initialDashboardIdList],
activeItem: 0,
}; };
this.content = dashboardManager.getCategories();
}
getDashboard(content: Array<DashboardItem>) { getDashboardRowRenderItem = ({
return ( item,
<FlatList index,
data={content} }: {
extraData={this.state} item: DashboardItem,
renderItem={this.dashboardRowRenderItem} index: number,
horizontal={true} }): React.Node => {
contentContainerStyle={{ const {activeItem} = this.state;
marginLeft: 'auto', return (
marginRight: 'auto', <DashboardEditPreviewItem
marginTop: 5, image={item.image}
}} onPress={() => {
/>); this.setState({activeItem: index});
} }}
isActive={activeItem === index}
/>
);
};
renderItem = ({item}: { item: ServiceCategory }) => { getDashboard(content: Array<DashboardItem>): React.Node {
return ( return (
<DashboardEditAccordion <FlatList
item={item} data={content}
onPress={this.updateDashboard} extraData={this.state}
activeDashboard={this.state.currentDashboardIdList} renderItem={this.getDashboardRowRenderItem}
/> horizontal
); contentContainerStyle={{
}; marginLeft: 'auto',
marginRight: 'auto',
marginTop: 5,
}}
/>
);
}
updateDashboard = (service: ServiceItem) => { getRenderItem = ({item}: {item: ServiceCategoryType}): React.Node => {
let currentDashboard = this.state.currentDashboard; const {currentDashboardIdList} = this.state;
let currentDashboardIdList = this.state.currentDashboardIdList; return (
currentDashboard[this.state.activeItem] = service; <DashboardEditAccordion
currentDashboardIdList[this.state.activeItem] = service.key; item={item}
this.setState({ onPress={this.updateDashboard}
currentDashboard: currentDashboard, activeDashboard={currentDashboardIdList}
currentDashboardIdList: currentDashboardIdList, />
}); );
AsyncStorageManager.set(AsyncStorageManager.PREFERENCES.dashboardItems.key, currentDashboardIdList); };
}
undoDashboard = () => { getListHeader(): React.Node {
this.setState({ const {currentDashboard} = this.state;
currentDashboard: [...this.initialDashboard], return (
currentDashboardIdList: [...this.initialDashboardIdList] <Card style={{margin: 5}}>
}); <Card.Content>
AsyncStorageManager.set(AsyncStorageManager.PREFERENCES.dashboardItems.key, this.initialDashboardIdList); <View style={{padding: 5}}>
} <Button
mode="contained"
onPress={this.undoDashboard}
style={{
marginLeft: 'auto',
marginRight: 'auto',
marginBottom: 10,
}}>
{i18n.t('screens.settings.dashboardEdit.undo')}
</Button>
<View style={{height: 50}}>
{this.getDashboard(currentDashboard)}
</View>
</View>
<Paragraph style={{textAlign: 'center'}}>
{i18n.t('screens.settings.dashboardEdit.message')}
</Paragraph>
</Card.Content>
</Card>
);
}
getListHeader() { updateDashboard = (service: ServiceItemType) => {
return ( const {currentDashboard, currentDashboardIdList, activeItem} = this.state;
<Card style={{margin: 5}}> currentDashboard[activeItem] = service;
<Card.Content> currentDashboardIdList[activeItem] = service.key;
<View style={{padding: 5}}> this.setState({
<Button currentDashboard,
mode={"contained"} currentDashboardIdList,
onPress={this.undoDashboard} });
style={{ AsyncStorageManager.set(
marginLeft: "auto", AsyncStorageManager.PREFERENCES.dashboardItems.key,
marginRight: "auto", currentDashboardIdList,
marginBottom: 10, );
}} };
>
{i18n.t("screens.settings.dashboardEdit.undo")}
</Button>
<View style={{height: 50}}>
{this.getDashboard(this.state.currentDashboard)}
</View>
</View>
<Paragraph style={{textAlign: "center"}}>
{i18n.t("screens.settings.dashboardEdit.message")}
</Paragraph>
</Card.Content>
</Card>
);
}
undoDashboard = () => {
this.setState({
currentDashboard: [...this.initialDashboard],
currentDashboardIdList: [...this.initialDashboardIdList],
});
AsyncStorageManager.set(
AsyncStorageManager.PREFERENCES.dashboardItems.key,
this.initialDashboardIdList,
);
};
render() { render(): React.Node {
return ( return (
<CollapsibleFlatList <CollapsibleFlatList
data={this.content} data={this.content}
renderItem={this.renderItem} renderItem={this.getRenderItem}
ListHeaderComponent={this.getListHeader()} ListHeaderComponent={this.getListHeader()}
style={{}} style={{}}
/> />
); );
} }
} }
export default withTheme(DashboardEditScreen); export default withTheme(DashboardEditScreen);