Convert planex group components to functional

This commit is contained in:
Arnaud Vergnet 2021-05-13 17:19:43 +02:00
parent 9675d329cc
commit 52651ecf85
3 changed files with 136 additions and 216 deletions

View file

@ -18,8 +18,8 @@
*/
import * as React from 'react';
import { List, withTheme } from 'react-native-paper';
import { FlatList, StyleSheet, View } from 'react-native';
import { List, useTheme } from 'react-native-paper';
import { FlatList, StyleSheet } from 'react-native';
import GroupListItem from './GroupListItem';
import AnimatedAccordion from '../../Animations/AnimatedAccordion';
import type {
@ -34,7 +34,6 @@ type PropsType = {
onGroupPress: (data: PlanexGroupType) => void;
onFavoritePress: (data: PlanexGroupType) => void;
currentSearchString: string;
theme: ReactNativePaper.Theme;
};
const LIST_ITEM_HEIGHT = 64;
@ -49,36 +48,22 @@ const styles = StyleSheet.create({
},
});
class GroupListAccordion extends React.Component<PropsType> {
shouldComponentUpdate(nextProps: PropsType): boolean {
const { props } = this;
return (
nextProps.currentSearchString !== props.currentSearchString ||
nextProps.favorites.length !== props.favorites.length ||
nextProps.item.content.length !== props.item.content.length
);
}
function GroupListAccordion(props: PropsType) {
const theme = useTheme();
getRenderItem = ({ item }: { item: PlanexGroupType }) => {
const { props } = this;
const onPress = () => {
props.onGroupPress(item);
};
const onStarPress = () => {
props.onFavoritePress(item);
};
const getRenderItem = ({ item }: { item: PlanexGroupType }) => {
return (
<GroupListItem
height={LIST_ITEM_HEIGHT}
item={item}
favorites={props.favorites}
onPress={onPress}
onStarPress={onStarPress}
isFav={props.favorites.some((f) => f.id === item.id)}
onPress={() => props.onGroupPress(item)}
onStarPress={() => props.onFavoritePress(item)}
/>
);
};
itemLayout = (
const itemLayout = (
_data: Array<PlanexGroupType> | null | undefined,
index: number
): { length: number; offset: number; index: number } => ({
@ -87,20 +72,17 @@ class GroupListAccordion extends React.Component<PropsType> {
index,
});
keyExtractor = (item: PlanexGroupType): string => item.id.toString();
const keyExtractor = (item: PlanexGroupType): string => item.id.toString();
render() {
const { props } = this;
const { item } = this.props;
var isFavorite = item.id === 0;
var isFavorite = props.item.id === 0;
var isEmptyFavorite = isFavorite && props.favorites.length === 0;
return (
<View>
<AnimatedAccordion
title={
isEmptyFavorite
? i18n.t('screens.planex.favorites.empty.title')
: item.name.replace(REPLACE_REGEX, ' ')
: props.item.name.replace(REPLACE_REGEX, ' ')
}
subtitle={
isEmptyFavorite
@ -113,7 +95,7 @@ class GroupListAccordion extends React.Component<PropsType> {
<List.Icon
style={iconProps.style}
icon={'star'}
color={props.theme.colors.tetrisScore}
color={theme.colors.tetrisScore}
/>
) : undefined
}
@ -127,17 +109,21 @@ class GroupListAccordion extends React.Component<PropsType> {
<FlatList
data={props.item.content}
extraData={props.currentSearchString + props.favorites.length}
renderItem={this.getRenderItem}
keyExtractor={this.keyExtractor}
listKey={item.id.toString()}
renderItem={getRenderItem}
keyExtractor={keyExtractor}
listKey={props.item.id.toString()}
// Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
getItemLayout={this.itemLayout}
removeClippedSubviews
getItemLayout={itemLayout}
removeClippedSubviews={true}
/>
</AnimatedAccordion>
</View>
);
}
}
export default withTheme(GroupListAccordion);
const propsEqual = (pp: PropsType, np: PropsType) =>
pp.currentSearchString === np.currentSearchString &&
pp.favorites.length === np.favorites.length &&
pp.item.content.length === np.item.content.length &&
pp.onFavoritePress === np.onFavoritePress;
export default React.memo(GroupListAccordion, propsEqual);

View file

@ -17,20 +17,19 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/
import * as React from 'react';
import { List, TouchableRipple, withTheme } from 'react-native-paper';
import React, { useRef } from 'react';
import { List, TouchableRipple, useTheme } from 'react-native-paper';
import * as Animatable from 'react-native-animatable';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import type { PlanexGroupType } from '../../../screens/Planex/GroupSelectionScreen';
import { StyleSheet, View } from 'react-native';
import { getPrettierPlanexGroupName } from '../../../utils/Utils';
type PropsType = {
theme: ReactNativePaper.Theme;
type Props = {
onPress: () => void;
onStarPress: () => void;
item: PlanexGroupType;
favorites: Array<PlanexGroupType>;
isFav: boolean;
height: number;
};
@ -49,55 +48,11 @@ const styles = StyleSheet.create({
},
});
class GroupListItem extends React.Component<PropsType> {
isFav: boolean;
function GroupListItem(props: Props) {
const theme = useTheme();
starRef: { current: null | (Animatable.View & View) };
const starRef = useRef<Animatable.View & View>(null);
constructor(props: PropsType) {
super(props);
this.starRef = React.createRef();
this.isFav = this.isGroupInFavorites(props.favorites);
}
shouldComponentUpdate(nextProps: PropsType): boolean {
const { favorites } = this.props;
const favChanged = favorites.length !== nextProps.favorites.length;
let newFavState = this.isFav;
if (favChanged) {
newFavState = this.isGroupInFavorites(nextProps.favorites);
}
const shouldUpdate = this.isFav !== newFavState;
this.isFav = newFavState;
return shouldUpdate;
}
onStarPress = () => {
const { props } = this;
const ref = this.starRef;
if (ref.current && ref.current.rubberBand && ref.current.swing) {
if (this.isFav) {
ref.current.rubberBand();
} else {
ref.current.swing();
}
}
props.onStarPress();
};
isGroupInFavorites(favorites: Array<PlanexGroupType>): boolean {
const { item } = this.props;
for (let i = 0; i < favorites.length; i += 1) {
if (favorites[i].id === item.id) {
return true;
}
}
return false;
}
render() {
const { props } = this;
const { colors } = props.theme;
return (
<List.Item
title={getPrettierPlanexGroupName(props.item.name)}
@ -110,16 +65,20 @@ class GroupListItem extends React.Component<PropsType> {
/>
)}
right={(iconProps) => (
<Animatable.View ref={this.starRef} useNativeDriver>
<Animatable.View
ref={starRef}
useNativeDriver={true}
animation={props.isFav ? 'rubberBand' : undefined}
>
<TouchableRipple
onPress={this.onStarPress}
onPress={props.onStarPress}
style={styles.iconContainer}
>
<MaterialCommunityIcons
size={30}
style={styles.icon}
name="star"
color={this.isFav ? colors.tetrisScore : iconProps.color}
color={props.isFav ? theme.colors.tetrisScore : iconProps.color}
/>
</TouchableRipple>
</Animatable.View>
@ -130,7 +89,10 @@ class GroupListItem extends React.Component<PropsType> {
}}
/>
);
}
}
export default withTheme(GroupListItem);
export default React.memo(
GroupListItem,
(pp: Props, np: Props) =>
pp.isFav === np.isFav && pp.onStarPress === np.onStarPress
);

View file

@ -17,7 +17,12 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/
import React, { useEffect, useLayoutEffect, useState } from 'react';
import React, {
useCallback,
useEffect,
useLayoutEffect,
useState,
} from 'react';
import { Platform } from 'react-native';
import i18n from 'i18n-js';
import { Searchbar } from 'react-native-paper';
@ -142,39 +147,31 @@ function GroupSelectionScreen() {
*
* @param item The item to add/remove from favorites
*/
const onListFavoritePress = (item: PlanexGroupType) => {
updateGroupFavorites(item);
const onListFavoritePress = useCallback(
(group: PlanexGroupType) => {
const removeGroupFromFavorites = (g: PlanexGroupType) => {
setFavoriteGroups(favoriteGroups.filter((f) => f.id !== g.id));
};
/**
* Checks if the given group is in the favorites list
*
* @param group The group to check
* @returns {boolean}
*/
const isGroupInFavorites = (group: PlanexGroupType): boolean => {
let isFav = false;
favoriteGroups.forEach((favGroup: PlanexGroupType) => {
if (group.id === favGroup.id) {
isFav = true;
}
});
return isFav;
const addGroupToFavorites = (g: PlanexGroupType) => {
setFavoriteGroups([...favoriteGroups, g].sort(sortName));
};
/**
* Adds or removes the given group to the favorites list, depending on whether it is already in it or not.
* Favorites are then saved in user preferences
*
* @param group The group to add/remove to favorites
*/
const updateGroupFavorites = (group: PlanexGroupType) => {
if (isGroupInFavorites(group)) {
if (favoriteGroups.some((f) => f.id === group.id)) {
removeGroupFromFavorites(group);
} else {
addGroupToFavorites(group);
}
};
},
[favoriteGroups]
);
useEffect(() => {
AsyncStorageManager.set(
AsyncStorageManager.PREFERENCES.planexFavoriteGroups.key,
favoriteGroups
);
}, [favoriteGroups]);
/**
* Generates the dataset to be used in the FlatList.
@ -220,31 +217,6 @@ function GroupSelectionScreen() {
return data;
};
/**
* Removes the given group from the favorites
*
* @param group The group to remove from the array
*/
const removeGroupFromFavorites = (group: PlanexGroupType) => {
setFavoriteGroups(favoriteGroups.filter((g) => g.id !== group.id));
};
useEffect(() => {
AsyncStorageManager.set(
AsyncStorageManager.PREFERENCES.planexFavoriteGroups.key,
favoriteGroups
);
}, [favoriteGroups]);
/**
* Adds the given group to favorites
*
* @param group The group to add to the array
*/
const addGroupToFavorites = (group: PlanexGroupType) => {
setFavoriteGroups([...favoriteGroups, group].sort(sortName));
};
return (
<WebSectionList
request={() => readData<PlanexGroupsType>(Urls.planex.groups)}