/* * Copyright (c) 2019 - 2020 Arnaud Vergnet. * * This file is part of Campus INSAT. * * Campus INSAT is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Campus INSAT is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Campus INSAT. If not, see . */ import React, { useLayoutEffect, useRef, useState } from 'react'; import { FlatList, NativeScrollEvent, NativeSyntheticEvent, SectionListData, StyleSheet, } from 'react-native'; import i18n from 'i18n-js'; import { Headline, useTheme } from 'react-native-paper'; import { CommonActions, useFocusEffect, useNavigation, } from '@react-navigation/native'; import { StackScreenProps } 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 ConnectionManager from '../../managers/ConnectionManager'; import LogoutDialog from '../../components/Amicale/LogoutDialog'; import { MASCOT_STYLE } from '../../components/Mascot/Mascot'; import MascotPopup from '../../components/Mascot/MascotPopup'; import { getDisplayEvent, getFutureEvents } from '../../utils/Home'; import type { PlanningEventType } from '../../utils/Planning'; import GENERAL_STYLES from '../../constants/Styles'; import Urls from '../../constants/Urls'; import { readData } from '../../utils/WebData'; import { TabRoutes, TabStackParamsList } from '../../navigation/TabNavigator'; import { ServiceItemType } from '../../utils/Services'; import { useCurrentDashboard } from '../../context/preferencesContext'; import { MainRoutes } from '../../navigation/MainNavigator'; 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 FullDashboardType = { today_menu: Array<{ [key: string]: object }>; 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 Props = StackScreenProps; const styles = StyleSheet.create({ dashboardRow: { marginLeft: 'auto', marginRight: 'auto', marginTop: 10, marginBottom: 10, }, sectionHeader: { textAlign: 'center', marginTop: 50, marginBottom: 10, }, sectionHeaderEmpty: { textAlign: 'center', marginTop: 50, marginBottom: 10, marginLeft: 20, marginRight: 20, }, activityIndicator: { marginTop: 10, }, content: { position: 'absolute', width: '100%', height: '100%', }, }); const sortFeedTime = (a: FeedItemType, b: FeedItemType): number => b.time - a.time; const generateNewsFeed = (rawFeed: RawNewsFeedType): Array => { const finalFeed: Array = []; Object.keys(rawFeed).forEach((key: string) => { const category: Array | null = rawFeed[key]; if (category != null && category.length > 0) { finalFeed.push(...category); } }); finalFeed.sort(sortFeedTime); return finalFeed; }; /** * Class defining the app's home screen */ function HomeScreen(props: Props) { const theme = useTheme(); const navigation = useNavigation(); const [dialogVisible, setDialogVisible] = useState(false); const fabRef = useRef(null); const [isLoggedIn, setIsLoggedIn] = useState( ConnectionManager.getInstance().isLoggedIn() ); const { currentDashboard } = useCurrentDashboard(); let homeDashboard: FullDashboardType | null = null; useLayoutEffect(() => { const getHeaderButton = () => { let onPressLog = () => navigation.navigate('login', { nextScreen: 'profile' }); let logIcon = 'login'; let logColor = theme.colors.primary; if (isLoggedIn) { onPressLog = () => showDisconnectDialog(); logIcon = 'logout'; logColor = theme.colors.text; } return ( navigation.navigate(MainRoutes.Settings)} /> ); }; navigation.setOptions({ headerRight: getHeaderButton, }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [navigation, isLoggedIn]); useFocusEffect( React.useCallback(() => { const handleNavigationParams = () => { const { route } = props; if (route.params != null) { if (route.params.nextScreen != null) { navigation.navigate(route.params.nextScreen, route.params.data); // reset params to prevent infinite loop navigation.dispatch(CommonActions.setParams({ nextScreen: null })); } } }; if (ConnectionManager.getInstance().isLoggedIn() !== isLoggedIn) { setIsLoggedIn(ConnectionManager.getInstance().isLoggedIn()); } // handle link open when home is not focused or created handleNavigationParams(); return () => {}; // eslint-disable-next-line react-hooks/exhaustive-deps }, [isLoggedIn]) ); /** * Gets the event dashboard render item. * If a preview is available, it will be rendered inside * * @param content * @return {*} */ const getDashboardEvent = (content: Array) => { 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 a row of shortcut buttons. * * @param content * @return {*} */ const getDashboardRow = (content: Array) => { return ( ); }; /** * Gets a dashboard shortcut item * * @param item * @returns {*} */ const getDashboardRowRenderItem = ({ item, }: { item: ServiceItemType | null; }) => { if (item != null) { return ( ); } return ; }; const getRenderItem = ({ item }: { item: FeedItemType }) => ( ); const getRenderSectionHeader = (data: { section: SectionListData; }) => { const icon = data.section.icon; if (data.section.data.length > 0) { return ( {data.section.title} ); } return ( {data.section.title} {icon ? ( ) : null} ); }; const getListHeader = (fetchedData: RawDashboardType | undefined) => { let dashboard = null; if (fetchedData != null) { dashboard = fetchedData.dashboard; } return ( {getDashboardRow(currentDashboard)} {getDashboardEvent(dashboard == null ? [] : dashboard.today_events)} ); }; const showDisconnectDialog = () => setDialogVisible(true); const hideDisconnectDialog = () => setDialogVisible(false); const openScanner = () => navigation.navigate('scanner'); /** * Creates the dataset to be used in the FlatList * * @param fetchedData * @param isLoading * @return {*} */ const createDataset = ( fetchedData: RawDashboardType | undefined, isLoading: boolean ): Array<{ title: string; data: [] | Array; icon?: string; id: string; }> => { let currentNewFeed: Array = []; if (fetchedData) { if (fetchedData.news_feed) { currentNewFeed = generateNewsFeed(fetchedData.news_feed); } if (fetchedData.dashboard) { homeDashboard = fetchedData.dashboard; } } if (currentNewFeed.length > 0) { return [ { title: i18n.t('screens.home.feedTitle'), data: currentNewFeed, id: SECTIONS_ID[1], }, ]; } return [ { title: isLoading ? i18n.t('screens.home.feedLoading') : i18n.t('screens.home.feedError'), data: [], icon: isLoading ? undefined : 'access-point-network-off', id: SECTIONS_ID[1], }, ]; }; const onEventContainerClick = () => navigation.navigate(TabRoutes.Planning); const onScroll = (event: NativeSyntheticEvent) => { if (fabRef.current) { 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. */ const onLogin = () => navigation.navigate(MainRoutes.Login, { nextScreen: 'profile', }); return ( readData(Urls.app.dashboard)} createDataset={createDataset} autoRefreshTime={REFRESH_TIME} refreshOnFocus={true} renderItem={getRenderItem} itemHeight={FEED_ITEM_HEIGHT} onScroll={onScroll} renderSectionHeader={getRenderSectionHeader} renderListHeaderComponent={getListHeader} /> {!isLoggedIn ? ( ) : null} ); } export default HomeScreen;