Improved flow type checking
This commit is contained in:
parent
bdeae6933a
commit
433306e2a7
1 changed files with 61 additions and 71 deletions
|
@ -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,25 +152,38 @@ 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)
|
||||||
index
|
return undefined;
|
||||||
});
|
return {
|
||||||
|
length: height,
|
||||||
|
offset: height * index,
|
||||||
|
index
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
renderSectionHeader = (data: Object) => {
|
renderSectionHeader = (data: { section: { [key: string]: any } }) => {
|
||||||
return (
|
if (this.props.renderSectionHeader != null) {
|
||||||
<Animatable.View
|
return (
|
||||||
animation={"fadeInUp"}
|
<Animatable.View
|
||||||
duration={500}
|
animation={"fadeInUp"}
|
||||||
useNativeDriver
|
duration={500}
|
||||||
>
|
useNativeDriver
|
||||||
{this.props.renderSectionHeader(data)}
|
>
|
||||||
</Animatable.View>
|
{this.props.renderSectionHeader(data)}
|
||||||
);
|
</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={{
|
||||||
|
|
Loading…
Reference in a new issue