Improved flow type checking

This commit is contained in:
Arnaud Vergnet 2020-04-18 23:55:20 +02:00
parent bdeae6933a
commit 433306e2a7

View file

@ -10,26 +10,28 @@ import BasicLoadingScreen from "./BasicLoadingScreen";
import {withCollapsible} from "../../utils/withCollapsible"; import {withCollapsible} from "../../utils/withCollapsible";
import * as Animatable from 'react-native-animatable'; import * as Animatable from 'react-native-animatable';
import CustomTabBar from "../Tabbar/CustomTabBar"; import CustomTabBar from "../Tabbar/CustomTabBar";
import {Collapsible} from "react-navigation-collapsible";
type Props = { type Props = {
navigation: Object, navigation: { [key: string]: any },
fetchUrl: string, fetchUrl: string,
autoRefreshTime: number, autoRefreshTime: number,
refreshOnFocus: boolean, refreshOnFocus: boolean,
renderItem: React.Node, renderItem: (data: { [key: string]: any }) => React.Node,
renderSectionHeader: React.Node, createDataset: (data: { [key: string]: any }) => Array<Object>,
stickyHeader: boolean, onScroll: (event: SyntheticEvent<EventTarget>) => void,
createDataset: Function, collapsibleStack: Collapsible,
updateData: number,
itemHeight: number | null, itemHeight?: number,
onScroll: Function, updateData?: number,
collapsibleStack: Object, renderSectionHeader?: (data: { [key: string]: any }) => React.Node,
stickyHeader?: boolean,
} }
type State = { type State = {
refreshing: boolean, refreshing: boolean,
firstLoading: boolean, firstLoading: boolean,
fetchedData: ?Object, fetchedData: { [key: string]: any } | null,
snackbarVisible: boolean snackbarVisible: boolean
}; };
@ -45,37 +47,21 @@ const MIN_REFRESH_TIME = 5 * 1000;
class WebSectionList extends React.PureComponent<Props, State> { class WebSectionList extends React.PureComponent<Props, State> {
static defaultProps = { static defaultProps = {
renderSectionHeader: null,
stickyHeader: false, stickyHeader: false,
updateData: 0, updateData: 0,
itemHeight: null,
}; };
scrollRef: Object; scrollRef: { current: null | Animated.SectionList };
refreshInterval: IntervalID; refreshInterval: IntervalID;
lastRefresh: Date; lastRefresh: Date | null;
state = { state = {
refreshing: false, refreshing: false,
firstLoading: true, firstLoading: true,
fetchedData: undefined, fetchedData: null,
snackbarVisible: false snackbarVisible: false
}; };
onRefresh: Function;
onFetchSuccess: Function;
onFetchError: Function;
getEmptySectionHeader: Function;
constructor() {
super();
// creating references to functions used in render()
this.onRefresh = this.onRefresh.bind(this);
this.onFetchSuccess = this.onFetchSuccess.bind(this);
this.onFetchError = this.onFetchError.bind(this);
this.getEmptySectionHeader = this.getEmptySectionHeader.bind(this);
}
/** /**
* Registers react navigation events on first screen load. * Registers react navigation events on first screen load.
* Allows to detect when the screen is focused * Allows to detect when the screen is focused
@ -87,13 +73,14 @@ class WebSectionList extends React.PureComponent<Props, State> {
this.props.navigation.addListener('blur', onScreenBlur); this.props.navigation.addListener('blur', onScreenBlur);
this.scrollRef = React.createRef(); this.scrollRef = React.createRef();
this.onRefresh(); this.onRefresh();
this.lastRefresh = null;
} }
/** /**
* Refreshes data when focusing the screen and setup a refresh interval if asked to * Refreshes data when focusing the screen and setup a refresh interval if asked to
*/ */
onScreenFocus() { onScreenFocus() {
if (this.props.refreshOnFocus && this.lastRefresh !== undefined) if (this.props.refreshOnFocus && this.lastRefresh)
this.onRefresh(); this.onRefresh();
if (this.props.autoRefreshTime > 0) if (this.props.autoRefreshTime > 0)
this.refreshInterval = setInterval(this.onRefresh, this.props.autoRefreshTime) this.refreshInterval = setInterval(this.onRefresh, this.props.autoRefreshTime)
@ -115,36 +102,37 @@ class WebSectionList extends React.PureComponent<Props, State> {
* *
* @param fetchedData The newly fetched data * @param fetchedData The newly fetched data
*/ */
onFetchSuccess(fetchedData: Object) { onFetchSuccess = (fetchedData: { [key: string]: any }) => {
this.setState({ this.setState({
fetchedData: fetchedData, fetchedData: fetchedData,
refreshing: false, refreshing: false,
firstLoading: false firstLoading: false
}); });
this.lastRefresh = new Date(); this.lastRefresh = new Date();
} };
/** /**
* Callback used when fetch encountered an error. * Callback used when fetch encountered an error.
* It will reset the displayed data and show an error. * It will reset the displayed data and show an error.
*/ */
onFetchError() { onFetchError = () => {
this.setState({ this.setState({
fetchedData: undefined, fetchedData: null,
refreshing: false, refreshing: false,
firstLoading: false firstLoading: false
}); });
this.showSnackBar(); this.showSnackBar();
} };
/** /**
* Refreshes data and shows an animations while doing it * Refreshes data and shows an animations while doing it
*/ */
onRefresh() { onRefresh = () => {
let canRefresh; let canRefresh;
if (this.lastRefresh !== undefined) if (this.lastRefresh != null) {
canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) > MIN_REFRESH_TIME; const last = this.lastRefresh;
else canRefresh = (new Date().getTime() - last.getTime()) > MIN_REFRESH_TIME;
} else
canRefresh = true; canRefresh = true;
if (canRefresh) { if (canRefresh) {
this.setState({refreshing: true}); this.setState({refreshing: true});
@ -152,17 +140,7 @@ class WebSectionList extends React.PureComponent<Props, State> {
.then(this.onFetchSuccess) .then(this.onFetchSuccess)
.catch(this.onFetchError); .catch(this.onFetchError);
} }
} };
/**
* Gets an empty section header
*
* @param section The current section
* @return {*}
*/
getEmptySectionHeader({section}: Object) {
return <View/>;
}
/** /**
* Shows the error popup * Shows the error popup
@ -174,13 +152,19 @@ class WebSectionList extends React.PureComponent<Props, State> {
*/ */
hideSnackBar = () => this.setState({snackbarVisible: false}); hideSnackBar = () => this.setState({snackbarVisible: false});
itemLayout = (data: Object, index: number) => ({ itemLayout = (data: { [key: string]: any }, index: number) => {
length: this.props.itemHeight, const height = this.props.itemHeight;
offset: this.props.itemHeight * index, if (height == null)
return undefined;
return {
length: height,
offset: height * index,
index index
}); }
};
renderSectionHeader = (data: Object) => { renderSectionHeader = (data: { section: { [key: string]: any } }) => {
if (this.props.renderSectionHeader != null) {
return ( return (
<Animatable.View <Animatable.View
animation={"fadeInUp"} animation={"fadeInUp"}
@ -190,9 +174,16 @@ class WebSectionList extends React.PureComponent<Props, State> {
{this.props.renderSectionHeader(data)} {this.props.renderSectionHeader(data)}
</Animatable.View> </Animatable.View>
); );
} else
return null;
} }
renderItem = (data: Object) => { renderItem = (data: {
item: { [key: string]: any },
index: number,
section: { [key: string]: any },
separators: { [key: string]: any },
}) => {
return ( return (
<Animatable.View <Animatable.View
animation={"fadeInUp"} animation={"fadeInUp"}
@ -204,16 +195,15 @@ class WebSectionList extends React.PureComponent<Props, State> {
); );
} }
onScroll = (event: Object) => { onScroll = (event: SyntheticEvent<EventTarget>) => {
if (this.props.onScroll) if (this.props.onScroll)
this.props.onScroll(event); this.props.onScroll(event);
} }
render() { render() {
let dataset = []; let dataset = [];
if (this.state.fetchedData !== undefined) if (this.state.fetchedData != null)
dataset = this.props.createDataset(this.state.fetchedData); dataset = this.props.createDataset(this.state.fetchedData);
const shouldRenderHeader = this.props.renderSectionHeader !== null;
const {containerPaddingTop, scrollIndicatorInsetTop, onScrollWithListener} = this.props.collapsibleStack; const {containerPaddingTop, scrollIndicatorInsetTop, onScrollWithListener} = this.props.collapsibleStack;
return ( return (
<View> <View>
@ -230,7 +220,7 @@ class WebSectionList extends React.PureComponent<Props, State> {
/> />
} }
//$FlowFixMe //$FlowFixMe
renderSectionHeader={shouldRenderHeader ? this.renderSectionHeader : this.getEmptySectionHeader} renderSectionHeader={this.renderSectionHeader}
//$FlowFixMe //$FlowFixMe
renderItem={this.renderItem} renderItem={this.renderItem}
stickySectionHeadersEnabled={this.props.stickyHeader} stickySectionHeadersEnabled={this.props.stickyHeader}
@ -242,7 +232,7 @@ class WebSectionList extends React.PureComponent<Props, State> {
errorCode={ERROR_TYPE.CONNECTION_ERROR} errorCode={ERROR_TYPE.CONNECTION_ERROR}
onRefresh={this.onRefresh}/> onRefresh={this.onRefresh}/>
} }
getItemLayout={this.props.itemHeight !== null ? this.itemLayout : undefined} getItemLayout={this.props.itemHeight != null ? this.itemLayout : undefined}
// Animations // Animations
onScroll={onScrollWithListener(this.onScroll)} onScroll={onScrollWithListener(this.onScroll)}
contentContainerStyle={{ contentContainerStyle={{