diff --git a/App.js b/App.js index 821cbdc..b50c13f 100644 --- a/App.js +++ b/App.js @@ -14,6 +14,7 @@ import ThemeManager from './utils/ThemeManager'; import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; import DrawerNavigator from './navigation/DrawerNavigator'; +import { enableScreens } from 'react-native-screens'; type Props = {}; @@ -38,6 +39,7 @@ export default class App extends React.Component { constructor(props: Object) { super(props); LocaleManager.initTranslations(); + enableScreens(); } /** diff --git a/components/CustomHeader.js b/components/CustomHeader.js index c72e46f..1a9f738 100644 --- a/components/CustomHeader.js +++ b/components/CustomHeader.js @@ -8,7 +8,6 @@ import Touchable from 'react-native-platform-touchable'; import ThemeManager from "../utils/ThemeManager"; import CustomMaterialIcon from "./CustomMaterialIcon"; import i18n from "i18n-js"; -import {NavigationActions} from 'react-navigation'; type Props = { hasBackButton: boolean, @@ -105,8 +104,7 @@ export default class CustomHeader extends React.Component { onPressBack() { - const backAction = NavigationActions.back(); - this.props.navigation.dispatch(backAction); + this.props.navigation.goBack(); } render() { diff --git a/components/FetchedDataSectionList.js b/components/FetchedDataSectionList.js deleted file mode 100644 index df715e1..0000000 --- a/components/FetchedDataSectionList.js +++ /dev/null @@ -1,399 +0,0 @@ -// @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; - state = { - refreshing: false, - firstLoading: true, - fetchedData: {}, - machinesWatched: [], - }; - - onRefresh: Function; - onFetchSuccess: Function; - onFetchError: 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.onFetchSuccess = this.onFetchSuccess.bind(this); - this.onFetchError = this.onFetchError.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); - } - - - /** - * 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', 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(); - } - - onFetchSuccess(fetchedData: Object) { - this.setState({ - fetchedData: fetchedData, - refreshing: false, - firstLoading: false - }); - this.lastRefresh = new Date(); - } - - onFetchError() { - this.setState({ - fetchedData: {}, - refreshing: false, - firstLoading: false - }); - this.webDataManager.showUpdateToast(this.getUpdateToastTranslations()[0], this.getUpdateToastTranslations()[1]); - } - - /** - * 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(this.onFetchSuccess) - .catch(this.onFetchError); - } - } - - /** - * 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 ; - } - - /** - * 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 []; - } - - - 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 - } - - hasStickyHeader() { - return false; - } - - hasSideMenu() { - return true; - } - - - renderSectionHeader(isEmpty: boolean, {section: {title}} : Object) { - return isEmpty ? - : - 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) { - let isEmpty = dataset[0].data.length === 0; - if (isEmpty) - dataset = this.createEmptyDataset(); - return ( - - } - 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) { - 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() { - // console.log("rendering FetchedDataSectionList"); - const dataset = this.createDataset(this.state.fetchedData); - return ( - - {this.hasTabs() ? - - {this.getTabbedView(dataset)} - - : - this.getSectionList(dataset) - } - - ); - } -} diff --git a/components/WebSectionList.js b/components/WebSectionList.js new file mode 100644 index 0000000..9a57c50 --- /dev/null +++ b/components/WebSectionList.js @@ -0,0 +1,221 @@ +// @flow + +import * as React from 'react'; +import {H3, Spinner, View} from "native-base"; +import ThemeManager from '../utils/ThemeManager'; +import WebDataManager from "../utils/WebDataManager"; +import CustomMaterialIcon from "./CustomMaterialIcon"; +import i18n from "i18n-js"; +import {RefreshControl, SectionList} from "react-native"; + +type Props = { + navigation: Object, + fetchUrl: string, + refreshTime: number, + renderItem: React.Node, + renderSectionHeader: React.Node, + stickyHeader: boolean, + createDataset: Function, + updateErrorText: string, +} + +type State = { + refreshing: boolean, + firstLoading: boolean, + fetchedData: Object, +}; + +/** + * Custom component defining a material icon using native base + * + * @prop active {boolean} Whether to set the icon color to active + * @prop icon {string} The icon string to use from MaterialCommunityIcons + * @prop color {string} The icon color. Use default theme color if unspecified + * @prop fontSize {number} The icon size. Use 26 if unspecified + * @prop width {number} The icon width. Use 30 if unspecified + */ +export default class WebSectionList extends React.Component { + + static defaultProps = { + renderSectionHeader: undefined, + stickyHeader: false, + }; + + webDataManager: WebDataManager; + + refreshInterval: IntervalID; + lastRefresh: Date; + + state = { + refreshing: false, + firstLoading: true, + fetchedData: {}, + }; + + onRefresh: Function; + onFetchSuccess: Function; + onFetchError: Function; + getEmptyRenderItem: 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.getEmptyRenderItem = this.getEmptyRenderItem.bind(this); + this.getEmptySectionHeader = this.getEmptySectionHeader.bind(this); + } + + /** + * Register react navigation events on first screen load. + * Allows to detect when the screen is focused + */ + componentDidMount() { + this.webDataManager = new WebDataManager(this.props.fetchUrl); + const onScreenFocus = this.onScreenFocus.bind(this); + const onScreenBlur = this.onScreenBlur.bind(this); + this.props.navigation.addListener('focus', onScreenFocus); + this.props.navigation.addListener('blur', onScreenBlur); + } + + /** + * Refresh data when focusing the screen and setup a refresh interval if asked to + */ + onScreenFocus() { + this.onRefresh(); + if (this.props.refreshTime > 0) + this.refreshInterval = setInterval(this.onRefresh, this.props.refreshTime) + } + + /** + * Remove any interval on un-focus + */ + onScreenBlur() { + clearInterval(this.refreshInterval); + } + + + onFetchSuccess(fetchedData: Object) { + this.setState({ + fetchedData: fetchedData, + refreshing: false, + firstLoading: false + }); + this.lastRefresh = new Date(); + } + + onFetchError() { + this.setState({ + fetchedData: {}, + refreshing: false, + firstLoading: false + }); + this.webDataManager.showUpdateToast(this.props.updateErrorText); + } + + /** + * 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()) > this.props.refreshTime; + else + canRefresh = true; + if (canRefresh) { + this.setState({refreshing: true}); + this.webDataManager.readData() + .then(this.onFetchSuccess) + .catch(this.onFetchError); + } + } + + getEmptySectionHeader({section}: Object) { + return ; + } + + getEmptyRenderItem({item}: Object) { + return ( + + + {this.state.refreshing ? + + : + } + + +

+ {item.text} +

+
); + } + + 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, + } + ]; + } + + datasetKeyExtractor(item: Object) { + return item.text + } + + render() { + let dataset = this.props.createDataset(this.state.fetchedData); + const isEmpty = dataset[0].data.length === 0; + const shouldRenderHeader = isEmpty || (this.props.renderSectionHeader !== undefined); + if (isEmpty) + dataset = this.createEmptyDataset(); + return ( + + } + renderSectionHeader={shouldRenderHeader ? this.getEmptySectionHeader : this.props.renderSectionHeader} + renderItem={isEmpty ? this.getEmptyRenderItem : this.props.renderItem} + style={{minHeight: 300, width: '100%'}} + stickySectionHeadersEnabled={this.props.stickyHeader} + contentContainerStyle={ + isEmpty ? + {flexGrow: 1, justifyContent: 'center', alignItems: 'center'} : {} + } + /> + ); + } +} diff --git a/screens/HomeScreen.js b/screens/HomeScreen.js index 2640ef5..03a4cbd 100644 --- a/screens/HomeScreen.js +++ b/screens/HomeScreen.js @@ -1,14 +1,16 @@ // @flow import * as React from 'react'; -import {Image, Linking, TouchableOpacity, View} from 'react-native'; -import {Body, Button, Card, CardItem, H1, Left, Text, Thumbnail} from 'native-base'; +import {Image, TouchableOpacity, View} from 'react-native'; +import {Body, Button, Card, CardItem, Left, Text, Thumbnail} from 'native-base'; import i18n from "i18n-js"; import CustomMaterialIcon from '../components/CustomMaterialIcon'; -import FetchedDataSectionList from "../components/FetchedDataSectionList"; import Autolink from 'react-native-autolink'; import ThemeManager from "../utils/ThemeManager"; import DashboardItem from "../components/DashboardItem"; +import * as WebBrowser from 'expo-web-browser'; +import BaseContainer from "../components/BaseContainer"; +import WebSectionList from "../components/WebSectionList"; // import DATA from "../dashboard_data.json"; @@ -25,46 +27,30 @@ const REFRESH_TIME = 1000 * 20; // Refresh every 20 seconds const CARD_BORDER_RADIUS = 10; -/** - * Opens a link in the device's browser - * @param link The link to open - */ -function openWebLink(link) { - Linking.openURL(link).catch((err) => console.error('Error opening link', err)); +type Props = { + navigation: Object, } /** * Class defining the app's home screen */ -export default class HomeScreen extends FetchedDataSectionList { +export default class HomeScreen extends React.Component { onProxiwashClick: Function; onTutorInsaClick: Function; onMenuClick: Function; onProximoClick: Function; + getRenderItem: Function; + createDataset: Function; constructor() { - super(DATA_URL, REFRESH_TIME); + super(); this.onProxiwashClick = this.onProxiwashClick.bind(this); this.onTutorInsaClick = this.onTutorInsaClick.bind(this); this.onMenuClick = this.onMenuClick.bind(this); this.onProximoClick = this.onProximoClick.bind(this); - } - - onProxiwashClick() { - this.props.navigation.navigate('Proxiwash'); - } - - onTutorInsaClick() { - this.props.navigation.navigate('TutorInsaScreen'); - } - - onProximoClick() { - this.props.navigation.navigate('Proximo'); - } - - onMenuClick() { - this.props.navigation.navigate('SelfMenuScreen'); + this.getRenderItem = this.getRenderItem.bind(this); + this.createDataset = this.createDataset.bind(this); } /** @@ -77,12 +63,20 @@ export default class HomeScreen extends FetchedDataSectionList { return date.toLocaleString(); } - getHeaderTranslation() { - return i18n.t("screens.home"); + onProxiwashClick() { + this.props.navigation.navigate('Proxiwash'); } - getUpdateToastTranslations() { - return [i18n.t("homeScreen.listUpdated"), i18n.t("homeScreen.listUpdateFail")]; + onTutorInsaClick() { + WebBrowser.openBrowserAsync("https://www.etud.insa-toulouse.fr/~tutorinsa/"); + } + + onProximoClick() { + this.props.navigation.navigate('Proximo'); + } + + onMenuClick() { + this.props.navigation.navigate('SelfMenuScreen'); } getKeyExtractor(item: Object) { @@ -154,25 +148,6 @@ export default class HomeScreen extends FetchedDataSectionList { return dataset } - getRenderSectionHeader(title: string) { - if (title === '') { - return ; - } else { - return ( - -

{title}

-
- ); - } - } - getDashboardItem(item: Object) { let content = item['content']; if (item['id'] === 'event') @@ -318,7 +293,7 @@ export default class HomeScreen extends FetchedDataSectionList { if (isAvailable) this.props.navigation.navigate('PlanningDisplayScreen', {data: displayEvent}); else - this.props.navigation.navigate('PlanningScreen'); + this.props.navigation.navigate('Planning'); }; @@ -345,13 +320,13 @@ export default class HomeScreen extends FetchedDataSectionList { subtitle = i18n.t('homeScreen.dashboard.todayEventsSubtitleNA'); let displayEvent = this.getDisplayEvent(futureEvents); - + const clickAction = this.clickAction.bind(this, isAvailable, displayEvent); return ( + - - - - - {NAME_AMICALE} - {HomeScreen.getFormattedDate(item.created_time)} - - - - + + - {item.full_picture !== '' && item.full_picture !== undefined ? - - - - : } - {item.message !== undefined ? - : - } + {NAME_AMICALE} + {HomeScreen.getFormattedDate(item.created_time)} - - - - - - - + + + + + {item.full_picture !== '' && item.full_picture !== undefined ? + + + + : } + {item.message !== undefined ? + : + } + + + + + + + + + ); + } + + getRenderItem({item, section}: Object) { + return (section['id'] === SECTIONS_ID[0] ? + this.getDashboardItem(item) : this.getFeedItem(item)); + } + + render() { + const nav = this.props.navigation; + return ( + + + ); } } diff --git a/screens/Proximo/ProximoListScreen.js b/screens/Proximo/ProximoListScreen.js index 2943fee..23887aa 100644 --- a/screens/Proximo/ProximoListScreen.js +++ b/screens/Proximo/ProximoListScreen.js @@ -41,7 +41,8 @@ function sortNameReverse(a, b) { } type Props = { - navigation: Object + navigation: Object, + route: Object, } type State = { @@ -60,16 +61,8 @@ export default class ProximoListScreen extends React.Component { modalRef: { current: null | Modalize }; originalData: Array; - navData = this.props.navigation.getParam('data', []); - shouldFocusSearchBar = this.props.navigation.getParam('shouldFocusSearchBar', false); - state = { - currentlyDisplayedData: this.navData['data'].sort(sortPrice), - currentSortMode: sortMode.price, - isSortReversed: false, - sortPriceIcon: '', - sortNameIcon: '', - modalCurrentDisplayItem: {}, - }; + shouldFocusSearchBar: boolean; + sortMenuRef: Menu; onMenuRef: Function; @@ -82,7 +75,16 @@ export default class ProximoListScreen extends React.Component { constructor(props: any) { super(props); this.modalRef = React.createRef(); - this.originalData = this.navData['data']; + this.originalData = this.props.route.params['data']['data']; + this.shouldFocusSearchBar = this.props.route.params['shouldFocusSearchBar']; + this.state = { + currentlyDisplayedData: this.originalData, + currentSortMode: sortMode.price, + isSortReversed: false, + sortPriceIcon: '', + sortNameIcon: '', + modalCurrentDisplayItem: {}, + }; this.onMenuRef = this.onMenuRef.bind(this); this.onSearchStringChange = this.onSearchStringChange.bind(this); @@ -365,7 +367,6 @@ export default class ProximoListScreen extends React.Component { shouldFocusSearchBar={this.shouldFocusSearchBar} rightButton={this.getSortMenu()} /> - { + + articles: Object; onPressSearchBtn: Function; onPressAboutBtn: Function; + getRenderItem: Function; + createDataset: Function; constructor() { - super(DATA_URL, 0); + super(); this.onPressSearchBtn = this.onPressSearchBtn.bind(this); this.onPressAboutBtn = this.onPressAboutBtn.bind(this); + this.getRenderItem = this.getRenderItem.bind(this); + this.createDataset = this.createDataset.bind(this); } static sortFinalData(a: Object, b: Object) { @@ -45,14 +59,6 @@ export default class ProximoMainScreen extends FetchedDataSectionList { return 0; } - getHeaderTranslation() { - return i18n.t("screens.proximo"); - } - - getUpdateToastTranslations() { - return [i18n.t("proximoScreen.listUpdated"), i18n.t("proximoScreen.listUpdateFail")]; - } - getKeyExtractor(item: Object) { return item !== undefined ? item.type['id'] : undefined; } @@ -62,7 +68,7 @@ export default class ProximoMainScreen extends FetchedDataSectionList { { title: '', data: this.generateData(fetchedData), - extraData: super.state, + extraData: this.state, keyExtractor: this.getKeyExtractor } ]; @@ -77,21 +83,22 @@ export default class ProximoMainScreen extends FetchedDataSectionList { */ generateData(fetchedData: Object) { let finalData = []; + this.articles = undefined; if (fetchedData.types !== undefined && fetchedData.articles !== undefined) { let types = fetchedData.types; - let articles = fetchedData.articles; + this.articles = fetchedData.articles; finalData.push({ type: { id: -1, name: i18n.t('proximoScreen.all'), icon: 'star' }, - data: this.getAvailableArticles(articles, undefined) + data: this.getAvailableArticles(this.articles, undefined) }); for (let i = 0; i < types.length; i++) { finalData.push({ type: types[i], - data: this.getAvailableArticles(articles, types[i]) + data: this.getAvailableArticles(this.articles, types[i]) }); } @@ -128,8 +135,8 @@ export default class ProximoMainScreen extends FetchedDataSectionList { name: i18n.t('proximoScreen.all'), icon: 'star' }, - data: this.state.fetchedData.articles !== undefined ? - this.getAvailableArticles(this.state.fetchedData.articles, undefined) : [] + data: this.articles !== undefined ? + this.getAvailableArticles(this.articles, undefined) : [] }, }; this.props.navigation.navigate('ProximoListScreen', searchScreenData); @@ -163,7 +170,7 @@ export default class ProximoMainScreen extends FetchedDataSectionList { ); } - getRenderItem(item: Object, section: Object) { + getRenderItem({item}: Object) { let dataToSend = { shouldFocusSearchBar: false, data: item, @@ -196,10 +203,26 @@ export default class ProximoMainScreen extends FetchedDataSectionList { ); - } else { + } else return ; - } + } + render() { + const nav = this.props.navigation; + return ( + + + + ); } } diff --git a/screens/Proxiwash/ProxiwashScreen.js b/screens/Proxiwash/ProxiwashScreen.js index ae7e7cc..46be088 100644 --- a/screens/Proxiwash/ProxiwashScreen.js +++ b/screens/Proxiwash/ProxiwashScreen.js @@ -6,12 +6,13 @@ import {Body, Card, CardItem, Left, Right, Text} from 'native-base'; import ThemeManager from '../../utils/ThemeManager'; import i18n from "i18n-js"; import CustomMaterialIcon from "../../components/CustomMaterialIcon"; -import FetchedDataSectionList from "../../components/FetchedDataSectionList"; +import WebSectionList from "../../components/WebSectionList"; import NotificationsManager from "../../utils/NotificationsManager"; import PlatformTouchable from "react-native-platform-touchable"; import Touchable from "react-native-platform-touchable"; import AsyncStorageManager from "../../utils/AsyncStorageManager"; import * as Expo from "expo"; +import BaseContainer from "../../components/BaseContainer"; const DATA_URL = "https://etud.insa-toulouse.fr/~amicale_app/washinsa/washinsa.json"; @@ -30,19 +31,41 @@ let stateColors = {}; const REFRESH_TIME = 1000 * 10; // Refresh every 10 seconds +type Props = { + navigation: Object, +} + +type State = { + refreshing: boolean, + firstLoading: boolean, + fetchedData: Object, + machinesWatched: Array, +}; + + /** * Class defining the app's proxiwash screen. This screen shows information about washing machines and * dryers, taken from a scrapper reading proxiwash website */ -export default class ProxiwashScreen extends FetchedDataSectionList { +export default class ProxiwashScreen extends React.Component { onAboutPress: Function; + getRenderItem: Function; + createDataset: Function; + + state = { + refreshing: false, + firstLoading: true, + fetchedData: {}, + // machinesWatched: JSON.parse(dataString), + machinesWatched: [], + }; /** * Creates machine state parameters using current theme and translations */ constructor() { - super(DATA_URL, REFRESH_TIME); + super(); let colors = ThemeManager.getCurrentThemeVariables(); stateColors[MACHINE_STATES.TERMINE] = colors.proxiwashFinishedColor; stateColors[MACHINE_STATES.DISPONIBLE] = colors.proxiwashReadyColor; @@ -69,23 +92,16 @@ export default class ProxiwashScreen extends FetchedDataSectionList { stateIcons[MACHINE_STATES.ERREUR] = 'alert'; // let dataString = AsyncStorageManager.getInstance().preferences.proxiwashWatchedMachines.current; - this.state = { - refreshing: false, - firstLoading: true, - fetchedData: {}, - // machinesWatched: JSON.parse(dataString), - machinesWatched: [], - }; - this.setMinTimeRefresh(30); - + // this.setMinTimeRefresh(30); this.onAboutPress = this.onAboutPress.bind(this); + this.getRenderItem = this.getRenderItem.bind(this); + this.createDataset = this.createDataset.bind(this); } /** * Setup notification channel for android and add listeners to detect notifications fired */ componentDidMount() { - super.componentDidMount(); if (AsyncStorageManager.getInstance().preferences.expoToken.current !== '') { // Get latest watchlist from server NotificationsManager.getMachineNotificationWatchlist((fetchedList) => { @@ -107,14 +123,6 @@ export default class ProxiwashScreen extends FetchedDataSectionList { } } - getHeaderTranslation() { - return i18n.t("screens.proxiwash"); - } - - getUpdateToastTranslations() { - return [i18n.t("proxiwashScreen.listUpdated"), i18n.t("proxiwashScreen.listUpdateFail")]; - } - getDryersKeyExtractor(item: Object) { return item !== undefined ? "dryer" + item.number : undefined; } @@ -212,28 +220,23 @@ export default class ProxiwashScreen extends FetchedDataSectionList { createDataset(fetchedData: Object) { return [ - { - title: i18n.t('proxiwashScreen.washers'), - icon: 'washing-machine', - data: fetchedData.washers === undefined ? [] : fetchedData.washers, - extraData: super.state, - keyExtractor: this.getWashersKeyExtractor - }, { title: i18n.t('proxiwashScreen.dryers'), icon: 'tumble-dryer', data: fetchedData.dryers === undefined ? [] : fetchedData.dryers, - extraData: super.state, + extraData: this.state, keyExtractor: this.getDryersKeyExtractor }, - + { + title: i18n.t('proxiwashScreen.washers'), + icon: 'washing-machine', + data: fetchedData.washers === undefined ? [] : fetchedData.washers, + extraData: this.state, + keyExtractor: this.getWashersKeyExtractor + }, ]; } - hasTabs(): boolean { - return true; - } - /** * Show an alert fo a machine, allowing to enable/disable notifications if running * @@ -292,6 +295,24 @@ export default class ProxiwashScreen extends FetchedDataSectionList { ); } + render() { + const nav = this.props.navigation; + return ( + + + + ); + } + /** * Get list item to be rendered * @@ -299,7 +320,7 @@ export default class ProxiwashScreen extends FetchedDataSectionList { * @param section The object describing the current SectionList section * @returns {React.Node} */ - getRenderItem(item: Object, section: Object) { + getRenderItem({item, section} : Object) { let isMachineRunning = MACHINE_STATES[item.state] === MACHINE_STATES["EN COURS"]; let machineName = (section.title === i18n.t('proxiwashScreen.dryers') ? i18n.t('proxiwashScreen.dryer') : i18n.t('proxiwashScreen.washer')) + ' n°' + item.number; let isDryer = section.title === i18n.t('proxiwashScreen.dryers'); diff --git a/screens/SelfMenuScreen.js b/screens/SelfMenuScreen.js index 98e01f8..e92cb08 100644 --- a/screens/SelfMenuScreen.js +++ b/screens/SelfMenuScreen.js @@ -5,22 +5,31 @@ import {View} from 'react-native'; import {Card, CardItem, H2, H3, Text} from 'native-base'; import ThemeManager from "../utils/ThemeManager"; import i18n from "i18n-js"; -import FetchedDataSectionList from "../components/FetchedDataSectionList"; +import BaseContainer from "../components/BaseContainer"; +import WebSectionList from "../components/WebSectionList"; const DATA_URL = "https://etud.insa-toulouse.fr/~amicale_app/menu/menu_data.json"; +type Props = { + navigation: Object, +} + /** * Class defining the app's menu screen. * This screen fetches data from etud to render the RU menu */ -export default class SelfMenuScreen extends FetchedDataSectionList { +export default class SelfMenuScreen extends React.Component { // Hard code strings as toLocaleDateString does not work on current android JS engine daysOfWeek = []; monthsOfYear = []; + getRenderItem: Function; + getRenderSectionHeader: Function; + createDataset: Function; + constructor() { - super(DATA_URL, 0); + super(); this.daysOfWeek.push(i18n.t("date.daysOfWeek.monday")); this.daysOfWeek.push(i18n.t("date.daysOfWeek.tuesday")); this.daysOfWeek.push(i18n.t("date.daysOfWeek.wednesday")); @@ -41,32 +50,16 @@ export default class SelfMenuScreen extends FetchedDataSectionList { this.monthsOfYear.push(i18n.t("date.monthsOfYear.october")); this.monthsOfYear.push(i18n.t("date.monthsOfYear.november")); this.monthsOfYear.push(i18n.t("date.monthsOfYear.december")); - } - getHeaderTranslation() { - return i18n.t("screens.menuSelf"); - } - - getUpdateToastTranslations() { - return [i18n.t("homeScreen.listUpdated"), i18n.t("homeScreen.listUpdateFail")]; + this.getRenderItem = this.getRenderItem.bind(this); + this.getRenderSectionHeader = this.getRenderSectionHeader.bind(this); + this.createDataset = this.createDataset.bind(this); } getKeyExtractor(item: Object) { return item !== undefined ? item['name'] : undefined; } - hasBackButton() { - return true; - } - - hasStickyHeader(): boolean { - return true; - } - - hasSideMenu(): boolean { - return false; - } - createDataset(fetchedData: Object) { let result = []; // Prevent crash by giving a default value when fetchedData is empty (not yet available) @@ -101,7 +94,8 @@ export default class SelfMenuScreen extends FetchedDataSectionList { return this.daysOfWeek[date.getDay() - 1] + " " + date.getDate() + " " + this.monthsOfYear[date.getMonth()] + " " + date.getFullYear(); } - getRenderSectionHeader(title: string) { + getRenderSectionHeader({section}: Object) { + let title = ""; return ( {title} + }}>{section.title} ); } - getRenderItem(item: Object, section: Object) { + getRenderItem({item}: Object) { return ( + + + ); + } } diff --git a/utils/WebDataManager.js b/utils/WebDataManager.js index d828f01..45eda76 100644 --- a/utils/WebDataManager.js +++ b/utils/WebDataManager.js @@ -45,14 +45,13 @@ export default class WebDataManager { /** * Show a toast message depending on the validity of the fetched data * - * @param successString * @param errorString */ - showUpdateToast(successString, errorString) { + showUpdateToast(errorString) { let isSuccess = this.isDataObjectValid(); if (!isSuccess) { Toast.show({ - text: isSuccess ? successString : errorString, + text: errorString, buttonText: 'OK', type: isSuccess ? "success" : "danger", duration: 2000