123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- // @flow
-
- import * as React from 'react';
- import WebDataManager from "../utils/WebDataManager";
- import {H3, Spinner, Tab, TabHeading, Tabs, Text} from "native-base";
- import {RefreshControl, SectionList, View} from "react-native";
- import CustomMaterialIcon from "./CustomMaterialIcon";
- import i18n from 'i18n-js';
- import ThemeManager from "../utils/ThemeManager";
- import BaseContainer from "./BaseContainer";
-
- type Props = {
- navigation: Object,
- }
-
- type State = {
- refreshing: boolean,
- firstLoading: boolean,
- fetchedData: Object,
- machinesWatched: Array<string>,
- };
-
- /**
- * Class used to create a basic list view using online json data.
- * Used by inheriting from it and redefining getters.
- */
- export default class FetchedDataSectionList extends React.Component<Props, State> {
- webDataManager: WebDataManager;
-
- willFocusSubscription: function;
- willBlurSubscription: function;
- refreshInterval: IntervalID;
- refreshTime: number;
- lastRefresh: Date;
-
- minTimeBetweenRefresh = 60;
- state = {
- refreshing: false,
- firstLoading: true,
- fetchedData: {},
- machinesWatched: [],
- };
-
- onRefresh: Function;
- renderSectionHeaderEmpty: Function;
- renderSectionHeaderNotEmpty: Function;
- renderItemEmpty: Function;
- renderItemNotEmpty: Function;
-
- constructor(fetchUrl: string, refreshTime: number) {
- super();
- this.webDataManager = new WebDataManager(fetchUrl);
- this.refreshTime = refreshTime;
- // creating references to functions used in render()
- this.onRefresh = this.onRefresh.bind(this);
- this.renderSectionHeaderEmpty = this.renderSectionHeader.bind(this, true);
- this.renderSectionHeaderNotEmpty = this.renderSectionHeader.bind(this, false);
- this.renderItemEmpty = this.renderItem.bind(this, true);
- this.renderItemNotEmpty = this.renderItem.bind(this, false);
- }
-
- shouldComponentUpdate(nextProps: Props, nextState: State): boolean {
- return this.state.refreshing !== nextState.refreshing ||
- nextState.firstLoading !== this.state.firstLoading ||
- nextState.machinesWatched.length !== this.state.machinesWatched.length ||
- nextState.fetchedData.len !== this.state.fetchedData.len;
- }
-
-
- /**
- * Get the translation for the header in the current language
- * @return {string}
- */
- getHeaderTranslation(): string {
- return "Header";
- }
-
- /**
- * Get the translation for the toasts in the current language
- * @return {string}
- */
- getUpdateToastTranslations(): Array<string> {
- return ["whoa", "nah"];
- }
-
- setMinTimeRefresh(value: number) {
- this.minTimeBetweenRefresh = value;
- }
-
- /**
- * Register react navigation events on first screen load.
- * Allows to detect when the screen is focused
- */
- componentDidMount() {
- this.willFocusSubscription = this.props.navigation.addListener(
- 'willFocus', this.onScreenFocus.bind(this));
- this.willBlurSubscription = this.props.navigation.addListener(
- 'willBlur', this.onScreenBlur.bind(this));
- }
-
- /**
- * Refresh data when focusing the screen and setup a refresh interval if asked to
- */
- onScreenFocus() {
- this.onRefresh();
- if (this.refreshTime > 0)
- this.refreshInterval = setInterval(this.onRefresh.bind(this), this.refreshTime)
- }
-
- /**
- * Remove any interval on un-focus
- */
- onScreenBlur() {
- clearInterval(this.refreshInterval);
- }
-
- /**
- * Unregister from event when un-mounting components
- */
- componentWillUnmount() {
- if (this.willBlurSubscription !== undefined)
- this.willBlurSubscription.remove();
- if (this.willFocusSubscription !== undefined)
- this.willFocusSubscription.remove();
- }
-
- /**
- * Refresh data and show a toast if any error occurred
- * @private
- */
- onRefresh() {
- let canRefresh;
- if (this.lastRefresh !== undefined)
- canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) / 1000 > this.minTimeBetweenRefresh;
- else
- canRefresh = true;
-
- if (canRefresh) {
- this.setState({refreshing: true});
- this.webDataManager.readData()
- .then((fetchedData) => {
- this.setState({
- fetchedData: fetchedData,
- refreshing: false,
- firstLoading: false
- });
- this.lastRefresh = new Date();
- })
- .catch(() => {
- this.setState({
- fetchedData: {},
- refreshing: false,
- firstLoading: false
- });
- this.webDataManager.showUpdateToast(this.getUpdateToastTranslations()[0], this.getUpdateToastTranslations()[1]);
- });
- }
- }
-
- /**
- * Get the render item to be used for display in the list.
- * Must be overridden by inheriting class.
- *
- * @param item
- * @param section
- * @return {*}
- */
- getRenderItem(item: Object, section: Object) {
- return <View/>;
- }
-
- /**
- * Get the render item to be used for the section title in the list.
- * Must be overridden by inheriting class.
- *
- * @param title
- * @return {*}
- */
- getRenderSectionHeader(title: string) {
- return <View/>;
- }
-
- /**
- * Get the render item to be used when the list is empty.
- * No need to be overridden, has good defaults.
- *
- * @param text
- * @param isSpinner
- * @param icon
- * @return {*}
- */
- getEmptyRenderItem(text: string, isSpinner: boolean, icon: string) {
- return (
- <View>
- <View style={{
- justifyContent: 'center',
- alignItems: 'center',
- width: '100%',
- height: 100,
- marginBottom: 20
- }}>
- {isSpinner ?
- <Spinner/>
- :
- <CustomMaterialIcon
- icon={icon}
- fontSize={100}
- width={100}
- color={ThemeManager.getCurrentThemeVariables().fetchedDataSectionListErrorText}/>}
- </View>
-
- <H3 style={{
- textAlign: 'center',
- marginRight: 20,
- marginLeft: 20,
- color: ThemeManager.getCurrentThemeVariables().fetchedDataSectionListErrorText
- }}>
- {text}
- </H3>
- </View>);
- }
-
- /**
- * Create the dataset to be used in the list from the data fetched.
- * Must be overridden.
- *
- * @param fetchedData {Object}
- * @return {Array}
- */
- createDataset(fetchedData: Object): Array<Object> {
- return [];
- }
-
-
- datasetKeyExtractor(item: Object) {
- return item.text
- }
-
- /**
- * Create the dataset when no fetched data is available.
- * No need to be overridden, has good defaults.
- *
- * @return
- */
- createEmptyDataset() {
- return [
- {
- title: '',
- data: [
- {
- text: this.state.refreshing ?
- i18n.t('general.loading') :
- i18n.t('general.networkError'),
- isSpinner: this.state.refreshing,
- icon: this.state.refreshing ?
- 'refresh' :
- 'access-point-network-off'
- }
- ],
- keyExtractor: this.datasetKeyExtractor,
- }
- ];
- }
-
- /**
- * Should the app use a tab layout instead of a section list ?
- * If yes, each section will be rendered in a new tab.
- * Can be overridden.
- *
- * @return {boolean}
- */
- hasTabs() {
- return false;
- }
-
- hasBackButton() {
- return false;
- }
-
- getRightButton() {
- return <View/>
- }
-
- hasStickyHeader() {
- return false;
- }
-
- hasSideMenu() {
- return true;
- }
-
-
- renderSectionHeader(isEmpty: boolean, {section: {title}} : Object) {
- return isEmpty ?
- <View/> :
- this.getRenderSectionHeader(title)
- }
-
- renderItem(isEmpty: boolean, {item, section}: Object) {
- return isEmpty ?
- this.getEmptyRenderItem(item.text, item.isSpinner, item.icon) :
- this.getRenderItem(item, section)
- }
-
- /**
- * Get the section list render using the generated dataset
- *
- * @param dataset
- * @return
- */
- getSectionList(dataset: Array<Object>) {
- let isEmpty = dataset[0].data.length === 0;
- if (isEmpty)
- dataset = this.createEmptyDataset();
- return (
- <SectionList
- sections={dataset}
- stickySectionHeadersEnabled={this.hasStickyHeader()}
- refreshControl={
- <RefreshControl
- refreshing={this.state.refreshing}
- onRefresh={this.onRefresh}
- />
- }
- renderSectionHeader={isEmpty ? this.renderSectionHeaderEmpty : this.renderSectionHeaderNotEmpty}
- renderItem={isEmpty ? this.renderItemEmpty : this.renderItemNotEmpty}
- style={{minHeight: 300, width: '100%'}}
- contentContainerStyle={
- isEmpty ?
- {flexGrow: 1, justifyContent: 'center', alignItems: 'center'} : {}
- }
- />
- );
- }
-
- /**
- * Generate the tabs containing the lists
- *
- * @param dataset
- * @return
- */
- getTabbedView(dataset: Array<Object>) {
- let tabbedView = [];
- for (let i = 0; i < dataset.length; i++) {
- tabbedView.push(
- <Tab heading={
- <TabHeading>
- <CustomMaterialIcon
- icon={dataset[i].icon}
- color={ThemeManager.getCurrentThemeVariables().tabIconColor}
- fontSize={20}
- />
- <Text>{dataset[i].title}</Text>
- </TabHeading>}
- key={dataset[i].title}
- style={{backgroundColor: ThemeManager.getCurrentThemeVariables().containerBgColor}}>
- {this.getSectionList(
- [
- {
- title: dataset[i].title,
- data: dataset[i].data,
- extraData: dataset[i].extraData,
- keyExtractor: dataset[i].keyExtractor
- }
- ]
- )}
- </Tab>);
- }
- return tabbedView;
- }
-
- render() {
- // console.log("rendering FetchedDataSectionList");
- const dataset = this.createDataset(this.state.fetchedData);
- return (
- <BaseContainer
- navigation={this.props.navigation}
- headerTitle={this.getHeaderTranslation()}
- headerRightButton={this.getRightButton()}
- hasTabs={this.hasTabs()}
- hasBackButton={this.hasBackButton()}
- hasSideMenu={this.hasSideMenu()}
- >
- {this.hasTabs() ?
- <Tabs
- tabContainerStyle={{
- elevation: 0, // Fix for android shadow
- }}
- >
- {this.getTabbedView(dataset)}
- </Tabs>
- :
- this.getSectionList(dataset)
- }
- </BaseContainer>
- );
- }
- }
|