// @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, }; /** * 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 { webDataManager: WebDataManager; willFocusSubscription: function; willBlurSubscription: function; refreshInterval: IntervalID; refreshTime: number; lastRefresh: Date; minTimeBetweenRefresh = 60; constructor(fetchUrl: string, refreshTime: number) { super(); this.webDataManager = new WebDataManager(fetchUrl); this.refreshTime = refreshTime; } state = { refreshing: false, firstLoading: true, fetchedData: {}, machinesWatched: [], }; /** * 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 { 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', payload => { this.onScreenFocus(); } ); this.willBlurSubscription = this.props.navigation.addListener( 'willBlur', payload => { this.onScreenBlur(); } ); } /** * 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(), 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((err) => { 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 * @param data * @return {*} */ getRenderItem(item: Object, section: Object, data: Object) { return ; } /** * 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 ; } /** * 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 ( {isSpinner ? : }

{text}

); } /** * 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 { return []; } /** * 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: (item: Object) => item.text, } ]; } /** * 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 } /** * Get the section list render using the generated dataset * * @param dataset * @return */ getSectionList(dataset: Array) { let isEmpty = dataset[0].data.length === 0; if (isEmpty) dataset = this.createEmptyDataset(); return ( } renderSectionHeader={({section: {title}}) => isEmpty ? : this.getRenderSectionHeader(title) } renderItem={({item, section}) => isEmpty ? this.getEmptyRenderItem(item.text, item.isSpinner, item.icon) : this.getRenderItem(item, section, dataset) } 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) { let tabbedView = []; for (let i = 0; i < dataset.length; i++) { tabbedView.push( {dataset[i].title} } 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 } ] )} ); } return tabbedView; } render() { const nav = this.props.navigation; const dataset = this.createDataset(this.state.fetchedData); return ( {this.hasTabs() ? {this.getTabbedView(dataset)} : this.getSectionList(dataset) } ); } }