application-amicale/components/Lists/WebSectionList.js

212 lines
6.3 KiB
JavaScript
Raw Normal View History

// @flow
import * as React from 'react';
2020-04-02 10:07:20 +02:00
import {readData} from "../../utils/WebData";
import i18n from "i18n-js";
import {Snackbar} from 'react-native-paper';
import {RefreshControl, SectionList, View} from "react-native";
import NetworkErrorComponent from "../Custom/NetworkErrorComponent";
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
type Props = {
navigation: Object,
fetchUrl: string,
2020-03-08 15:50:34 +01:00
autoRefreshTime: number,
refreshOnFocus: boolean,
renderItem: React.Node,
renderSectionHeader: React.Node,
stickyHeader: boolean,
createDataset: Function,
2020-03-10 16:43:52 +01:00
updateData: number,
}
type State = {
refreshing: boolean,
firstLoading: boolean,
fetchedData: ?Object,
2020-03-07 09:15:25 +01:00
snackbarVisible: boolean
};
2020-03-08 15:50:34 +01:00
2020-03-29 14:46:44 +02:00
const MIN_REFRESH_TIME = 5 * 1000;
/**
2020-03-29 14:46:44 +02:00
* Component used to render a SectionList with data fetched from the web
*
2020-03-10 16:43:52 +01:00
* This is a pure component, meaning it will only update if a shallow comparison of state and props is different.
* To force the component to update, change the value of updateData.
*/
export default class WebSectionList extends React.PureComponent<Props, State> {
static defaultProps = {
2020-03-07 09:15:25 +01:00
renderSectionHeader: null,
stickyHeader: false,
2020-03-22 17:02:00 +01:00
updateData: 0,
};
refreshInterval: IntervalID;
lastRefresh: Date;
state = {
refreshing: false,
firstLoading: true,
fetchedData: undefined,
2020-03-07 09:15:25 +01:00
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);
}
/**
2020-03-29 14:46:44 +02:00
* Registers react navigation events on first screen load.
* Allows to detect when the screen is focused
*/
componentDidMount() {
const onScreenFocus = this.onScreenFocus.bind(this);
const onScreenBlur = this.onScreenBlur.bind(this);
this.props.navigation.addListener('focus', onScreenFocus);
this.props.navigation.addListener('blur', onScreenBlur);
2020-03-08 15:50:34 +01:00
this.onRefresh();
}
/**
2020-03-29 14:46:44 +02:00
* Refreshes data when focusing the screen and setup a refresh interval if asked to
*/
onScreenFocus() {
2020-03-08 15:50:34 +01:00
if (this.props.refreshOnFocus && this.lastRefresh !== undefined)
this.onRefresh();
if (this.props.autoRefreshTime > 0)
this.refreshInterval = setInterval(this.onRefresh, this.props.autoRefreshTime)
}
/**
2020-03-29 14:46:44 +02:00
* Removes any interval on un-focus
*/
onScreenBlur() {
clearInterval(this.refreshInterval);
}
2020-03-29 14:46:44 +02:00
/**
* Callback used when fetch is successful.
* It will update the displayed data and stop the refresh animation
*
* @param fetchedData The newly fetched data
*/
onFetchSuccess(fetchedData: Object) {
this.setState({
fetchedData: fetchedData,
refreshing: false,
firstLoading: false
});
this.lastRefresh = new Date();
}
2020-03-29 14:46:44 +02:00
/**
* Callback used when fetch encountered an error.
* It will reset the displayed data and show an error.
*/
onFetchError() {
this.setState({
fetchedData: undefined,
refreshing: false,
firstLoading: false
});
2020-03-07 09:15:25 +01:00
this.showSnackBar();
}
/**
2020-03-29 14:46:44 +02:00
* Refreshes data and shows an animations while doing it
*/
onRefresh() {
let canRefresh;
if (this.lastRefresh !== undefined)
2020-03-08 15:50:34 +01:00
canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) > MIN_REFRESH_TIME;
else
canRefresh = true;
if (canRefresh) {
this.setState({refreshing: true});
2020-03-30 15:28:08 +02:00
readData(this.props.fetchUrl)
.then(this.onFetchSuccess)
.catch(this.onFetchError);
}
}
2020-03-29 14:46:44 +02:00
/**
* Gets an empty section header
*
* @param section The current section
* @return {*}
*/
getEmptySectionHeader({section}: Object) {
return <View/>;
}
2020-03-29 14:46:44 +02:00
/**
* Shows the error popup
*/
showSnackBar = () => this.setState({snackbarVisible: true});
2020-03-07 09:15:25 +01:00
2020-03-29 14:46:44 +02:00
/**
* Hides the error popup
*/
hideSnackBar = () => this.setState({snackbarVisible: false});
2020-03-07 09:15:25 +01:00
render() {
let dataset = [];
if (this.state.fetchedData !== undefined)
dataset = this.props.createDataset(this.state.fetchedData);
const shouldRenderHeader = this.props.renderSectionHeader !== null;
return (
2020-03-07 09:15:25 +01:00
<View>
{/*$FlowFixMe*/}
2020-03-07 09:15:25 +01:00
<SectionList
sections={dataset}
extraData={this.props.updateData}
2020-03-07 09:15:25 +01:00
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this.onRefresh}
/>
}
2020-03-22 17:02:00 +01:00
//$FlowFixMe
2020-03-07 09:15:25 +01:00
renderSectionHeader={shouldRenderHeader ? this.props.renderSectionHeader : this.getEmptySectionHeader}
2020-03-22 17:02:00 +01:00
//$FlowFixMe
renderItem={this.props.renderItem}
2020-03-07 09:15:25 +01:00
stickySectionHeadersEnabled={this.props.stickyHeader}
contentContainerStyle={{minHeight: '100%'}}
style={{minHeight: '100%'}}
ListEmptyComponent={this.state.refreshing
? <BasicLoadingScreen/>
: <NetworkErrorComponent
message={i18n.t('general.networkError')}
icon={"access-point-network-off"}
onRefresh={this.onRefresh}/>
2020-03-07 09:15:25 +01:00
}
/>
<Snackbar
visible={this.state.snackbarVisible}
onDismiss={this.hideSnackBar}
action={{
label: 'OK',
onPress: () => {},
}}
duration={4000}
>
{i18n.t("homeScreen.listUpdateFail")}
</Snackbar>
2020-03-07 09:15:25 +01:00
</View>
);
}
}