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

View file

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

View file

@ -2,57 +2,57 @@
import * as React from 'react';
import {TouchableRipple, withTheme} from 'react-native-paper';
import {Dimensions, Image, View} from "react-native";
import type {CustomTheme} from "../../../managers/ThemeManager";
import {Dimensions, Image, View} from 'react-native';
import type {CustomTheme} from '../../../managers/ThemeManager';
type Props = {
image: string,
isActive: boolean,
onPress: () => void,
theme: CustomTheme,
type PropsType = {
image: string,
isActive: boolean,
onPress: () => void,
theme: CustomTheme,
};
/**
* 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: Props) {
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>
);
}
constructor(props: PropsType) {
super(props);
this.itemSize = Dimensions.get('window').width / 8;
}
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
import * as React from 'react';
import {StackNavigationProp} from "@react-navigation/stack";
import type {CustomTheme} from "../../../managers/ThemeManager";
import {Button, Card, Paragraph, withTheme} from "react-native-paper";
import type {ServiceCategory, ServiceItem} from "../../../managers/ServicesManager";
import DashboardManager from "../../../managers/DashboardManager";
import DashboardItem from "../../../components/Home/EventDashboardItem";
import {FlatList} from "react-native";
import {View} from "react-native-animatable";
import DashboardEditAccordion from "../../../components/Lists/DashboardEdit/DashboardEditAccordion";
import DashboardEditPreviewItem from "../../../components/Lists/DashboardEdit/DashboardEditPreviewItem";
import AsyncStorageManager from "../../../managers/AsyncStorageManager";
import i18n from "i18n-js";
import CollapsibleFlatList from "../../../components/Collapsible/CollapsibleFlatList";
import {StackNavigationProp} from '@react-navigation/stack';
import {Button, Card, Paragraph, withTheme} from 'react-native-paper';
import {FlatList} from 'react-native';
import {View} from 'react-native-animatable';
import i18n from 'i18n-js';
import type {
ServiceCategoryType,
ServiceItemType,
} from '../../../managers/ServicesManager';
import DashboardManager from '../../../managers/DashboardManager';
import DashboardItem from '../../../components/Home/EventDashboardItem';
import DashboardEditAccordion from '../../../components/Lists/DashboardEdit/DashboardEditAccordion';
import DashboardEditPreviewItem from '../../../components/Lists/DashboardEdit/DashboardEditPreviewItem';
import AsyncStorageManager from '../../../managers/AsyncStorageManager';
import CollapsibleFlatList from '../../../components/Collapsible/CollapsibleFlatList';
type Props = {
navigation: StackNavigationProp,
theme: CustomTheme,
type PropsType = {
navigation: StackNavigationProp,
};
type State = {
currentDashboard: Array<ServiceItem>,
currentDashboardIdList: Array<string>,
activeItem: number,
type StateType = {
currentDashboard: Array<ServiceItemType | null>,
currentDashboardIdList: Array<string>,
activeItem: number,
};
/**
* 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<ServiceItem>;
initialDashboardIdList: Array<string>;
initialDashboard: Array<ServiceItemType | null>;
constructor(props: Props) {
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();
}
initialDashboardIdList: Array<string>;
dashboardRowRenderItem = ({item, index}: { item: DashboardItem, index: number }) => {
return (
<DashboardEditPreviewItem
image={item.image}
onPress={() => this.setState({activeItem: index})}
isActive={this.state.activeItem === index}
/>
);
constructor(props: PropsType) {
super(props);
const dashboardManager = new DashboardManager(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();
}
getDashboard(content: Array<DashboardItem>) {
return (
<FlatList
data={content}
extraData={this.state}
renderItem={this.dashboardRowRenderItem}
horizontal={true}
contentContainerStyle={{
marginLeft: 'auto',
marginRight: 'auto',
marginTop: 5,
}}
/>);
}
getDashboardRowRenderItem = ({
item,
index,
}: {
item: DashboardItem,
index: number,
}): React.Node => {
const {activeItem} = this.state;
return (
<DashboardEditPreviewItem
image={item.image}
onPress={() => {
this.setState({activeItem: index});
}}
isActive={activeItem === index}
/>
);
};
renderItem = ({item}: { item: ServiceCategory }) => {
return (
<DashboardEditAccordion
item={item}
onPress={this.updateDashboard}
activeDashboard={this.state.currentDashboardIdList}
/>
);
};
getDashboard(content: Array<DashboardItem>): React.Node {
return (
<FlatList
data={content}
extraData={this.state}
renderItem={this.getDashboardRowRenderItem}
horizontal
contentContainerStyle={{
marginLeft: 'auto',
marginRight: 'auto',
marginTop: 5,
}}
/>
);
}
updateDashboard = (service: ServiceItem) => {
let currentDashboard = this.state.currentDashboard;
let currentDashboardIdList = this.state.currentDashboardIdList;
currentDashboard[this.state.activeItem] = service;
currentDashboardIdList[this.state.activeItem] = service.key;
this.setState({
currentDashboard: currentDashboard,
currentDashboardIdList: currentDashboardIdList,
});
AsyncStorageManager.set(AsyncStorageManager.PREFERENCES.dashboardItems.key, currentDashboardIdList);
}
getRenderItem = ({item}: {item: ServiceCategoryType}): React.Node => {
const {currentDashboardIdList} = this.state;
return (
<DashboardEditAccordion
item={item}
onPress={this.updateDashboard}
activeDashboard={currentDashboardIdList}
/>
);
};
undoDashboard = () => {
this.setState({
currentDashboard: [...this.initialDashboard],
currentDashboardIdList: [...this.initialDashboardIdList]
});
AsyncStorageManager.set(AsyncStorageManager.PREFERENCES.dashboardItems.key, this.initialDashboardIdList);
}
getListHeader(): React.Node {
const {currentDashboard} = this.state;
return (
<Card style={{margin: 5}}>
<Card.Content>
<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() {
return (
<Card style={{margin: 5}}>
<Card.Content>
<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(this.state.currentDashboard)}
</View>
</View>
<Paragraph style={{textAlign: "center"}}>
{i18n.t("screens.settings.dashboardEdit.message")}
</Paragraph>
</Card.Content>
</Card>
);
}
updateDashboard = (service: ServiceItemType) => {
const {currentDashboard, currentDashboardIdList, activeItem} = this.state;
currentDashboard[activeItem] = service;
currentDashboardIdList[activeItem] = service.key;
this.setState({
currentDashboard,
currentDashboardIdList,
});
AsyncStorageManager.set(
AsyncStorageManager.PREFERENCES.dashboardItems.key,
currentDashboardIdList,
);
};
undoDashboard = () => {
this.setState({
currentDashboard: [...this.initialDashboard],
currentDashboardIdList: [...this.initialDashboardIdList],
});
AsyncStorageManager.set(
AsyncStorageManager.PREFERENCES.dashboardItems.key,
this.initialDashboardIdList,
);
};
render() {
return (
<CollapsibleFlatList
data={this.content}
renderItem={this.renderItem}
ListHeaderComponent={this.getListHeader()}
style={{}}
/>
);
}
render(): React.Node {
return (
<CollapsibleFlatList
data={this.content}
renderItem={this.getRenderItem}
ListHeaderComponent={this.getListHeader()}
style={{}}
/>
);
}
}
export default withTheme(DashboardEditScreen);