Improved flow typing

This commit is contained in:
Arnaud Vergnet 2020-04-20 09:29:21 +02:00
parent 7f24eb77ac
commit dc3aed8bda
6 changed files with 103 additions and 83 deletions

View file

@ -1,7 +1,7 @@
// @flow
import * as React from 'react';
import {Card, List, Text, withTheme} from 'react-native-paper';
import {Card, List, Text} from 'react-native-paper';
import {StyleSheet, View} from "react-native";
import i18n from 'i18n-js';
import AnimatedAccordion from "../../Animations/AnimatedAccordion";
@ -13,14 +13,6 @@ type Props = {
class ClubListHeader extends React.Component<Props> {
colors: Object;
constructor(props) {
super(props);
this.colors = props.theme.colors;
}
getCategoriesRender() {
let final = [];
for (let i = 0; i < this.props.categories.length; i++) {
@ -67,4 +59,4 @@ const styles = StyleSheet.create({
},
});
export default withTheme(ClubListHeader);
export default ClubListHeader;

View file

@ -3,22 +3,23 @@
import * as React from 'react';
import {Avatar, Chip, List, withTheme} from 'react-native-paper';
import {View} from "react-native";
import type {category, club} from "../../../screens/Amicale/Clubs/ClubListScreen";
import type {CustomTheme} from "../../../managers/ThemeManager";
type Props = {
onPress: Function,
categoryTranslator: Function,
item: Object,
onPress: () => void,
categoryTranslator: (id: number) => category,
item: club,
height: number,
theme: CustomTheme,
}
class ClubListItem extends React.Component<Props> {
colors: Object;
hasManagers: boolean;
constructor(props) {
super(props);
this.colors = props.theme.colors;
this.hasManagers = props.item.responsibles.length > 0;
}
@ -30,7 +31,7 @@ class ClubListItem extends React.Component<Props> {
let final = [];
for (let i = 0; i < categories.length; i++) {
if (categories[i] !== null) {
const category = this.props.categoryTranslator(categories[i]);
const category: category = this.props.categoryTranslator(categories[i]);
final.push(
<Chip
style={{marginRight: 5, marginBottom: 5}}
@ -46,6 +47,7 @@ class ClubListItem extends React.Component<Props> {
render() {
const categoriesRender = this.getCategoriesRender.bind(this, this.props.item.category);
const colors = this.props.theme.colors;
return (
<List.Item
title={this.props.item.name}
@ -65,7 +67,7 @@ class ClubListItem extends React.Component<Props> {
}}
size={48}
icon={this.hasManagers ? "check-circle-outline" : "alert-circle-outline"}
color={this.hasManagers ? this.colors.success : this.colors.primary}
color={this.hasManagers ? colors.success : colors.primary}
/>}
style={{
height: this.props.height,

View file

@ -4,18 +4,19 @@ import * as React from 'react';
import {List, withTheme} from 'react-native-paper';
import {FlatList, View} from "react-native";
import {stringMatchQuery} from "../../../utils/Search";
import * as Animatable from "react-native-animatable";
import GroupListItem from "./GroupListItem";
import AnimatedAccordion from "../../Animations/AnimatedAccordion";
import type {group, groupCategory} from "../../../screens/Planex/GroupSelectionScreen";
import type {CustomTheme} from "../../../managers/ThemeManager";
type Props = {
item: Object,
onGroupPress: Function,
onFavoritePress: Function,
item: groupCategory,
onGroupPress: (group) => void,
onFavoritePress: (group) => void,
currentSearchString: string,
favoriteNumber: number,
height: number,
theme: Object,
theme: CustomTheme,
}
type State = {
@ -23,18 +24,14 @@ type State = {
}
const LIST_ITEM_HEIGHT = 64;
const AnimatedListIcon = Animatable.createAnimatableComponent(List.Icon);
class GroupListAccordion extends React.Component<Props, State> {
chevronRef: Object;
constructor(props) {
super(props);
this.state = {
expanded: props.item.id === "0",
}
this.chevronRef = React.createRef();
}
shouldComponentUpdate(nextProps: Props, nextSate: State) {
@ -47,24 +44,24 @@ class GroupListAccordion extends React.Component<Props, State> {
|| (nextProps.item.content.length !== this.props.item.content.length);
}
keyExtractor = (item: Object) => item.id.toString();
keyExtractor = (item: group) => item.id.toString();
renderItem = ({item}: Object) => {
renderItem = ({item}: {item: group}) => {
if (stringMatchQuery(item.name, this.props.currentSearchString)) {
const onPress = () => this.props.onGroupPress(item);
const onStartPress = () => this.props.onFavoritePress(item);
const onStarPress = () => this.props.onFavoritePress(item);
return (
<GroupListItem
height={LIST_ITEM_HEIGHT}
item={item}
onPress={onPress}
onStartPress={onStartPress}/>
onStarPress={onStarPress}/>
);
} else
return null;
}
itemLayout = (data: Object, 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;
@ -86,12 +83,13 @@ class GroupListAccordion extends React.Component<Props, State> {
: null}
unmountWhenCollapsed={true}// Only render list if expanded for increased performance
>
{/*$FlowFixMe*/}
<FlatList
data={item.content}
extraData={this.props.currentSearchString}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
listKey={item.id}
listKey={item.id.toString()}
// Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
getItemLayout={this.itemLayout} // Broken with search
removeClippedSubviews={true}

View file

@ -2,12 +2,14 @@
import * as React from 'react';
import {IconButton, List, withTheme} from 'react-native-paper';
import type {CustomTheme} from "../../../managers/ThemeManager";
import type {group} from "../../../screens/Planex/GroupSelectionScreen";
type Props = {
theme: Object,
onPress: Function,
onStartPress: Function,
item: Object,
theme: CustomTheme,
onPress: () => void,
onStarPress: () => void,
item: group,
height: number,
}
@ -17,11 +19,8 @@ type State = {
class GroupListItem extends React.Component<Props, State> {
colors: Object;
constructor(props) {
super(props);
this.colors = props.theme.colors;
this.state = {
isFav: (props.item.isFav !== undefined && props.item.isFav),
}
@ -33,10 +32,11 @@ class GroupListItem extends React.Component<Props, State> {
onStarPress = () => {
this.setState({isFav: !this.state.isFav});
this.props.onStartPress();
this.props.onStarPress();
}
render() {
const colors = this.props.theme.colors;
return (
<List.Item
title={this.props.item.name}
@ -51,7 +51,7 @@ class GroupListItem extends React.Component<Props, State> {
icon={"star"}
onPress={this.onStarPress}
color={this.state.isFav
? this.props.theme.colors.tetrisScore
? colors.tetrisScore
: props.color}
/>}
style={{

View file

@ -2,7 +2,7 @@
import * as React from 'react';
import {Animated, Platform} from "react-native";
import {Chip, Searchbar, withTheme} from 'react-native-paper';
import {Chip, Searchbar} from 'react-native-paper';
import AuthenticatedScreen from "../../../components/Amicale/AuthenticatedScreen";
import i18n from "i18n-js";
import ClubListItem from "../../../components/Lists/Clubs/ClubListItem";
@ -10,11 +10,29 @@ import {isItemInCategoryFilter, stringMatchQuery} from "../../../utils/Search";
import ClubListHeader from "../../../components/Lists/Clubs/ClubListHeader";
import MaterialHeaderButtons, {Item} from "../../../components/Overrides/CustomHeaderButton";
import {withCollapsible} from "../../../utils/withCollapsible";
import {StackNavigationProp} from "@react-navigation/stack";
import type {CustomTheme} from "../../../managers/ThemeManager";
import {Collapsible} from "react-navigation-collapsible";
export type category = {
id: number,
name: string,
};
export type club = {
id: number,
name: string,
description: string,
logo: string,
email:string,
category: [number, number],
responsibles: Array<string>,
};
type Props = {
navigation: Object,
theme: Object,
collapsibleStack: Object,
navigation: StackNavigationProp,
theme: CustomTheme,
collapsibleStack: Collapsible,
}
type State = {
@ -31,14 +49,7 @@ class ClubListScreen extends React.Component<Props, State> {
currentSearchString: '',
};
colors: Object;
categories: Array<Object>;
constructor(props) {
super(props);
this.colors = props.theme.colors;
}
categories: Array<category>;
/**
* Creates the header content
@ -88,19 +99,25 @@ class ClubListScreen extends React.Component<Props, State> {
this.updateFilteredData(str, null);
};
keyExtractor = (item: Object) => {
keyExtractor = (item: club) => {
return item.id.toString();
};
itemLayout = (data, index) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index});
getScreen = (data: Object) => {
this.categories = data[0].categories;
getScreen = (data: Array<{categories: Array<category>, clubs: Array<club>} | null>) => {
let categoryList = [];
let clubList = [];
if (data[0] != null) {
categoryList = data[0].categories;
clubList = data[0].clubs;
}
this.categories = categoryList;
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
return (
//$FlowFixMe
<Animated.FlatList
data={data[0].clubs}
data={clubList}
keyExtractor={this.keyExtractor}
renderItem={this.getRenderItem}
ListHeaderComponent={this.getListHeader()}
@ -138,7 +155,7 @@ class ClubListScreen extends React.Component<Props, State> {
})
}
getChipRender = (category: Object, key: string) => {
getChipRender = (category: category, key: string) => {
const onPress = this.onChipSelect.bind(this, category.id);
return <Chip
selected={isItemInCategoryFilter(this.state.currentlySelectedCategories, [category.id])}
@ -165,7 +182,7 @@ class ClubListScreen extends React.Component<Props, State> {
}
};
shouldRenderItem(item) {
shouldRenderItem(item: club) {
let shouldRender = this.state.currentlySelectedCategories.length === 0
|| isItemInCategoryFilter(this.state.currentlySelectedCategories, item.category);
if (shouldRender)
@ -173,7 +190,7 @@ class ClubListScreen extends React.Component<Props, State> {
return shouldRender;
}
getRenderItem = ({item}: Object) => {
getRenderItem = ({item}: {item: club}) => {
const onPress = this.onListItemPress.bind(this, item);
if (this.shouldRenderItem(item)) {
return (
@ -194,7 +211,7 @@ class ClubListScreen extends React.Component<Props, State> {
*
* @param item The article pressed
*/
onListItemPress(item: Object) {
onListItemPress(item: club) {
this.props.navigation.navigate("club-information", {data: item, categories: this.categories});
}
@ -215,4 +232,4 @@ class ClubListScreen extends React.Component<Props, State> {
}
}
export default withCollapsible(withTheme(ClubListScreen));
export default withCollapsible(ClubListScreen);

View file

@ -3,26 +3,37 @@
import * as React from 'react';
import {Platform} from "react-native";
import i18n from "i18n-js";
import {Searchbar, withTheme} from "react-native-paper";
import {Searchbar} from "react-native-paper";
import {stringMatchQuery} from "../../utils/Search";
import WebSectionList from "../../components/Screens/WebSectionList";
import GroupListAccordion from "../../components/Lists/PlanexGroups/GroupListAccordion";
import AsyncStorageManager from "../../managers/AsyncStorageManager";
import {StackNavigationProp} from "@react-navigation/stack";
const LIST_ITEM_HEIGHT = 70;
export type group = {
name: string,
id: number,
isFav: boolean,
};
export type groupCategory = {
name: string,
id: number,
content: Array<group>,
};
type Props = {
navigation: Object,
route: Object,
theme: Object,
navigation: StackNavigationProp,
}
type State = {
currentSearchString: string,
favoriteGroups: Array<Object>,
favoriteGroups: Array<group>,
};
function sortName(a, b) {
function sortName(a: group | groupCategory, b: group | groupCategory) {
if (a.name.toLowerCase() < b.name.toLowerCase())
return -1;
if (a.name.toLowerCase() > b.name.toLowerCase())
@ -38,7 +49,7 @@ const REPLACE_REGEX = /_/g;
*/
class GroupSelectionScreen extends React.Component<Props, State> {
constructor(props) {
constructor(props: Props) {
super(props);
this.state = {
currentSearchString: '',
@ -88,18 +99,18 @@ class GroupSelectionScreen extends React.Component<Props, State> {
*
* @param item The article pressed
*/
onListItemPress = (item: Object) => {
onListItemPress = (item: group) => {
this.props.navigation.navigate("planex", {
screen: "index",
params: {group: item}
});
};
onListFavoritePress = (item: Object) => {
onListFavoritePress = (item: group) => {
this.updateGroupFavorites(item);
};
isGroupInFavorites(group: Object) {
isGroupInFavorites(group: group) {
let isFav = false;
for (let i = 0; i < this.state.favoriteGroups.length; i++) {
if (group.id === this.state.favoriteGroups[i].id) {
@ -110,7 +121,7 @@ class GroupSelectionScreen extends React.Component<Props, State> {
return isFav;
}
removeGroupFromFavorites(favorites: Array<Object>, group: Object) {
removeGroupFromFavorites(favorites: Array<group>, group: group) {
for (let i = 0; i < favorites.length; i++) {
if (group.id === favorites[i].id) {
favorites.splice(i, 1);
@ -119,13 +130,13 @@ class GroupSelectionScreen extends React.Component<Props, State> {
}
}
addGroupToFavorites(favorites: Array<Object>, group: Object) {
addGroupToFavorites(favorites: Array<group>, group: group) {
group.isFav = true;
favorites.push(group);
favorites.sort(sortName);
}
updateGroupFavorites(group: Object) {
updateGroupFavorites(group: group) {
let newFavorites = [...this.state.favoriteGroups]
if (this.isGroupInFavorites(group))
this.removeGroupFromFavorites(newFavorites, group);
@ -137,7 +148,7 @@ class GroupSelectionScreen extends React.Component<Props, State> {
JSON.stringify(newFavorites));
}
shouldDisplayAccordion(item: Object) {
shouldDisplayAccordion(item: groupCategory) {
let shouldDisplay = false;
for (let i = 0; i < item.content.length; i++) {
if (stringMatchQuery(item.content[i].name, this.state.currentSearchString)) {
@ -154,7 +165,7 @@ class GroupSelectionScreen extends React.Component<Props, State> {
* @param item The article to render
* @return {*}
*/
renderItem = ({item}: Object) => {
renderItem = ({item}: { item: groupCategory }) => {
if (this.shouldDisplayAccordion(item)) {
return (
<GroupListAccordion
@ -170,18 +181,18 @@ class GroupSelectionScreen extends React.Component<Props, State> {
return null;
};
generateData(fetchedData: Object) {
generateData(fetchedData: { [key: string]: groupCategory }) {
let data = [];
for (let key in fetchedData) {
this.formatGroups(fetchedData[key]);
data.push(fetchedData[key]);
}
data.sort(sortName);
data.unshift({name: "FAVORITES", id: "0", content: this.state.favoriteGroups});
data.unshift({name: "FAVORITES", id: 0, content: this.state.favoriteGroups});
return data;
}
formatGroups(item: Object) {
formatGroups(item: groupCategory) {
for (let i = 0; i < item.content.length; i++) {
item.content[i].name = item.content[i].name.replace(REPLACE_REGEX, " ")
item.content[i].isFav = this.isGroupInFavorites(item.content[i]);
@ -194,7 +205,7 @@ class GroupSelectionScreen extends React.Component<Props, State> {
* @param fetchedData
* @return {*}
* */
createDataset = (fetchedData: Object) => {
createDataset = (fetchedData: { [key: string]: groupCategory }) => {
return [
{
title: '',
@ -219,4 +230,4 @@ class GroupSelectionScreen extends React.Component<Props, State> {
}
}
export default withTheme(GroupSelectionScreen);
export default GroupSelectionScreen;