Convert planex group components to functional
This commit is contained in:
parent
9675d329cc
commit
52651ecf85
3 changed files with 136 additions and 216 deletions
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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)}
|
||||
|
|
Loading…
Reference in a new issue