// @flow import * as React from 'react'; import {FlatList} from 'react-native'; import i18n from 'i18n-js'; import {ActivityIndicator, Headline, withTheme} from 'react-native-paper'; import {CommonActions} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; import * as Animatable from 'react-native-animatable'; import {View} from 'react-native-animatable'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import DashboardItem from '../../components/Home/EventDashboardItem'; import WebSectionList from '../../components/Screens/WebSectionList'; import FeedItem from '../../components/Home/FeedItem'; import SmallDashboardItem from '../../components/Home/SmallDashboardItem'; import PreviewEventDashboardItem from '../../components/Home/PreviewEventDashboardItem'; import ActionsDashBoardItem from '../../components/Home/ActionsDashboardItem'; import MaterialHeaderButtons, { Item, } from '../../components/Overrides/CustomHeaderButton'; import AnimatedFAB from '../../components/Animations/AnimatedFAB'; import type {CustomThemeType} from '../../managers/ThemeManager'; import ConnectionManager from '../../managers/ConnectionManager'; import LogoutDialog from '../../components/Amicale/LogoutDialog'; import AsyncStorageManager from '../../managers/AsyncStorageManager'; import {MASCOT_STYLE} from '../../components/Mascot/Mascot'; import MascotPopup from '../../components/Mascot/MascotPopup'; import DashboardManager from '../../managers/DashboardManager'; import type {ServiceItemType} from '../../managers/ServicesManager'; import {getDisplayEvent, getFutureEvents} from '../../utils/Home'; // import DATA from "../dashboard_data.json"; const DATA_URL = 'https://etud.insa-toulouse.fr/~amicale_app/v2/dashboard/dashboard_data.json'; const FEED_ITEM_HEIGHT = 500; const SECTIONS_ID = ['dashboard', 'news_feed']; const REFRESH_TIME = 1000 * 20; // Refresh every 20 seconds export type FeedItemType = { id: string, message: string, url: string, image: string | null, video: string | null, link: string | null, time: number, page_id: string, }; export type EventType = { id: number, title: string, logo: string | null, date_begin: string, date_end: string, description: string, club: string, category_id: number, url: string, }; export type FullDashboardType = { today_menu: Array<{[key: string]: {...}}>, proximo_articles: number, available_dryers: number, available_washers: number, today_events: Array, available_tutorials: number, }; type RawNewsFeedType = {[key: string]: Array}; type RawDashboardType = { news_feed: RawNewsFeedType, dashboard: FullDashboardType, }; type PropsType = { navigation: StackNavigationProp, route: {params: {nextScreen: string, data: {...}}}, theme: CustomThemeType, }; type StateType = { dialogVisible: boolean, }; /** * Class defining the app's home screen */ class HomeScreen extends React.Component { static sortFeedTime = (a: FeedItemType, b: FeedItemType): number => b.time - a.time; static generateNewsFeed(rawFeed: RawNewsFeedType): Array { const finalFeed = []; Object.keys(rawFeed).forEach((key: string) => { const category: Array | null = rawFeed[key]; if (category != null && category.length > 0) finalFeed.push(...category); }); finalFeed.sort(HomeScreen.sortFeedTime); return finalFeed; } isLoggedIn: boolean | null; fabRef: {current: null | AnimatedFAB}; currentNewFeed: Array; currentDashboard: FullDashboardType | null; dashboardManager: DashboardManager; constructor(props: PropsType) { super(props); this.fabRef = React.createRef(); this.dashboardManager = new DashboardManager(props.navigation); this.currentNewFeed = []; this.currentDashboard = null; this.isLoggedIn = ConnectionManager.getInstance().isLoggedIn(); props.navigation.setOptions({ headerRight: this.getHeaderButton, }); this.state = { dialogVisible: false, }; } componentDidMount() { const {props} = this; props.navigation.addListener('focus', this.onScreenFocus); // Handle link open when home is focused props.navigation.addListener('state', this.handleNavigationParams); } /** * Updates login state and navigation parameters on screen focus */ onScreenFocus = () => { const {props} = this; if (ConnectionManager.getInstance().isLoggedIn() !== this.isLoggedIn) { this.isLoggedIn = ConnectionManager.getInstance().isLoggedIn(); props.navigation.setOptions({ headerRight: this.getHeaderButton, }); } // handle link open when home is not focused or created this.handleNavigationParams(); }; /** * Gets header buttons based on login state * * @returns {*} */ getHeaderButton = (): React.Node => { const {props} = this; let onPressLog = (): void => props.navigation.navigate('login', {nextScreen: 'profile'}); let logIcon = 'login'; let logColor = props.theme.colors.primary; if (this.isLoggedIn) { onPressLog = (): void => this.showDisconnectDialog(); logIcon = 'logout'; logColor = props.theme.colors.text; } const onPressSettings = (): void => props.navigation.navigate('settings'); return ( ); }; /** * Gets the event dashboard render item. * If a preview is available, it will be rendered inside * * @param content * @return {*} */ getDashboardEvent(content: Array): React.Node { const futureEvents = getFutureEvents(content); const displayEvent = getDisplayEvent(futureEvents); // const clickPreviewAction = () => // this.props.navigation.navigate('students', { // screen: 'planning-information', // params: {data: displayEvent} // }); return ( ); } /** * Gets a dashboard item with action buttons * * @returns {*} */ getDashboardActions(): React.Node { const {props} = this; return ( ); } /** * Gets a dashboard item with a row of shortcut buttons. * * @param content * @return {*} */ getDashboardRow(content: Array): React.Node { return ( // $FlowFixMe ); } /** * Gets a dashboard shortcut item * * @param item * @returns {*} */ getDashboardRowRenderItem = ({ item, }: { item: ServiceItemType | null, }): React.Node => { if (item != null) return ( ); return ; }; /** * Gets a render item for the given feed object * * @param item The feed item to display * @return {*} */ getFeedItem(item: FeedItemType): React.Node { const {props} = this; return ( ); } /** * Gets a FlatList render item * * @param item The item to display * @param section The current section * @return {*} */ getRenderItem = ({item}: {item: FeedItemType}): React.Node => this.getFeedItem(item); getRenderSectionHeader = ( data: { section: { data: Array<{...}>, title: string, }, }, isLoading: boolean, ): React.Node => { const {props} = this; if (data.section.data.length > 0) return ( {data.section.title} ); return ( {data.section.title} {isLoading ? ( ) : ( )} ); }; getListHeader = (fetchedData: RawDashboardType): React.Node => { let dashboard = null; if (fetchedData != null) dashboard = fetchedData.dashboard; return ( {this.getDashboardActions()} {this.getDashboardRow(this.dashboardManager.getCurrentDashboard())} {this.getDashboardEvent( dashboard == null ? [] : dashboard.today_events, )} ); }; /** * Navigates to the a new screen if navigation parameters specify one */ handleNavigationParams = () => { const {props} = this; if (props.route.params != null) { if (props.route.params.nextScreen != null) { props.navigation.navigate( props.route.params.nextScreen, props.route.params.data, ); // reset params to prevent infinite loop props.navigation.dispatch(CommonActions.setParams({nextScreen: null})); } } }; showDisconnectDialog = (): void => this.setState({dialogVisible: true}); hideDisconnectDialog = (): void => this.setState({dialogVisible: false}); openScanner = () => { const {props} = this; props.navigation.navigate('scanner'); }; /** * Creates the dataset to be used in the FlatList * * @param fetchedData * @param isLoading * @return {*} */ createDataset = ( fetchedData: RawDashboardType | null, isLoading: boolean, ): Array<{ title: string, data: [] | Array, id: string, }> => { // fetchedData = DATA; if (fetchedData != null) { if (fetchedData.news_feed != null) this.currentNewFeed = HomeScreen.generateNewsFeed( fetchedData.news_feed, ); if (fetchedData.dashboard != null) this.currentDashboard = fetchedData.dashboard; } if (this.currentNewFeed.length > 0) return [ { title: i18n.t('screens.home.feedTitle'), data: this.currentNewFeed, id: SECTIONS_ID[1], }, ]; return [ { title: isLoading ? i18n.t('screens.home.feedLoading') : i18n.t('screens.home.feedError'), data: [], id: SECTIONS_ID[1], }, ]; }; onEventContainerClick = () => { const {props} = this; props.navigation.navigate('planning'); }; onScroll = (event: SyntheticEvent) => { if (this.fabRef.current != null) this.fabRef.current.onScroll(event); }; /** * Callback when pressing the login button on the banner. * This hides the banner and takes the user to the login page. */ onLogin = () => { const {props} = this; props.navigation.navigate('login', { nextScreen: 'profile', }); }; render(): React.Node { const {props, state} = this; return ( {!this.isLoggedIn ? ( ) : null} ); } } export default withTheme(HomeScreen);