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
+ }
+
+