diff --git a/src/components/Home/DashboardShortcuts.tsx b/src/components/Home/DashboardShortcuts.tsx new file mode 100644 index 00000000..abd2b511 --- /dev/null +++ b/src/components/Home/DashboardShortcuts.tsx @@ -0,0 +1,147 @@ +/* + * 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 * as React from 'react'; +import { FlatList, StyleSheet } from 'react-native'; +import type { PlanningEventType } from '../../utils/Planning'; +import SmallDashboardItem from './SmallDashboardItem'; +import DashboardItem from './EventDashboardItem'; +import PreviewEventDashboardItem from './PreviewEventDashboardItem'; +import type { ServiceItemType } from '../../utils/Services'; +import { readData } from '../../utils/WebData'; +import Urls from '../../constants/Urls'; +import { getDisplayEvent, getFutureEvents } from '../../utils/Home'; +import { useNavigation } from '@react-navigation/native'; +import { TabRoutes } from '../../navigation/TabNavigator'; + +export type FullDashboardType = { + today_menu: Array<{ [key: string]: object }>; + available_dryers: number; + available_washers: number; + today_events: Array; + available_tutorials: number; + latest_notification: number; +}; + +export type RawDashboardType = { + dashboard: FullDashboardType; +}; + +type Props = { + services: Array; + style?: object; +}; + +const styles = StyleSheet.create({ + dashboardRow: { + marginLeft: 'auto', + marginRight: 'auto', + marginTop: 10, + marginBottom: 10, + }, +}); + +/** + * Shortcut shown on Home Screen. + */ +const DashboardShortcutItem = ({ + item, + dashboardData, +}: { + item: ServiceItemType | undefined; + dashboardData: FullDashboardType | null; +}) => { + if (item != null) { + return ( + + ); + } + return ; +}; + +/** + * Home screen dashboard shortcuts row. + * Fetches and updates badge count for the number of available washing machines... + */ +function DashboardShortcuts({ services, style }: Props) { + const [dashboardData, setDashboardData] = + React.useState(null); + const navigation = useNavigation(); + + React.useEffect(() => { + const fetchDashboardData = async () => { + try { + const data = await readData(Urls.app.dashboard); + if (data?.dashboard) { + setDashboardData(data.dashboard); + } + } catch (error) { + console.error('Failed to fetch dashboard data:', error); + } + }; + + fetchDashboardData(); + }, []); + + const renderItem = ({ item }: { item: ServiceItemType | undefined }) => ( + + ); + + const onEventContainerClick = () => navigation.navigate(TabRoutes.Planning); + + const getDashboardEvent = (content: Array) => { + const futureEvents = getFutureEvents(content); + const displayEvent = getDisplayEvent(futureEvents); + return ( + + + + ); + }; + + return ( + <> + + {dashboardData != null + ? getDashboardEvent(dashboardData.today_events) + : null} + + ); +} + +export default DashboardShortcuts; diff --git a/src/components/Home/Feed.tsx b/src/components/Home/Feed.tsx new file mode 100644 index 00000000..096eec14 --- /dev/null +++ b/src/components/Home/Feed.tsx @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2019 - 2021 Arnaud Vergnet. + * Copyright (c) 2021 - 2026 Paul ALNET. + * + * 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 from 'react'; +import { SectionListData, StyleSheet } from 'react-native'; +import i18n from 'i18n-js'; +import { Headline, useTheme } from 'react-native-paper'; +import { View } from 'react-native-animatable'; +import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; +import WebSectionList from '../../components/Screens/WebSectionList'; +import FeedItem from '../../components/Home/FeedItem'; +import GENERAL_STYLES from '../../constants/Styles'; +import { useAuthenticatedRequest } from '../../context/loginContext'; + +const REFRESH_TIME = 1000 * 20; // Refresh every 20 seconds + +export type FeedPostType = { + id: number; + clubId: number; + clubName: string; + clubLogo: string; // URL + date: number; // Unix timestamp + title: string; + content: string; // HTML + image: string | null; // URL +}; + +type FeedResponseType = { + posts: Array; + next: string; // URL to next page + canReact: boolean; // Can the user react to the posts +}; + +const styles = StyleSheet.create({ + sectionHeader: { + textAlign: 'center', + marginTop: 50, + marginBottom: 10, + }, + sectionHeaderEmpty: { + textAlign: 'center', + marginTop: 50, + marginBottom: 10, + marginLeft: 20, + marginRight: 20, + }, +}); + +function Feed() { + const theme = useTheme(); + + const request = useAuthenticatedRequest('feed'); + + const getRenderItem = ({ item }: { item: FeedPostType }) => ( + + ); + + const getRenderSectionHeader = (data: { + section: SectionListData; + }) => { + const icon = data.section.icon; + if (data.section.data.length > 0) return; + + return ( + + + {data.section.title} + + {icon ? ( + + ) : null} + + ); + }; + + /** + * Creates the dataset to be used in the FlatList + */ + const createDataset = ( + fetchedData: FeedResponseType | undefined, + isLoading: boolean + ): Array<{ + title: string; + data: [] | Array; + icon?: string; + id: string; + }> => { + let currentNewFeed: Array = []; + if (fetchedData && fetchedData.posts) { + currentNewFeed = fetchedData.posts; + } + if (currentNewFeed.length > 0) { + return [ + { + title: i18n.t('screens.home.feedTitle'), + data: currentNewFeed, + id: 'feed', + }, + ]; + } + return [ + { + title: isLoading + ? i18n.t('screens.home.feedLoading') + : i18n.t('screens.home.feedError'), + data: [], + icon: isLoading ? undefined : 'access-point-network-off', + id: 'feed', + }, + ]; + }; + + return ( + + ); +} + +export default Feed; diff --git a/src/components/Home/FeedItem.tsx b/src/components/Home/FeedItem.tsx index 2ba00ab5..d66bc3f7 100644 --- a/src/components/Home/FeedItem.tsx +++ b/src/components/Home/FeedItem.tsx @@ -18,23 +18,19 @@ */ import * as React from 'react'; -import { Button, Card, Text, TouchableRipple } from 'react-native-paper'; -import { Image, StyleSheet, View } from 'react-native'; -import Autolink from 'react-native-autolink'; +import { Button, Card, TouchableRipple } from 'react-native-paper'; +import { StyleSheet, View } from 'react-native'; import i18n from 'i18n-js'; -import type { FeedItemType } from '../../screens/Home/HomeScreen'; -import NewsSourcesConstants, { - AvailablePages, -} from '../../constants/NewsSourcesConstants'; -import type { NewsSourceType } from '../../constants/NewsSourcesConstants'; +import type { FeedPostType } from '../../components/Home/Feed'; import ImageGalleryButton from '../Media/ImageGalleryButton'; import { useNavigation } from '@react-navigation/native'; import GENERAL_STYLES from '../../constants/Styles'; import { MainRoutes } from '../../navigation/MainNavigator'; +import { Avatar } from 'react-native-paper'; +import CustomHTML from '../Overrides/CustomHTML'; type PropsType = { - item: FeedItemType; - height: number; + item: FeedPostType; }; /** @@ -60,6 +56,11 @@ const styles = StyleSheet.create({ action: { marginLeft: 'auto', }, + avatar: { + backgroundColor: 'transparent', + marginLeft: 10, + marginRight: 10, + }, }); /** @@ -70,61 +71,52 @@ function FeedItem(props: PropsType) { const onPress = () => { navigation.navigate(MainRoutes.FeedInformation, { data: item, - date: getFormattedDate(props.item.time), + date: getFormattedDate(props.item.date), }); }; - const { item, height } = props; + const { item } = props; const image = item.image !== '' && item.image != null ? item.image : null; - const pageSource: NewsSourceType = - NewsSourcesConstants[item.page_id as AvailablePages]; const cardMargin = 10; - const cardHeight = height - 2 * cardMargin; const imageSize = 250; const titleHeight = 80; const actionsHeight = 60; - const textHeight = - image != null - ? cardHeight - titleHeight - actionsHeight - imageSize - : cardHeight - titleHeight - actionsHeight; return ( } + title={item.title} + subtitle={getFormattedDate(item.date)} + left={() => ( + + )} style={{ height: titleHeight }} /> - {image != null ? ( - - ) : null} - - {item.message !== undefined ? ( - - ) : null} + ) : null + } + +